# frozen_string_literal: true require 'json' require 'net/http' require 'net/https' require 'uri' # Direct Signal CLI API client for communicating with signal-cli-rest-api # All Signal operations go through this single class class CdrSignalApi def initialize(base_url = nil) @base_url = base_url || ENV.fetch('SIGNAL_CLI_URL', 'http://signal-cli-rest-api:8080') end # Fetch pending messages for a phone number # GET /v1/receive/{number} def fetch_messages(phone_number) url = "#{@base_url}/v1/receive/#{CGI.escape(phone_number)}" response = Faraday.get(url, nil, { 'Accept' => 'application/json' }) return [] unless response.success? JSON.parse(response.body) rescue JSON::ParserError, Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to fetch messages for #{phone_number}: #{e.message}" [] end # Fetch an attachment by ID # GET /v1/attachments/{id} def fetch_attachment(attachment_id) url = "#{@base_url}/v1/attachments/#{CGI.escape(attachment_id)}" response = Faraday.get(url) return nil unless response.success? response.body rescue Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to fetch attachment #{attachment_id}: #{e.message}" nil end # List all groups for a phone number # GET /v1/groups/{number} def list_groups(phone_number) url = "#{@base_url}/v1/groups/#{CGI.escape(phone_number)}" response = Faraday.get(url, nil, { 'Accept' => 'application/json' }) return [] unless response.success? JSON.parse(response.body) rescue JSON::ParserError, Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to list groups for #{phone_number}: #{e.message}" [] end # Check if a phone number is registered with signal-cli # GET /v1/about def check_number(phone_number) # Verify we can connect to signal-cli-rest-api url = "#{@base_url}/v1/about" response = Faraday.get(url, nil, { 'Accept' => 'application/json' }) return false unless response.success? # Try to list groups for this number to verify it's registered groups_url = "#{@base_url}/v1/groups/#{CGI.escape(phone_number)}" groups_response = Faraday.get(groups_url, nil, { 'Accept' => 'application/json' }) groups_response.success? rescue Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to check number #{phone_number}: #{e.message}" false end # Send a message via Signal CLI # POST /v2/send def send_message(from_number, recipients, message, options = {}) url = "#{@base_url}/v2/send" data = { number: from_number, recipients: Array(recipients), message: message } # Add base64 attachments if provided if options[:attachments].present? data[:base64Attachments] = options[:attachments].map { |a| a[:data] } end # Add quote parameters if provided if options[:quote_timestamp] && options[:quote_author] && options[:quote_message] data[:quoteTimestamp] = options[:quote_timestamp] data[:quoteAuthor] = options[:quote_author] data[:quoteMessage] = options[:quote_message] end response = Faraday.post(url, data.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }) unless response.success? Rails.logger.error "CdrSignalApi: Failed to send message: #{response.status} #{response.body}" raise "Failed to send Signal message: #{response.status}" end JSON.parse(response.body) rescue Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to send message: #{e.message}" raise "Failed to send Signal message: #{e.message}" end # Create a new Signal group # POST /v1/groups/{number} def create_group(phone_number, name:, members:, description: nil) url = "#{@base_url}/v1/groups/#{CGI.escape(phone_number)}" data = { name: name, members: Array(members), description: description }.compact response = Faraday.post(url, data.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }) unless response.success? Rails.logger.error "CdrSignalApi: Failed to create group: #{response.status} #{response.body}" raise "Failed to create Signal group: #{response.status}" end JSON.parse(response.body) rescue Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to create group: #{e.message}" raise "Failed to create Signal group: #{e.message}" end # Update a Signal group # PUT /v1/groups/{number}/{groupId} def update_group(phone_number, group_id, name: nil, description: nil) url = "#{@base_url}/v1/groups/#{CGI.escape(phone_number)}/#{CGI.escape(group_id)}" data = {} data[:name] = name if name.present? data[:description] = description if description.present? response = Faraday.put(url, data.to_json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }) response.success? rescue Faraday::Error => e Rails.logger.error "CdrSignalApi: Failed to update group: #{e.message}" false end end