From 2ba565be166f197d948d791592d0b039d0e52eee Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Fri, 15 Sep 2023 15:49:26 +0200 Subject: [PATCH 1/3] Introduce idea of tracking received messages - using delivery receipts for Signal --- app/adapters/signal_adapter/inbound.rb | 3 ++ app/components/checkbox/checkbox.html.erb | 2 +- .../signal_adapter/receive_polling_job.rb | 16 +++++++-- ...30915070544_add_received_at_to_messages.rb | 7 ++++ db/schema.rb | 3 +- spec/adapters/signal_adapter/inbound_spec.rb | 36 +++++++++++++++---- 6 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20230915070544_add_received_at_to_messages.rb diff --git a/app/adapters/signal_adapter/inbound.rb b/app/adapters/signal_adapter/inbound.rb index 2c370ac18..b93dccbe6 100644 --- a/app/adapters/signal_adapter/inbound.rb +++ b/app/adapters/signal_adapter/inbound.rb @@ -4,6 +4,7 @@ module SignalAdapter UNKNOWN_CONTRIBUTOR = :unknown_contributor UNKNOWN_CONTENT = :unknown_content CONNECT = :connect + HANDLE_DELIVERY_RECEIPT = :handle_delivery_receipt class Inbound UNKNOWN_CONTENT_KEYS = %w[mentions contacts sticker].freeze @@ -66,6 +67,8 @@ def initialize_sender(signal_message) def initialize_message(signal_message) is_data_message = signal_message.dig(:envelope, :dataMessage) is_remove_emoji = signal_message.dig(:envelope, :dataMessage, :reaction, :isRemove) + is_delivery_receipt = signal_message.dig(:envelope, :receiptMessage) + trigger(HANDLE_DELIVERY_RECEIPT, is_delivery_receipt, sender) if is_delivery_receipt return nil if !is_data_message || is_remove_emoji data_message = signal_message.dig(:envelope, :dataMessage) diff --git a/app/components/checkbox/checkbox.html.erb b/app/components/checkbox/checkbox.html.erb index 0174fc3dd..dced42f0d 100644 --- a/app/components/checkbox/checkbox.html.erb +++ b/app/components/checkbox/checkbox.html.erb @@ -6,4 +6,4 @@ value='1' <%= checked ? 'checked' : '' %> <%= required ? 'required' : '' %> -/> \ No newline at end of file +/> diff --git a/app/jobs/signal_adapter/receive_polling_job.rb b/app/jobs/signal_adapter/receive_polling_job.rb index 45a79ea22..7161fba6f 100644 --- a/app/jobs/signal_adapter/receive_polling_job.rb +++ b/app/jobs/signal_adapter/receive_polling_job.rb @@ -17,9 +17,7 @@ def perform(*_args) adapter = SignalAdapter::Inbound.new adapter.on(SignalAdapter::CONNECT) do |contributor| - contributor.update!(signal_onboarding_completed_at: Time.zone.now) - SignalAdapter::Outbound.send_welcome_message!(contributor) - SignalAdapter::AttachContributorsAvatarJob.perform_later(contributor) + handle_connect(contributor) end adapter.on(SignalAdapter::UNKNOWN_CONTRIBUTOR) do |signal_phone_number| @@ -31,6 +29,12 @@ def perform(*_args) SignalAdapter::Outbound.send_unknown_content_message!(contributor) end + adapter.on(SignalAdapter::HANDLE_DELIVERY_RECEIPT) do |delivery_receipt, contributor| + if delivery_receipt[:isDelivery] + contributor.received_messages.first.update(received_at: Time.zone.at(delivery_receipt[:when]).to_datetime) + end + end + signal_messages.each do |raw_message| adapter.consume(raw_message) { |m| m.contributor.reply(adapter) } rescue StandardError => e @@ -60,5 +64,11 @@ def ping_monitoring_service def queue_empty? Delayed::Job.where(queue: queue_name, failed_at: nil).none? end + + def handle_connect(contributor) + contributor.update!(signal_onboarding_completed_at: Time.zone.now) + SignalAdapter::Outbound.send_welcome_message!(contributor) + SignalAdapter::AttachContributorsAvatarJob.perform_later(contributor) + end end end diff --git a/db/migrate/20230915070544_add_received_at_to_messages.rb b/db/migrate/20230915070544_add_received_at_to_messages.rb new file mode 100644 index 000000000..3696d62ab --- /dev/null +++ b/db/migrate/20230915070544_add_received_at_to_messages.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddReceivedAtToMessages < ActiveRecord::Migration[6.1] + def change + add_column :messages, :received_at, :datetime, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 3e6fb9997..339d795f7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_06_30_061952) do +ActiveRecord::Schema.define(version: 2023_09_15_070544) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -169,6 +169,7 @@ t.boolean "highlighted", default: false t.bigint "creator_id" t.string "sender_type" + t.datetime "received_at" t.index ["creator_id"], name: "index_messages_on_creator_id" t.index ["recipient_id"], name: "index_messages_on_recipient_id" t.index ["request_id"], name: "index_messages_on_request_id" diff --git a/spec/adapters/signal_adapter/inbound_spec.rb b/spec/adapters/signal_adapter/inbound_spec.rb index e7d3fb5d5..9f96968e2 100644 --- a/spec/adapters/signal_adapter/inbound_spec.rb +++ b/spec/adapters/signal_adapter/inbound_spec.rb @@ -24,17 +24,20 @@ { envelope: { source: '+4912345789', + sourceNumber: '+4912345789', + sourceUuid: 'valid_uuid', + sourceName: 'Signal Contributor', sourceDevice: 1, - timestamp: 1_626_711_330_462, + timestamp: 1_694_759_894_782, receiptMessage: { - when: 1_626_711_330_462, + when: 1_694_759_894_782, isDelivery: true, isRead: false, - timestamps: [ - 1_626_711_326_111 - ] + isViewed: false, + timestamps: [1_694_759_894_066] } - } + }, + account: Setting.signal_server_phone_number } end @@ -205,6 +208,7 @@ end context 'given a receipt message' do + before { create(:message, recipient_id: contributor.id) } let(:signal_message) { signal_receipt_message } it { should be(nil) } @@ -454,5 +458,25 @@ it { should have_received(:call).with(contributor) } end end + + describe 'HANDLE_DELIVERY_RECEIPT' do + let(:handle_delivery_receipt_callback) { spy('handle_delivery_receipt_callback') } + let(:signal_message) { signal_receipt_message } + + before do + adapter.on(SignalAdapter::HANDLE_DELIVERY_RECEIPT) do |delivery_receipt, contributor| + handle_delivery_receipt_callback.call(delivery_receipt, contributor) + end + end + + subject do + adapter.consume(signal_message) + handle_delivery_receipt_callback + end + + describe 'if the message is a delivery receipt' do + it { should have_received(:call) } + end + end end end From 59b9d99dd764ea40776947996bce4e288ef47fb1 Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Wed, 4 Oct 2023 19:10:47 +0200 Subject: [PATCH 2/3] Upgrade signa-cli-rest-api after fix applied upstream - signal-cli >0.12 was broken with native mode --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7242e90c7..9c777d8b9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: - postgres_data:/var/lib/postgresql/data signal: - image: bbernhard/signal-cli-rest-api:0.119-dev + image: bbernhard/signal-cli-rest-api:0.69 environment: - MODE=native #- AUTO_RECEIVE_SCHEDULE=0 22 * * * #enable this parameter on demand (see description below) From 9d91c1f858ea11f1aa8a71e448423d11240eb5dd Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Tue, 24 Oct 2023 17:19:38 +0200 Subject: [PATCH 3/3] Add read_at to messages, fix datetime logic - Signal sends 13 digit timestamps(milliseconds), which need to be converted to seconds before converting to a datetime in ruby otherwise the datetime is not accurate --- app/jobs/signal_adapter/receive_polling_job.rb | 11 ++++++++--- db/migrate/20231024144124_add_read_at_to_messages.rb | 7 +++++++ db/schema.rb | 3 ++- 3 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20231024144124_add_read_at_to_messages.rb diff --git a/app/jobs/signal_adapter/receive_polling_job.rb b/app/jobs/signal_adapter/receive_polling_job.rb index 7161fba6f..30d918ce5 100644 --- a/app/jobs/signal_adapter/receive_polling_job.rb +++ b/app/jobs/signal_adapter/receive_polling_job.rb @@ -30,9 +30,7 @@ def perform(*_args) end adapter.on(SignalAdapter::HANDLE_DELIVERY_RECEIPT) do |delivery_receipt, contributor| - if delivery_receipt[:isDelivery] - contributor.received_messages.first.update(received_at: Time.zone.at(delivery_receipt[:when]).to_datetime) - end + handle_delivery_receipt(delivery_receipt, contributor) end signal_messages.each do |raw_message| @@ -70,5 +68,12 @@ def handle_connect(contributor) SignalAdapter::Outbound.send_welcome_message!(contributor) SignalAdapter::AttachContributorsAvatarJob.perform_later(contributor) end + + def handle_delivery_receipt(delivery_receipt, contributor) + datetime = Time.zone.at(delivery_receipt[:when] / 1000).to_datetime + latest_received_message = contributor.received_messages.first + latest_received_message.update(received_at: datetime) if delivery_receipt[:isDelivery] + latest_received_message.update(read_at: datetime) if delivery_receipt[:isRead] + end end end diff --git a/db/migrate/20231024144124_add_read_at_to_messages.rb b/db/migrate/20231024144124_add_read_at_to_messages.rb new file mode 100644 index 000000000..5dbf6a6ad --- /dev/null +++ b/db/migrate/20231024144124_add_read_at_to_messages.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddReadAtToMessages < ActiveRecord::Migration[6.1] + def change + add_column :messages, :read_at, :datetime, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 339d795f7..74b98d80a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_09_15_070544) do +ActiveRecord::Schema.define(version: 2023_10_24_144124) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -170,6 +170,7 @@ t.bigint "creator_id" t.string "sender_type" t.datetime "received_at" + t.datetime "read_at" t.index ["creator_id"], name: "index_messages_on_creator_id" t.index ["recipient_id"], name: "index_messages_on_recipient_id" t.index ["request_id"], name: "index_messages_on_request_id"