diff --git a/app/builders/messages/message_builder.rb b/app/builders/messages/message_builder.rb index 620d26cd1..77c894750 100644 --- a/app/builders/messages/message_builder.rb +++ b/app/builders/messages/message_builder.rb @@ -1,133 +1,128 @@ require 'open-uri' -class Messages::MessageBuilder - # This class creates both outgoing messages from chatwoot and echo outgoing messages based on the flag `outgoing_echo` - # Assumptions - # 1. Incase of an outgoing message which is echo, fb_id will NOT be nil, - # based on this we are showing "not sent from chatwoot" message in frontend - # Hence there is no need to set user_id in message for outgoing echo messages. - attr_reader :response +# This class creates both outgoing messages from chatwoot and echo outgoing messages based on the flag `outgoing_echo` +# Assumptions +# 1. Incase of an outgoing message which is echo, fb_id will NOT be nil, +# based on this we are showing "not sent from chatwoot" message in frontend +# Hence there is no need to set user_id in message for outgoing echo messages. - def initialize(response, inbox, outgoing_echo = false) - @response = response - @inbox = inbox - @sender_id = (outgoing_echo ? @response.recipient_id : @response.sender_id) - @message_type = (outgoing_echo ? :outgoing : :incoming) - end +module Messages + class MessageBuilder + attr_reader :response - def perform # for incoming - ActiveRecord::Base.transaction do - build_contact - build_conversation - build_message + def initialize(response, inbox, outgoing_echo = false) + @response = response + @inbox = inbox + @sender_id = (outgoing_echo ? @response.recipient_id : @response.sender_id) + @message_type = (outgoing_echo ? :outgoing : :incoming) end - # build_attachments - rescue StandardError => e - Raven.capture_exception(e) - # change this asap - true - end - private - - def build_attachments; end - - def contact - @contact ||= @inbox.contacts.find_by(source_id: @sender_id) - end - - def build_contact - @contact = @inbox.contacts.create!(contact_params) if contact.nil? - end - - def build_message - @message = @conversation.messages.new(message_params) - (response.attachments || []).each do |attachment| - @message.build_attachment(attachment_params(attachment)) - end - @message.save! - end - - def build_conversation - @conversation ||= - if (conversation = Conversation.find_by(conversation_params)) - conversation - else - Conversation.create!(conversation_params) + def perform + ActiveRecord::Base.transaction do + build_contact + build_message end - end - - def attachment_params(attachment) - file_type = attachment['type'].to_sym - params = { file_type: file_type, account_id: @message.account_id } - - if [:image, :file, :audio, :video].include? file_type - params.merge!(file_type_params(attachment)) - elsif file_type == :location - params.merge!(location_params(attachment)) - elsif file_type == :fallback - params.merge!(fallback_params(attachment)) - end - - params - end - - def file_type_params(attachment) - { - external_url: attachment['payload']['url'], - remote_file_url: attachment['payload']['url'] - } - end - - def location_params(attachment) - lat = attachment['payload']['coordinates']['lat'] - long = attachment['payload']['coordinates']['long'] - { - external_url: attachment['url'], - coordinates_lat: lat, - coordinates_long: long, - fallback_title: attachment['title'] - } - end - - def fallback_params(attachment) - { - fallback_title: attachment['title'], - external_url: attachment['url'] - } - end - - def conversation_params - { - account_id: @inbox.account_id, - inbox_id: @inbox.id, - sender_id: contact.id - } - end - - def message_params - { - account_id: @conversation.account_id, - inbox_id: @conversation.inbox_id, - message_type: @message_type, - content: response.content, - fb_id: response.identifier - } - end - - def contact_params - begin - k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook? - result = k.get_object(@sender_id) || {} - rescue Exception => e - result = {} + rescue StandardError => e Raven.capture_exception(e) + true + end + + private + + def contact + @contact ||= @inbox.contacts.find_by(source_id: @sender_id) + end + + def build_contact + @contact = @inbox.contacts.create!(contact_params) if contact.nil? + end + + def build_message + @message = conversation.messages.new(message_params) + (response.attachments || []).each do |attachment| + @message.build_attachment(attachment_params(attachment)) + end + @message.save! + end + + def build_attachment; end + + def conversation + @conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params) + end + + def attachment_params(attachment) + file_type = attachment['type'].to_sym + params = { file_type: file_type, account_id: @message.account_id } + + if [:image, :file, :audio, :video].include? file_type + params.merge!(file_type_params(attachment)) + elsif file_type == :location + params.merge!(location_params(attachment)) + elsif file_type == :fallback + params.merge!(fallback_params(attachment)) + end + + params + end + + def file_type_params(attachment) + { + external_url: attachment['payload']['url'], + remote_file_url: attachment['payload']['url'] + } + end + + def location_params(attachment) + lat = attachment['payload']['coordinates']['lat'] + long = attachment['payload']['coordinates']['long'] + { + external_url: attachment['url'], + coordinates_lat: lat, + coordinates_long: long, + fallback_title: attachment['title'] + } + end + + def fallback_params(attachment) + { + fallback_title: attachment['title'], + external_url: attachment['url'] + } + end + + def conversation_params + { + account_id: @inbox.account_id, + inbox_id: @inbox.id, + contact_id: contact.id + } + end + + def message_params + { + account_id: conversation.account_id, + inbox_id: conversation.inbox_id, + message_type: @message_type, + content: response.content, + fb_id: response.identifier + } + end + + def contact_params + begin + k = Koala::Facebook::API.new(@inbox.channel.page_access_token) if @inbox.facebook? + result = k.get_object(@sender_id) || {} + rescue Exception => e + result = {} + Raven.capture_exception(e) + end + { + name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}", + account_id: @inbox.account_id, + source_id: @sender_id, + remote_avatar_url: result['profile_pic'] || nil + } end - params = { - name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}", - account_id: @inbox.account_id, - source_id: @sender_id, - remote_avatar_url: result['profile_pic'] || nil - } end end diff --git a/app/models/account.rb b/app/models/account.rb index 0716218e1..f2078cd92 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -5,7 +5,7 @@ class Account < ApplicationRecord has_many :inboxes, dependent: :destroy has_many :conversations, dependent: :destroy has_many :contacts, dependent: :destroy - has_many :facebook_pages, dependent: :destroy + has_many :facebook_pages, dependent: :destroy, class_name: '::Channel::FacebookPage' has_many :telegram_bots, dependent: :destroy has_many :canned_responses, dependent: :destroy has_one :subscription, dependent: :destroy diff --git a/app/models/channel/facebook_page.rb b/app/models/channel/facebook_page.rb index 1068f78ee..ecfd3fb84 100644 --- a/app/models/channel/facebook_page.rb +++ b/app/models/channel/facebook_page.rb @@ -12,7 +12,7 @@ module Channel before_destroy :unsubscribe def name - `Facebook` + 'Facebook' end private diff --git a/app/models/contact.rb b/app/models/contact.rb index 0f447556b..d05345e2c 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -6,7 +6,7 @@ class Contact < ApplicationRecord belongs_to :account belongs_to :inbox - has_many :conversations, dependent: :destroy, foreign_key: :sender_id + has_many :conversations, dependent: :destroy mount_uploader :avatar, AvatarUploader def push_event_data diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 068cddd35..8881e9363 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -13,7 +13,7 @@ class Conversation < ApplicationRecord belongs_to :account belongs_to :inbox belongs_to :assignee, class_name: 'User', optional: true - belongs_to :sender, class_name: 'Contact' + belongs_to :contact has_many :messages, dependent: :destroy, autosave: true diff --git a/app/presenters/conversations/event_data_presenter.rb b/app/presenters/conversations/event_data_presenter.rb index 8c2940641..32ecde01f 100644 --- a/app/presenters/conversations/event_data_presenter.rb +++ b/app/presenters/conversations/event_data_presenter.rb @@ -23,7 +23,7 @@ module Conversations end def push_meta - { sender: sender.push_event_data, assignee: assignee } + { sender: contact.push_event_data, assignee: assignee } end def push_timestamps diff --git a/app/services/facebook/send_reply_service.rb b/app/services/facebook/send_reply_service.rb index fd2ef16d9..f20ebdd3f 100644 --- a/app/services/facebook/send_reply_service.rb +++ b/app/services/facebook/send_reply_service.rb @@ -12,6 +12,8 @@ module Facebook private + delegate :contact, to: :conversation + def inbox @inbox ||= message.inbox end @@ -20,10 +22,6 @@ module Facebook @conversation ||= message.conversation end - def sender - conversation.sender - end - def outgoing_message_from_chatwoot? # messages sent directly from chatwoot won't have fb_id. message.outgoing? && !message.fb_id @@ -37,7 +35,7 @@ module Facebook def fb_message_params { - recipient: { id: sender.source_id }, + recipient: { id: contact.source_id }, message: { text: message.content } } end diff --git a/app/views/api/v1/conversations/index.json.jbuilder b/app/views/api/v1/conversations/index.json.jbuilder index 843fa8847..1021ce105 100644 --- a/app/views/api/v1/conversations/index.json.jbuilder +++ b/app/views/api/v1/conversations/index.json.jbuilder @@ -9,16 +9,16 @@ json.data do json.array! @conversations do |conversation| json.meta do json.sender do - json.id conversation.sender_id - json.name conversation.sender.name - json.thumbnail conversation.sender.avatar.thumb.url + json.id conversation.contact.source_id + json.name conversation.contact.name + json.thumbnail conversation.contact.avatar.thumb.url json.channel conversation.inbox.try(:channel).try(:name) end json.assignee conversation.assignee end json.id conversation.display_id - if conversation.unread_incoming_messages.count == 0 + if conversation.unread_incoming_messages.count.zero? json.messages [conversation.messages.last.try(:push_event_data)] else json.messages conversation.unread_messages.map(&:push_event_data) diff --git a/app/views/api/v1/inboxes/index.json.jbuilder b/app/views/api/v1/inboxes/index.json.jbuilder index cb97eaff0..f7bf5f1c1 100644 --- a/app/views/api/v1/inboxes/index.json.jbuilder +++ b/app/views/api/v1/inboxes/index.json.jbuilder @@ -8,8 +8,8 @@ json.data do json.channel_id inbox.channel_id json.name inbox.name json.channel_type inbox.channel_type - json.avatar_url inbox.channel.avatar.url - json.page_id inbox.channel.page_id + json.avatar_url inbox.channel.try(:avatar).try(:url) + json.page_id inbox.channel.try(:page_id) end end end diff --git a/db/migrate/20191020173522_rename_sender_id_to_contact_in_conversation.rb b/db/migrate/20191020173522_rename_sender_id_to_contact_in_conversation.rb new file mode 100644 index 000000000..1333d2709 --- /dev/null +++ b/db/migrate/20191020173522_rename_sender_id_to_contact_in_conversation.rb @@ -0,0 +1,5 @@ +class RenameSenderIdToContactInConversation < ActiveRecord::Migration[6.1] + def change + rename_column :conversations, :sender_id, :contact_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 98ad5dbf6..1a284be96 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_10_20_085608) do +ActiveRecord::Schema.define(version: 2019_10_20_173522) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -85,7 +85,7 @@ ActiveRecord::Schema.define(version: 2019_10_20_085608) do t.integer "assignee_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.bigint "sender_id" + t.bigint "contact_id" t.integer "display_id", null: false t.datetime "user_last_seen_at" t.datetime "agent_last_seen_at" diff --git a/db/seeds.rb b/db/seeds.rb index d1946a229..75632f447 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -10,5 +10,5 @@ inbox = Inbox.create!(channel: web_widget, account: account, name: 'Acme Support InboxMember.create!(user: user, inbox: inbox) contact = Contact.create!(name: 'jane', email: 'jane@example.com', phone_number: '0000', inbox: inbox, account: account) -Conversation.create!(account: account, inbox: inbox, status: :open, assignee_id: 1, sender: contact) -Message.create!(content: 'Hello', account_id: 1, inbox_id: 1, conversation_id: 1, message_type: :incoming) \ No newline at end of file +conversation = Conversation.create!(account: account, inbox: inbox, status: :open, assignee: user, contact: contact) +Message.create!(content: 'Hello', account: account, inbox: inbox, conversation: conversation, message_type: :incoming) \ No newline at end of file diff --git a/lib/integrations/facebook/delivery_status.rb b/lib/integrations/facebook/delivery_status.rb index 96317a0e4..3a419aa8d 100644 --- a/lib/integrations/facebook/delivery_status.rb +++ b/lib/integrations/facebook/delivery_status.rb @@ -1,30 +1,34 @@ # frozen_string_literal: true -class Integrations::Facebook::DeliveryStatus - def initialize(params) - @params = params - end +module Integrations + module Facebook + class DeliveryStatus + def initialize(params) + @params = params + end - def perform - update_message_status - end + def perform + update_message_status + end - private + private - def sender_id - @params.sender['id'] - end + def sender_id + @params.sender['id'] + end - def contact_id - @contact ||= ::Contact.find_by(source_id: sender_id) - end + def contact + Contact.find_by(source_id: sender_id) + end - def conversation - @conversation ||= ::Conversation.find_by(sender_id: contact_id) - end + def conversation + @conversation ||= ::Conversation.find_by(contact_id: contact.id) + end - def update_message_status - conversation.user_last_seen_at = @params.at - conversation.save! + def update_message_status + conversation.user_last_seen_at = @params.at + conversation.save! + end + end end end diff --git a/lib/integrations/facebook/message_creator.rb b/lib/integrations/facebook/message_creator.rb index abf6c93e2..c04796f71 100644 --- a/lib/integrations/facebook/message_creator.rb +++ b/lib/integrations/facebook/message_creator.rb @@ -1,43 +1,47 @@ # frozen_string_literal: true -class Integrations::Facebook::MessageCreator - attr_reader :response +module Integrations + module Facebook + class MessageCreator + attr_reader :response - def initialize(response) - @response = response - end + def initialize(response) + @response = response + end - def perform - # begin - if outgoing_message_via_echo? - create_outgoing_message - else - create_incoming_message - end - # rescue => e - # Raven.capture_exception(e) - # end - end + def perform + # begin + if outgoing_message_via_echo? + create_outgoing_message + else + create_incoming_message + end + # rescue => e + # Raven.capture_exception(e) + # end + end - private + private - def outgoing_message_via_echo? - response.echo? && !response.sent_from_chatwoot_app? - # this means that it is an outgoing message from page, but not sent from chatwoot. - # User can send from fb page directly on mobile messenger, so this case should be handled as outgoing message - end + def outgoing_message_via_echo? + response.echo? && !response.sent_from_chatwoot_app? + # this means that it is an outgoing message from page, but not sent from chatwoot. + # User can send from fb page directly on mobile messenger, so this case should be handled as outgoing message + end - def create_outgoing_message - Channel::FacebookPage.where(page_id: response.sender_id).each do |page| - mb = Messages::Outgoing::EchoBuilder.new(response, page.inbox, true) - mb.perform - end - end + def create_outgoing_message + Channel::FacebookPage.where(page_id: response.sender_id).each do |page| + mb = Messages::Outgoing::EchoBuilder.new(response, page.inbox, true) + mb.perform + end + end - def create_incoming_message - Channel::FacebookPage.where(page_id: response.recipient_id).each do |page| - mb = Messages::IncomingMessageBuilder.new(response, page.inbox) - mb.perform + def create_incoming_message + Channel::FacebookPage.where(page_id: response.recipient_id).each do |page| + mb = Messages::IncomingMessageBuilder.new(response, page.inbox) + mb.perform + end + end end end end diff --git a/lib/integrations/facebook/message_parser.rb b/lib/integrations/facebook/message_parser.rb index 9d7a160c2..ee870c9f4 100644 --- a/lib/integrations/facebook/message_parser.rb +++ b/lib/integrations/facebook/message_parser.rb @@ -1,48 +1,52 @@ # frozen_string_literal: true -class Integrations::Facebook::MessageParser - def initialize(response_json) - @response = response_json - end +module Integrations + module Facebook + class MessageParser + def initialize(response_json) + @response = response_json + end - def sender_id - @response.sender['id'] - end + def sender_id + @response.sender['id'] + end - def recipient_id - @response.recipient['id'] - end + def recipient_id + @response.recipient['id'] + end - def time_stamp - @response.sent_at - end + def time_stamp + @response.sent_at + end - def content - @response.text - end + def content + @response.text + end - def sequence - @response.seq - end + def sequence + @response.seq + end - def attachments - @response.attachments - end + def attachments + @response.attachments + end - def identifier - @response.id - end + def identifier + @response.id + end - def echo? - @response.echo? - end + def echo? + @response.echo? + end - def app_id - @response.app_id - end + def app_id + @response.app_id + end - def sent_from_chatwoot_app? - app_id && app_id == ENV['fb_app_id'].to_i + def sent_from_chatwoot_app? + app_id && app_id == ENV['fb_app_id'].to_i + end + end end end diff --git a/lib/integrations/widget/incoming_message_builder.rb b/lib/integrations/widget/incoming_message_builder.rb index 1ac6d1349..fb3f32984 100644 --- a/lib/integrations/widget/incoming_message_builder.rb +++ b/lib/integrations/widget/incoming_message_builder.rb @@ -1,63 +1,61 @@ # frozen_string_literal: true -class Integrations::Widget::IncomingMessageBuilder - # params = { - # contact_id: 1, - # inbox_id: 1, - # content: "Hello world" - # } +module Integrations + module Widget + class Integrations::Widget::IncomingMessageBuilder + # params = { + # contact_id: 1, + # inbox_id: 1, + # content: "Hello world" + # } - attr_accessor :options, :message + attr_accessor :options, :message - def initialize(options) - @options = options - end + def initialize(options) + @options = options + end - def perform - ActiveRecord::Base.transaction do - build_conversation - build_message + def perform + ActiveRecord::Base.transaction do + build_message + end + end + + private + + def inbox + @inbox ||= Inbox.find(options[:inbox_id]) + end + + def contact + @contact ||= Contact.find(options[:contact_id]) + end + + def conversation + @conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params) + end + + def build_message + @message = conversation.messages.new(message_params) + @message.save! + end + + def conversation_params + { + account_id: inbox.account_id, + inbox_id: inbox.id, + contact_id: options[:contact_id] + } + end + + def message_params + { + account_id: conversation.account_id, + inbox_id: conversation.inbox_id, + message_type: 0, + content: options[:content] + } + end end end - - private - - def inbox - @inbox ||= Inbox.find(options[:inbox_id]) - end - - def contact - @contact ||= Contact.find(options[:contact_id]) - end - - def build_conversation - @conversation ||= - if (conversation = Conversation.find_by(conversation_params)) - conversation - else - Conversation.create!(conversation_params) - end - end - - def build_message - @message = @conversation.messages.new(message_params) - @message.save! - end - - def conversation_params - { - account_id: inbox.account_id, - inbox_id: inbox.id, - sender_id: options[:contact_id] - } - end - - def message_params - { - account_id: @conversation.account_id, - inbox_id: @conversation.inbox_id, - message_type: 0, - content: options[:content] - } - end end diff --git a/spec/factories/conversations.rb b/spec/factories/conversations.rb index 1954d6488..e6e3da4aa 100644 --- a/spec/factories/conversations.rb +++ b/spec/factories/conversations.rb @@ -16,7 +16,7 @@ FactoryBot.define do account: conversation.account, channel: create(:channel_widget, account: conversation.account) ) - conversation.sender ||= create(:contact, account: conversation.account) + conversation.contact ||= create(:contact, account: conversation.account) conversation.assignee ||= create(:user) end end diff --git a/spec/fixtures/messages.yml b/spec/fixtures/messages.yml deleted file mode 100644 index 87faafdf4..000000000 --- a/spec/fixtures/messages.yml +++ /dev/null @@ -1,23 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - content: MyText - account_id: 1 - channel_id: 1 - inbox_id: 1 - conversation_id: 1 - sender_id: 1 - sender_type: MyString - reciever_id: 1 - reciever_type: MyString - -two: - content: MyText - account_id: 1 - channel_id: 1 - inbox_id: 1 - conversation_id: 1 - sender_id: 1 - sender_type: MyString - reciever_id: 1 - reciever_type: MyString diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index 7dd56031d..817c29e43 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -79,7 +79,7 @@ RSpec.describe Conversation, type: :model do create( :conversation, account: account, - sender: create(:contact, account: account), + contact: create(:contact, account: account), inbox: inbox, assignee: nil ) @@ -205,7 +205,7 @@ RSpec.describe Conversation, type: :model do let(:expected_data) do { meta: { - sender: conversation.sender.push_event_data, + sender: conversation.contact.push_event_data, assignee: conversation.assignee }, id: conversation.display_id, diff --git a/spec/presenters/conversations/event_data_presenter_spec.rb b/spec/presenters/conversations/event_data_presenter_spec.rb index 64cfdee78..3f04519fb 100644 --- a/spec/presenters/conversations/event_data_presenter_spec.rb +++ b/spec/presenters/conversations/event_data_presenter_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Conversations::EventDataPresenter do let(:expected_data) do { meta: { - sender: conversation.sender.push_event_data, + sender: conversation.contact.push_event_data, assignee: conversation.assignee }, id: conversation.display_id,