mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-03-12 05:05:16 +09:00
* Bump Ruby to 3.4.5
* Update dependencies
* Adjust for Rails 8 and Ruby 3.5 API changes
* Mark params strings as mutable in prepapration for frozen strings in Ruby 3.5
* Update test for HTML5 sanitizer
With Rails 7.1 the HTML5 sanitizer became the default, this breakts this test because the old sanitizer used to delete unpermitted nodes, while the new one returns their content
The final string is safe, but different then it used to be in Rails 7.0
* Remove direct Turbo tesh helpers require & parallelize tests
* Fix Zeitwerk issues with rails extensions
* Update Resque setup for Redis 5+
* Remove unused views
* Remove GID v1 handler
80 lines
2.7 KiB
Ruby
80 lines
2.7 KiB
Ruby
require "net/http"
|
|
require "uri"
|
|
|
|
class Webhook < ApplicationRecord
|
|
ENDPOINT_TIMEOUT = 7.seconds
|
|
|
|
belongs_to :user
|
|
|
|
def deliver(message)
|
|
post(payload(message)).tap do |response|
|
|
if text = extract_text_from(response)
|
|
receive_text_reply_to(message.room, text: text)
|
|
elsif attachment = extract_attachment_from(response)
|
|
receive_attachment_reply_to(message.room, attachment: attachment)
|
|
end
|
|
end
|
|
rescue Net::OpenTimeout, Net::ReadTimeout
|
|
receive_text_reply_to message.room, text: "Failed to respond within #{ENDPOINT_TIMEOUT} seconds"
|
|
end
|
|
|
|
private
|
|
def post(payload)
|
|
http.request \
|
|
Net::HTTP::Post.new(uri, "Content-Type" => "application/json").tap { |request| request.body = payload }
|
|
end
|
|
|
|
def http
|
|
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
|
http.use_ssl = (uri.scheme == "https")
|
|
http.open_timeout = ENDPOINT_TIMEOUT
|
|
http.read_timeout = ENDPOINT_TIMEOUT
|
|
end
|
|
end
|
|
|
|
def uri
|
|
@uri ||= URI(url)
|
|
end
|
|
|
|
def payload(message)
|
|
{
|
|
user: { id: message.creator.id, name: message.creator.name },
|
|
room: { id: message.room.id, name: message.room.name, path: room_bot_messages_path(message) },
|
|
message: { id: message.id, body: { html: message.body.body, plain: without_recipient_mentions(message.plain_text_body) }, path: message_path(message) }
|
|
}.to_json
|
|
end
|
|
|
|
def message_path(message)
|
|
Rails.application.routes.url_helpers.room_at_message_path(message.room, message)
|
|
end
|
|
|
|
def room_bot_messages_path(message)
|
|
Rails.application.routes.url_helpers.room_bot_messages_path(message.room, user.bot_key)
|
|
end
|
|
|
|
def extract_text_from(response)
|
|
response.body.dup.force_encoding("UTF-8") if response.code == "200" && response.content_type.in?(%w[ text/html text/plain ])
|
|
end
|
|
|
|
def receive_text_reply_to(room, text:)
|
|
room.messages.create!(body: text, creator: user).broadcast_create
|
|
end
|
|
|
|
def extract_attachment_from(response)
|
|
if response.content_type && mime_type = Mime::Type.lookup(response.content_type)
|
|
ActiveStorage::Blob.create_and_upload! \
|
|
io: StringIO.new(response.body), filename: "attachment.#{mime_type.symbol}", content_type: mime_type.to_s
|
|
end
|
|
end
|
|
|
|
def receive_attachment_reply_to(room, attachment:)
|
|
room.messages.create_with_attachment!(attachment: attachment, creator: user).broadcast_create
|
|
end
|
|
|
|
def without_recipient_mentions(body)
|
|
body \
|
|
.gsub(user.attachable_plain_text_representation(nil), "") # Remove mentions of the recipient user
|
|
.gsub(/\A\p{Space}+|\p{Space}+\z/, "") # Remove leading and trailing whitespace uncluding unicode spaces
|
|
end
|
|
end
|