diff --git a/apps/bridge-worker/scripts/test-zammad-group-webhook.ts b/apps/bridge-worker/scripts/test-zammad-group-webhook.ts new file mode 100755 index 0000000..9afa720 --- /dev/null +++ b/apps/bridge-worker/scripts/test-zammad-group-webhook.ts @@ -0,0 +1,62 @@ +#!/usr/bin/env tsx +/** + * Test script to verify Zammad group webhook integration + * + * This tests the group_created event webhook that is sent from bridge-worker + * to Zammad when a Signal group is created. + */ + +// Using native fetch which is available in Node.js 18+ + +const ZAMMAD_URL = process.env.ZAMMAD_URL || 'http://localhost:8001'; +const CHANNEL_TOKEN = process.env.CHANNEL_TOKEN || 'test-token'; + +async function testGroupWebhook() { + console.log('Testing Zammad Signal group webhook...\n'); + + const webhookUrl = `${ZAMMAD_URL}/api/v1/channels_cdr_signal_webhook/${CHANNEL_TOKEN}`; + + const payload = { + event: 'group_created', + conversation_id: '12345', // This would be a real ticket number + original_recipient: '+1234567890', + group_id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890', + timestamp: new Date().toISOString() + }; + + console.log('Webhook URL:', webhookUrl); + console.log('Payload:', JSON.stringify(payload, null, 2)); + console.log('\nSending request...\n'); + + try { + const response = await fetch(webhookUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + + const responseText = await response.text(); + console.log('Response Status:', response.status); + console.log('Response Headers:', [...response.headers.entries()]); + console.log('Response Body:', responseText); + + if (response.ok) { + console.log('\n✅ Webhook test successful!'); + try { + const data = JSON.parse(responseText); + console.log('Parsed response:', data); + } catch (e) { + // Response might not be JSON + } + } else { + console.log('\n❌ Webhook test failed!'); + } + } catch (error) { + console.error('\n❌ Error testing webhook:', error); + } +} + +// Run the test +testGroupWebhook(); \ No newline at end of file diff --git a/docker-compose.bridge-dev.yml b/docker-compose.bridge-dev.yml new file mode 100644 index 0000000..33850b4 --- /dev/null +++ b/docker-compose.bridge-dev.yml @@ -0,0 +1,67 @@ +version: '3.8' + +services: + zammad-railsserver: + volumes: + # Controllers + - ${PWD}/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb:/opt/zammad/app/controllers/channels_cdr_signal_controller.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_voice_controller.rb:/opt/zammad/app/controllers/channels_cdr_voice_controller.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb:/opt/zammad/app/controllers/channels_cdr_whatsapp_controller.rb:ro + + # Models + - ${PWD}/packages/zammad-addon-bridge/src/app/models/channel/driver/cdr_signal.rb:/opt/zammad/app/models/channel/driver/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/channel/driver/cdr_whatsapp.rb:/opt/zammad/app/models/channel/driver/cdr_whatsapp.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/ticket/article/enqueue_communicate_cdr_signal_job.rb:/opt/zammad/app/models/ticket/article/enqueue_communicate_cdr_signal_job.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/ticket/article/enqueue_communicate_cdr_whatsapp_job.rb:/opt/zammad/app/models/ticket/article/enqueue_communicate_cdr_whatsapp_job.rb:ro + + # Jobs + - ${PWD}/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb:/opt/zammad/app/jobs/communicate_cdr_signal_job.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb:/opt/zammad/app/jobs/communicate_cdr_whatsapp_job.rb:ro + + # Policies + - ${PWD}/packages/zammad-addon-bridge/src/app/policies/controllers/channels_cdr_signal_controller_policy.rb:/opt/zammad/app/policies/controllers/channels_cdr_signal_controller_policy.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/policies/controllers/channels_cdr_voice_controller_policy.rb:/opt/zammad/app/policies/controllers/channels_cdr_voice_controller_policy.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/policies/controllers/channels_cdr_whatsapp_controller_policy.rb:/opt/zammad/app/policies/controllers/channels_cdr_whatsapp_controller_policy.rb:ro + + # Config - initializers + - ${PWD}/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb:/opt/zammad/config/initializers/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/config/initializers/cdr_whatsapp.rb:/opt/zammad/config/initializers/cdr_whatsapp.rb:ro + + # Config - routes + - ${PWD}/packages/zammad-addon-bridge/src/config/routes/channel_cdr_signal.rb:/opt/zammad/config/routes/channel_cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/config/routes/channel_cdr_voice.rb:/opt/zammad/config/routes/channel_cdr_voice.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/config/routes/channel_cdr_whatsapp.rb:/opt/zammad/config/routes/channel_cdr_whatsapp.rb:ro + + # Database migrations + - ${PWD}/packages/zammad-addon-bridge/src/db/addon/bridge/20210525091356_cdr_signal_channel.rb:/opt/zammad/db/addon/bridge/20210525091356_cdr_signal_channel.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/db/addon/bridge/20210525091357_cdr_voice_channel.rb:/opt/zammad/db/addon/bridge/20210525091357_cdr_voice_channel.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/db/addon/bridge/20210525091358_cdr_whatsapp_channel.rb:/opt/zammad/db/addon/bridge/20210525091358_cdr_whatsapp_channel.rb:ro + + # Lib files + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_signal.rb:/opt/zammad/lib/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb:/opt/zammad/lib/cdr_signal_api.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb:/opt/zammad/lib/cdr_whatsapp.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_whatsapp_api.rb:/opt/zammad/lib/cdr_whatsapp_api.rb:ro + + # Also map to scheduler for background jobs + zammad-scheduler: + volumes: + # Models + - ${PWD}/packages/zammad-addon-bridge/src/app/models/channel/driver/cdr_signal.rb:/opt/zammad/app/models/channel/driver/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/channel/driver/cdr_whatsapp.rb:/opt/zammad/app/models/channel/driver/cdr_whatsapp.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/ticket/article/enqueue_communicate_cdr_signal_job.rb:/opt/zammad/app/models/ticket/article/enqueue_communicate_cdr_signal_job.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/models/ticket/article/enqueue_communicate_cdr_whatsapp_job.rb:/opt/zammad/app/models/ticket/article/enqueue_communicate_cdr_whatsapp_job.rb:ro + + # Jobs + - ${PWD}/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb:/opt/zammad/app/jobs/communicate_cdr_signal_job.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb:/opt/zammad/app/jobs/communicate_cdr_whatsapp_job.rb:ro + + # Config - initializers + - ${PWD}/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb:/opt/zammad/config/initializers/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/config/initializers/cdr_whatsapp.rb:/opt/zammad/config/initializers/cdr_whatsapp.rb:ro + + # Lib files + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_signal.rb:/opt/zammad/lib/cdr_signal.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb:/opt/zammad/lib/cdr_signal_api.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb:/opt/zammad/lib/cdr_whatsapp.rb:ro + - ${PWD}/packages/zammad-addon-bridge/src/lib/cdr_whatsapp_api.rb:/opt/zammad/lib/cdr_whatsapp_api.rb:ro \ No newline at end of file diff --git a/docker/compose/bridge.yml b/docker/compose/bridge.yml index 83d948e..e76ae96 100644 --- a/docker/compose/bridge.yml +++ b/docker/compose/bridge.yml @@ -20,6 +20,7 @@ x-bridge-vars: &common-bridge-variables NEXTAUTH_URL: ${BRIDGE_DOMAIN} NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} BRIDGE_SIGNAL_URL: ${BRIDGE_SIGNAL_URL} + BRIDGE_SIGNAL_AUTO_GROUPS: ${BRIDGE_SIGNAL_AUTO_GROUPS} services: bridge-frontend: diff --git a/docker/scripts/docker.js b/docker/scripts/docker.js index c9b8b0e..33d5da3 100644 --- a/docker/scripts/docker.js +++ b/docker/scripts/docker.js @@ -1,4 +1,5 @@ const { spawn } = require("child_process"); +const path = require('path'); const app = process.argv[2]; const command = process.argv[3]; @@ -16,10 +17,19 @@ const files = { zammad: ["zammad", "postgresql", "opensearch"], }; + +const envFile = path.resolve(process.cwd(), '.env'); const finalFiles = files[app] .map((file) => ['-f', `docker/compose/${file}.yml`]).flat(); + +// Add bridge-dev.yml for dev commands that include zammad +const devAppsWithZammad = ['linkDev', 'bridgeDev', 'all']; +if (devAppsWithZammad.includes(app) && files[app].includes('zammad')) { + finalFiles.push('-f', 'docker-compose.bridge-dev.yml'); +} + const finalCommand = command === "up" ? ["up", "-d", "--remove-orphans"] : [command]; -const dockerCompose = spawn('docker', ['compose', '--env-file', '.env', ...finalFiles, ...finalCommand]); +const dockerCompose = spawn('docker', ['compose', '--env-file', envFile, ...finalFiles, ...finalCommand]); dockerCompose.stdout.on('data', (data) => { console.info(`${data}`); diff --git a/docker/zammad/install.rb b/docker/zammad/install.rb index bd9ffa0..179b98d 100644 --- a/docker/zammad/install.rb +++ b/docker/zammad/install.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'json' +require 'base64' + ROOT = '/opt/zammad' def _read_file(file, fullpath: false) diff --git a/package.json b/package.json index 0b08c54..33c7126 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "docker:bridge:dev:down": "node docker/scripts/docker.js bridgeDev down", "docker:bridge:up": "node docker/scripts/docker.js bridge up", "docker:bridge:down": "node docker/scripts/docker.js bridge down", - "docker:bridge:build": "node docker/scripts/docker.js bridge build" + "docker:bridge:build": "node docker/scripts/docker.js bridge build", + "docker:zammad:restart": "docker restart zammad-railsserver zammad-scheduler" }, "workspaces": [ "apps/*", diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee index a4deac2..f8f8028 100644 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee +++ b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee @@ -1,4 +1,4 @@ -class Index extends App.ControllerSubContent +class ChannelCdrSignal extends App.ControllerSubContent requiredPermission: 'admin.channel_cdr_signal' events: 'click .js-new': 'new' @@ -246,4 +246,4 @@ class FormEdit extends App.ControllerModal @el.find('.alert').removeClass('hidden').text(error_message) ) -App.Config.set('cdr_signal', { prio: 5100, name: 'Signal', parent: '#channels', target: '#channels/cdr_signal', controller: Index, permission: ['admin.channel_cdr_signal'] }, 'NavBarAdmin') +App.Config.set('cdr_signal', { prio: 5100, name: 'Signal', parent: '#channels', target: '#channels/cdr_signal', controller: ChannelCdrSignal, permission: ['admin.channel_cdr_signal'] }, 'NavBarAdmin') diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_voice.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_voice.coffee index 72862ec..575aca2 100644 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_voice.coffee +++ b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_voice.coffee @@ -1,4 +1,4 @@ -class Index extends App.ControllerSubContent +class ChannelCdrVoice extends App.ControllerSubContent requiredPermission: 'admin.channel_cdr_voice' events: 'click .js-new': 'new' @@ -246,4 +246,4 @@ class FormEdit extends App.ControllerModal @el.find('.alert').removeClass('hidden').text(error_message) ) -App.Config.set('cdr_voice', { prio: 5100, name: 'Voice', parent: '#channels', target: '#channels/cdr_voice', controller: Index, permission: ['admin.channel_cdr_voice'] }, 'NavBarAdmin') +App.Config.set('cdr_voice', { prio: 5100, name: 'Voice', parent: '#channels', target: '#channels/cdr_voice', controller: ChannelCdrVoice, permission: ['admin.channel_cdr_voice'] }, 'NavBarAdmin') diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_whatsapp.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_whatsapp.coffee index 5689e67..6876092 100644 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_whatsapp.coffee +++ b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_whatsapp.coffee @@ -1,4 +1,4 @@ -class Index extends App.ControllerSubContent +class ChannelCdrWhatsapp extends App.ControllerSubContent requiredPermission: 'admin.channel_cdr_whatsapp' events: 'click .js-new': 'new' @@ -246,4 +246,4 @@ class FormEdit extends App.ControllerModal @el.find('.alert').removeClass('hidden').text(error_message) ) -App.Config.set('cdr_whatsapp', { prio: 5100, name: 'Whatsapp', parent: '#channels', target: '#channels/cdr_whatsapp', controller: Index, permission: ['admin.channel_cdr_whatsapp'] }, 'NavBarAdmin') +App.Config.set('cdr_whatsapp', { prio: 5100, name: 'Whatsapp', parent: '#channels', target: '#channels/cdr_whatsapp', controller: ChannelCdrWhatsapp, permission: ['admin.channel_cdr_whatsapp'] }, 'NavBarAdmin') diff --git a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb index 3eb7c94..3bd34af 100644 --- a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb +++ b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb @@ -117,6 +117,11 @@ class ChannelsCdrSignalController < ApplicationController return render json: {}, status: 401 if !channel || !channel.active return render json: {}, status: 401 if channel.options[:token] != token + # Handle group creation events + if params[:event] == 'group_created' + return update_group + end + channel_id = channel.id # validate input @@ -141,6 +146,17 @@ class ChannelsCdrSignalController < ApplicationController receiver_phone_number = params[:to].strip sender_phone_number = params[:from].strip + + # Check if this is a group message using the isGroup 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 + # Note: We can't rely on UUID format alone because users with privacy settings + # may have UUID identifiers instead of phone numbers + is_group_message = params[:isGroup] == true || params[:is_group] == true + # If it's a group message, the 'to' field contains the group ID + group_id = is_group_message ? params[:to] : nil + customer = User.find_by(phone: sender_phone_number) customer ||= User.find_by(mobile: sender_phone_number) unless customer @@ -192,13 +208,28 @@ class ChannelsCdrSignalController < ApplicationController # find ticket or create one state_ids = Ticket::State.where(name: %w[closed merged removed]).pluck(:id) - ticket = Ticket.where(customer_id: customer.id).where.not(state_id: state_ids).order(:updated_at).first + + # For group messages, find ticket by group_id in preferences + if is_group_message + ticket = Ticket.joins("LEFT JOIN tickets AS t2 ON t2.preferences::jsonb -> 'cdr_signal' ->> 'group_id' = '#{group_id}'") + .where(customer_id: customer.id) + .where.not(state_id: state_ids) + .where("tickets.preferences::jsonb -> 'cdr_signal' ->> 'group_id' = ?", group_id) + .order(:updated_at) + .first + else + ticket = Ticket.where(customer_id: customer.id).where.not(state_id: state_ids).order(:updated_at).first + end + if ticket # check if title need to be updated ticket.title = title if ticket.title == '-' new_state = Ticket::State.find_by(default_create: true) 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 ? group_id : sender_phone_number + ticket = Ticket.new( group_id: channel.group_id, title: title, @@ -207,10 +238,15 @@ class ChannelsCdrSignalController < ApplicationController channel_id: channel.id, cdr_signal: { bot_token: channel.options[:bot_token], # change to bot id - chat_id: sender_phone_number + chat_id: chat_id } } ) + + # Store group_id if this is a group message + if is_group_message + ticket.preferences[:cdr_signal][:group_id] = group_id + end end ticket.save! @@ -265,4 +301,66 @@ class ChannelsCdrSignalController < ApplicationController render json: result, status: :ok end + + # Webhook endpoint for receiving group creation notifications from bridge-worker + # This is called when a Signal group is created for a conversation + # Expected payload: + # { + # "event": "group_created", + # "conversation_id": "ticket_id_or_number", + # "original_recipient": "+1234567890", + # "group_id": "uuid-of-signal-group", + # "timestamp": "ISO8601 timestamp" + # } + def update_group + # Validate required parameters + errors = {} + errors['event'] = 'required' unless params[:event].present? + errors['conversation_id'] = 'required' unless params[:conversation_id].present? + errors['group_id'] = 'required' unless params[:group_id].present? + + if errors.present? + render json: { + errors: errors + }, status: :bad_request + return + end + + # Only handle group_created events for now + unless params[:event] == 'group_created' + render json: { error: 'Unsupported event type' }, status: :bad_request + return + end + + # Find the ticket by ID or number + ticket = if params[:conversation_id].to_s.match?(/^\d+$/) + Ticket.find_by(id: params[:conversation_id]) + else + Ticket.find_by(number: params[:conversation_id]) + end + + unless ticket + Rails.logger.error "Signal group update: Ticket not found for conversation_id #{params[:conversation_id]}" + render json: { error: 'Ticket not found' }, status: :not_found + return + end + + # Update ticket preferences with the group information + ticket.preferences ||= {} + ticket.preferences[:cdr_signal] ||= {} + ticket.preferences[:cdr_signal][:group_id] = params[:group_id] + ticket.preferences[:cdr_signal][:chat_id] = params[:group_id] + ticket.preferences[:cdr_signal][:original_recipient] = params[:original_recipient] if params[:original_recipient].present? + ticket.preferences[:cdr_signal][:group_created_at] = params[:timestamp] if params[:timestamp].present? + + ticket.save! + + Rails.logger.info "Signal group #{params[:group_id]} associated with ticket #{ticket.id}" + + render json: { + success: true, + ticket_id: ticket.id, + ticket_number: ticket.number + }, status: :ok + end end diff --git a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb index 4452c52..b2dbc78 100644 --- a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb +++ b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb @@ -43,10 +43,7 @@ class CommunicateCdrSignalJob < ApplicationJob has_error = false begin - result = channel.deliver( - to: ticket.preferences[:cdr_signal][:chat_id], - body: article.body - ) + result = channel.deliver(article) rescue StandardError => e log_error(article, e.message) has_error = true diff --git a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb index 33b03e1..70e60b7 100644 --- a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb +++ b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb @@ -43,10 +43,7 @@ class CommunicateCdrWhatsappJob < ApplicationJob has_error = false begin - result = channel.deliver( - to: ticket.preferences[:cdr_whatsapp][:chat_id], - body: article.body - ) + result = channel.deliver(article) rescue StandardError => e log_error(article, e.message) has_error = true diff --git a/packages/zammad-addon-bridge/src/lib/cdr_signal.rb b/packages/zammad-addon-bridge/src/lib/cdr_signal.rb index 4efc972..5c910a3 100644 --- a/packages/zammad-addon-bridge/src/lib/cdr_signal.rb +++ b/packages/zammad-addon-bridge/src/lib/cdr_signal.rb @@ -312,8 +312,21 @@ class CdrSignal def from_article(article) # sends a message from a zammad article - Rails.logger.debug { "Create signal message from article to '#{article[:to]}'..." } + Rails.logger.debug { "Create signal message from article..." } - @api.send_message(article[:to], article[:body]) + # Get the recipient from ticket preferences + ticket = Ticket.find_by(id: article.ticket_id) + raise "No ticket found for article #{article.id}" unless ticket + + recipient = ticket.preferences.dig('cdr_signal', 'chat_id') + raise "No Signal chat_id found in ticket preferences" unless recipient + + Rails.logger.debug { "Sending to recipient: '#{recipient}'" } + + # Include ticket ID as conversationId for group creation callback + options = {} + options[:conversationId] = ticket.number if ticket + + @api.send_message(recipient, article[:body], options) end end diff --git a/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb b/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb index 608466b..02ac423 100644 --- a/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb +++ b/packages/zammad-addon-bridge/src/lib/cdr_signal_api.rb @@ -35,6 +35,12 @@ class CdrSignalApi end def send_message(recipient, text, options = {}) - post('send', { to: recipient.to_s, message: text }.merge(parse_hash(options))) + # Don't encode conversationId with CGI + params = { to: recipient.to_s, message: text } + if options[:conversationId] + params[:conversationId] = options[:conversationId] + options.delete(:conversationId) + end + post('send', params.merge(parse_hash(options))) end end diff --git a/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb b/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb index 62725ec..e20303a 100644 --- a/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb +++ b/packages/zammad-addon-bridge/src/lib/cdr_whatsapp.rb @@ -312,8 +312,17 @@ class CdrWhatsapp def from_article(article) # sends a message from a zammad article - Rails.logger.debug { "Create whatsapp message from article to '#{article[:to]}'..." } + Rails.logger.debug { "Create whatsapp message from article..." } - @api.send_message(article[:to], article[:body]) + # Get the recipient from ticket preferences + ticket = Ticket.find_by(id: article.ticket_id) + raise "No ticket found for article #{article.id}" unless ticket + + recipient = ticket.preferences.dig('cdr_whatsapp', 'chat_id') + raise "No WhatsApp chat_id found in ticket preferences" unless recipient + + Rails.logger.debug { "Sending to recipient: '#{recipient}'" } + + @api.send_message(recipient, article[:body]) end end