diff --git a/app/builders/messages/facebook/message_builder.rb b/app/builders/messages/facebook/message_builder.rb index 23c9d92b4..4ffb4b101 100644 --- a/app/builders/messages/facebook/message_builder.rb +++ b/app/builders/messages/facebook/message_builder.rb @@ -148,6 +148,14 @@ class Messages::Facebook::MessageBuilder } end + def process_contact_params_result(result) + { + name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}", + account_id: @inbox.account_id, + remote_avatar_url: result['profile_pic'] || '' + } + end + def contact_params begin k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook? @@ -155,14 +163,15 @@ class Messages::Facebook::MessageBuilder rescue Koala::Facebook::AuthenticationError @inbox.channel.authorization_error! raise + rescue Koala::Facebook::ClientError => e + result = {} + # OAuthException, code: 100, error_subcode: 2018218, message: (#100) No profile available for this user + # We don't need to capture this error as we don't care about contact params in case of echo messages + Sentry.capture_exception(e) unless outgoing_echo? rescue StandardError => e result = {} Sentry.capture_exception(e) end - { - name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}", - account_id: @inbox.account_id, - remote_avatar_url: result['profile_pic'] || '' - } + process_contact_params_result(result) end end diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb index cb01fc380..df055923c 100644 --- a/app/controllers/api/v1/accounts/inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/inboxes_controller.rb @@ -100,6 +100,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController end def update_channel_feature_flags + return unless @inbox.web_widget? return unless permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel].key? :selected_feature_flags @inbox.channel.selected_feature_flags = permitted_params(Channel::WebWidget::EDITABLE_ATTRS)[:channel][:selected_feature_flags] diff --git a/app/controllers/super_admin/dashboard_controller.rb b/app/controllers/super_admin/dashboard_controller.rb index b5f3d34eb..56d19d563 100644 --- a/app/controllers/super_admin/dashboard_controller.rb +++ b/app/controllers/super_admin/dashboard_controller.rb @@ -3,10 +3,9 @@ class SuperAdmin::DashboardController < SuperAdmin::ApplicationController def index @data = Conversation.unscoped.group_by_day(:created_at, range: 30.days.ago..2.seconds.ago).count.to_a - @accounts_count = number_with_delimiter(Account.all.length) - @users_count = number_with_delimiter(User.all.length) - @inboxes_count = number_with_delimiter(Inbox.all.length) - @conversations_count = number_with_delimiter(Conversation.all.length) - @messages_count = number_with_delimiter(Message.all.length) + @accounts_count = number_with_delimiter(Account.count) + @users_count = number_with_delimiter(User.count) + @inboxes_count = number_with_delimiter(Inbox.count) + @conversations_count = number_with_delimiter(Conversation.count) end end diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index 6d65ba2dd..5ca64d215 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -194,7 +194,7 @@ export default { }); }, methods: { - handleKeyEvents(e) { + getKeyboardListenerParams() { const allConversations = this.$refs.activeConversation.querySelectorAll( 'div.conversations-list div.conversation' ); @@ -205,7 +205,19 @@ export default { activeConversation ); const lastConversationIndex = allConversations.length - 1; + return { + allConversations, + activeConversation, + activeConversationIndex, + lastConversationIndex, + }; + }, + handleKeyEvents(e) { if (hasPressedAltAndJKey(e)) { + const { + allConversations, + activeConversationIndex, + } = this.getKeyboardListenerParams(); if (activeConversationIndex === -1) { allConversations[0].click(); } @@ -214,6 +226,11 @@ export default { } } if (hasPressedAltAndKKey(e)) { + const { + allConversations, + activeConversationIndex, + lastConversationIndex, + } = this.getKeyboardListenerParams(); if (activeConversationIndex === -1) { allConversations[lastConversationIndex].click(); } else if (activeConversationIndex < lastConversationIndex) { diff --git a/app/javascript/dashboard/components/buttons/ResolveAction.vue b/app/javascript/dashboard/components/buttons/ResolveAction.vue index 76090b89f..637424815 100644 --- a/app/javascript/dashboard/components/buttons/ResolveAction.vue +++ b/app/javascript/dashboard/components/buttons/ResolveAction.vue @@ -176,7 +176,9 @@ export default { '.conversations-list .conversation' ); if (hasPressedAltAndMKey(e)) { - this.$refs.arrowDownButton.$el.click(); + if (this.$refs.arrowDownButton) { + this.$refs.arrowDownButton.$el.click(); + } } if (hasPressedAltAndEKey(e)) { const activeConversation = document.querySelector( diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index e8c0e21f8..07314e9c0 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -95,10 +95,15 @@ import AddAccountModal from './sidebarComponents/AddAccountModal.vue'; import AddLabelModal from '../../routes/dashboard/settings/labels/AddLabel'; import WootKeyShortcutModal from 'components/widgets/modal/WootKeyShortcutModal'; import { + hasPressedAltAndCKey, + hasPressedAltAndRKey, + hasPressedAltAndSKey, + hasPressedAltAndVKey, hasPressedCommandAndForwardSlash, isEscape, } from 'shared/helpers/KeyboardHelpers'; import eventListenerMixins from 'shared/mixins/eventListenerMixins'; +import router from '../../routes'; export default { components: { @@ -276,6 +281,27 @@ export default { if (isEscape(e)) { this.closeKeyShortcutModal(); } + + if (hasPressedAltAndCKey(e)) { + if (!this.isCurrentRouteSameAsNavigation('home')) { + router.push({ name: 'home' }); + } + } else if (hasPressedAltAndVKey(e)) { + if (!this.isCurrentRouteSameAsNavigation('contacts_dashboard')) { + router.push({ name: 'contacts_dashboard' }); + } + } else if (hasPressedAltAndRKey(e)) { + if (!this.isCurrentRouteSameAsNavigation('settings_account_reports')) { + router.push({ name: 'settings_account_reports' }); + } + } else if (hasPressedAltAndSKey(e)) { + if (!this.isCurrentRouteSameAsNavigation('agent_list')) { + router.push({ name: 'agent_list' }); + } + } + }, + isCurrentRouteSameAsNavigation(routeName) { + return router.currentRoute && router.currentRoute.name === routeName; }, toggleSupportChatWindow() { window.$chatwoot.toggle(); diff --git a/app/javascript/dashboard/components/layout/SidebarItem.vue b/app/javascript/dashboard/components/layout/SidebarItem.vue index 1799a829a..462853e6a 100644 --- a/app/javascript/dashboard/components/layout/SidebarItem.vue +++ b/app/javascript/dashboard/components/layout/SidebarItem.vue @@ -33,7 +33,7 @@
@@ -59,17 +59,10 @@ import { mapGetters } from 'vuex'; import router from '../../routes'; -import { - hasPressedAltAndCKey, - hasPressedAltAndVKey, - hasPressedAltAndRKey, - hasPressedAltAndSKey, -} from 'shared/helpers/KeyboardHelpers'; import adminMixin from '../../mixins/isAdmin'; -import eventListenerMixins from 'shared/mixins/eventListenerMixins'; import { getInboxClassByType } from 'dashboard/helper/inbox'; export default { - mixins: [adminMixin, eventListenerMixins], + mixins: [adminMixin], props: { menuItem: { type: Object, @@ -124,20 +117,6 @@ export default { } } }, - handleKeyEvents(e) { - if (hasPressedAltAndCKey(e)) { - router.push({ name: 'home' }); - } - if (hasPressedAltAndVKey(e)) { - router.push({ name: 'contacts_dashboard' }); - } - if (hasPressedAltAndRKey(e)) { - router.push({ name: 'settings_account_reports' }); - } - if (hasPressedAltAndSKey(e)) { - router.push({ name: 'settings_home' }); - } - }, showItem(item) { return this.isAdmin && item.newLink !== undefined; }, diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue index b9c9688fd..e06cdb239 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue @@ -76,7 +76,6 @@ import { mapGetters } from 'vuex'; import { mixin as clickaway } from 'vue-clickaway'; import alertMixin from 'shared/mixins/alertMixin'; -import eventListenerMixins from 'shared/mixins/eventListenerMixins'; import EmojiInput from 'shared/components/emoji/EmojiInput'; import CannedResponse from './CannedResponse'; @@ -108,13 +107,7 @@ export default { ReplyBottomPanel, WootMessageEditor, }, - mixins: [ - clickaway, - inboxMixin, - uiSettingsMixin, - alertMixin, - eventListenerMixins, - ], + mixins: [clickaway, inboxMixin, uiSettingsMixin, alertMixin], props: { selectedTweet: { type: [Object, String], @@ -304,6 +297,15 @@ export default { } }, }, + + mounted() { + // Donot use the keyboard listener mixin here as the events here are supposed to be + // working even if input/textarea is focussed. + document.addEventListener('keydown', this.handleKeyEvents); + }, + destroyed() { + document.removeEventListener('keydown', this.handleKeyEvents); + }, methods: { toggleUserMention(currentMentionState) { this.hasUserMention = currentMentionState; diff --git a/app/javascript/dashboard/helper/inbox.js b/app/javascript/dashboard/helper/inbox.js index af8917dab..3b91f1bc9 100644 --- a/app/javascript/dashboard/helper/inbox.js +++ b/app/javascript/dashboard/helper/inbox.js @@ -22,7 +22,10 @@ export const getInboxClassByType = (type, phoneNumber) => { case INBOX_TYPES.EMAIL: return 'ion-ios-email'; + case INBOX_TYPES.TELEGRAM: + return 'ion-ios-navigate'; + default: - return ''; + return 'ion-ios-chatbubble'; } }; diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json index 329c48d31..4cd9de91f 100644 --- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json +++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json @@ -195,6 +195,10 @@ "SUBMIT_BUTTON": "Create LINE Channel", "API": { "ERROR_MESSAGE": "We were not able to save the LINE channel" + }, + "API_CALLBACK": { + "TITLE": "Callback URL", + "SUBTITLE": "You have to configure the webhook URL in LINE application with the URL mentioned here." } }, "TELEGRAM_CHANNEL": { diff --git a/app/javascript/dashboard/i18n/locale/it/chatlist.json b/app/javascript/dashboard/i18n/locale/it/chatlist.json index e419e2e00..0f7988618 100644 --- a/app/javascript/dashboard/i18n/locale/it/chatlist.json +++ b/app/javascript/dashboard/i18n/locale/it/chatlist.json @@ -13,18 +13,18 @@ "STATUS_TABS": [ { "NAME": "Apri", - "KEY": "contaaperture" + "KEY": "openCount" }, { "NAME": "Risolti", - "KEY": "Conteggio" + "KEY": "allConvCount" } ], "ASSIGNEE_TYPE_TABS": [ { "NAME": "Miniera", "KEY": "Io", - "COUNT_KEY": "contaMinore" + "COUNT_KEY": "mineCount" }, { "NAME": "Non assegnato", @@ -40,11 +40,11 @@ "CHAT_STATUS_ITEMS": [ { "TEXT": "Apri", - "VALUE": "Aperto" + "VALUE": "open" }, { "TEXT": "Risolti", - "VALUE": "risolto" + "VALUE": "resolved" }, { "TEXT": "Pending", diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/AddAgents.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/AddAgents.vue index 3a8c93350..b42e5e9c3 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/AddAgents.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/AddAgents.vue @@ -88,7 +88,7 @@ export default { const selectedAgents = this.selectedAgents.map(x => x.id); try { - await InboxMembersAPI.create({ inboxId, agentList: selectedAgents }); + await InboxMembersAPI.update({ inboxId, agentList: selectedAgents }); router.replace({ name: 'settings_inbox_finish', params: { diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue index ad2bab48d..95cbd7646 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/FinishSetup.vue @@ -17,7 +17,7 @@
@@ -25,7 +25,7 @@ @@ -93,6 +93,12 @@ export default { )}`; } + if (this.isALineInbox) { + return `${this.$t('INBOX_MGMT.FINISH.MESSAGE')}. ${this.$t( + 'INBOX_MGMT.ADD.LINE_CHANNEL.API_CALLBACK.SUBTITLE' + )}`; + } + if (this.isAEmailInbox) { return this.$t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE'); } diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue index dc5ec797a..a708f8064 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Index.vue @@ -51,6 +51,9 @@ Telegram + + Line + {{ globalConfig.apiChannelName || 'API' }} diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue index 686497baa..4a241102f 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue @@ -259,7 +259,21 @@ :title="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.TITLE')" :sub-title="$t('INBOX_MGMT.ADD.TWILIO.API_CALLBACK.SUBTITLE')" > - + + + +
+ +
@@ -398,7 +412,12 @@ export default { ]; } - if (this.isATwilioChannel || this.isAPIInbox || this.isAnEmailChannel) { + if ( + this.isATwilioChannel || + this.isALineChannel || + this.isAPIInbox || + this.isAnEmailChannel + ) { return [ ...visibleToAllChannelTabs, { diff --git a/app/javascript/sdk/sdk.js b/app/javascript/sdk/sdk.js index 324b5a835..ebdbdf1ca 100644 --- a/app/javascript/sdk/sdk.js +++ b/app/javascript/sdk/sdk.js @@ -99,6 +99,7 @@ export const SDK_CSS = `.woot-widget-holder { .woot--close::before, .woot--close::after { background-color: #fff; content: ' '; + display: inline; height: 24px; left: 32px; position: absolute; @@ -149,7 +150,7 @@ export const SDK_CSS = `.woot-widget-holder { max-height: 100vh; padding: 0 8px; } - + .woot-widget-holder.has-unread-view iframe { min-height: unset !important; } @@ -157,7 +158,7 @@ export const SDK_CSS = `.woot-widget-holder { .woot-widget-holder.has-unread-view.woot-elements--left { left: 0; } - + .woot-widget-bubble.woot--close { bottom: 60px; opacity: 0; diff --git a/app/javascript/shared/mixins/eventListenerMixins.js b/app/javascript/shared/mixins/eventListenerMixins.js index aad6952b3..0a426c2aa 100644 --- a/app/javascript/shared/mixins/eventListenerMixins.js +++ b/app/javascript/shared/mixins/eventListenerMixins.js @@ -1,8 +1,28 @@ +import { isEscape } from '../helpers/KeyboardHelpers'; + export default { mounted() { - document.addEventListener('keydown', this.handleKeyEvents); + document.addEventListener('keydown', this.onKeyDownHandler); }, - destroyed() { - document.removeEventListener('keydown', this.handleKeyEvents); + beforeDestroy() { + document.removeEventListener('keydown', this.onKeyDownHandler); + }, + methods: { + onKeyDownHandler(e) { + const isEventFromAnInputBox = + e.target?.tagName === 'INPUT' || e.target?.tagName === 'TEXTAREA'; + const isEventFromProseMirror = e.target?.className?.includes( + 'ProseMirror' + ); + + if (isEventFromAnInputBox || isEventFromProseMirror) { + if (isEscape(e)) { + e.target.blur(); + } + return; + } + + this.handleKeyEvents(e); + }, }, }; diff --git a/app/javascript/shared/mixins/inboxMixin.js b/app/javascript/shared/mixins/inboxMixin.js index 361ffdce2..3afd16508 100644 --- a/app/javascript/shared/mixins/inboxMixin.js +++ b/app/javascript/shared/mixins/inboxMixin.js @@ -5,6 +5,8 @@ export const INBOX_TYPES = { TWILIO: 'Channel::TwilioSms', API: 'Channel::Api', EMAIL: 'Channel::Email', + TELEGRAM: 'Channel::Telegram', + LINE: 'Channel::Line', }; export default { @@ -27,6 +29,9 @@ export default { isATwilioChannel() { return this.channelType === INBOX_TYPES.TWILIO; }, + isALineChannel() { + return this.channelType === INBOX_TYPES.LINE; + }, isAnEmailChannel() { return this.channelType === INBOX_TYPES.EMAIL; }, diff --git a/app/jobs/contact_ip_lookup_job.rb b/app/jobs/contact_ip_lookup_job.rb index 9b9d7f4b3..22ced17fe 100644 --- a/app/jobs/contact_ip_lookup_job.rb +++ b/app/jobs/contact_ip_lookup_job.rb @@ -27,6 +27,7 @@ class ContactIpLookupJob < ApplicationJob geocoder_result = Geocoder.search(ip).first return unless geocoder_result + contact.additional_attributes ||= {} contact.additional_attributes['city'] = geocoder_result.city contact.additional_attributes['country'] = geocoder_result.country contact.additional_attributes['country_code'] = geocoder_result.country_code @@ -34,7 +35,7 @@ class ContactIpLookupJob < ApplicationJob end def get_contact_ip(contact) - contact.additional_attributes['updated_at_ip'] || contact.additional_attributes['created_at_ip'] + contact.additional_attributes&.dig('updated_at_ip') || contact.additional_attributes&.dig('created_at_ip') end def ensure_look_up_db diff --git a/app/models/inbox.rb b/app/models/inbox.rb index 9853ce79d..437e7fd99 100644 --- a/app/models/inbox.rb +++ b/app/models/inbox.rb @@ -31,6 +31,7 @@ class Inbox < ApplicationRecord include Avatarable include OutOfOffisable + validates :name, presence: true validates :account_id, presence: true validates :timezone, inclusion: { in: TZInfo::Timezone.all_identifiers } @@ -93,9 +94,9 @@ class Inbox < ApplicationRecord } end - def webhook_url + def callback_webhook_url case channel_type - when 'Channel::TwilioSMS' + when 'Channel::TwilioSms' "#{ENV['FRONTEND_URL']}/twilio/callback" when 'Channel::Line' "#{ENV['FRONTEND_URL']}/webhooks/line/#{channel.line_channel_id}" diff --git a/app/services/line/incoming_message_service.rb b/app/services/line/incoming_message_service.rb index 60204c8ad..80e10c2e1 100644 --- a/app/services/line/incoming_message_service.rb +++ b/app/services/line/incoming_message_service.rb @@ -1,9 +1,17 @@ +# ref : https://developers.line.biz/en/docs/messaging-api/receiving-messages/#webhook-event-types +# https://developers.line.biz/en/reference/messaging-api/#message-event + class Line::IncomingMessageService include ::FileTypeHelper pattr_initialize [:inbox!, :params!] def perform + # probably test events + return if params[:events].blank? + line_contact_info + return if line_contact_info['userId'].blank? + set_contact set_conversation # TODO: iterate over the events and handle the attachments in future diff --git a/app/services/telegram/incoming_message_service.rb b/app/services/telegram/incoming_message_service.rb index 0e47f2813..ac1459887 100644 --- a/app/services/telegram/incoming_message_service.rb +++ b/app/services/telegram/incoming_message_service.rb @@ -1,8 +1,14 @@ +# Find the various telegram payload samples here: https://core.telegram.org/bots/webhooks#testing-your-bot-with-updates +# https://core.telegram.org/bots/api#available-types + class Telegram::IncomingMessageService include ::FileTypeHelper pattr_initialize [:inbox!, :params!] def perform + # chatwoot doesn't support group conversations at the moment + return unless private_message? + set_contact update_contact_avatar set_conversation @@ -20,6 +26,10 @@ class Telegram::IncomingMessageService private + def private_message? + params.dig(:message, :chat, :type) == 'private' + end + def account @account ||= inbox.account end diff --git a/app/views/api/v1/models/_inbox.json.jbuilder b/app/views/api/v1/models/_inbox.json.jbuilder index 4d97d0a18..20e1ca36c 100644 --- a/app/views/api/v1/models/_inbox.json.jbuilder +++ b/app/views/api/v1/models/_inbox.json.jbuilder @@ -1,4 +1,5 @@ json.id resource.id +json.avatar_url resource.try(:avatar_url) json.channel_id resource.channel_id json.name resource.name json.channel_type resource.channel_type @@ -6,30 +7,42 @@ json.greeting_enabled resource.greeting_enabled json.greeting_message resource.greeting_message json.working_hours_enabled resource.working_hours_enabled json.enable_email_collect resource.enable_email_collect -json.out_of_office_message resource.out_of_office_message json.csat_survey_enabled resource.csat_survey_enabled +json.enable_auto_assignment resource.enable_auto_assignment +json.out_of_office_message resource.out_of_office_message json.working_hours resource.weekly_schedule json.timezone resource.timezone -json.webhook_url resource.webhook_url -json.avatar_url resource.try(:avatar_url) -json.page_id resource.channel.try(:page_id) +json.callback_webhook_url resource.callback_webhook_url + +## Channel specific settings +## TODO : Clean up and move the attributes into channel sub section + +## WebWidget Attributes json.widget_color resource.channel.try(:widget_color) json.website_url resource.channel.try(:website_url) json.welcome_title resource.channel.try(:welcome_title) json.welcome_tagline resource.channel.try(:welcome_tagline) -json.enable_auto_assignment resource.enable_auto_assignment json.web_widget_script resource.channel.try(:web_widget_script) json.website_token resource.channel.try(:website_token) -json.forward_to_email resource.channel.try(:forward_to_email) -json.phone_number resource.channel.try(:phone_number) json.selected_feature_flags resource.channel.try(:selected_feature_flags) json.reply_time resource.channel.try(:reply_time) -json.reauthorization_required resource.channel.try(:reauthorization_required?) if resource.facebook? if resource.web_widget? json.hmac_token resource.channel.try(:hmac_token) json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled) json.pre_chat_form_options resource.channel.try(:pre_chat_form_options) end + +## Facebook Attributes +json.page_id resource.channel.try(:page_id) +json.reauthorization_required resource.channel.try(:reauthorization_required?) if resource.facebook? + +## Twilio Attributes +json.phone_number resource.channel.try(:phone_number) + +## Email Channel Attributes +json.forward_to_email resource.channel.try(:forward_to_email) json.email resource.channel.try(:email) if resource.email? + +## API Channel Attributes json.webhook_url resource.channel.try(:webhook_url) if resource.api? json.inbox_identifier resource.channel.try(:identifier) if resource.api? diff --git a/app/views/super_admin/dashboard/index.html.erb b/app/views/super_admin/dashboard/index.html.erb index 967c00e55..0121a8a75 100644 --- a/app/views/super_admin/dashboard/index.html.erb +++ b/app/views/super_admin/dashboard/index.html.erb @@ -53,10 +53,6 @@ It renders the `_table` partial to display details about the resources.
<%= @conversations_count %>
Conversations
-
-
<%= @messages_count %>
-
Messages
-
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 3070d2877..1e1aa9cf8 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -54,13 +54,19 @@ class Rack::Attack # ref: https://github.com/rack/rack-attack/issues/399 throttle('login/email', limit: 20, period: 5.minutes) do |req| - email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence - email.to_s.downcase.gsub(/\s+/, '') if req.path == '/auth/sign_in' && req.post? + if req.path == '/auth/sign_in' && req.post? + # NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8 + # Hence placed in the if block + email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence + email.to_s.downcase.gsub(/\s+/, '') + end end throttle('reset_password/email', limit: 5, period: 1.hour) do |req| - email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence - email.to_s.downcase.gsub(/\s+/, '') if req.path == '/auth/password' && req.post? + if req.path == '/auth/password' && req.post? + email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence + email.to_s.downcase.gsub(/\s+/, '') + end end end diff --git a/deployment/setup_18.04.sh b/deployment/setup_18.04.sh index 551b08e63..37d63e2c1 100644 --- a/deployment/setup_18.04.sh +++ b/deployment/setup_18.04.sh @@ -63,7 +63,7 @@ sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env sed -i -e '/POSTGRES_USERNAME/ s/=.*/=chatwoot/' .env sed -i -e "/POSTGRES_PASSWORD/ s/=.*/=$pg_pass/" .env sed -i -e '/RAILS_ENV/ s/=.*/=$RAILS_ENV/' .env -echo -en "\nINSTALLATION_ENV=LINUX_SCRIPT" >> ".env" +echo -en "\nINSTALLATION_ENV=linux_script" >> ".env" RAILS_ENV=production bundle exec rake db:create RAILS_ENV=production bundle exec rake db:reset diff --git a/deployment/setup_20.04.sh b/deployment/setup_20.04.sh index 9ab2352a0..0b7aaecea 100644 --- a/deployment/setup_20.04.sh +++ b/deployment/setup_20.04.sh @@ -70,7 +70,7 @@ sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env sed -i -e '/POSTGRES_USERNAME/ s/=.*/=chatwoot/' .env sed -i -e "/POSTGRES_PASSWORD/ s/=.*/=$pg_pass/" .env sed -i -e '/RAILS_ENV/ s/=.*/=$RAILS_ENV/' .env -echo -en "\nINSTALLATION_ENV=LINUX_SCRIPT" >> ".env" +echo -en "\nINSTALLATION_ENV=linux_script" >> ".env" RAILS_ENV=production bundle exec rake db:create RAILS_ENV=production bundle exec rake db:reset diff --git a/lib/integrations/dialogflow/processor_service.rb b/lib/integrations/dialogflow/processor_service.rb index 32359813a..66911313f 100644 --- a/lib/integrations/dialogflow/processor_service.rb +++ b/lib/integrations/dialogflow/processor_service.rb @@ -7,14 +7,18 @@ class Integrations::Dialogflow::ProcessorService return unless processable_message?(message) return unless message.conversation.pending? - response = get_dialogflow_response(message.conversation.contact_inbox.source_id, message_content(message)) - process_response(message, response) + content = message_content(message) + response = get_dialogflow_response(message.conversation.contact_inbox.source_id, content) if content.present? + process_response(message, response) if response.present? end private def message_content(message) - return message.content_attributes['submitted_values'].first['value'] if event_name == 'message.updated' + # TODO: might needs to change this to a way that we fetch the updated value from event data instead + # cause the message.updated event could be that that the message was deleted + + return message.content_attributes['submitted_values']&.dig 'value' if event_name == 'message.updated' message.content end diff --git a/lib/integrations/slack/send_on_slack_service.rb b/lib/integrations/slack/send_on_slack_service.rb index 87040c04a..ba3e68669 100644 --- a/lib/integrations/slack/send_on_slack_service.rb +++ b/lib/integrations/slack/send_on_slack_service.rb @@ -45,6 +45,8 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService end def send_message + return if message_content.blank? + @slack_message = slack_client.chat_postMessage( channel: hook.reference_id, text: message_content, diff --git a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb index 2e8ecdaea..6148c5148 100644 --- a/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/inboxes_controller_spec.rb @@ -306,6 +306,7 @@ RSpec.describe 'Inboxes API', type: :request do expect(response).to have_http_status(:success) expect(response.body).to include('Line Inbox') + expect(response.body).to include('callback_webhook_url') end end end @@ -352,7 +353,7 @@ RSpec.describe 'Inboxes API', type: :request do 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' } }, + params: { enable_auto_assignment: false, channel: { webhook_url: 'webhook.test', selected_feature_flags: [] } }, as: :json expect(response).to have_http_status(:success) diff --git a/spec/services/telegram/incoming_message_service_spec.rb b/spec/services/telegram/incoming_message_service_spec.rb index 79e361c79..9a1433794 100644 --- a/spec/services/telegram/incoming_message_service_spec.rb +++ b/spec/services/telegram/incoming_message_service_spec.rb @@ -18,10 +18,28 @@ describe Telegram::IncomingMessageService do } }.with_indifferent_access described_class.new(inbox: telegram_channel.inbox, params: params).perform - expect(telegram_channel.inbox.conversations).not_to eq(0) + 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 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 end end