chore: Handle attachments in Whatsapp Channel (#3299)

send and receive attachments in 360Dialog WhatsApp channels
This commit is contained in:
Sojan Jose 2021-11-11 11:33:48 +04:00 committed by GitHub
parent b3e313a200
commit a4c87f2052
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 422 additions and 45 deletions

View file

@ -144,6 +144,8 @@ group :test do
gem 'cypress-on-rails', '~> 1.0' gem 'cypress-on-rails', '~> 1.0'
# fast cleaning of database # fast cleaning of database
gem 'database_cleaner' gem 'database_cleaner'
# mock http calls
gem 'webmock'
end end
group :development, :test do group :development, :test do
@ -171,5 +173,4 @@ group :development, :test do
gem 'simplecov', '0.17.1', require: false gem 'simplecov', '0.17.1', require: false
gem 'spring' gem 'spring'
gem 'spring-watcher-listen' gem 'spring-watcher-listen'
gem 'webmock'
end end

View file

@ -228,7 +228,7 @@ export default {
return ( return (
this.isAWebWidgetInbox || this.isAWebWidgetInbox ||
this.isAFacebookInbox || this.isAFacebookInbox ||
this.isATwilioWhatsappChannel || this.isAWhatsappChannel ||
this.isAPIInbox || this.isAPIInbox ||
this.isAnEmailChannel || this.isAnEmailChannel ||
this.isATwilioSMSChannel || this.isATwilioSMSChannel ||

View file

@ -2,7 +2,12 @@
<div class="wizard-body small-12 medium-9 columns height-auto"> <div class="wizard-body small-12 medium-9 columns height-auto">
<page-header <page-header
:header-title="$t('INBOX_MGMT.ADD.AUTH.TITLE')" :header-title="$t('INBOX_MGMT.ADD.AUTH.TITLE')"
:header-content="$t('INBOX_MGMT.ADD.AUTH.DESC')" :header-content="
useInstallationName(
$t('INBOX_MGMT.ADD.AUTH.DESC'),
globalConfig.installationName
)
"
/> />
<div class="row channels"> <div class="row channels">
<channel-item <channel-item
@ -21,12 +26,14 @@ import ChannelItem from 'dashboard/components/widgets/ChannelItem';
import router from '../../../index'; import router from '../../../index';
import PageHeader from '../SettingsSubPageHeader'; import PageHeader from '../SettingsSubPageHeader';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
export default { export default {
components: { components: {
ChannelItem, ChannelItem,
PageHeader, PageHeader,
}, },
mixins: [globalConfigMixin],
data() { data() {
return { return {
enabledFeatures: {}, enabledFeatures: {},

View file

@ -43,7 +43,7 @@ class Channel::Telegram < ApplicationRecord
response = HTTParty.get("#{telegram_api_url}/getUserProfilePhotos", query: { user_id: user_id }) response = HTTParty.get("#{telegram_api_url}/getUserProfilePhotos", query: { user_id: user_id })
return nil unless response.success? return nil unless response.success?
photos = response.parsed_response['result']['photos'] photos = response.parsed_response.dig('result', 'photos')
return if photos.blank? return if photos.blank?
get_telegram_file_path(photos.first.last['file_id']) get_telegram_file_path(photos.first.last['file_id'])

View file

@ -36,15 +36,19 @@ class Channel::Whatsapp < ApplicationRecord
# Extract later into provider Service # Extract later into provider Service
def send_message(phone_number, message) def send_message(phone_number, message)
HTTParty.post( if message.attachments.present?
"#{api_base_path}/messages", send_attachment_message(phone_number, message)
headers: { 'D360-API-KEY': provider_config['api_key'], 'Content-Type': 'application/json' }, else
body: { send_text_message(phone_number, message)
to: phone_number, end
text: { body: message }, end
type: 'text'
}.to_json def media_url(media_id)
) "#{api_base_path}/media/#{media_id}"
end
def api_headers
{ 'D360-API-KEY' => provider_config['api_key'], 'Content-Type' => 'application/json' }
end end
def has_24_hour_messaging_window? def has_24_hour_messaging_window?
@ -53,6 +57,36 @@ class Channel::Whatsapp < ApplicationRecord
private private
def send_text_message(phone_number, message)
HTTParty.post(
"#{api_base_path}/messages",
headers: { 'D360-API-KEY': provider_config['api_key'], 'Content-Type': 'application/json' },
body: {
to: phone_number,
text: { body: message.content },
type: 'text'
}.to_json
)
end
def send_attachment_message(phone_number, message)
attachment = message.attachments.first
type = %w[image audio video].include?(attachment.file_type) ? attachment.file_type : 'document'
attachment_url = attachment.file_url
HTTParty.post(
"#{api_base_path}/messages",
headers: { 'D360-API-KEY': provider_config['api_key'], 'Content-Type': 'application/json' },
body: {
'to' => phone_number,
'type' => type,
type.to_s => {
'link': attachment_url,
'caption': message.content
}
}.to_json
)
end
# Extract later into provider Service # Extract later into provider Service
def validate_provider_config def validate_provider_config
response = HTTParty.post( response = HTTParty.post(

View file

@ -1,5 +1,5 @@
# Find the various telegram payload samples here: https://core.telegram.org/bots/webhooks#testing-your-bot-with-updates # https://docs.360dialog.com/whatsapp-api/whatsapp-api/media
# https://core.telegram.org/bots/api#available-types # https://developers.facebook.com/docs/whatsapp/api/media/
class Whatsapp::IncomingMessageService class Whatsapp::IncomingMessageService
pattr_initialize [:inbox!, :params!] pattr_initialize [:inbox!, :params!]
@ -12,7 +12,7 @@ class Whatsapp::IncomingMessageService
return if params[:messages].blank? return if params[:messages].blank?
@message = @conversation.messages.create( @message = @conversation.messages.build(
content: params[:messages].first.dig(:text, :body), content: params[:messages].first.dig(:text, :body),
account_id: @inbox.account_id, account_id: @inbox.account_id,
inbox_id: @inbox.id, inbox_id: @inbox.id,
@ -20,6 +20,7 @@ class Whatsapp::IncomingMessageService
sender: @contact, sender: @contact,
source_id: params[:messages].first[:id].to_s source_id: params[:messages].first[:id].to_s
) )
attach_files
@message.save! @message.save!
end end
@ -58,4 +59,31 @@ class Whatsapp::IncomingMessageService
@conversation = ::Conversation.create!(conversation_params) @conversation = ::Conversation.create!(conversation_params)
end end
def file_content_type(file_type)
return :image if %w[image sticker].include?(file_type)
return :audio if %w[audio voice].include?(file_type)
return :video if ['video'].include?(file_type)
'document'
end
def attach_files
message_type = params[:messages].first[:type]
return if message_type == 'text'
attachment_payload = params[:messages].first[message_type.to_sym]
attachment_file = Down.download(inbox.channel.media_url(attachment_payload[:id]), headers: inbox.channel.api_headers)
@message.content ||= attachment_payload[:caption]
@message.attachments.new(
account_id: @message.account_id,
file_type: file_content_type(message_type),
file: {
io: attachment_file,
filename: attachment_file,
content_type: attachment_file.content_type
}
)
end
end end

View file

@ -6,6 +6,6 @@ class Whatsapp::SendOnWhatsappService < Base::SendOnChannelService
end end
def perform_reply def perform_reply
channel.send_message(message.conversation.contact_inbox.source_id, message.content) channel.send_message(message.conversation.contact_inbox.source_id, message)
end end
end end

View file

@ -19,7 +19,7 @@ describe ::ContactIdentifyAction do
end end
it 'enques avatar job when avatar url parameter is passed' do it 'enques avatar job when avatar url parameter is passed' do
params = { name: 'test', avatar_url: 'https://via.placeholder.com/250x250.png' } params = { name: 'test', avatar_url: 'https://chatwoot-assets.local/sample.png' }
expect(ContactAvatarJob).to receive(:perform_later).with(contact, params[:avatar_url]).once expect(ContactAvatarJob).to receive(:perform_later).with(contact, params[:avatar_url]).once
described_class.new(contact: contact, params: params).perform described_class.new(contact: contact, params: params).perform
end end

BIN
spec/assets/sample.mov Normal file

Binary file not shown.

BIN
spec/assets/sample.mp3 Normal file

Binary file not shown.

BIN
spec/assets/sample.ogg Normal file

Binary file not shown.

198
spec/assets/sample.pdf Normal file
View file

@ -0,0 +1,198 @@
%PDF-1.3
%âãÏÓ
1 0 obj
<<
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<<
/Type /Outlines
/Count 0
>>
endobj
3 0 obj
<<
/Type /Pages
/Count 2
/Kids [ 4 0 R 6 0 R ]
>>
endobj
4 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 5 0 R
>>
endobj
5 0 obj
<< /Length 1074 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( A Simple PDF File ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( This is a small demonstration .pdf file - ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 628.8480 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 616.8960 Td
( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj
ET
BT
/F1 0010 Tf
69.2500 604.9440 Td
( more text. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 592.9920 Td
( And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 569.0880 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 557.1360 Td
( text. And more text. And more text. Even more. Continued on page 2 ...) Tj
ET
endstream
endobj
6 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 7 0 R
>>
endobj
7 0 obj
<< /Length 676 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( Simple PDF File 2 ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( ...continued from page 1. Yet more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 676.6560 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( paint dry. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 640.8000 Td
( Boring. More, a little more text. The end, and just as well. ) Tj
ET
endstream
endobj
8 0 obj
[/PDF /Text]
endobj
9 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
>>
endobj
10 0 obj
<<
/Creator (Rave \(http://www.nevrona.com/rave\))
/Producer (Nevrona Designs)
/CreationDate (D:20060301072826)
>>
endobj
xref
0 11
0000000000 65535 f
0000000019 00000 n
0000000093 00000 n
0000000147 00000 n
0000000222 00000 n
0000000390 00000 n
0000001522 00000 n
0000001690 00000 n
0000002423 00000 n
0000002456 00000 n
0000002574 00000 n
trailer
<<
/Size 11
/Root 1 0 R
/Info 10 0 R
>>
startxref
2714
%%EOF

BIN
spec/assets/sample.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 KiB

View file

@ -199,6 +199,10 @@ describe ::ContactInboxBuilder do
end end
describe 'facebook inbox' do describe 'facebook inbox' do
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page, account: account) } let!(:facebook_channel) { create(:channel_facebook_page, account: account) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) } let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }

View file

@ -3,6 +3,10 @@ require 'rails_helper'
describe ::Messages::Facebook::MessageBuilder do describe ::Messages::Facebook::MessageBuilder do
subject(:message_builder) { described_class.new(incoming_fb_text_message, facebook_channel.inbox).perform } subject(:message_builder) { described_class.new(incoming_fb_text_message, facebook_channel.inbox).perform }
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page) } let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:message_object) { build(:incoming_fb_text_message).to_json } let!(:message_object) { build(:incoming_fb_text_message).to_json }
let!(:incoming_fb_text_message) { Integrations::Facebook::MessageParser.new(message_object) } let!(:incoming_fb_text_message) { Integrations::Facebook::MessageParser.new(message_object) }
@ -16,7 +20,7 @@ describe ::Messages::Facebook::MessageBuilder do
first_name: 'Jane', first_name: 'Jane',
last_name: 'Dae', last_name: 'Dae',
account_id: facebook_channel.inbox.account_id, account_id: facebook_channel.inbox.account_id,
profile_pic: 'https://via.placeholder.com/250x250.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
message_builder message_builder

View file

@ -3,6 +3,10 @@ require 'rails_helper'
describe ::Messages::Instagram::MessageBuilder do describe ::Messages::Instagram::MessageBuilder do
subject(:instagram_message_builder) { described_class } subject(:instagram_message_builder) { described_class }
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:account) { create(:account) } let!(:account) { create(:account) }
let!(:instagram_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') } let!(:instagram_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) } let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) }
@ -19,7 +23,7 @@ describe ::Messages::Instagram::MessageBuilder do
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: 'Sender-id-1',
account_id: instagram_inbox.account_id, account_id: instagram_inbox.account_id,
profile_pic: 'https://via.placeholder.com/250x250.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
messaging = dm_params[:entry][0]['messaging'][0] messaging = dm_params[:entry][0]['messaging'][0]

View file

@ -1,16 +1,8 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe 'Callbacks API', type: :request do RSpec.describe 'Callbacks API', type: :request do
let(:account) { create(:account) }
let(:valid_params) { attributes_for(:channel_facebook_page).merge(inbox_name: 'Test Inbox') }
let(:inbox) { create(:inbox, account: account) }
let!(:facebook_page) { create(:channel_facebook_page, inbox: inbox, account: account) }
# Doubles
let(:koala_api) { instance_double(Koala::Facebook::API) }
let(:koala_oauth) { instance_double(Koala::Facebook::OAuth) }
before do before do
stub_request(:any, /graph.facebook.com/)
# Mock new and return instance doubles defined above # Mock new and return instance doubles defined above
allow(Koala::Facebook::OAuth).to receive(:new).and_return(koala_oauth) allow(Koala::Facebook::OAuth).to receive(:new).and_return(koala_oauth)
allow(Koala::Facebook::API).to receive(:new).and_return(koala_api) allow(Koala::Facebook::API).to receive(:new).and_return(koala_api)
@ -22,6 +14,15 @@ RSpec.describe 'Callbacks API', type: :request do
allow(koala_oauth).to receive(:exchange_access_token_info).and_return('access_token' => SecureRandom.hex(10)) allow(koala_oauth).to receive(:exchange_access_token_info).and_return('access_token' => SecureRandom.hex(10))
end end
let(:account) { create(:account) }
let!(:facebook_page) { create(:channel_facebook_page, inbox: inbox, account: account) }
let(:valid_params) { attributes_for(:channel_facebook_page).merge(inbox_name: 'Test Inbox') }
let(:inbox) { create(:inbox, account: account) }
# Doubles
let(:koala_api) { instance_double(Koala::Facebook::API) }
let(:koala_oauth) { instance_double(Koala::Facebook::OAuth) }
describe 'POST /api/v1/accounts/{account.id}/callbacks/register_facebook_page' do describe 'POST /api/v1/accounts/{account.id}/callbacks/register_facebook_page' do
context 'when it is an unauthenticated user' do context 'when it is an unauthenticated user' do
it 'returns unauthorized' do it 'returns unauthorized' do

View file

@ -4,7 +4,7 @@ FactoryBot.define do
factory :bot_message_card, class: Hash do factory :bot_message_card, class: Hash do
title { Faker::Book.name } title { Faker::Book.name }
description { Faker::Movie.quote } description { Faker::Movie.quote }
media_url { 'https://via.placeholder.com/250x250.png' } media_url { 'https://chatwoot-assets.local/sample.png' }
actions do actions do
[{ [{
text: 'Select', text: 'Select',

View file

@ -22,12 +22,12 @@ FactoryBot.define do
'1' => { '1' => {
id: '1', id: '1',
name: 'person 1', name: 'person 1',
profile_image_url: 'https://via.placeholder.com/250x250.png' profile_image_url: 'https://chatwoot-assets.local/sample.png'
}, },
'2' => { '2' => {
id: '1', id: '1',
name: 'person 1', name: 'person 1',
profile_image_url: 'https://via.placeholder.com/250x250.png' profile_image_url: 'https://chatwoot-assets.local/sample.png'
} }
} }
end end

View file

@ -19,6 +19,7 @@ RSpec.describe SendReplyJob, type: :job do
end end
it 'calls Facebook::SendOnFacebookService when its facebook message' do it 'calls Facebook::SendOnFacebookService when its facebook message' do
stub_request(:post, /graph.facebook.com/)
facebook_channel = create(:channel_facebook_page) facebook_channel = create(:channel_facebook_page)
facebook_inbox = create(:inbox, channel: facebook_channel) facebook_inbox = create(:inbox, channel: facebook_channel)
message = create(:message, conversation: create(:conversation, inbox: facebook_inbox)) message = create(:message, conversation: create(:conversation, inbox: facebook_inbox))
@ -66,6 +67,7 @@ RSpec.describe SendReplyJob, type: :job do
end end
it 'calls ::Whatsapp:SendOnWhatsappService when its line message' do it 'calls ::Whatsapp:SendOnWhatsappService when its line message' do
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
whatsapp_channel = create(:channel_whatsapp) whatsapp_channel = create(:channel_whatsapp)
message = create(:message, conversation: create(:conversation, inbox: whatsapp_channel.inbox)) message = create(:message, conversation: create(:conversation, inbox: whatsapp_channel.inbox))
allow(::Whatsapp::SendOnWhatsappService).to receive(:new).with(message: message).and_return(process_service) allow(::Whatsapp::SendOnWhatsappService).to receive(:new).with(message: message).and_return(process_service)

View file

@ -4,6 +4,10 @@ require 'webhooks/twitter'
describe Webhooks::InstagramEventsJob do describe Webhooks::InstagramEventsJob do
subject(:instagram_webhook) { described_class } subject(:instagram_webhook) { described_class }
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:account) { create(:account) } let!(:account) { create(:account) }
let!(:instagram_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') } let!(:instagram_channel) { create(:channel_instagram_fb_page, account: account, instagram_id: 'chatwoot-app-user-id-1') }
let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) } let!(:instagram_inbox) { create(:inbox, channel: instagram_channel, account: account, greeting_enabled: false) }
@ -20,7 +24,7 @@ describe Webhooks::InstagramEventsJob do
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: 'Sender-id-1',
account_id: instagram_inbox.account_id, account_id: instagram_inbox.account_id,
profile_pic: 'https://via.placeholder.com/250x250.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
instagram_webhook.perform_now(dm_params[:entry]) instagram_webhook.perform_now(dm_params[:entry])
@ -39,7 +43,7 @@ describe Webhooks::InstagramEventsJob do
name: 'Jane', name: 'Jane',
id: 'Sender-id-1', id: 'Sender-id-1',
account_id: instagram_inbox.account_id, account_id: instagram_inbox.account_id,
profile_pic: 'https://via.placeholder.com/250x250.png' profile_pic: 'https://chatwoot-assets.local/sample.png'
}.with_indifferent_access }.with_indifferent_access
) )
instagram_webhook.perform_now(test_params[:entry]) instagram_webhook.perform_now(test_params[:entry])

View file

@ -10,6 +10,14 @@ describe Integrations::Slack::IncomingMessageBuilder do
let!(:hook) { create(:integrations_hook, account: account, reference_id: message_params[:event][:channel]) } let!(:hook) { create(:integrations_hook, account: account, reference_id: message_params[:event][:channel]) }
let!(:conversation) { create(:conversation, identifier: message_params[:event][:thread_ts]) } let!(:conversation) { create(:conversation, identifier: message_params[:event][:thread_ts]) }
before do
stub_request(:get, 'https://chatwoot-assets.local/sample.png').to_return(
status: 200,
body: File.read('spec/assets/sample.png'),
headers: {}
)
end
describe '#perform' do describe '#perform' do
context 'when url verification' do context 'when url verification' do
it 'return challenge code as response' do it 'return challenge code as response' do

View file

@ -25,6 +25,10 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer, type: :ma
end end
describe 'facebook_disconnect' do describe 'facebook_disconnect' do
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page, account: account) } let!(:facebook_channel) { create(:channel_facebook_page, account: account) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) } let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }
let(:mail) { described_class.with(account: account).facebook_disconnect(facebook_inbox).deliver_now } let(:mail) { described_class.with(account: account).facebook_disconnect(facebook_inbox).deliver_now }

View file

@ -22,6 +22,10 @@ RSpec.describe Campaign, type: :model do
end end
context 'when Inbox other then Website or Twilio SMS' do context 'when Inbox other then Website or Twilio SMS' do
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page) } let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel) } let!(:facebook_inbox) { create(:inbox, channel: facebook_channel) }
let(:campaign) { build(:campaign, inbox: facebook_inbox) } let(:campaign) { build(:campaign, inbox: facebook_inbox) }

View file

@ -4,6 +4,10 @@ require 'rails_helper'
require Rails.root.join 'spec/models/concerns/reauthorizable_shared.rb' require Rails.root.join 'spec/models/concerns/reauthorizable_shared.rb'
RSpec.describe Channel::FacebookPage do RSpec.describe Channel::FacebookPage do
before do
stub_request(:post, /graph.facebook.com/)
end
let(:channel) { create(:channel_facebook_page) } let(:channel) { create(:channel_facebook_page) }
it { is_expected.to validate_presence_of(:account_id) } it { is_expected.to validate_presence_of(:account_id) }

View file

@ -394,6 +394,10 @@ RSpec.describe Conversation, type: :model do
end end
describe 'on channels with 24 hour restriction' do describe 'on channels with 24 hour restriction' do
before do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page) } let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: facebook_channel.account) } let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: facebook_channel.account) }
let!(:conversation) { create(:conversation, inbox: facebook_inbox, account: facebook_channel.account) } let!(:conversation) { create(:conversation, inbox: facebook_inbox, account: facebook_channel.account) }

