chatwoot-develop/enterprise/app/services/captain/tools/tool_runner.rb

136 lines
4.1 KiB
Ruby

require 'net/http'
require 'uri'
module Captain
module Tools
class ToolRunner
def self.run(assistant:, tool_key:, inbox:, conversation:, additional_data: {})
new(assistant, tool_key, inbox, conversation, additional_data).run
end
def initialize(assistant, tool_key, inbox, conversation, additional_data)
@assistant = assistant
@tool_key = tool_key
@inbox = inbox
@conversation = conversation
@definition = Captain::Tools::Definitions::ALL[tool_key]
@config = Captain::ToolConfig.find_by(captain_assistant_id: assistant.id, tool_key: tool_key) ||
Captain::ToolConfig.find_by(inbox: inbox, account: inbox.account, tool_key: tool_key)
@contact = conversation.contact
@additional_data = additional_data
end
def run
return failed_response('Tool not configured or disabled') unless @config&.is_enabled
return failed_response('Tool definition not found') unless @definition
start_time = Time.current
result = case @definition[:type]
when :http then execute_http
when :webhook then execute_webhook
else failed_response('Unknown tool type')
end
duration = (Time.current - start_time) * 1000
result.merge(duration_ms: duration)
end
private
def execute_http
uri = URI(@definition[:url])
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = 8
request = Net::HTTP::Get.new(uri)
request['PLUG-PLAY-ID'] = @config.plug_play_id
request['PLUG-PLAY-TOKEN'] = @config.plug_play_token
response = http.request(request)
if response.is_a?(Net::HTTPSuccess)
parsed_body = parse_response(response.body)
{ success: true, body: parsed_body, status: response.code }
else
{ success: false, status: response.code, error: 'HTTP Request Failed' }
end
rescue StandardError => e
{ success: false, error: e.message }
end
def execute_webhook
url = @config.webhook_url
return failed_response('Webhook URL missing') if url.blank?
payload = build_webhook_payload
# Specific logic for escalate_human
if @tool_key == 'escalar_humano'
# Label logic handles in brain/response service if success
end
response = RestClient::Request.execute(
method: :post,
url: url,
payload: payload.to_json,
headers: { content_type: :json, accept: :json },
timeout: 8
)
{ success: true, status: response.code, body: { message: 'Webhook sent' } }
rescue StandardError => e
{ success: false, error: e.message }
end
def build_webhook_payload
base = {
conversation_id: @conversation.id,
contact_id: @contact.id,
event: @tool_key
}
# Specific payloads
if @tool_key == 'maria_fotos'
base.merge!(
suite_category: extract_suite_category || 'Indefinida',
message: 'User requested photos'
)
elsif @tool_key == 'escalar_humano'
base.merge!(reason: 'User requested human agent')
end
base
end
def extract_suite_category
# Simple extraction or ask JasmineBrain to help later?
# For V1, we try to grab from message content or nil
msg = @additional_data[:message]&.downcase || ''
return 'Alexa' if msg.include?('alexa')
return 'Stilo' if msg.include?('stilo')
return 'Hidromassagem' if msg.include?('hidro')
nil
end
def parse_response(body)
case @tool_key
when 'status_suites'
Captain::Tools::Parsers::StatusSuitesParser.parse(body)
else
begin
JSON.parse(body)
rescue StandardError
body
end
end
end
def failed_response(msg)
{ success: false, error: msg }
end
end
end
end