feat: Save automation rules (#3359)
This commit is contained in:
@@ -8,9 +8,11 @@ class Messages::MessageBuilder
|
||||
@conversation = conversation
|
||||
@user = user
|
||||
@message_type = params[:message_type] || 'outgoing'
|
||||
@items = params.to_unsafe_h&.dig(:content_attributes, :items)
|
||||
@attachments = params[:attachments]
|
||||
return unless params.instance_of?(ActionController::Parameters)
|
||||
|
||||
@in_reply_to = params.to_unsafe_h&.dig(:content_attributes, :in_reply_to)
|
||||
@items = params.to_unsafe_h&.dig(:content_attributes, :items)
|
||||
end
|
||||
|
||||
def perform
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseController
|
||||
before_action :check_authorization
|
||||
|
||||
def index
|
||||
@automation_rules = Current.account.automation_rules
|
||||
end
|
||||
|
||||
def create
|
||||
@automation_rule = Current.account.automation_rules.create(automation_rules_permit)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def automation_rules_permit
|
||||
params.permit(
|
||||
:name, :description, :event_name, :account_id,
|
||||
conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }],
|
||||
actions: [:action_name, { action_params: [:intiated_at] }]
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -16,7 +16,8 @@ class AsyncDispatcher < BaseDispatcher
|
||||
HookListener.instance,
|
||||
InstallationWebhookListener.instance,
|
||||
NotificationListener.instance,
|
||||
WebhookListener.instance
|
||||
WebhookListener.instance,
|
||||
AutomationRuleListener.instance
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
29
app/listeners/automation_rule_listener.rb
Normal file
29
app/listeners/automation_rule_listener.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
class AutomationRuleListener < BaseListener
|
||||
def conversation_status_changed(event_obj)
|
||||
conversation = event_obj.data[:conversation]
|
||||
return unless rule_present?('conversation_status_changed', conversation)
|
||||
|
||||
@rules.each do |rule|
|
||||
conditions_match = ::AutomationRules::ConditionsFilterService.new(rule, conversation).perform
|
||||
AutomationRules::ActionService.new(rule, conversation).perform if conditions_match.present?
|
||||
end
|
||||
end
|
||||
|
||||
def conversation_created(event_obj)
|
||||
conversation = event_obj.data[:conversation]
|
||||
return unless rule_present?('conversation_created', conversation)
|
||||
|
||||
@rules.each do |rule|
|
||||
conditions_match = AutomationRule::ConditionsFilterService.new(rule, conversation).perform
|
||||
AutomationRule::ActionService.new(rule, conversation).perform if conditions_match.present?
|
||||
end
|
||||
end
|
||||
|
||||
def rule_present?(event_name, conversation)
|
||||
@rules = AutomationRule.where(
|
||||
event_name: event_name,
|
||||
account_id: conversation.account_id
|
||||
)
|
||||
@rules.any?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
class TeamNotifications::AutomationNotificationMailer < ApplicationMailer
|
||||
def conversation_creation(conversation, team, message)
|
||||
return unless smtp_config_set_or_development?
|
||||
|
||||
@agents = team.team_members
|
||||
@conversation = conversation
|
||||
@message = message
|
||||
@action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id)
|
||||
|
||||
send_an_email_to_team
|
||||
end
|
||||
|
||||
def conversation_updated(conversation, team)
|
||||
return unless smtp_config_set_or_development?
|
||||
|
||||
@agents = team.team_members
|
||||
@conversation = conversation
|
||||
@message = message
|
||||
@action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id)
|
||||
|
||||
send_an_email_to_team
|
||||
end
|
||||
|
||||
def message_created(message, agent)
|
||||
return unless smtp_config_set_or_development?
|
||||
|
||||
@agent = agent
|
||||
@conversation = message.conversation
|
||||
@message = message
|
||||
subject = "#{@agent.available_name}, You have been mentioned in conversation [ID - #{@conversation.display_id}]"
|
||||
@action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id)
|
||||
send_mail_with_liquid(to: @agent.email, subject: subject)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_an_email_to_team
|
||||
@agents.each do |agent|
|
||||
subject = "#{@agent.available_name}, A new conversation [ID - #{@conversation.display_id}] has been created in #{@conversation.inbox&.name}."
|
||||
@action_url = app_account_conversation_url(account_id: @conversation.account_id, id: @conversation.display_id)
|
||||
send_mail_with_liquid(to: agent.email, subject: subject)
|
||||
end
|
||||
end
|
||||
|
||||
def liquid_droppables
|
||||
super.merge({
|
||||
user: @agent,
|
||||
conversation: @conversation,
|
||||
inbox: @conversation.inbox,
|
||||
message: @message
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -69,6 +69,7 @@ class Account < ApplicationRecord
|
||||
has_many :webhooks, dependent: :destroy_async
|
||||
has_many :whatsapp_channels, dependent: :destroy_async, class_name: '::Channel::Whatsapp'
|
||||
has_many :working_hours, dependent: :destroy_async
|
||||
has_many :automation_rules, dependent: :destroy
|
||||
|
||||
has_flags ACCOUNT_SETTINGS_FLAGS.merge(column: 'settings_flags').merge(DEFAULT_QUERY_SETTING)
|
||||
|
||||
|
||||
44
app/models/automation_rule.rb
Normal file
44
app/models/automation_rule.rb
Normal file
@@ -0,0 +1,44 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: automation_rules
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# actions :jsonb not null
|
||||
# conditions :jsonb not null
|
||||
# description :text
|
||||
# event_name :string not null
|
||||
# name :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_automation_rules_on_account_id (account_id)
|
||||
#
|
||||
class AutomationRule < ApplicationRecord
|
||||
belongs_to :account
|
||||
|
||||
validates :account, presence: true
|
||||
validate :json_conditions_format
|
||||
validate :json_actions_format
|
||||
|
||||
CONDITIONS_ATTRS = %w[country_code status browser_language assignee_id team_id referer].freeze
|
||||
ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agents].freeze
|
||||
|
||||
private
|
||||
|
||||
def json_conditions_format
|
||||
return if conditions.nil?
|
||||
|
||||
attributes = conditions.map { |obj, _| obj['attribute_key'] }
|
||||
(attributes - CONDITIONS_ATTRS).blank?
|
||||
end
|
||||
|
||||
def json_actions_format
|
||||
return if actions.nil?
|
||||
|
||||
attributes = actions.map { |obj, _| obj['attribute_key'] }
|
||||
(attributes - ACTIONS_ATTRS).blank?
|
||||
end
|
||||
end
|
||||
@@ -8,4 +8,9 @@ module Labelable
|
||||
def update_labels(labels = nil)
|
||||
update!(label_list: labels)
|
||||
end
|
||||
|
||||
def add_labels(new_labels = nil)
|
||||
new_labels << labels
|
||||
update!(label_list: new_labels)
|
||||
end
|
||||
end
|
||||
|
||||
9
app/policies/automation_rule_policy.rb
Normal file
9
app/policies/automation_rule_policy.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AutomationRulePolicy < ApplicationPolicy
|
||||
def index?
|
||||
@account_user.administrator?
|
||||
end
|
||||
|
||||
def create?
|
||||
@account_user.administrator?
|
||||
end
|
||||
end
|
||||
63
app/services/automation_rules/action_service.rb
Normal file
63
app/services/automation_rules/action_service.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
class AutomationRules::ActionService
|
||||
def initialize(rule, conversation)
|
||||
@rule = rule
|
||||
@conversation = conversation
|
||||
@account = @conversation.account
|
||||
end
|
||||
|
||||
def perform
|
||||
@rule.actions.each do |action, _current_index|
|
||||
action = action.with_indifferent_access
|
||||
send(action[:action_name], action[:action_params])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_message(message)
|
||||
# params = { content: message, private: false }
|
||||
# mb = Messages::MessageBuilder.new(@administrator, @conversation, params)
|
||||
# mb.perform
|
||||
end
|
||||
|
||||
def assign_team(team_ids = [])
|
||||
return unless team_belongs_to_account?(team_ids)
|
||||
|
||||
@account.teams.find_by(id: team_ids)
|
||||
@conversation.update!(team_id: team_ids[0])
|
||||
end
|
||||
|
||||
def assign_best_agents(agent_ids = [])
|
||||
return unless agent_belongs_to_account?(agent_ids)
|
||||
|
||||
@agent = @account.users.find_by(id: agent_ids)
|
||||
@conversation.update_assignee(@agent)
|
||||
end
|
||||
|
||||
def add_label(labels = [])
|
||||
@conversation.add_labels(labels)
|
||||
end
|
||||
|
||||
def send_email_to_team(params)
|
||||
team = Team.find(params[:team_ids][0])
|
||||
|
||||
case @rule.event_name
|
||||
when 'conversation_created', 'conversation_status_changed'
|
||||
TeamNotifications::AutomationNotificationMailer.conversation_creation(@conversation, team, params[:message])
|
||||
when 'conversation_updated'
|
||||
TeamNotifications::AutomationNotificationMailer.conversation_updated(@conversation, team, params[:message])
|
||||
end
|
||||
end
|
||||
|
||||
def administrator
|
||||
@administrator ||= @account.administrators.first
|
||||
end
|
||||
|
||||
def agent_belongs_to_account?(agent_ids)
|
||||
@account.agents.pluck(:id).include?(agent_ids[0])
|
||||
end
|
||||
|
||||
def team_belongs_to_account?(team_ids)
|
||||
@account.team_ids.include?(team_ids[0])
|
||||
end
|
||||
end
|
||||
45
app/services/automation_rules/conditions_filter_service.rb
Normal file
45
app/services/automation_rules/conditions_filter_service.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
require 'json'
|
||||
|
||||
class AutomationRules::ConditionsFilterService < FilterService
|
||||
def initialize(rule, conversation)
|
||||
super([], nil)
|
||||
@rule = rule
|
||||
@conversation = conversation
|
||||
file = File.read('./lib/filters/filter_keys.json')
|
||||
@filters = JSON.parse(file)
|
||||
end
|
||||
|
||||
def perform
|
||||
conversation_filters = @filters['conversations']
|
||||
|
||||
@rule.conditions.each_with_index do |query_hash, current_index|
|
||||
current_filter = conversation_filters[query_hash['attribute_key']]
|
||||
@query_string += conversation_query_string(current_filter, query_hash.with_indifferent_access, current_index)
|
||||
end
|
||||
|
||||
records = base_relation.where(@query_string, @filter_values.with_indifferent_access)
|
||||
records.any?
|
||||
end
|
||||
|
||||
def conversation_query_string(current_filter, query_hash, current_index)
|
||||
attribute_key = query_hash['attribute_key']
|
||||
query_operator = query_hash['query_operator']
|
||||
|
||||
filter_operator_value = filter_operation(query_hash, current_index)
|
||||
|
||||
case current_filter['attribute_type']
|
||||
when 'additional_attributes'
|
||||
" conversations.additional_attributes ->> '#{attribute_key}' #{filter_operator_value} #{query_operator} "
|
||||
when 'standard'
|
||||
if attribute_key == 'labels'
|
||||
" tags.id #{filter_operator_value} #{query_operator} "
|
||||
else
|
||||
" conversations.#{attribute_key} #{filter_operator_value} #{query_operator} "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def base_relation
|
||||
Conversation.where(id: @conversation)
|
||||
end
|
||||
end
|
||||
@@ -51,7 +51,6 @@ class Instagram::SendOnInstagramService < Base::SendOnChannelService
|
||||
def send_to_facebook_page(message_content)
|
||||
access_token = channel.page_access_token
|
||||
app_secret_proof = calculate_app_secret_proof(GlobalConfigService.load('FB_APP_SECRET', ''), access_token)
|
||||
|
||||
query = { access_token: access_token }
|
||||
query[:appsecret_proof] = app_secret_proof if app_secret_proof
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
json.partial! 'api/v1/accounts/automation_rules/partials/automation_rule.json.jbuilder', automation_rule: @automation_rule
|
||||
@@ -0,0 +1,5 @@
|
||||
json.data do
|
||||
json.array! @automation_rules do |automation_rule|
|
||||
json.partial! 'api/v1/accounts/automation_rules/partials/automation_rule.json.jbuilder', automation_rule: automation_rule
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
json.id automation_rule.id
|
||||
json.account_id automation_rule.account_id
|
||||
json.name automation_rule.name
|
||||
json.description automation_rule.description
|
||||
json.event_name automation_rule.event_name
|
||||
json.conditions automation_rule.conditions
|
||||
json.actions automation_rule.actions
|
||||
@@ -0,0 +1,8 @@
|
||||
<p>Hi {{user.available_name}}</p>
|
||||
|
||||
|
||||
<p>Time to save the world. A new conversation has been created in {{ inbox.name }}</p>
|
||||
|
||||
<p>
|
||||
Click <a href="{{ action_url }}">here</a> to get cracking.
|
||||
</p>
|
||||
Reference in New Issue
Block a user