View file

@ -1,6 +1,10 @@
require 'rails_helper' require 'rails_helper'
describe Contacts::ContactableInboxesService do describe Contacts::ContactableInboxesService do
before do
stub_request(:post, /graph.facebook.com/)
end
let(:account) { create(:account) } let(:account) { create(:account) }
let(:contact) { create(:contact, account: account, email: 'contact@example.com', phone_number: '+2320000') } let(:contact) { create(:contact, account: account, email: 'contact@example.com', phone_number: '+2320000') }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) } let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }

View file

@ -4,6 +4,7 @@ describe Instagram::SendOnInstagramService do
subject(:send_reply_service) { described_class.new(message: message) } subject(:send_reply_service) { described_class.new(message: message) }
before do before do
stub_request(:post, /graph.facebook.com/)
create(:message, message_type: :incoming, inbox: instagram_inbox, account: account, conversation: conversation) create(:message, message_type: :incoming, inbox: instagram_inbox, account: account, conversation: conversation)
end end

View file

@ -1,6 +1,35 @@
require 'rails_helper' require 'rails_helper'
describe Telegram::IncomingMessageService do 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!(:telegram_channel) { create(:channel_telegram) }
describe '#perform' do describe '#perform' do
@ -64,7 +93,7 @@ describe Telegram::IncomingMessageService do
context 'when valid audio messages params' do context 'when valid audio messages params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.mp3') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.mp3')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {
@ -92,7 +121,7 @@ describe Telegram::IncomingMessageService do
context 'when valid image attachment params' do context 'when valid image attachment params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.png') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.png')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {
@ -117,7 +146,7 @@ describe Telegram::IncomingMessageService do
context 'when valid sticker attachment params' do context 'when valid sticker attachment params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.png') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.png')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {
@ -147,7 +176,7 @@ describe Telegram::IncomingMessageService do
context 'when valid video messages params' do context 'when valid video messages params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.mov') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.mov')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {
@ -175,7 +204,7 @@ describe Telegram::IncomingMessageService do
context 'when valid voice attachment params' do context 'when valid voice attachment params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.oga') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.ogg')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {
@ -200,7 +229,7 @@ describe Telegram::IncomingMessageService do
context 'when valid document message params' do context 'when valid document message params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-public-assets.s3.amazonaws.com/test-files/rspec/sample.pdf') allow(telegram_channel.inbox.channel).to receive(:get_telegram_file_path).and_return('https://chatwoot-assets.local/sample.pdf')
params = { params = {
'update_id' => 2_342_342_343_242, 'update_id' => 2_342_342_343_242,
'message' => { 'message' => {

View file

@ -1,9 +1,13 @@
require 'rails_helper' require 'rails_helper'
describe Whatsapp::IncomingMessageService do describe Whatsapp::IncomingMessageService do
describe '#perform' do
before do
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
end
let!(:whatsapp_channel) { create(:channel_whatsapp) } let!(:whatsapp_channel) { create(:channel_whatsapp) }
describe '#perform' do
context 'when valid text message params' do context 'when valid text message params' do
it 'creates appropriate conversations, message and contacts' do it 'creates appropriate conversations, message and contacts' do
params = { params = {
@ -17,5 +21,29 @@ describe Whatsapp::IncomingMessageService do
expect(whatsapp_channel.inbox.messages.first.content).to eq('Test') expect(whatsapp_channel.inbox.messages.first.content).to eq('Test')
end end
end end
context 'when valid attachment message params' do
it 'creates appropriate conversations, message and contacts' do
stub_request(:get, whatsapp_channel.media_url('b1c68f38-8734-4ad3-b4a1-ef0c10d683')).to_return(
status: 200,
body: File.read('spec/assets/sample.png'),
headers: {}
)
params = {
'contacts' => [{ 'profile' => { 'name' => 'Sojan Jose' }, 'wa_id' => '2423423243' }],
'messages' => [{ 'from' => '2423423243', 'id' => 'SDFADSf23sfasdafasdfa',
'image' => { 'id' => 'b1c68f38-8734-4ad3-b4a1-ef0c10d683',
'mime_type' => 'image/jpeg',
'sha256' => '29ed500fa64eb55fc19dc4124acb300e5dcca0f822a301ae99944db',
'caption' => 'Check out my product!' },
'timestamp' => '1633034394', 'type' => 'image' }]
}.with_indifferent_access
described_class.new(inbox: whatsapp_channel.inbox, params: params).perform
expect(whatsapp_channel.inbox.conversations.count).not_to eq(0)
expect(Contact.all.first.name).to eq('Sojan Jose')
expect(whatsapp_channel.inbox.messages.first.content).to eq('Check out my product!')
expect(whatsapp_channel.inbox.messages.first.attachments.present?).to eq true
end
end
end end
end end

View file

@ -2,6 +2,10 @@ require 'rails_helper'
describe Whatsapp::SendOnWhatsappService do describe Whatsapp::SendOnWhatsappService do
describe '#perform' do describe '#perform' do
before do
stub_request(:post, 'https://waba.360dialog.io/v1/configs/webhook')
end
context 'when a valid message' do context 'when a valid message' do
it 'calls channel.send_message' do it 'calls channel.send_message' do
whatsapp_request = double whatsapp_request = double

View file

@ -2,7 +2,7 @@ require 'simplecov'
require 'webmock/rspec' require 'webmock/rspec'
SimpleCov.start 'rails' SimpleCov.start 'rails'
WebMock.allow_net_connect! WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config| RSpec.configure do |config|
config.expect_with :rspec do |expectations| config.expect_with :rspec do |expectations|

View file

@ -78,11 +78,11 @@ module SlackStubs
[ [
{ {
mimetype: 'image/png', mimetype: 'image/png',
url_private: 'https://via.placeholder.com/250x250.png', url_private: 'https://chatwoot-assets.local/sample.png',
name: 'name_of_the_file', name: 'name_of_the_file',
title: 'title_of_the_file', title: 'title_of_the_file',
filetype: 'png', filetype: 'png',
url_private_download: 'https://via.placeholder.com/250x250.png' url_private_download: 'https://chatwoot-assets.local/sample.png'
} }
] ]
end end