Skip to content

Commit

Permalink
Merge pull request #15 from ZaikoXander/feat/support-file-sending
Browse files Browse the repository at this point in the history
Feat/support file sending
  • Loading branch information
douglara authored Oct 13, 2023
2 parents fdca6db + f5f11f8 commit 53871e2
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 16 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
# Ignore bundler config.
/.bundle

# Ignore IDE config
.idea

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
.byebug_history

# Ignore pidfiles, but keep the directory.
/tmp/pids/*
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ group :development, :test do
gem 'dotenv-rails'
gem 'vcr'
gem 'webmock'
gem 'byebug'
end

group :development do
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ GEM
bootsnap (1.16.0)
msgpack (~> 1.2)
builder (3.2.4)
byebug (11.1.3)
concurrent-ruby (1.2.2)
crack (0.4.5)
rexml
Expand Down Expand Up @@ -185,6 +186,7 @@ PLATFORMS

DEPENDENCIES
bootsnap
byebug
debug
dotenv-rails
faraday
Expand Down
11 changes: 9 additions & 2 deletions app/use_cases/chatwoot/receive_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,24 @@ def process_event(event)
botpress_endpoint = event['botpress_endpoint'] || ENV['BOTPRESS_ENDPOINT']
botpress_bot_id = Chatwoot::GetDynamicAttribute.call(event: event, attribute: 'botpress_bot_id').data[:attribute]

botpress_responses = Chatwoot::SendToBotpress.call(event: event, botpress_endpoint: botpress_endpoint, botpress_bot_id: botpress_bot_id)
botpress_responses = Chatwoot::SendToBotpress.call(
event: event,
botpress_endpoint: botpress_endpoint,
botpress_bot_id: botpress_bot_id
)
chatwoot_responses = []
botpress_responses.data['responses'].each do | response |
result = Chatwoot::SendToChatwoot.call(event: event, botpress_response: response)
chatwoot_responses << result.data

if result.failure?
Failure result: { message: 'Error send to chatwoot' }
end

sleep(ENV['CHATWOOT_MESSAGES_DELAY'].to_i) if ENV['CHATWOOT_MESSAGES_DELAY']
end

Success result: { botpress: botpress_responses.data , botpress_bot_id: botpress_bot_id }
Success result: { botpress: botpress_responses.data , botpress_bot_id: botpress_bot_id, chatwoot_responses: chatwoot_responses }
else
Failure result: { message: 'Invalid event' }
end
Expand Down
67 changes: 67 additions & 0 deletions app/use_cases/chatwoot/send_file_to_chatwoot_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'net/http'
require 'openssl'
require 'uri'
require 'open-uri'

class Chatwoot::SendFileToChatwootRequest < Micro::Case
attributes :account_id
attributes :conversation_id
attribute :botpress_response

attributes :chatwoot_endpoint
attributes :chatwoot_bot_token

def call!
file_url = botpress_response['image'] || botpress_response['file'] || botpress_response['video']
file_name = get_file_name_by_url(file_url)
file = download_file(file_url)

url = URI("#{chatwoot_endpoint}/api/v1/accounts/#{account_id}/conversations/#{conversation_id}/messages")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request['api_access_token'] = chatwoot_bot_token

form_data = [['attachments[]', file, { filename: file_name , content_type: file.content_type }]]

unless botpress_response['title'].nil?
form_data << ['content', botpress_response['title']]
end

request.set_form(form_data, 'multipart/form-data')
response = http.request(request)

Rails.logger.info("Chatwoot response")
Rails.logger.info("Status code: #{response.code}")
Rails.logger.info("Body: #{response.body}")

if response.code == "200"
Success result: JSON.parse(response.body)
elsif response.code == "404" && response.body.include?('Resource could not be found')
Failure result: { message: 'Chatwoot resource could not be found' }
elsif response.code == "404"
Failure result: { message: 'Invalid chatwoot endpoint' }
elsif response.code == "401"
Failure result: { message: 'Invalid chatwoot access token' }
else
Failure result: { message: 'Chatwoot server error' }
end
end

def download_file(file_url)
begin
OpenURI::open_uri(file_url)
rescue
raise "File url is invalid"
end
end

def get_file_name_by_url(file_url)
incoming_file_name = File.basename(file_url)
raise "Invalid file name" unless incoming_file_name.include?('-')
incoming_file_name.sub(/^[^-]+-/, '')
end
end
52 changes: 40 additions & 12 deletions app/use_cases/chatwoot/send_to_chatwoot.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'faraday'

class Chatwoot::SendToChatwoot < Micro::Case
attributes :event
attribute :botpress_response
Expand All @@ -10,26 +8,56 @@ def call!
chatwoot_endpoint = event['chatwoot_endpoint'] || ENV['CHATWOOT_ENDPOINT']
chatwoot_bot_token = event['chatwoot_bot_token'] || ENV['CHATWOOT_BOT_TOKEN']

if botpress_response_choise_options?(botpress_response)
return Chatwoot::SendToChatwootRequest.call(
account_id: account_id, conversation_id: conversation_id,
if response_image?(botpress_response) || response_file?(botpress_response) || response_video?(botpress_response)
Chatwoot::SendFileToChatwootRequest.call(
account_id: account_id, conversation_id: conversation_id,
chatwoot_endpoint: chatwoot_endpoint, chatwoot_bot_token: chatwoot_bot_token,
botpress_response: botpress_response
)
elsif response_choice_options?(botpress_response)
Chatwoot::SendToChatwootRequest.call(
account_id: account_id, conversation_id: conversation_id,
chatwoot_endpoint: chatwoot_endpoint, chatwoot_bot_token: chatwoot_bot_token,
body: build_choise_options_body(botpress_response)
body: build_choice_options_body(botpress_response)
)
else
return Chatwoot::SendToChatwootRequest.call(
account_id: account_id, conversation_id: conversation_id,
Chatwoot::SendToChatwootRequest.call(
account_id: account_id, conversation_id: conversation_id,
chatwoot_endpoint: chatwoot_endpoint, chatwoot_bot_token: chatwoot_bot_token,
body: { content: botpress_response['text'] }
)
end
end

def botpress_response_choise_options?(botpress_response)
botpress_response['type'] == 'single-choice'
def response_image?(response)
response['type'] == 'image'
end

def response_file?(response)
response['type'] == 'file'
end

def response_video?(response)
response['type'] == 'video'
end

def response_choice_options?(response)
response['type'] == 'single-choice'
end

def build_choise_options_body(botpress_response)
{ content: botpress_response['text'], content_type: 'input_select', content_attributes: { items: botpress_response['choices'].map { | option | { title: option['title'], value: option['value'] } } } }
def build_choice_options_body(botpress_response)
{
content: botpress_response['text'],
content_type: 'input_select',
content_attributes: {
items: botpress_response['choices'].map {
| option |
{
title: option['title'],
value: option['value']
}
}
}
}
end
end
76 changes: 74 additions & 2 deletions test/controllers/chatwoot_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ChatwootControllerTest < ActionDispatch::IntegrationTest
stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":64374,"content":"Testing","inbox_id":10,"conversation_id":11791,"message_type":1,"content_type":"text","content_attributes":{},"created_at":1656268790,"private":false,"source_id":null,"sender":{"id":3,"name":"Botpress Testing","avatar_url":"","type":"agent_bot"}}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = File.read(Rails.root.to_s + "/test/fixtures/files/new_message.json")
body = File.read(Rails.root + "test/fixtures/files/new_message.json")
post chatwoot_webhook_url, params: body, headers: { "Content-Type": "application/json" }
assert_response :success
end
Expand All @@ -18,10 +18,82 @@ class ChatwootControllerTest < ActionDispatch::IntegrationTest
stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":64374,"content":"Testing","inbox_id":10,"conversation_id":11791,"message_type":1,"content_type":"text","content_attributes":{},"created_at":1656268790,"private":false,"source_id":null,"sender":{"id":3,"name":"Botpress Testing","avatar_url":"","type":"agent_bot"}}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = JSON.parse(File.read(Rails.root.to_s + "/test/fixtures/files/new_message.json"))
body = JSON.parse(File.read(Rails.root + "test/fixtures/files/new_message.json"))
body.merge!({ "botpress_bot_id" => "test123" })

post chatwoot_webhook_url, params: body.to_json, headers: { "Content-Type": "application/json" }
assert_response :success
end

test "should send image" do
stub_request(:post, Regexp.new(ENV['BOTPRESS_ENDPOINT'])).
to_return(status: 200, body: '{"responses":[{"type":"image","workflow":{},"image":"https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/42rghrwr2zndfjw3fn4q-C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png","title":"Texto da imagem de teste","typing":true},{"type":"text","workflow":{},"text":"Bye!","markdown":true,"typing":true}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

stub_request(:get, Regexp.new(/api\/v1\/bots\/teste-file\/media/)).
to_return(status: 200, body: File.read(Rails.root + 'test/fixtures/files/image-file.png'), headers: {'Content-Type': 'image/png'})

stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":332180,"content":"Texto da imagem de teste","inbox_id":73,"conversation_id":24,"message_type":1,"content_type":"text","status":"sent","content_attributes":{},"created_at":1696877253,"private":false,"source_id":null,"sender":{"id":13,"name":"Bot File local","avatar_url":"","type":"agent_bot"},"attachments":[{"id":17524,"message_id":332180,"file_type":"image","account_id":19,"extension":null,"data_url":"https://chat.zimobi.com.br/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa2ZQIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--b40880556adb1d59d7920e57dcd01177c6d6d003/C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png","thumb_url":"https://chat.zimobi.com.br/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa2ZQIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--b40880556adb1d59d7920e57dcd01177c6d6d003/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RTNKbGMybDZaVjkwYjE5bWFXeHNXd2RwQWZvdyIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--ca4751066a2e1a2af79f9cc6d5fa306d7d1c71cb/C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png","file_size":65733}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = JSON.parse(File.read(Rails.root + "test/fixtures/files/new_message.json"))

post chatwoot_webhook_url, params: body.to_json, headers: { "Content-Type": "application/json" }
assert_response :success
assert_match /file_type"=>"image/, JSON.parse(response.body)['chatwoot_responses'].to_s
assert_match /content"=>"Texto da imagem de teste/, JSON.parse(response.body)['chatwoot_responses'].to_s
end

test "should send video" do
stub_request(:post, Regexp.new(ENV['BOTPRESS_ENDPOINT'])).
to_return(status: 200, body: '{"responses":[{"type":"video", "workflow":{}, "video":"https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/oj3s5liagedo2bmaaddy-video-test.mp4", "title":"titulo video teste", "typing":true}, {"type":"text", "workflow":{}, "text":"Bye!", "markdown":true, "typing":true}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

stub_request(:get, Regexp.new(/api\/v1\/bots\/teste-file\/media/)).
to_return(status: 200, body: File.read(Rails.root + 'test/fixtures/files/video-file.mp4'), headers: {'Content-Type': 'video/mp4'})

stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":332201,"content":"titulo video teste","inbox_id":73,"conversation_id":24,"message_type":1,"content_type":"text","status":"sent","content_attributes":{},"created_at":1696877453,"private":false,"source_id":null,"sender":{"id":13,"name":"Bot File local","avatar_url":"","type":"agent_bot"},"attachments":[{"id":17527,"message_id":332201,"file_type":"video","account_id":19,"extension":null,"data_url":"https://chat.zimobi.com.br/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa3ZQIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--4565cf175fec254c76c622b336df6f89d5473527/video-test.mp4","thumb_url":"","file_size":0}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = JSON.parse(File.read(Rails.root + "test/fixtures/files/new_message.json"))

post chatwoot_webhook_url, params: body.to_json, headers: { "Content-Type": "application/json" }
assert_response :success
assert_match /file_type"=>"video/, JSON.parse(response.body)['chatwoot_responses'].to_s
assert_match /content"=>"titulo video teste/, JSON.parse(response.body)['chatwoot_responses'].to_s
end

test "should send file" do
stub_request(:post, Regexp.new(ENV['BOTPRESS_ENDPOINT'])).
to_return(status: 200, body: '{"responses":[{"type":"file", "workflow":{}, "file":"https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/nmb8ifmr423ugq4xox4d-a.pdf", "typing":true}, {"type":"text", "workflow":{}, "text":"Bye!", "markdown":true, "typing":true}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

stub_request(:get, Regexp.new(/api\/v1\/bots\/teste-file\/media/)).
to_return(status: 200, body: File.read(Rails.root + 'test/fixtures/files/pdf-file.pdf'), headers: {'Content-Type': 'application/pdf'})

stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":337821,"content":null,"inbox_id":73,"conversation_id":25,"message_type":1,"content_type":"text","status":"sent","content_attributes":{},"created_at":1696959105,"private":false,"source_id":null,"sender":{"id":13,"name":"Bot File local","avatar_url":"","type":"agent_bot"},"attachments":[{"id":17855,"message_id":337821,"file_type":"file","account_id":19,"extension":null,"data_url":"https://chat.zimobi.com.br/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa0xSIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--64e0af67db22c6f8ec318fd11163ca5354e9ab61/a.pdf","thumb_url":"https://chat.zimobi.com.br/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBa0xSIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--64e0af67db22c6f8ec318fd11163ca5354e9ab61/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCam9UY21WemFYcGxYM1J2WDJacGJHeGJCMmtCK2pBPSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--2942dc8de0003672434590effad890a9d0ca8a09/a.pdf","file_size":0}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = JSON.parse(File.read(Rails.root + "test/fixtures/files/new_message.json"))

post chatwoot_webhook_url, params: body.to_json, headers: { "Content-Type": "application/json" }
assert_response :success
assert_match /file_type"=>"file/, JSON.parse(response.body)['chatwoot_responses'].to_s
assert_match /content"=>nil/, JSON.parse(response.body)['chatwoot_responses'].to_s
end

test "should send file with message" do
stub_request(:post, Regexp.new(ENV['BOTPRESS_ENDPOINT'])).
to_return(status: 200, body: '{"responses":[{"type":"file", "workflow":{}, "file":"https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/nmb8ifmr423ugq4xox4d-a.pdf", "title":"pdf teste", "typing":true}, {"type":"text", "workflow":{}, "text":"Bye!", "markdown":true, "typing":true}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

stub_request(:get, Regexp.new(/api\/v1\/bots\/teste-file\/media/)).
to_return(status: 200, body: File.read(Rails.root + 'test/fixtures/files/pdf-file.pdf'), headers: {'Content-Type': 'application/pdf'})

stub_request(:post, Regexp.new(ENV['CHATWOOT_ENDPOINT'])).
to_return(status: 200, body: '{"id":336815,"content":"pdf teste","inbox_id":73,"conversation_id":25,"message_type":1,"content_type":"text","status":"sent","content_attributes":{},"created_at":1696953209,"private":false,"source_id":null,"sender":{"id":13,"name":"Bot File local","avatar_url":"","type":"agent_bot"},"attachments":[{"id":17814,"message_id":336815,"file_type":"file","account_id":19,"extension":null,"data_url":"https://chat.zimobi.com.br/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBdnJRIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--00e63700ad55f84724cddde66b187cc8475a6b0d/a.pdf","thumb_url":"https://chat.zimobi.com.br/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBdnJRIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--00e63700ad55f84724cddde66b187cc8475a6b0d/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCam9UY21WemFYcGxYM1J2WDJacGJHeGJCMmtCK2pBPSIsImV4cCI6bnVsbCwicHVyIjoidmFyaWF0aW9uIn19--2942dc8de0003672434590effad890a9d0ca8a09/a.pdf","file_size":0}]}', headers: {'Content-Type': 'application/json; charset=utf-8'})

body = JSON.parse(File.read(Rails.root + "test/fixtures/files/new_message.json"))

post chatwoot_webhook_url, params: body.to_json, headers: { "Content-Type": "application/json" }
assert_response :success
assert_match /file_type"=>"file/, JSON.parse(response.body)['chatwoot_responses'].to_s
assert_match /content"=>"pdf teste/, JSON.parse(response.body)['chatwoot_responses'].to_s
end
end
Binary file added test/fixtures/files/image-file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/files/pdf-file.pdf
Binary file not shown.
1 change: 1 addition & 0 deletions test/fixtures/files/video-file.mp4
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

18 changes: 18 additions & 0 deletions test/use_cases/chatwoot/send_file_to_chatwoot_request_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require "test_helper"

class SendFileToChatwootRequestTest < ActionDispatch::IntegrationTest
setup do
@service = Chatwoot::SendFileToChatwootRequest.send(:new, {})
end

test "should return valid file name" do
result = @service.get_file_name_by_url('https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/42rghrwr2zndfjw3fn4q-C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png')
assert_equal result, 'C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png'
end

test "should throw invalid file name error" do
assert_raises "Invalid file name" do
@service.get_file_name_by_url('https://app.botpress.zimobi.com.br/api/v1/bots/teste-file/media/42rghrwr2zndfjw3fn4q_C%C3%B3pia%20de%20Wo%20(1024%C2%A0%C3%97%C2%A01024%C2%A0px)%20(2).png')
end
end
end

0 comments on commit 53871e2

Please sign in to comment.