require 'rails_helper' describe Telegram::IncomingMessageService do before do stub_request(:any, /api.telegram.org/).to_return(headers: { content_type: 'application/json' }, body: {}.to_json, status: 200) stub_request(:get, 'https://chatwoot-assets.local/sample.png').to_return( status: 200, body: File.read('spec/assets/sample.png'), headers: {} ) stub_request(:get, 'https://chatwoot-assets.local/sample.mov').to_return( status: 200, body: File.read('spec/assets/sample.mov'), headers: {} ) stub_request(:get, 'https://chatwoot-assets.local/sample.mp3').to_return( status: 200, body: File.read('spec/assets/sample.mp3'), headers: {} ) stub_request(:get, 'https://chatwoot-assets.local/sample.ogg').to_return( status: 200, body: File.read('spec/assets/sample.ogg'), headers: {} ) stub_request(:get, 'https://chatwoot-assets.local/sample.pdf').to_return( status: 200, body: File.read('spec/assets/sample.pdf'), headers: {} ) end let!(:telegram_channel) { create(:channel_telegram) } let!(:message_params) do { 'message_id' => 1, 'from' => { 'id' => 23, 'is_bot' => false, 'first_name' => 'Sojan', 'last_name' => 'Jose', 'username' => 'sojan', 'language_code' => 'en' }, 'chat' => { 'id' => 23, 'first_name' => 'Sojan', 'last_name' => 'Jose', 'username' => 'sojan', 'type' => 'private' }, 'date' => 1_631_132_077 } end describe '#perform' do context 'when valid text message params' do it 'creates appropriate conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'text' => 'test' }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.content).to eq('test') end end context 'when valid caption params' do it 'creates appropriate conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'caption' => 'test' }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(23) expect(Contact.all.first.additional_attributes['social_telegram_user_name']).to eq('sojan') expect(telegram_channel.inbox.messages.first.content).to eq('test') end end context 'when group messages' do it 'doesnot create conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'message_id' => 1, 'from' => { 'id' => 23, 'is_bot' => false, 'first_name' => 'Sojan', 'last_name' => 'Jose', 'username' => 'sojan', 'language_code' => 'en' }, 'chat' => { 'id' => 23, 'first_name' => 'Sojan', 'last_name' => 'Jose', 'username' => 'sojan', 'type' => 'group' }, 'date' => 1_631_132_077, 'text' => 'test' } }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).to eq(0) end end context 'when business connection messages' do subject do described_class.new(inbox: telegram_channel.inbox, params: params).perform end let(:business_message_params) { message_params.merge('business_connection_id' => 'eooW3KF5WB5HxTD7T826') } let(:params) do { 'update_id' => 2_342_342_343_242, 'business_message' => { 'text' => 'test' }.deep_merge(business_message_params) }.with_indifferent_access end it 'creates appropriate conversations, message and contacts' do subject expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(telegram_channel.inbox.conversations.last.additional_attributes).to include({ 'chat_id' => 23, 'business_connection_id' => 'eooW3KF5WB5HxTD7T826' }) contact = Contact.all.first expect(contact.name).to eq('Sojan Jose') expect(contact.additional_attributes['language_code']).to eq('en') message = telegram_channel.inbox.messages.first expect(message.content).to eq('test') expect(message.message_type).to eq('incoming') expect(message.sender).to eq(contact) end context 'when sender is your business account' do let(:business_message_params) do message_params.merge( 'business_connection_id' => 'eooW3KF5WB5HxTD7T826', 'from' => { 'id' => 42, 'is_bot' => false, 'first_name' => 'John', 'last_name' => 'Doe', 'username' => 'johndoe', 'language_code' => 'en' } ) end it 'creates appropriate conversations, message and contacts' do subject expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(telegram_channel.inbox.conversations.last.additional_attributes).to include({ 'chat_id' => 23, 'business_connection_id' => 'eooW3KF5WB5HxTD7T826' }) contact = Contact.all.first expect(contact.name).to eq('Sojan Jose') # TODO: The language code is not present when we send the first message to the client. # Should we update it when the user replies? expect(contact.additional_attributes['language_code']).to be_nil message = telegram_channel.inbox.messages.first expect(message.content).to eq('test') expect(message.message_type).to eq('outgoing') expect(message.sender).to be_nil end end end context 'when valid audio messages params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.mp3') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'audio' => { 'file_id' => 'AwADBAADbXXXXXXXXXXXGBdhD2l6_XX', 'duration' => 243, 'mime_type' => 'audio/mpeg', 'file_size' => 3_897_500, 'title' => 'Test music file' } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(23) expect(Contact.all.first.additional_attributes['social_telegram_user_name']).to eq('sojan') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('audio') end end context 'when valid image attachment params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.png') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'photo' => [{ 'file_id' => 'AgACAgUAAxkBAAODYV3aGZlD6vhzKsE2WNmblsr6zKwAAi-tMRvCoeBWNQ1ENVBzJdwBAAMCAANzAAMhBA', 'file_unique_id' => 'AQADL60xG8Kh4FZ4', 'file_size' => 1883, 'width' => 90, 'height' => 67 }] }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('image') end end context 'when valid sticker attachment params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.png') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'sticker' => { 'emoji' => '👍', 'width' => 512, 'height' => 512, 'set_name' => 'a834556273_by_HopSins_1_anim', 'is_animated' => 1, 'thumb' => { 'file_id' => 'AAMCAQADGQEAA0dhXpKorj9CiRpNX3QOn7YPZ6XS4AAC4wADcVG-MexptyOf8SbfAQAHbQADIQQ', 'file_unique_id' => 'AQAD4wADcVG-MXI', 'file_size' => 4690, 'width' => 128, 'height' => 128 }, 'file_id' => 'CAACAgEAAxkBAANHYV6SqK4_QokaTV90Dp-2D2el0uAAAuMAA3FRvjHsabcjn_Em3yEE', 'file_unique_id' => 'AgAD4wADcVG-MQ', 'file_size' => 7340 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('image') end end context 'when valid video messages params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.mov') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'video' => { 'duration' => 1, 'width' => 720, 'height' => 1280, 'file_name' => 'IMG_2170.MOV', 'mime_type' => 'video/mp4', 'thumb' => { 'file_id' => 'AAMCBQADGQEAA4ZhXd78Xz6_c6gCzbdIkgGiXJcwwwACqwMAAp3x8Fbhf3EWamgCWAEAB20AAyEE', 'file_unique_id' => 'AQADqwMAAp3x8FZy', 'file_size' => 11_462, 'width' => 180, 'height' => 320 }, 'file_id' => 'BAACAgUAAxkBAAOGYV3e_F8-v3OoAs23SJIBolyXMMMAAqsDAAKd8fBW4X9xFmpoAlghBA', 'file_unique_id' => 'AgADqwMAAp3x8FY', 'file_size' => 291_286 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('video') end end context 'when valid video_note messages params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.mov') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'video_note' => { 'duration' => 3, 'length' => 240, 'thumb' => { 'file_id' => 'AAMCBQADGQEAA4ZhXd78Xz6_c6gCzbdIkgGiXJcwwwACqwMAAp3x8Fbhf3EWamgCWAEAB20AAyEE', 'file_unique_id' => 'AQADqwMAAp3x8FZy', 'file_size' => 11_462, 'width' => 240, 'height' => 240 }, 'file_id' => 'DQACAgUAAxkBAAIBY2FdJlhf8PC2E3IalXSvXWO5m8GBAALJAwACwqHgVhb0truM0uhwIQQ', 'file_unique_id' => 'AgADyQMAAsKh4FY', 'file_size' => 132_446 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('video') end end context 'when valid voice attachment params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.ogg') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'voice' => { 'duration' => 2, 'mime_type' => 'audio/ogg', 'file_id' => 'AwACAgUAAxkBAANjYVwnWF_w8LYTchqVdK9dY7mbwYEAAskDAALCoeBWFvS2u4zS6HAhBA', 'file_unique_id' => 'AgADyQMAAsKh4FY', 'file_size' => 11_833 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('audio') end end context 'when valid document message params' do it 'creates appropriate conversations, message and contacts' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.pdf') params = { 'update_id' => 2_342_342_343_242, 'message' => { 'document' => { 'file_id' => 'AwADBAADbXXXXXXXXXXXGBdhD2l6_XX', 'file_name' => 'Screenshot 2021-09-27 at 2.01.14 PM.png', 'mime_type' => 'application/png', 'file_size' => 536_392 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('file') end end context 'when the API call to get the download path returns an error' do it 'does not process the attachment' do allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return(nil) params = { 'update_id' => 2_342_342_343_242, 'message' => { 'document' => { 'file_id' => 'AwADBAADbXXXXXXXXXXXGBdhD2l6_XX', 'file_name' => 'Screenshot 2021-09-27 at 2.01.14 PM.png', 'mime_type' => 'application/png', 'file_size' => 536_392 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.messages.first.attachments.count).to eq(0) end end context 'when valid location message params' do it 'creates appropriate conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'location': { 'latitude': 37.7893768, 'longitude': -122.3895553 } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('location') end it 'creates appropriate conversations, message and contacts if venue is present' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'location': { 'latitude': 37.7893768, 'longitude': -122.3895553 }, venue: { title: 'San Francisco' } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') attachment = telegram_channel.inbox.messages.first.attachments.first expect(attachment.file_type).to eq('location') expect(attachment.coordinates_lat).to eq(37.7893768) expect(attachment.coordinates_long).to eq(-122.3895553) expect(attachment.fallback_title).to eq('San Francisco') end end context 'when valid callback_query params' do it 'creates appropriate conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'callback_query' => { 'id' => '2342342309929423', 'from' => { 'id' => 5_171_248, 'is_bot' => false, 'first_name' => 'Sojan', 'last_name' => 'Jose', 'username' => 'sojan', 'language_code' => 'en', 'is_premium' => true }, 'message' => message_params, 'chat_instance' => '-89923842384923492', 'data' => 'Option 1' } }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(5_171_248) expect(telegram_channel.inbox.messages.first.content).to eq('Option 1') end end context 'when valid contact message params' do it 'creates appropriate conversations, message and contacts' do params = { 'update_id' => 2_342_342_343_242, 'message' => { 'contact': { 'phone_number': '+918660944581' } }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('contact') end end end context 'when lock to single conversation is enabled' do before do # ensure message_params exists in this context and has from.id message_params[:from] ||= {} message_params[:from][:id] ||= 23 end it 'reopens last conversation if last conversation is resolved' do telegram_channel.inbox.update!(lock_to_single_conversation: true) contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci| ci.contact = create(:contact) end resolved_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :resolved) params = { 'update_id' => 2_342_342_343_242, 'message' => { 'text' => 'test' }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).to eq(1) expect(resolved_conversation.reload.messages.last.content).to eq('test') end end context 'when lock to single conversation is disabled' do before do # ensure message_params exists in this context and has from.id message_params[:from] ||= {} message_params[:from][:id] ||= 23 end it 'creates new conversation if last conversation is resolved' do telegram_channel.inbox.update!(lock_to_single_conversation: false) contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci| ci.contact = create(:contact) end _resolved_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :resolved) params = { 'update_id' => 2_342_342_343_242, 'message' => { 'text' => 'test' }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).to eq(2) expect(telegram_channel.inbox.conversations.last.messages.first.content).to eq('test') expect(telegram_channel.inbox.conversations.last.status).to eq('open') end it 'appends to last conversation if last conversation is not resolved' do telegram_channel.inbox.update!(lock_to_single_conversation: false) contact_inbox = ContactInbox.find_or_create_by(inbox: telegram_channel.inbox, source_id: message_params[:from][:id]) do |ci| ci.contact = create(:contact) end open_conversation = create(:conversation, inbox: telegram_channel.inbox, contact_inbox: contact_inbox, status: :open) params = { 'update_id' => 2_342_342_343_242, 'message' => { 'text' => 'test' }.merge(message_params) }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).to eq(1) expect(open_conversation.reload.messages.last.content).to eq('test') end end end