# frozen_string_literal: true module ChatSDK class Message # Creates a new message in a chat channel. # # @param raw [String] The content of the message. # @param channel_id [Integer] The ID of the chat channel. # @param guardian [Guardian] The user's guardian object, for policy enforcement. # @param in_reply_to_id [Integer, nil] The ID of the message this is in reply to (optional). # @param thread_id [Integer, nil] The ID of the thread this message belongs to (optional). # @param upload_ids [Array, nil] The IDs of any uploads associated with the message (optional). # @param streaming [Boolean] Whether the message is part of a streaming operation (default: false). # @param enforce_membership [Boolean] Allows to ensure the guardian will be allowed in the channel (default: false). # @yield [helper, message] Offers a block with a helper and the message for streaming operations. # @yieldparam helper [Helper] The helper object for streaming operations. # @yieldparam message [Message] The newly created message object. # @return [Chat::Message] The created message object. # # @example Creating a simple message # ChatSDK::Message.create(raw: "Hello, world!", channel_id: 1, guardian: Guardian.new) # # @example Creating a message with a block for streaming # ChatSDK::Message.create_with_stream(raw: "Streaming message", channel_id: 1, guardian: Guardian.new) do |helper, message| # helper.stream(raw: "Continuation of the message") # end def self.create(...) new.create(...) end # Creates a new message with streaming enabled by default. # # This method is a convenience wrapper around `create` with `streaming: true` set by default. # It supports all the same parameters and block usage as `create`. # # @see #create def self.create_with_stream(**params, &block) self.create(**params, streaming: true, strip_whitespaces: false, &block) end # Streams to a specific chat message. # # @param raw [String] text to append to the existing message. # @param message_id [Integer] the ID of the message to stream. # @param guardian [Guardian] an instance of the guardian class, representing the user's permissions. # @return [Chat::Message] The message object. # @example Streaming a message # ChatSDK::Message.stream(message_id: 42, guardian: guardian, raw: "text") def self.stream(...) new.stream(...) end # Starts streaming for a specific chat message. # # @param message_id [Integer] the ID of the message for which streaming should be stopped. # @param guardian [Guardian] an instance of the guardian class, representing the user's permissions. # @return [Chat::Message] The message object. # @example Starting the streaming of a message # ChatSDK::Message.start_stream(message_id: 42, guardian: guardian) def self.start_stream(...) new.start_stream(...) end # Stops streaming for a specific chat message. # # @param message_id [Integer] the ID of the message for which streaming should be stopped. # @param guardian [Guardian] an instance of the guardian class, representing the user's permissions. # @return [Chat::Message] The message object. # @example Stopping the streaming of a message # ChatSDK::Message.stop_stream(message_id: 42, guardian: guardian) def self.stop_stream(...) new.stop_stream(...) end def start_stream(message_id:, guardian:) message = Chat::Message.find(message_id) guardian.ensure_can_edit_chat!(message) message.update!(streaming: true) ::Chat::Publisher.publish_edit!(message.chat_channel, message.reload) message end def stream(message_id:, raw:, guardian:, &block) message = Chat::Message.find(message_id) helper = StreamHelper.new(message, guardian) helper.stream(raw: raw) ::Chat::Publisher.publish_edit!(message.chat_channel, message.reload) message end def stop_stream(message_id:, guardian:) Chat::StopMessageStreaming.call(params: { message_id: }, guardian:) do on_success { |message:| message } on_model_not_found(:message) { raise "Couldn't find message with id: `#{message_id}`" } on_model_not_found(:membership) do raise "Couldn't find membership for user with id: `#{guardian.user.id}`" end on_failed_policy(:can_join_channel) do raise "User with id: `#{guardian.user.id}` can't join this channel" end on_failed_policy(:can_stop_streaming) do raise "User with id: `#{guardian.user.id}` can't stop streaming this message" end on_failure { raise "Unexpected error" } end end def create( raw:, channel_id:, guardian:, in_reply_to_id: nil, thread_id: nil, upload_ids: nil, streaming: false, enforce_membership: false, force_thread: false, strip_whitespaces: true, blocks: nil, **params, &block ) message = Chat::CreateMessage.call( params: { message: raw, chat_channel_id: channel_id, in_reply_to_id:, thread_id:, upload_ids:, blocks:, **params, }, options: { created_by_sdk: true, streaming:, enforce_membership:, force_thread:, strip_whitespaces:, }, guardian:, ) do on_model_not_found(:channel) { raise "Couldn't find channel with id: `#{channel_id}`" } on_model_not_found(:membership) do raise "Couldn't find membership for user with id: `#{guardian.user.id}`" end on_failed_policy(:ensure_valid_thread_for_channel) do raise "Couldn't find thread with id: `#{thread_id}`" end on_failed_policy(:accept_blocks) { raise "Only bots can create messages with blocks" } on_failed_policy(:allowed_to_join_channel) do raise "User with id: `#{guardian.user.id}` can't join this channel" end on_failed_contract { |contract| raise contract.errors.full_messages.join(", ") } on_success { |message_instance:| message_instance } on_failure { raise "Unexpected error" } end if streaming && block_given? helper = StreamHelper.new(message, guardian) block.call(helper, message) end message ensure if message && streaming message.update!(streaming: false) ::Chat::Publisher.publish_edit!(message.chat_channel, message.reload) end end end class StreamHelper attr_reader :message attr_reader :guardian def initialize(message, guardian) @message = message.reload @guardian = guardian end def stream(raw: nil) return false if !message.streaming || !raw Chat::UpdateMessage.call( guardian: guardian, params: { message_id: message.id, message: message.message + raw, }, options: { strip_whitespaces: false, }, ) { on_failure { raise "Unexpected error" } } message end end end