diff --git a/app/controllers/webhooks/instagram_controller.rb b/app/controllers/webhooks/instagram_controller.rb index 3d46334ca..569c2524b 100644 --- a/app/controllers/webhooks/instagram_controller.rb +++ b/app/controllers/webhooks/instagram_controller.rb @@ -4,7 +4,16 @@ class Webhooks::InstagramController < ActionController::API def events Rails.logger.info('Instagram webhook received events') if params['object'].casecmp('instagram').zero? - ::Webhooks::InstagramEventsJob.perform_later(params.to_unsafe_hash[:entry]) + entry_params = params.to_unsafe_hash[:entry] + + if contains_echo_event?(entry_params) + # Add delay to prevent race condition where echo arrives before send message API completes + # This avoids duplicate messages when echo comes early during API processing + ::Webhooks::InstagramEventsJob.set(wait: 2.seconds).perform_later(entry_params) + else + ::Webhooks::InstagramEventsJob.perform_later(entry_params) + end + render json: :ok else Rails.logger.warn("Message is not received from the instagram webhook event: #{params['object']}") @@ -14,6 +23,16 @@ class Webhooks::InstagramController < ActionController::API private + def contains_echo_event?(entry_params) + return false unless entry_params.is_a?(Array) + + entry_params.any? do |entry| + # Check messaging array for echo events + messaging_events = entry[:messaging] || [] + messaging_events.any? { |messaging| messaging.dig(:message, :is_echo).present? } + end + end + def valid_token?(token) # Validates against both IG_VERIFY_TOKEN (Instagram channel via Facebook page) and # INSTAGRAM_VERIFY_TOKEN (Instagram channel via direct Instagram login) diff --git a/config/initializers/facebook_messenger.rb b/config/initializers/facebook_messenger.rb index 354687cbd..f93829e65 100644 --- a/config/initializers/facebook_messenger.rb +++ b/config/initializers/facebook_messenger.rb @@ -39,6 +39,8 @@ Rails.application.reloader.to_prepare do end Facebook::Messenger::Bot.on :message_echo do |message| - Webhooks::FacebookEventsJob.perform_later(message.to_json) + # Add delay to prevent race condition where echo arrives before send message API completes + # This avoids duplicate messages when echo comes early during API processing + Webhooks::FacebookEventsJob.set(wait: 2.seconds).perform_later(message.to_json) end end diff --git a/spec/controllers/webhooks/instagram_controller_spec.rb b/spec/controllers/webhooks/instagram_controller_spec.rb index df5a6596d..c31d65a2b 100644 --- a/spec/controllers/webhooks/instagram_controller_spec.rb +++ b/spec/controllers/webhooks/instagram_controller_spec.rb @@ -33,5 +33,21 @@ RSpec.describe 'Webhooks::InstagramController', type: :request do post '/webhooks/instagram', params: instagram_params expect(response).to have_http_status(:success) end + + context 'when processing echo events' do + let!(:echo_params) { build(:instagram_story_mention_event_with_echo).with_indifferent_access } + + it 'delays processing for echo events by 2 seconds' do + job_double = class_double(Webhooks::InstagramEventsJob) + allow(Webhooks::InstagramEventsJob).to receive(:set).with(wait: 2.seconds).and_return(job_double) + allow(job_double).to receive(:perform_later) + + instagram_params = echo_params.merge(object: 'instagram') + post '/webhooks/instagram', params: instagram_params + expect(response).to have_http_status(:success) + expect(Webhooks::InstagramEventsJob).to have_received(:set).with(wait: 2.seconds) + expect(job_double).to have_received(:perform_later) + end + end end end