mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-30 02:32:29 +00:00
feat: Implement UI for Agent Bots in settings and remove CSML support (#11276)
- Add agent bots management UI in settings with avatar upload - Enable agent bot configuration for all inbox types - Implement proper CRUD operations with webhook URL support - Fix agent bots menu item visibility in settings sidebar - Remove all CSML-related code and features - Add migration to convert existing CSML bots to webhook bots - Simplify agent bot model and services to focus on webhook bots - Improve UI to differentiate between system bots and account bots ## Video https://github.com/user-attachments/assets/3f4edbb7-b758-468c-8dd6-a9537b983f7d --------- Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
class CsmlEngine
|
||||
API_KEY_HEADER = 'X-Api-Key'.freeze
|
||||
|
||||
def initialize
|
||||
@host_url = GlobalConfigService.load('CSML_BOT_HOST', '')
|
||||
@api_key = GlobalConfigService.load('CSML_BOT_API_KEY', '')
|
||||
|
||||
raise ArgumentError, 'Missing Credentials' if @host_url.blank? || @api_key.blank?
|
||||
end
|
||||
|
||||
def status
|
||||
response = HTTParty.get("#{@host_url}/status")
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
def run(bot, params)
|
||||
payload = {
|
||||
bot: bot,
|
||||
event: {
|
||||
request_id: SecureRandom.uuid,
|
||||
client: params[:client],
|
||||
payload: params[:payload],
|
||||
metadata: params[:metadata],
|
||||
ttl_duration: 4000
|
||||
}
|
||||
}
|
||||
response = post('run', payload)
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
def validate(bot)
|
||||
response = post('validate', bot)
|
||||
process_response(response)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_response(response)
|
||||
return response.parsed_response if response.success?
|
||||
|
||||
{ error: response.parsed_response, status: response.code }
|
||||
end
|
||||
|
||||
def post(path, payload)
|
||||
HTTParty.post(
|
||||
"#{@host_url}/#{path}", {
|
||||
headers: { API_KEY_HEADER => @api_key, 'Content-Type' => 'application/json' },
|
||||
body: payload.to_json
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,4 @@
|
||||
class Integrations::BotProcessorService
|
||||
# TODO: In CSML processor service, the argument is agent bot, update initializers accordingly.
|
||||
pattr_initialize [:event_name!, :hook!, :event_data!]
|
||||
|
||||
def perform
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
class Integrations::Csml::ProcessorService < Integrations::BotProcessorService
|
||||
pattr_initialize [:event_name!, :event_data!, :agent_bot!]
|
||||
|
||||
private
|
||||
|
||||
def csml_client
|
||||
@csml_client ||= CsmlEngine.new
|
||||
end
|
||||
|
||||
def get_response(session_id, content)
|
||||
csml_client.run(
|
||||
bot_payload,
|
||||
{
|
||||
client: client_params(session_id),
|
||||
payload: message_payload(content),
|
||||
metadata: metadata_params
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def client_params(session_id)
|
||||
{
|
||||
bot_id: "chatwoot-bot-#{conversation.inbox.id}",
|
||||
channel_id: "chatwoot-bot-inbox-#{conversation.inbox.id}",
|
||||
user_id: session_id
|
||||
}
|
||||
end
|
||||
|
||||
def message_payload(content)
|
||||
{
|
||||
content_type: 'text',
|
||||
content: { text: content }
|
||||
}
|
||||
end
|
||||
|
||||
def metadata_params
|
||||
{
|
||||
conversation: conversation,
|
||||
contact: conversation.contact
|
||||
}
|
||||
end
|
||||
|
||||
def bot_payload
|
||||
{
|
||||
id: "chatwoot-csml-bot-#{agent_bot.id}",
|
||||
name: "chatwoot-csml-bot-#{agent_bot.id}",
|
||||
default_flow: 'chatwoot_bot_flow',
|
||||
flows: [
|
||||
{
|
||||
id: "chatwoot-csml-bot-flow-#{agent_bot.id}-inbox-#{conversation.inbox.id}",
|
||||
name: 'chatwoot_bot_flow',
|
||||
content: agent_bot.bot_config['csml_content'],
|
||||
commands: []
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
def process_response(message, response)
|
||||
csml_messages = response['messages']
|
||||
has_conversation_ended = response['conversation_end']
|
||||
|
||||
process_action(message, 'handoff') if has_conversation_ended.present?
|
||||
|
||||
return if csml_messages.blank?
|
||||
|
||||
# We do not support wait, typing now.
|
||||
csml_messages.each do |csml_message|
|
||||
create_messages(csml_message, conversation)
|
||||
end
|
||||
end
|
||||
|
||||
def create_messages(message, conversation)
|
||||
message_payload = message['payload']
|
||||
|
||||
case message_payload['content_type']
|
||||
when 'text'
|
||||
process_text_messages(message_payload, conversation)
|
||||
when 'question'
|
||||
process_question_messages(message_payload, conversation)
|
||||
when 'image'
|
||||
process_image_messages(message_payload, conversation)
|
||||
end
|
||||
end
|
||||
|
||||
def process_text_messages(message_payload, conversation)
|
||||
conversation.messages.create!(
|
||||
{
|
||||
message_type: :outgoing,
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
content: message_payload['content']['text'],
|
||||
sender: agent_bot
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def process_question_messages(message_payload, conversation)
|
||||
buttons = message_payload['content']['buttons'].map do |button|
|
||||
{ title: button['content']['title'], value: button['content']['payload'] }
|
||||
end
|
||||
conversation.messages.create!(
|
||||
{
|
||||
message_type: :outgoing,
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
content: message_payload['content']['title'],
|
||||
content_type: 'input_select',
|
||||
content_attributes: { items: buttons },
|
||||
sender: agent_bot
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def prepare_attachment(message_payload, message, account_id)
|
||||
attachment_params = { file_type: :image, account_id: account_id }
|
||||
attachment_url = message_payload['content']['url']
|
||||
attachment = message.attachments.new(attachment_params)
|
||||
attachment_file = Down.download(attachment_url)
|
||||
attachment.file.attach(
|
||||
io: attachment_file,
|
||||
filename: attachment_file.original_filename,
|
||||
content_type: attachment_file.content_type
|
||||
)
|
||||
end
|
||||
|
||||
def process_image_messages(message_payload, conversation)
|
||||
message = conversation.messages.new(
|
||||
{
|
||||
message_type: :outgoing,
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
content: '',
|
||||
content_type: 'text',
|
||||
sender: agent_bot
|
||||
}
|
||||
)
|
||||
|
||||
prepare_attachment(message_payload, message, conversation.account_id)
|
||||
message.save!
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user