Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Email collect message hooks #331

Merged
merged 28 commits into from
Jan 9, 2020
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cefb0f1
[#273] email collect message hooks
sojan-official Nov 30, 2019
39d72c9
should happen only on web widget conversations
sojan-official Nov 30, 2019
0420478
Fix specs
sojan-official Dec 1, 2019
2e222d1
Reduce cognitive complexity
sojan-official Dec 2, 2019
01566b6
Tests for existing methods in widgetcontrollers
sojan-official Dec 2, 2019
adb9607
End point to update contact
sojan-official Dec 2, 2019
b8a48a8
Save the attributes in content attributes
sojan-official Dec 5, 2019
1da773e
Show input if message_type = template
pranavrajs Dec 6, 2019
5f4e1df
Token Service
pranavrajs Dec 9, 2019
9000fa1
Fix import errors
pranavrajs Dec 15, 2019
0d1dd18
Add bot image
pranavrajs Dec 15, 2019
fbf37e0
Fix rspec
pranavrajs Dec 24, 2019
d326f7e
Spec for hook execution services
sojan-official Dec 26, 2019
8a7b1a0
Tests to behave like production
sojan-official Dec 26, 2019
cb24885
Spec for widget messages controller
sojan-official Dec 26, 2019
86d1a96
Returns token after updating contact
sojan-official Dec 27, 2019
4ebb791
Update the token on update contact
pranavrajs Dec 28, 2019
2731788
Remove contact_id from cw_conversation
pranavrajs Jan 7, 2020
7db6c15
Add contact_inboxes_id to converstions
pranavrajs Jan 7, 2020
cae0787
Revert schema.rb rubocop changes
pranavrajs Jan 7, 2020
3eb046d
Fix contact_inbox_id attribute
pranavrajs Jan 7, 2020
175abbc
Parse name from email, fix code climate issue
pranavrajs Jan 8, 2020
bc576fe
Fix schema.rb
pranavrajs Jan 8, 2020
5b797c1
Fix breaking specs
pranavrajs Jan 8, 2020
ecf36d6
Spec fixes
sojan-official Jan 8, 2020
7d1c1a6
Fix rubocop
sojan-official Jan 8, 2020
e7f26fe
Rubocop Fixes
pranavrajs Jan 9, 2020
122b70b
Fix breaking migration
pranavrajs Jan 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -4,13 +4,13 @@ require:
- rubocop-rspec
inherit_from: .rubocop_todo.yml

Metrics/LineLength:
Layout/LineLength:
Max: 150
Metrics/ClassLength:
Max: 125
RSpec/ExampleLength:
Max: 15
Documentation:
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
215 changes: 110 additions & 105 deletions app/builders/messages/message_builder.rb
Original file line number Diff line number Diff line change
@@ -6,132 +6,137 @@
# 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.

module Messages
class MessageBuilder
attr_reader :response

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
class Messages::MessageBuilder
attr_reader :response

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

def perform
ActiveRecord::Base.transaction do
build_contact
build_message
end
rescue StandardError => e
Raven.capture_exception(e)
true
def perform
ActiveRecord::Base.transaction do
build_contact
build_message
end
rescue StandardError => e
Raven.capture_exception(e)
true
end

private
private

def contact
@contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact
end
def contact
@contact ||= @inbox.contact_inboxes.find_by(source_id: @sender_id)&.contact
end

def build_contact
return if contact.present?
def build_contact
return if contact.present?

@contact = Contact.create!(contact_params.except(:remote_avatar_url))
avatar_resource = LocalResource.new(contact_params[:remote_avatar_url])
@contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
@contact = Contact.create!(contact_params.except(:remote_avatar_url))
avatar_resource = LocalResource.new(contact_params[:remote_avatar_url])
@contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)

ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
end
@contact_inbox = ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
end

def build_message
@message = conversation.messages.create!(message_params)
(response.attachments || []).each do |attachment|
attachment_obj = @message.build_attachment(attachment_params(attachment).except(:remote_file_url))
attachment_obj.save!
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
end
def build_message
@message = conversation.messages.create!(message_params)
(response.attachments || []).each do |attachment|
attachment_obj = @message.build_attachment(attachment_params(attachment).except(:remote_file_url))
attachment_obj.save!
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
end
end

def attach_file(attachment, file_url)
file_resource = LocalResource.new(file_url)
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
end
def attach_file(attachment, file_url)
file_resource = LocalResource.new(file_url)
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
end

def conversation
@conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params)
end
def conversation
@conversation ||= Conversation.find_by(conversation_params) || build_conversation
end

