module Whatsapp::BaileysHandlers::MessagesUpdate include Whatsapp::BaileysHandlers::Helpers class MessageNotFoundError < StandardError; end private def process_messages_update updates = processed_params[:data] updates.each do |update| @message = nil @raw_message = update next handle_update if incoming? # NOTE: Shared lock with Whatsapp::SendOnWhatsappService # Avoids race conditions when sending messages. with_baileys_channel_lock_on_outgoing_message(inbox.channel.id) { handle_update } end end def handle_update raise MessageNotFoundError unless find_message_by_source_id(raw_message_id) update_status if @raw_message.dig(:update, :status).present? handle_edited_content if @raw_message.dig(:update, :message).present? end def update_status status = status_mapper update_last_seen_at if incoming? && status == 'read' @message.update!(status: status) if status.present? && status_transition_allowed?(status) end def status_mapper # NOTE: Baileys status codes vs. Chatwoot support: # - (0) ERROR → (3) failed # - (1) PENDING → (0) sent # - (2) SERVER_ACK → (0) sent # - (3) DELIVERY_ACK → (1) delivered # - (4) READ → (2) read # - (5) PLAYED → (unsupported: PLAYED) # For details: https://github.com/WhiskeySockets/Baileys/blob/v6.7.16/WAProto/index.d.ts#L36694 status = @raw_message.dig(:update, :status) case status when 0 'failed' when 1, 2 'sent' when 3 'delivered' when 4 'read' when 5 Rails.logger.warn 'Baileys unsupported message update status: PLAYED(5)' nil else Rails.logger.warn "Baileys unsupported message update status: #{status}" nil end end def update_last_seen_at conversation = @message.conversation to_update = { agent_last_seen_at: Time.current } to_update[:assignee_last_seen_at] = Time.current if conversation.assignee_id.present? conversation.update_columns(to_update) # rubocop:disable Rails/SkipsModelValidations end def status_transition_allowed?(new_status) return false if @message.status == 'read' return false if @message.status == 'delivered' && new_status == 'sent' true end def handle_edited_content @raw_message = @raw_message.dig(:update, :message, :editedMessage) content = message_content return @message.update!(content: content, is_edited: true, previous_content: @message.content) if content Rails.logger.warn 'No valid message content found in the edit event' end end