mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 10:42:38 +00:00
feat: Telegram Channel (#2901)
- Ability to configure telegram bots as a channel in chatwoot - Receive a message sent to the telegram bot in chatwoot - Ability to reply to telegram users from chatwoot - Receive attachment messages in chatwoot fixes: #1843
This commit is contained in:
@@ -100,6 +100,7 @@ Metrics/AbcSize:
|
|||||||
- 'app/controllers/concerns/auth_helper.rb'
|
- 'app/controllers/concerns/auth_helper.rb'
|
||||||
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
|
- 'db/migrate/20190819005836_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb'
|
||||||
- 'db/migrate/20161123131628_devise_token_auth_create_users.rb'
|
- 'db/migrate/20161123131628_devise_token_auth_create_users.rb'
|
||||||
|
- 'app/controllers/api/v1/accounts/inboxes_controller.rb'
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 7
|
Max: 7
|
||||||
Exclude:
|
Exclude:
|
||||||
|
|||||||
1
Gemfile
1
Gemfile
@@ -78,7 +78,6 @@ gem 'wisper', '2.0.0'
|
|||||||
##--- gems for channels ---##
|
##--- gems for channels ---##
|
||||||
# TODO: bump up gem to 2.0
|
# TODO: bump up gem to 2.0
|
||||||
gem 'facebook-messenger'
|
gem 'facebook-messenger'
|
||||||
gem 'telegram-bot-ruby'
|
|
||||||
gem 'twilio-ruby', '~> 5.32.0'
|
gem 'twilio-ruby', '~> 5.32.0'
|
||||||
# twitty will handle subscription of twitter account events
|
# twitty will handle subscription of twitter account events
|
||||||
# gem 'twitty', git: 'https://github.com/chatwoot/twitty'
|
# gem 'twitty', git: 'https://github.com/chatwoot/twitty'
|
||||||
|
|||||||
20
Gemfile.lock
20
Gemfile.lock
@@ -98,10 +98,6 @@ GEM
|
|||||||
aws-sigv4 (~> 1.1)
|
aws-sigv4 (~> 1.1)
|
||||||
aws-sigv4 (1.2.4)
|
aws-sigv4 (1.2.4)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
axiom-types (0.1.1)
|
|
||||||
descendants_tracker (~> 0.0.4)
|
|
||||||
ice_nine (~> 0.11.0)
|
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
|
||||||
azure-storage-blob (2.0.1)
|
azure-storage-blob (2.0.1)
|
||||||
azure-storage-common (~> 2.0)
|
azure-storage-common (~> 2.0)
|
||||||
nokogiri (~> 1.11.0.rc2)
|
nokogiri (~> 1.11.0.rc2)
|
||||||
@@ -130,8 +126,6 @@ GEM
|
|||||||
thor (~> 1.0)
|
thor (~> 1.0)
|
||||||
byebug (11.1.3)
|
byebug (11.1.3)
|
||||||
coderay (1.1.3)
|
coderay (1.1.3)
|
||||||
coercible (1.0.0)
|
|
||||||
descendants_tracker (~> 0.0.1)
|
|
||||||
commonmarker (0.22.0)
|
commonmarker (0.22.0)
|
||||||
concurrent-ruby (1.1.9)
|
concurrent-ruby (1.1.9)
|
||||||
connection_pool (2.2.5)
|
connection_pool (2.2.5)
|
||||||
@@ -152,8 +146,6 @@ GEM
|
|||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
msgpack
|
msgpack
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
descendants_tracker (0.0.4)
|
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
|
||||||
devise (4.8.0)
|
devise (4.8.0)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
@@ -179,7 +171,6 @@ GEM
|
|||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
down (5.2.3)
|
down (5.2.3)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
dry-inflector (0.2.1)
|
|
||||||
ecma-re-validator (0.3.0)
|
ecma-re-validator (0.3.0)
|
||||||
regexp_parser (~> 2.0)
|
regexp_parser (~> 2.0)
|
||||||
erubi (1.10.0)
|
erubi (1.10.0)
|
||||||
@@ -292,7 +283,6 @@ GEM
|
|||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.8.10)
|
i18n (1.8.10)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
|
||||||
image_processing (1.12.1)
|
image_processing (1.12.1)
|
||||||
mini_magick (>= 4.9.5, < 5)
|
mini_magick (>= 4.9.5, < 5)
|
||||||
ruby-vips (>= 2.0.17, < 3)
|
ruby-vips (>= 2.0.17, < 3)
|
||||||
@@ -564,13 +554,8 @@ GEM
|
|||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
squasher (0.6.2)
|
squasher (0.6.2)
|
||||||
statsd-ruby (1.5.0)
|
statsd-ruby (1.5.0)
|
||||||
telegram-bot-ruby (0.16.0)
|
|
||||||
dry-inflector
|
|
||||||
faraday
|
|
||||||
virtus (~> 2.0)
|
|
||||||
telephone_number (1.4.12)
|
telephone_number (1.4.12)
|
||||||
thor (1.1.0)
|
thor (1.1.0)
|
||||||
thread_safe (0.3.6)
|
|
||||||
tilt (2.0.10)
|
tilt (2.0.10)
|
||||||
time_diff (0.3.0)
|
time_diff (0.3.0)
|
||||||
activesupport
|
activesupport
|
||||||
@@ -598,10 +583,6 @@ GEM
|
|||||||
valid_email2 (4.0.0)
|
valid_email2 (4.0.0)
|
||||||
activemodel (>= 3.2)
|
activemodel (>= 3.2)
|
||||||
mail (~> 2.5)
|
mail (~> 2.5)
|
||||||
virtus (2.0.0)
|
|
||||||
axiom-types (~> 0.1)
|
|
||||||
coercible (~> 1.0)
|
|
||||||
descendants_tracker (~> 0.0, >= 0.0.3)
|
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
web-console (4.1.0)
|
web-console (4.1.0)
|
||||||
@@ -716,7 +697,6 @@ DEPENDENCIES
|
|||||||
spring
|
spring
|
||||||
spring-watcher-listen
|
spring-watcher-listen
|
||||||
squasher
|
squasher
|
||||||
telegram-bot-ruby
|
|
||||||
telephone_number
|
telephone_number
|
||||||
time_diff
|
time_diff
|
||||||
twilio-ruby (~> 5.32.0)
|
twilio-ruby (~> 5.32.0)
|
||||||
|
|||||||
@@ -27,22 +27,23 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
channel = create_channel
|
channel = create_channel
|
||||||
@inbox = Current.account.inboxes.build(
|
@inbox = Current.account.inboxes.build(
|
||||||
name: permitted_params[:name],
|
{
|
||||||
greeting_message: permitted_params[:greeting_message],
|
name: inbox_name(channel),
|
||||||
greeting_enabled: permitted_params[:greeting_enabled],
|
channel: channel
|
||||||
channel: channel
|
}.merge(
|
||||||
|
permitted_params.except(:channel)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
@inbox.avatar.attach(permitted_params[:avatar])
|
|
||||||
@inbox.save!
|
@inbox.save!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@inbox.update(inbox_update_params.except(:channel))
|
@inbox.update(permitted_params.except(:channel))
|
||||||
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
|
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
|
||||||
return unless @inbox.channel.is_a?(Channel::WebWidget) && inbox_update_params[:channel].present?
|
|
||||||
|
|
||||||
@inbox.channel.update!(inbox_update_params[:channel])
|
channel_attributes = get_channel_attributes(@inbox.channel_type)
|
||||||
|
@inbox.channel.update!(permitted_params(channel_attributes)[:channel])
|
||||||
update_channel_feature_flags
|
update_channel_feature_flags
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,43 +78,52 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
@agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot]
|
@agent_bot = AgentBot.find(params[:agent_bot]) if params[:agent_bot]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inbox_name(channel)
|
||||||
|
return channel.try(:bot_name) if channel.is_a?(Channel::Telegram)
|
||||||
|
|
||||||
|
permitted_params[:name]
|
||||||
|
end
|
||||||
|
|
||||||
def create_channel
|
def create_channel
|
||||||
case permitted_params[:channel][:type]
|
case permitted_params[:channel][:type]
|
||||||
when 'web_widget'
|
when 'web_widget'
|
||||||
Current.account.web_widgets.create!(permitted_params[:channel].except(:type))
|
Current.account.web_widgets.create!(permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel].except(:type))
|
||||||
when 'api'
|
when 'api'
|
||||||
Current.account.api_channels.create!(permitted_params[:channel].except(:type))
|
Current.account.api_channels.create!(permitted_params(Channel::Api::EDITABLE_ATTRS)[:channel].except(:type))
|
||||||
when 'email'
|
when 'email'
|
||||||
Current.account.email_channels.create!(permitted_params[:channel].except(:type))
|
Current.account.email_channels.create!(permitted_params(Channel::Email::EDITABLE_ATTRS)[:channel].except(:type))
|
||||||
|
when 'telegram'
|
||||||
|
Current.account.telegram_channels.create!(permitted_params(Channel::Telegram::EDITABLE_ATTRS)[:channel].except(:type))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_channel_feature_flags
|
def update_channel_feature_flags
|
||||||
return unless inbox_update_params[:channel].key? :selected_feature_flags
|
return unless permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel].key? :selected_feature_flags
|
||||||
|
|
||||||
@inbox.channel.selected_feature_flags = inbox_update_params[:channel][:selected_feature_flags]
|
@inbox.channel.selected_feature_flags = permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel][:selected_feature_flags]
|
||||||
@inbox.channel.save!
|
@inbox.channel.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params(channel_attributes = [])
|
||||||
params.permit(:id, :avatar, :name, :greeting_message, :greeting_enabled, :enable_email_collect, :csat_survey_enabled, channel:
|
params.permit(
|
||||||
[:type, :website_url, :widget_color, :welcome_title, :welcome_tagline, :webhook_url, :email, :reply_time])
|
:name, :avatar, :greeting_enabled, :greeting_message, :enable_email_collect, :csat_survey_enabled,
|
||||||
|
:enable_auto_assignment, :working_hours_enabled, :out_of_office_message, :timezone,
|
||||||
|
channel: [:type, *channel_attributes]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox_update_params
|
def get_channel_attributes(channel_type)
|
||||||
params.permit(:enable_auto_assignment, :enable_email_collect, :name, :avatar, :greeting_message, :greeting_enabled, :csat_survey_enabled,
|
case channel_type
|
||||||
:working_hours_enabled, :out_of_office_message, :timezone,
|
when 'Channel::WebWidget'
|
||||||
channel: [
|
Channel::WebWidget::EDITABLE_ATTRS
|
||||||
:website_url,
|
when 'Channel::Api'
|
||||||
:widget_color,
|
Channel::Api::EDITABLE_ATTRS
|
||||||
:welcome_title,
|
when 'Channel::Email'
|
||||||
:welcome_tagline,
|
Channel::Email::EDITABLE_ATTRS
|
||||||
:webhook_url,
|
when 'Channel::Telegram'
|
||||||
:email,
|
Channel::Telegram::EDITABLE_ATTRS
|
||||||
:reply_time,
|
else
|
||||||
:pre_chat_form_enabled,
|
[]
|
||||||
{ pre_chat_form_options: [:pre_chat_message, :require_email] },
|
end
|
||||||
{ selected_feature_flags: [] }
|
|
||||||
])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
6
app/controllers/webhooks/telegram_controller.rb
Normal file
6
app/controllers/webhooks/telegram_controller.rb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
class Webhooks::TelegramController < ActionController::API
|
||||||
|
def process_payload
|
||||||
|
Webhooks::TelegramEventsJob.perform_later(params.to_unsafe_hash)
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -76,7 +76,7 @@ export default {
|
|||||||
if (key === 'email') {
|
if (key === 'email') {
|
||||||
return this.enabledFeatures.channel_email;
|
return this.enabledFeatures.channel_email;
|
||||||
}
|
}
|
||||||
return ['website', 'twilio', 'api', 'whatsapp', 'sms'].includes(key);
|
return ['website', 'twilio', 'api', 'whatsapp', 'sms', 'telegram'].includes(key);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -35,6 +35,13 @@
|
|||||||
:style="badgeStyle"
|
:style="badgeStyle"
|
||||||
src="~dashboard/assets/images/channels/whatsapp.png"
|
src="~dashboard/assets/images/channels/whatsapp.png"
|
||||||
/>
|
/>
|
||||||
|
<img
|
||||||
|
v-if="badge === 'Channel::Telegram'"
|
||||||
|
id="badge"
|
||||||
|
class="source-badge"
|
||||||
|
:style="badgeStyle"
|
||||||
|
src="~dashboard/assets/images/channels/telegram.png"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="showStatusIndicator"
|
v-if="showStatusIndicator"
|
||||||
:class="`source-badge user-online-status user-online-status--${status}`"
|
:class="`source-badge user-online-status user-online-status--${status}`"
|
||||||
|
|||||||
@@ -171,6 +171,19 @@
|
|||||||
"ERROR_MESSAGE": "We were not able to save the email channel"
|
"ERROR_MESSAGE": "We were not able to save the email channel"
|
||||||
},
|
},
|
||||||
"FINISH_MESSAGE": "Start forwarding your emails to the following email address."
|
"FINISH_MESSAGE": "Start forwarding your emails to the following email address."
|
||||||
|
},
|
||||||
|
"TELEGRAM_CHANNEL": {
|
||||||
|
"TITLE": "Telegram Channel",
|
||||||
|
"DESC": "Integrate with Telegram channel and start supporting your customers.",
|
||||||
|
"BOT_TOKEN": {
|
||||||
|
"LABEL": "Bot Token",
|
||||||
|
"SUBTITLE": "Configure the bot token you have obtained from Telegram BotFather.",
|
||||||
|
"PLACEHOLDER": "Bot Token"
|
||||||
|
},
|
||||||
|
"SUBMIT_BUTTON": "Create Telegram Channel",
|
||||||
|
"API": {
|
||||||
|
"ERROR_MESSAGE": "We were not able to save the telegram channel"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"AUTH": {
|
"AUTH": {
|
||||||
"TITLE": "Choose a channel",
|
"TITLE": "Choose a channel",
|
||||||
|
|||||||
@@ -48,6 +48,9 @@
|
|||||||
<span v-if="item.channel_type === 'Channel::Email'">
|
<span v-if="item.channel_type === 'Channel::Email'">
|
||||||
Email
|
Email
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="item.channel_type === 'Channel::Telegram'">
|
||||||
|
Telegram
|
||||||
|
</span>
|
||||||
<span v-if="item.channel_type === 'Channel::Api'">
|
<span v-if="item.channel_type === 'Channel::Api'">
|
||||||
{{ globalConfig.apiChannelName || 'API' }}
|
{{ globalConfig.apiChannelName || 'API' }}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Api from './channels/Api';
|
|||||||
import Email from './channels/Email';
|
import Email from './channels/Email';
|
||||||
import Sms from './channels/Sms';
|
import Sms from './channels/Sms';
|
||||||
import Whatsapp from './channels/Whatsapp';
|
import Whatsapp from './channels/Whatsapp';
|
||||||
|
import Telegram from './channels/Telegram';
|
||||||
|
|
||||||
const channelViewList = {
|
const channelViewList = {
|
||||||
facebook: Facebook,
|
facebook: Facebook,
|
||||||
@@ -14,6 +15,7 @@ const channelViewList = {
|
|||||||
email: Email,
|
email: Email,
|
||||||
sms: Sms,
|
sms: Sms,
|
||||||
whatsapp: Whatsapp,
|
whatsapp: Whatsapp,
|
||||||
|
telegram: Telegram,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
<template>
|
||||||
|
<div class="wizard-body small-9 columns">
|
||||||
|
<page-header
|
||||||
|
:header-title="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.TITLE')"
|
||||||
|
:header-content="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.DESC')"
|
||||||
|
/>
|
||||||
|
<form class="row" @submit.prevent="createChannel()">
|
||||||
|
<div class="medium-8 columns">
|
||||||
|
<label :class="{ error: $v.botToken.$error }">
|
||||||
|
{{ $t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.BOT_TOKEN.LABEL') }}
|
||||||
|
<input
|
||||||
|
v-model.trim="botToken"
|
||||||
|
type="text"
|
||||||
|
:placeholder="
|
||||||
|
$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.BOT_TOKEN.PLACEHOLDER')
|
||||||
|
"
|
||||||
|
@blur="$v.botToken.$touch"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<p class="help-text">
|
||||||
|
{{ $t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.BOT_TOKEN.SUBTITLE') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="medium-12 columns">
|
||||||
|
<woot-submit-button
|
||||||
|
:loading="uiFlags.isCreating"
|
||||||
|
:button-text="$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.SUBMIT_BUTTON')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import alertMixin from 'shared/mixins/alertMixin';
|
||||||
|
import { required } from 'vuelidate/lib/validators';
|
||||||
|
import router from '../../../../index';
|
||||||
|
import PageHeader from '../../SettingsSubPageHeader';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PageHeader,
|
||||||
|
},
|
||||||
|
mixins: [alertMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
botToken: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
uiFlags: 'inboxes/getUIFlags',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
validations: {
|
||||||
|
botToken: { required },
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createChannel() {
|
||||||
|
this.$v.$touch();
|
||||||
|
if (this.$v.$invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const telegramChannel = await this.$store.dispatch(
|
||||||
|
'inboxes/createChannel',
|
||||||
|
{
|
||||||
|
channel: {
|
||||||
|
type: 'telegram',
|
||||||
|
bot_token: this.botToken,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.replace({
|
||||||
|
name: 'settings_inboxes_add_agents',
|
||||||
|
params: {
|
||||||
|
page: 'new',
|
||||||
|
inbox_id: telegramChannel.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
this.showAlert(
|
||||||
|
this.$t('INBOX_MGMT.ADD.TELEGRAM_CHANNEL.API.ERROR_MESSAGE')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -11,6 +11,8 @@ class SendReplyJob < ApplicationJob
|
|||||||
::Twitter::SendOnTwitterService.new(message: message).perform
|
::Twitter::SendOnTwitterService.new(message: message).perform
|
||||||
when 'Channel::TwilioSms'
|
when 'Channel::TwilioSms'
|
||||||
::Twilio::SendOnTwilioService.new(message: message).perform
|
::Twilio::SendOnTwilioService.new(message: message).perform
|
||||||
|
when 'Channel::Telegram'
|
||||||
|
::Telegram::SendOnTelegramService.new(message: message).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
12
app/jobs/webhooks/telegram_events_job.rb
Normal file
12
app/jobs/webhooks/telegram_events_job.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class Webhooks::TelegramEventsJob < ApplicationJob
|
||||||
|
queue_as :default
|
||||||
|
|
||||||
|
def perform(params = {})
|
||||||
|
return unless params[:bot_token]
|
||||||
|
|
||||||
|
channel = Channel::Telegram.find_by(bot_token: params[:bot_token])
|
||||||
|
return unless channel
|
||||||
|
|
||||||
|
Telegram::IncomingMessageService.new(inbox: channel.inbox, params: params['telegram'].with_indifferent_access).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -51,6 +51,7 @@ class Account < ApplicationRecord
|
|||||||
has_many :web_widgets, dependent: :destroy, class_name: '::Channel::WebWidget'
|
has_many :web_widgets, dependent: :destroy, class_name: '::Channel::WebWidget'
|
||||||
has_many :email_channels, dependent: :destroy, class_name: '::Channel::Email'
|
has_many :email_channels, dependent: :destroy, class_name: '::Channel::Email'
|
||||||
has_many :api_channels, dependent: :destroy, class_name: '::Channel::Api'
|
has_many :api_channels, dependent: :destroy, class_name: '::Channel::Api'
|
||||||
|
has_many :telegram_channels, dependent: :destroy, class_name: '::Channel::Telegram'
|
||||||
has_many :canned_responses, dependent: :destroy
|
has_many :canned_responses, dependent: :destroy
|
||||||
has_many :webhooks, dependent: :destroy
|
has_many :webhooks, dependent: :destroy
|
||||||
has_many :labels, dependent: :destroy
|
has_many :labels, dependent: :destroy
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
class Channel::Api < ApplicationRecord
|
class Channel::Api < ApplicationRecord
|
||||||
self.table_name = 'channel_api'
|
self.table_name = 'channel_api'
|
||||||
|
EDITABLE_ATTRS = [:webhook_url].freeze
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
class Channel::Email < ApplicationRecord
|
class Channel::Email < ApplicationRecord
|
||||||
self.table_name = 'channel_email'
|
self.table_name = 'channel_email'
|
||||||
|
EDITABLE_ATTRS = [:email].freeze
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|||||||
89
app/models/channel/telegram.rb
Normal file
89
app/models/channel/telegram.rb
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: channel_telegram
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# bot_name :string
|
||||||
|
# bot_token :string not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :integer not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_channel_telegram_on_bot_token (bot_token) UNIQUE
|
||||||
|
#
|
||||||
|
|
||||||
|
class Channel::Telegram < ApplicationRecord
|
||||||
|
self.table_name = 'channel_telegram'
|
||||||
|
EDITABLE_ATTRS = [:bot_token].freeze
|
||||||
|
|
||||||
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
before_validation :ensure_valid_bot_token, on: :create
|
||||||
|
validates :account_id, presence: true
|
||||||
|
validates :bot_token, presence: true, uniqueness: true
|
||||||
|
before_save :setup_telegram_webhook
|
||||||
|
|
||||||
|
def name
|
||||||
|
'Telegram'
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_24_hour_messaging_window?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def telegram_api_url
|
||||||
|
"https://api.telegram.org/bot#{bot_token}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_message_on_telegram(message, chat_id)
|
||||||
|
response = HTTParty.post("#{telegram_api_url}/sendMessage",
|
||||||
|
body: {
|
||||||
|
chat_id: chat_id,
|
||||||
|
text: message
|
||||||
|
})
|
||||||
|
|
||||||
|
response.parsed_response['result']['message_id'] if response.success?
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_telegram_profile_image(user_id)
|
||||||
|
# get profile image from telegram
|
||||||
|
response = HTTParty.get("#{telegram_api_url}/getUserProfilePhotos", query: { user_id: user_id })
|
||||||
|
return nil unless response.success?
|
||||||
|
|
||||||
|
photos = response.parsed_response['result']['photos']
|
||||||
|
return if photos.blank?
|
||||||
|
|
||||||
|
get_telegram_file_path(photos.first.last['file_id'])
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_telegram_file_path(file_id)
|
||||||
|
response = HTTParty.get("#{telegram_api_url}/getFile", query: { file_id: file_id })
|
||||||
|
return nil unless response.success?
|
||||||
|
|
||||||
|
"https://api.telegram.org/file/bot#{bot_token}/#{response.parsed_response['result']['file_path']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_valid_bot_token
|
||||||
|
response = HTTParty.get("#{telegram_api_url}/getMe")
|
||||||
|
unless response.success?
|
||||||
|
errors.add(:bot_token, 'invalid token')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bot_name = response.parsed_response['result']['username']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup_telegram_webhook
|
||||||
|
HTTParty.post("#{telegram_api_url}/deleteWebhook")
|
||||||
|
response = HTTParty.post("#{telegram_api_url}/setWebhook",
|
||||||
|
body: {
|
||||||
|
url: "#{ENV['FRONTEND_URL']}/webhooks/telegram/#{bot_token}"
|
||||||
|
})
|
||||||
|
errors.add(:bot_token, 'error setting up the webook') unless response.success?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -26,8 +26,10 @@
|
|||||||
|
|
||||||
class Channel::WebWidget < ApplicationRecord
|
class Channel::WebWidget < ApplicationRecord
|
||||||
include FlagShihTzu
|
include FlagShihTzu
|
||||||
|
|
||||||
self.table_name = 'channel_web_widgets'
|
self.table_name = 'channel_web_widgets'
|
||||||
|
EDITABLE_ATTRS = [:website_url, :widget_color, :welcome_title, :welcome_tagline, :reply_time, :pre_chat_form_enabled,
|
||||||
|
{ pre_chat_form_options: [:pre_chat_message, :require_email] },
|
||||||
|
{ selected_feature_flags: [] }].freeze
|
||||||
|
|
||||||
validates :website_url, presence: true
|
validates :website_url, presence: true
|
||||||
validates :widget_color, presence: true
|
validates :widget_color, presence: true
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ class Inbox < ApplicationRecord
|
|||||||
channel_type == 'Channel::Api'
|
channel_type == 'Channel::Api'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def email?
|
||||||
|
channel_type == 'Channel::Email'
|
||||||
|
end
|
||||||
|
|
||||||
def inbox_type
|
def inbox_type
|
||||||
channel.name
|
channel.name
|
||||||
end
|
end
|
||||||
|
|||||||
106
app/services/telegram/incoming_message_service.rb
Normal file
106
app/services/telegram/incoming_message_service.rb
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
class Telegram::IncomingMessageService
|
||||||
|
include ::FileTypeHelper
|
||||||
|
pattr_initialize [:inbox!, :params!]
|
||||||
|
|
||||||
|
def perform
|
||||||
|
set_contact
|
||||||
|
update_contact_avatar
|
||||||
|
set_conversation
|
||||||
|
@message = @conversation.messages.create(
|
||||||
|
content: params[:message][:text],
|
||||||
|
account_id: @inbox.account_id,
|
||||||
|
inbox_id: @inbox.id,
|
||||||
|
message_type: :incoming,
|
||||||
|
sender: @contact,
|
||||||
|
source_id: (params[:message][:message_id]).to_s
|
||||||
|
)
|
||||||
|
attach_files
|
||||||
|
@message.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def account
|
||||||
|
@account ||= inbox.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_contact
|
||||||
|
contact_inbox = ::ContactBuilder.new(
|
||||||
|
source_id: params[:message][:from][:id],
|
||||||
|
inbox: inbox,
|
||||||
|
contact_attributes: contact_attributes
|
||||||
|
).perform
|
||||||
|
|
||||||
|
@contact_inbox = contact_inbox
|
||||||
|
@contact = contact_inbox.contact
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_contact_avatar
|
||||||
|
return if @contact.avatar.attached?
|
||||||
|
|
||||||
|
avatar_url = inbox.channel.get_telegram_profile_image(params[:message][:from][:id])
|
||||||
|
::ContactAvatarJob.perform_later(@contact, avatar_url) if avatar_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_params
|
||||||
|
{
|
||||||
|
account_id: @inbox.account_id,
|
||||||
|
inbox_id: @inbox.id,
|
||||||
|
contact_id: @contact.id,
|
||||||
|
contact_inbox_id: @contact_inbox.id,
|
||||||
|
additional_attributes: conversation_additional_attributes
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_conversation
|
||||||
|
@conversation = @contact_inbox.conversations.first
|
||||||
|
return if @conversation
|
||||||
|
|
||||||
|
@conversation = ::Conversation.create!(conversation_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contact_attributes
|
||||||
|
{
|
||||||
|
name: "#{params[:message][:from][:first_name]} #{params[:message][:from][:last_name]}",
|
||||||
|
additional_attributes: additional_attributes
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def additional_attributes
|
||||||
|
{
|
||||||
|
username: params[:message][:from][:username],
|
||||||
|
language_code: params[:message][:from][:language_code]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_additional_attributes
|
||||||
|
{
|
||||||
|
chat_id: params[:message][:chat][:id]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def file_content_type
|
||||||
|
params[:message][:photo].present? ? :image : file_type(params[:message][:document][:mime_type])
|
||||||
|
end
|
||||||
|
|
||||||
|
def attach_files
|
||||||
|
file = params[:message][:document]
|
||||||
|
file ||= params[:message][:photo]&.last
|
||||||
|
|
||||||
|
return unless file
|
||||||
|
|
||||||
|
attachment_file = Down.download(
|
||||||
|
inbox.channel.get_telegram_file_path(file[:file_id])
|
||||||
|
)
|
||||||
|
|
||||||
|
@message.attachments.new(
|
||||||
|
account_id: @message.account_id,
|
||||||
|
file_type: file_content_type,
|
||||||
|
file: {
|
||||||
|
io: attachment_file,
|
||||||
|
filename: attachment_file.original_filename,
|
||||||
|
content_type: attachment_file.content_type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
22
app/services/telegram/send_on_telegram_service.rb
Normal file
22
app/services/telegram/send_on_telegram_service.rb
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
class Telegram::SendOnTelegramService < Base::SendOnChannelService
|
||||||
|
private
|
||||||
|
|
||||||
|
def channel_class
|
||||||
|
Channel::Telegram
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform_reply
|
||||||
|
## send reply to telegram message api
|
||||||
|
# https://core.telegram.org/bots/api#sendmessage
|
||||||
|
message_id = channel.send_message_on_telegram(message.content, conversation[:additional_attributes]['chat_id'])
|
||||||
|
message.update!(source_id: message_id) if message_id.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def inbox
|
||||||
|
@inbox ||= message.inbox
|
||||||
|
end
|
||||||
|
|
||||||
|
def channel
|
||||||
|
@channel ||= inbox.channel
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -29,4 +29,6 @@ if resource.web_widget?
|
|||||||
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
||||||
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
||||||
end
|
end
|
||||||
|
json.email resource.channel.try(:email) if resource.email?
|
||||||
|
json.webhook_url resource.channel.try(:webhook_url) if resource.api?
|
||||||
json.inbox_identifier resource.channel.try(:identifier) if resource.api?
|
json.inbox_identifier resource.channel.try(:identifier) if resource.api?
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ Rails.application.routes.draw do
|
|||||||
mount Facebook::Messenger::Server, at: 'bot'
|
mount Facebook::Messenger::Server, at: 'bot'
|
||||||
get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc'
|
get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc'
|
||||||
post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events'
|
post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events'
|
||||||
|
post 'webhooks/telegram/:bot_token', to: 'webhooks/telegram#process_payload'
|
||||||
|
|
||||||
namespace :twitter do
|
namespace :twitter do
|
||||||
resource :callback, only: [:show]
|
resource :callback, only: [:show]
|
||||||
|
|||||||
10
db/migrate/20210828124043_add_telegram_channel.rb
Normal file
10
db/migrate/20210828124043_add_telegram_channel.rb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class AddTelegramChannel < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
create_table :channel_telegram do |t|
|
||||||
|
t.string :bot_name
|
||||||
|
t.integer :account_id, null: false
|
||||||
|
t.string :bot_token, null: false, index: { unique: true }
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
db/schema.rb
11
db/schema.rb
@@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2021_08_27_120929) do
|
ActiveRecord::Schema.define(version: 2021_08_28_124043) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
@@ -185,6 +185,15 @@ ActiveRecord::Schema.define(version: 2021_08_27_120929) do
|
|||||||
t.index ["page_id"], name: "index_channel_facebook_pages_on_page_id"
|
t.index ["page_id"], name: "index_channel_facebook_pages_on_page_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "channel_telegram", force: :cascade do |t|
|
||||||
|
t.string "bot_name"
|
||||||
|
t.integer "account_id", null: false
|
||||||
|
t.string "bot_token", null: false
|
||||||
|
t.datetime "created_at", precision: 6, null: false
|
||||||
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.index ["bot_token"], name: "index_channel_telegram_on_bot_token", unique: true
|
||||||
|
end
|
||||||
|
|
||||||
create_table "channel_twilio_sms", force: :cascade do |t|
|
create_table "channel_twilio_sms", force: :cascade do |t|
|
||||||
t.string "phone_number", null: false
|
t.string "phone_number", null: false
|
||||||
t.string "auth_token", null: false
|
t.string "auth_token", null: false
|
||||||
|
|||||||
@@ -255,16 +255,6 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||||
let(:valid_params) { { name: 'test', channel: { type: 'web_widget', website_url: 'test.com' } } }
|
let(:valid_params) { { name: 'test', channel: { type: 'web_widget', website_url: 'test.com' } } }
|
||||||
|
|
||||||
it 'creates inbox' do
|
|
||||||
post "/api/v1/accounts/#{account.id}/inboxes",
|
|
||||||
headers: admin.create_new_auth_token,
|
|
||||||
params: valid_params,
|
|
||||||
as: :json
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
expect(response.body).to include('test.com')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'will not create inbox for agent' do
|
it 'will not create inbox for agent' do
|
||||||
agent = create(:user, account: account, role: :agent)
|
agent = create(:user, account: account, role: :agent)
|
||||||
|
|
||||||
@@ -275,6 +265,26 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates a webwidget inbox when administrator' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: valid_params,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.body).to include('test.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a email inbox when administrator' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: { name: 'test', channel: { type: 'email', email: 'test@test.com' } },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.body).to include('test@test.com')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -314,6 +324,34 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
expect(inbox.reload.enable_auto_assignment).to be_falsey
|
expect(inbox.reload.enable_auto_assignment).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'updates api inbox when administrator' do
|
||||||
|
api_channel = create(:channel_api, account: account)
|
||||||
|
api_inbox = create(:inbox, channel: api_channel, account: account)
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inboxes/#{api_inbox.id}",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: { enable_auto_assignment: false, channel: { webhook_url: 'webhook.test' } },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(api_inbox.reload.enable_auto_assignment).to be_falsey
|
||||||
|
expect(api_channel.reload.webhook_url).to eq('webhook.test')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates email inbox when administrator' do
|
||||||
|
email_channel = create(:channel_email, account: account)
|
||||||
|
email_inbox = create(:inbox, channel: email_channel, account: account)
|
||||||
|
|
||||||
|
patch "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: { enable_auto_assignment: false, channel: { email: 'emailtest@email.test' } },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(email_inbox.reload.enable_auto_assignment).to be_falsey
|
||||||
|
expect(email_channel.reload.email).to eq('emailtest@email.test')
|
||||||
|
end
|
||||||
|
|
||||||
it 'updates avatar when administrator' do
|
it 'updates avatar when administrator' do
|
||||||
# no avatar before upload
|
# no avatar before upload
|
||||||
expect(inbox.avatar.attached?).to eq(false)
|
expect(inbox.avatar.attached?).to eq(false)
|
||||||
|
|||||||
12
spec/controllers/webhooks/telegram_controller_spec.rb
Normal file
12
spec/controllers/webhooks/telegram_controller_spec.rb
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Webhooks::TelegramController', type: :request do
|
||||||
|
describe 'POST /webhooks/telegram/{:bot_token}' do
|
||||||
|
it 'call the telegram events job with the params' do
|
||||||
|
allow(Webhooks::TelegramEventsJob).to receive(:perform_later)
|
||||||
|
expect(Webhooks::TelegramEventsJob).to receive(:perform_later)
|
||||||
|
post '/webhooks/telegram/random_bot_token', params: { content: 'hello' }
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
16
spec/factories/channel/channel_telegram.rb
Normal file
16
spec/factories/channel/channel_telegram.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FactoryBot.define do
|
||||||
|
factory :channel_telegram, class: 'Channel::Telegram' do
|
||||||
|
bot_token { '2324234324' }
|
||||||
|
account
|
||||||
|
|
||||||
|
before(:create) do |channel_telegram|
|
||||||
|
# we are skipping some of the validation methods
|
||||||
|
channel_telegram.define_singleton_method(:ensure_valid_bot_token) { return }
|
||||||
|
channel_telegram.define_singleton_method(:setup_telegram_webhook) { return }
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:create) do |channel_telegram|
|
||||||
|
create(:inbox, channel: channel_telegram, account: channel_telegram.account)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
59
spec/jobs/send_reply_job_spec.rb
Normal file
59
spec/jobs/send_reply_job_spec.rb
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe SendReplyJob, type: :job do
|
||||||
|
subject(:job) { described_class.perform_later(message) }
|
||||||
|
|
||||||
|
let(:message) { create(:message) }
|
||||||
|
|
||||||
|
it 'enqueues the job' do
|
||||||
|
expect { job }.to have_enqueued_job(described_class)
|
||||||
|
.with(message)
|
||||||
|
.on_queue('high')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the job is triggered on a new message' do
|
||||||
|
let(:process_service) { double }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(process_service).to receive(:perform)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls Facebook::SendOnFacebookService when its facebook message' do
|
||||||
|
facebook_channel = create(:channel_facebook_page)
|
||||||
|
facebook_inbox = create(:inbox, channel: facebook_channel)
|
||||||
|
message = create(:message, conversation: create(:conversation, inbox: facebook_inbox))
|
||||||
|
allow(Facebook::SendOnFacebookService).to receive(:new).with(message: message).and_return(process_service)
|
||||||
|
expect(Facebook::SendOnFacebookService).to receive(:new).with(message: message)
|
||||||
|
expect(process_service).to receive(:perform)
|
||||||
|
described_class.perform_now(message.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls ::Twitter::SendOnTwitterService when its twitter message' do
|
||||||
|
twitter_channel = create(:channel_twitter_profile)
|
||||||
|
twitter_inbox = create(:inbox, channel: twitter_channel)
|
||||||
|
message = create(:message, conversation: create(:conversation, inbox: twitter_inbox))
|
||||||
|
allow(::Twitter::SendOnTwitterService).to receive(:new).with(message: message).and_return(process_service)
|
||||||
|
expect(::Twitter::SendOnTwitterService).to receive(:new).with(message: message)
|
||||||
|
expect(process_service).to receive(:perform)
|
||||||
|
described_class.perform_now(message.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls ::Twilio::SendOnTwilioService when its twilio message' do
|
||||||
|
twilio_channel = create(:channel_twilio_sms)
|
||||||
|
message = create(:message, conversation: create(:conversation, inbox: twilio_channel.inbox))
|
||||||
|
allow(::Twilio::SendOnTwilioService).to receive(:new).with(message: message).and_return(process_service)
|
||||||
|
expect(::Twilio::SendOnTwilioService).to receive(:new).with(message: message)
|
||||||
|
expect(process_service).to receive(:perform)
|
||||||
|
described_class.perform_now(message.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calls ::Telegram::SendOnTelegramService when its telegram message' do
|
||||||
|
telegram_channel = create(:channel_telegram)
|
||||||
|
message = create(:message, conversation: create(:conversation, inbox: telegram_channel.inbox))
|
||||||
|
allow(::Telegram::SendOnTelegramService).to receive(:new).with(message: message).and_return(process_service)
|
||||||
|
expect(::Telegram::SendOnTelegramService).to receive(:new).with(message: message)
|
||||||
|
expect(process_service).to receive(:perform)
|
||||||
|
described_class.perform_now(message.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
36
spec/jobs/webhooks/telegram_events_job_spec.rb
Normal file
36
spec/jobs/webhooks/telegram_events_job_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Webhooks::TelegramEventsJob, type: :job do
|
||||||
|
subject(:job) { described_class.perform_later(params) }
|
||||||
|
|
||||||
|
let!(:telegram_channel) { create(:channel_telegram) }
|
||||||
|
let!(:params) { { bot_token: telegram_channel.bot_token, 'telegram' => { test: 'test' } } }
|
||||||
|
|
||||||
|
it 'enqueues the job' do
|
||||||
|
expect { job }.to have_enqueued_job(described_class)
|
||||||
|
.with(params)
|
||||||
|
.on_queue('default')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when invalid params' do
|
||||||
|
it 'returns nil when no bot_token' do
|
||||||
|
expect(described_class.perform_now({})).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil when invalid bot_token' do
|
||||||
|
expect(described_class.perform_now({ bot_token: 'invalid' })).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when valid params' do
|
||||||
|
it 'calls Telegram::IncomingMessageService' do
|
||||||
|
process_service = double
|
||||||
|
allow(Telegram::IncomingMessageService).to receive(:new).and_return(process_service)
|
||||||
|
allow(process_service).to receive(:perform)
|
||||||
|
expect(Telegram::IncomingMessageService).to receive(:new).with(inbox: telegram_channel.inbox,
|
||||||
|
params: params['telegram'].with_indifferent_access)
|
||||||
|
expect(process_service).to receive(:perform)
|
||||||
|
described_class.perform_now(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
27
spec/services/telegram/incoming_message_service_spec.rb
Normal file
27
spec/services/telegram/incoming_message_service_spec.rb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Telegram::IncomingMessageService do
|
||||||
|
let!(:telegram_channel) { create(:channel_telegram) }
|
||||||
|
|
||||||
|
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' => {
|
||||||
|
'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, 'text' => 'test'
|
||||||
|
}
|
||||||
|
}.with_indifferent_access
|
||||||
|
described_class.new(inbox: telegram_channel.inbox, params: params).perform
|
||||||
|
expect(telegram_channel.inbox.conversations).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
|
||||||
|
end
|
||||||
|
end
|
||||||
19
spec/services/telegram/send_on_telegram_service_spec.rb
Normal file
19
spec/services/telegram/send_on_telegram_service_spec.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Telegram::SendOnTelegramService do
|
||||||
|
describe '#perform' do
|
||||||
|
context 'when a valid message' do
|
||||||
|
it 'calls channel.send_message_on_telegram' do
|
||||||
|
telegram_request = double
|
||||||
|
telegram_channel = create(:channel_telegram)
|
||||||
|
message = create(:message, message_type: :outgoing, content: 'test',
|
||||||
|
conversation: create(:conversation, inbox: telegram_channel.inbox, additional_attributes: { 'chat_id' => '123' }))
|
||||||
|
allow(HTTParty).to receive(:post).and_return(telegram_request)
|
||||||
|
allow(telegram_request).to receive(:success?).and_return(true)
|
||||||
|
allow(telegram_request).to receive(:parsed_response).and_return({ 'result' => { 'message_id' => 'telegram_123' } })
|
||||||
|
described_class.new(message: message).perform
|
||||||
|
expect(message.source_id).to eq('telegram_123')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user