Add user ID support for Baileys 7 LIDs and Signal UUIDs

Baileys 7 uses LIDs (Linked IDs) instead of phone numbers in remoteJid for
some messages. This caused messages to be matched to wrong tickets because
the LID was used as the sender identifier. This commit adds proper support
for both phone numbers and user IDs across WhatsApp and Signal channels.

Changes:

Database:
- Add migration for whatsapp_user_id and signal_user_id fields on users table

Zammad controllers:
- Update user lookup with 3-step fallback: phone → dedicated user_id field →
  user_id in phone field (legacy)
- Store user IDs in dedicated fields when available
- Update phone field when we receive actual phone number for legacy records
- Fix redundant condition in Signal controller

Bridge services:
- Extract both phone (from senderPn/participantPn) and LID (from remoteJid)
- Send both identifiers to Zammad via webhooks
- Use camelCase (userId) in bridge-whatsapp, convert to snake_case (user_id)
  in bridge-worker for Zammad compatibility

Baileys 7 compliance:
- Remove broken loadAllUnreadMessages() call (removed in Baileys 7)
- Return descriptive error directing users to use webhooks instead

Misc:
- Add docs/ to .gitignore
This commit is contained in:
Darren Clarke 2026-01-15 10:01:15 +01:00
parent 57d7173485
commit 3d8f794cab
8 changed files with 154 additions and 36 deletions

View file

@ -154,16 +154,31 @@ class ChannelsCdrSignalController < ApplicationController
return if Ticket::Article.exists?(message_id: "cdr_signal.#{message_id}")
receiver_phone_number = params[:to].strip
sender_phone_number = params[:from].strip
sender_phone_number = params[:from].present? ? params[:from].strip : nil
sender_user_id = params[:user_id].present? ? params[:user_id].strip : nil
# Check if this is a group message using the is_group flag from bridge-worker
# This flag is set when:
# 1. The original message came from a Signal group
# 2. Bridge-worker created a new group for the conversation
is_group_message = params[:is_group].to_s == 'true' || params[:is_group].to_s == 'true'
is_group_message = params[:is_group].to_s == 'true'
# Lookup customer with fallback chain:
# 1. Phone number in phone/mobile fields (preferred)
# 2. Signal user ID in signal_user_id field
# 3. User ID in phone/mobile fields (legacy - we used to store UUIDs there)
customer = nil
if sender_phone_number.present?
customer = User.find_by(phone: sender_phone_number)
customer ||= User.find_by(mobile: sender_phone_number)
end
if customer.nil? && sender_user_id.present?
customer = User.find_by(signal_user_id: sender_user_id)
# Legacy fallback: user ID might be stored in phone field
customer ||= User.find_by(phone: sender_user_id)
customer ||= User.find_by(mobile: sender_user_id)
end
customer = User.find_by(phone: sender_phone_number)
customer ||= User.find_by(mobile: sender_phone_number)
unless customer
role_ids = Role.signup_role_ids
customer = User.create(
@ -171,7 +186,8 @@ class ChannelsCdrSignalController < ApplicationController
lastname: '',
email: '',
password: '',
phone: sender_phone_number,
phone: sender_phone_number.presence || sender_user_id,
signal_user_id: sender_user_id,
note: 'CDR Signal',
active: true,
role_ids: role_ids,
@ -180,6 +196,15 @@ class ChannelsCdrSignalController < ApplicationController
)
end
# Update signal_user_id if we have it and customer doesn't
if sender_user_id.present? && customer.signal_user_id.blank?
customer.update(signal_user_id: sender_user_id)
end
# Update phone if we have it and customer only has user_id in phone field
if sender_phone_number.present? && customer.phone == sender_user_id
customer.update(phone: sender_phone_number)
end
# set current user
UserInfo.current_user_id = customer.id
current_user_set(customer, 'token_auth')
@ -208,7 +233,8 @@ class ChannelsCdrSignalController < ApplicationController
attachment_data_base64 = params[:attachment]
attachment_filename = params[:filename]
attachment_mimetype = params[:mime_type]
title = "Message from #{sender_phone_number} at #{sent_at}"
sender_display = sender_phone_number.presence || sender_user_id
title = "Message from #{sender_display} at #{sent_at}"
body = message
# find ticket or create one
@ -218,7 +244,7 @@ class ChannelsCdrSignalController < ApplicationController
Rails.logger.info "=== SIGNAL GROUP TICKET LOOKUP ==="
Rails.logger.info "Looking for ticket with group_id: #{receiver_phone_number}"
Rails.logger.info "Customer ID: #{customer.id}"
Rails.logger.info "Customer Phone: #{sender_phone_number}"
Rails.logger.info "Customer Phone: #{sender_display}"
Rails.logger.info "Channel ID: #{channel.id}"
begin
@ -256,12 +282,13 @@ class ChannelsCdrSignalController < ApplicationController
ticket.state = Ticket::State.find_by(default_follow_up: true) if ticket.state_id != new_state.id
else
# Set up chat_id based on whether this is a group message
chat_id = is_group_message ? receiver_phone_number : sender_phone_number
chat_id = is_group_message ? receiver_phone_number : (sender_phone_number.presence || sender_user_id)
# Build preferences with group_id included if needed
cdr_signal_prefs = {
bot_token: channel.options[:bot_token], # change to bot id
chat_id: chat_id
bot_token: channel.options[:bot_token],
chat_id: chat_id,
user_id: sender_user_id
}
Rails.logger.info "=== CREATING NEW TICKET ==="
@ -283,7 +310,7 @@ class ChannelsCdrSignalController < ApplicationController
ticket.save!
article_params = {
from: sender_phone_number,
from: sender_display,
to: receiver_phone_number,
sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id,
subject: title,
@ -296,7 +323,8 @@ class ChannelsCdrSignalController < ApplicationController
cdr_signal: {
timestamp: sent_at,
message_id: message_id,
from: sender_phone_number
from: sender_phone_number,
user_id: sender_user_id
}
}
}