def attachment_params(attachment)
file_type = attachment['type'].to_sym
params = { file_type: file_type, account_id: @message.account_id }
def build_conversation
@contact_inbox ||= contact.contact_inboxes.find_by!(source_id: @sender_id)
Conversation.create!(conversation_params.merge(
contact_inbox_id: @contact_inbox.id
))
end

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
def attachment_params(attachment)
file_type = attachment['type'].to_sym
params = { file_type: file_type, account_id: @message.account_id }

params
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

def file_type_params(attachment)
{
external_url: attachment['payload']['url'],
remote_file_url: attachment['payload']['url']
}
end
params
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 file_type_params(attachment)
{
external_url: attachment['payload']['url'],
remote_file_url: attachment['payload']['url']
}
end

def fallback_params(attachment)
{
fallback_title: attachment['title'],
external_url: attachment['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 conversation_params
{
account_id: @inbox.account_id,
inbox_id: @inbox.id,
contact_id: contact.id
}
end
def fallback_params(attachment)
{
fallback_title: attachment['title'],
external_url: attachment['url']
}
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 conversation_params
{
account_id: @inbox.account_id,
inbox_id: @inbox.id,
contact_id: contact.id
}
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,
remote_avatar_url: result['profile_pic'] || ''
}
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,
remote_avatar_url: result['profile_pic'] || ''
}
end
end
84 changes: 40 additions & 44 deletions app/controllers/api/v1/inbox_members_controller.rb
Original file line number Diff line number Diff line change
@@ -1,55 +1,51 @@
module Api
module V1
class InboxMembersController < Api::BaseController
before_action :fetch_inbox, only: [:create, :show]
before_action :current_agents_ids, only: [:create]

def create
# update also done via same action
if @inbox
begin
update_agents_list
head :ok
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
render_could_not_create_error('Could not add agents to inbox')
end
else
render_not_found_error('Agents or inbox not found')
end
class Api::V1::InboxMembersController < Api::BaseController
before_action :fetch_inbox, only: [:create, :show]
before_action :current_agents_ids, only: [:create]

def create
# update also done via same action
if @inbox
begin
update_agents_list
head :ok
rescue StandardError => e
Rails.logger.debug "Rescued: #{e.inspect}"
render_could_not_create_error('Could not add agents to inbox')
end
else
render_not_found_error('Agents or inbox not found')
end
end

def show
@agents = current_account.users.where(id: @inbox.members.pluck(:user_id))
end
def show
@agents = current_account.users.where(id: @inbox.members.pluck(:user_id))
end

private
private

def update_agents_list
# get all the user_ids which the inbox currently has as members.
# get the list of user_ids from params
# the missing ones are the agents which are to be deleted from the inbox
# the new ones are the agents which are to be added to the inbox
def update_agents_list
# get all the user_ids which the inbox currently has as members.
# get the list of user_ids from params
# the missing ones are the agents which are to be deleted from the inbox
# the new ones are the agents which are to be added to the inbox

agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) }
agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) }
end
agents_to_be_added_ids.each { |user_id| @inbox.add_member(user_id) }
agents_to_be_removed_ids.each { |user_id| @inbox.remove_member(user_id) }
end

def agents_to_be_added_ids
params[:user_ids] - @current_agents_ids
end
def agents_to_be_added_ids
params[:user_ids] - @current_agents_ids
end

def agents_to_be_removed_ids
@current_agents_ids - params[:user_ids]
end
def agents_to_be_removed_ids
@current_agents_ids - params[:user_ids]
end

def current_agents_ids
@current_agents_ids = @inbox.members.pluck(:id)
end
def current_agents_ids
@current_agents_ids = @inbox.members.pluck(:id)
end

def fetch_inbox
@inbox = current_account.inboxes.find(params[:inbox_id])
end
end
def fetch_inbox
@inbox = current_account.inboxes.find(params[:inbox_id])
end
end
29 changes: 29 additions & 0 deletions app/controllers/api/v1/widget/base_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class Api::V1::Widget::BaseController < ApplicationController
private

def conversation
@conversation ||= @contact_inbox.conversations.find_by(
inbox_id: auth_token_params[:inbox_id]
)
end

def auth_token_params
@auth_token_params ||= ::Widget::TokenService.new(token: request.headers[header_name]).decode_token
end

def header_name
'X-Auth-Token'
end

def set_web_widget
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
@account = @web_widget.account
end

def set_contact
@contact_inbox = @web_widget.inbox.contact_inboxes.find_by(
source_id: auth_token_params[:source_id]
)
@contact = @contact_inbox.contact
end
end
Loading