* feat(whatsapp): show contact typing and recording indicators via baileys presence Subscribe to WhatsApp presence updates via the baileys-api provider to display real-time typing and recording indicators in the dashboard. - Handle presence.update webhook events (composing, recording, paused, available) and broadcast via ActionCable - Add conversation.recording event to ActionCable, webhook, and channel listeners for parity with typing_on/typing_off - Show "typing..." / "recording..." in green text on the chat list, replacing the message preview - Show "X is typing" / "X is recording audio" in the conversation view - Add presence_subscribe provider config option (default off) to gate all subscription calls to the baileys-api - Subscribe to presence on conversation open and periodically (1 min) for the top 10 chat list conversations - Consolidate contact LID from presence.update jidAlt payload - Prevent echo-back of contact typing events to the channel Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback - Filter chat list typing indicator to contact-only events - Add dedupe to presence subscribe bulk calls - Use strong parameters for conversation_ids - Remove redundant YAML quotes in swagger webhook enum Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback - Extract phone from data[:id] when JID is @s.whatsapp.net (fallback when jidAlt is absent) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback - Filter recording users in getTypingUsersText to show correct names - Add 10s timeout to presence_subscribe HTTP request Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: scope typing timer per user instead of per conversation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: make presence subscribe best-effort with rescue per channel Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address review feedback - Add messagePreviewClass to typing preview for consistent padding - Fix specs to use WebMock assertions instead of instance spying Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
47 lines
1.2 KiB
Ruby
47 lines
1.2 KiB
Ruby
class Conversations::PresenceSubscribeService
|
|
def initialize(account, conversation_ids)
|
|
@account = account
|
|
@conversation_ids = Array(conversation_ids).first(10)
|
|
end
|
|
|
|
def perform
|
|
return if @conversation_ids.blank?
|
|
|
|
jids_by_channel = collect_jids
|
|
jids_by_channel.each do |channel, jids|
|
|
channel.presence_subscribe(jids)
|
|
rescue StandardError => e
|
|
Rails.logger.error "PresenceSubscribeService: failed for channel #{channel.id}: #{e.message}"
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def collect_jids
|
|
conversations = @account.conversations
|
|
.where(display_id: @conversation_ids)
|
|
.includes(inbox: :channel, contact: [])
|
|
result = {}
|
|
conversations.each do |conv|
|
|
channel = conv.inbox.channel
|
|
next unless channel.is_a?(Channel::Whatsapp) && channel.provider_config&.dig('presence_subscribe')
|
|
|
|
jid = contact_jid(conv.contact)
|
|
next if jid.blank?
|
|
|
|
(result[channel] ||= []) << jid
|
|
end
|
|
result
|
|
end
|
|
|
|
def contact_jid(contact)
|
|
contact.identifier.presence || phone_jid(contact.phone_number)
|
|
end
|
|
|
|
def phone_jid(phone_number)
|
|
return if phone_number.blank?
|
|
|
|
"#{phone_number.delete('+')}@s.whatsapp.net"
|
|
end
|
|
end
|