2016-04-15 21:56:10 +00:00
|
|
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
|
|
|
class Transaction::Slack
|
2016-04-18 00:02:31 +00:00
|
|
|
|
2016-08-20 19:29:22 +00:00
|
|
|
=begin
|
2016-04-18 00:02:31 +00:00
|
|
|
|
2016-08-20 19:29:22 +00:00
|
|
|
backend = Transaction::Slack.new(
|
2016-04-15 21:56:10 +00:00
|
|
|
object: 'Ticket',
|
|
|
|
type: 'update',
|
2016-04-22 06:55:10 +00:00
|
|
|
object_id: 123,
|
2016-08-20 19:29:22 +00:00
|
|
|
interface_handle: 'application_server', # application_server|websocket|scheduler
|
2016-04-15 21:56:10 +00:00
|
|
|
changes: {
|
|
|
|
'attribute1' => [before, now],
|
|
|
|
'attribute2' => [before, now],
|
2016-07-16 21:43:08 +00:00
|
|
|
},
|
|
|
|
created_at: Time.zone.now,
|
2016-04-27 07:31:11 +00:00
|
|
|
user_id: 123,
|
2016-08-20 19:29:22 +00:00
|
|
|
)
|
|
|
|
backend.perform
|
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
=end
|
2016-08-20 19:29:22 +00:00
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
def initialize(item, params = {})
|
|
|
|
@item = item
|
|
|
|
@params = params
|
|
|
|
end
|
|
|
|
|
|
|
|
def perform
|
2016-04-22 06:55:10 +00:00
|
|
|
|
|
|
|
# return if we run import mode
|
|
|
|
return if Setting.get('import_mode')
|
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
return if @item[:object] != 'Ticket'
|
|
|
|
return if !Setting.get('slack_integration')
|
|
|
|
|
|
|
|
config = Setting.get('slack_config')
|
|
|
|
return if !config
|
|
|
|
return if !config['items']
|
|
|
|
|
2016-04-22 06:55:10 +00:00
|
|
|
ticket = Ticket.find(@item[:object_id])
|
2016-04-15 21:56:10 +00:00
|
|
|
if @item[:article_id]
|
|
|
|
article = Ticket::Article.find(@item[:article_id])
|
2016-05-16 15:01:59 +00:00
|
|
|
|
|
|
|
# ignore notifications
|
|
|
|
sender = Ticket::Article::Sender.lookup(id: article.sender_id)
|
|
|
|
if sender && sender.name == 'System'
|
|
|
|
return if @item[:changes].empty?
|
|
|
|
article = nil
|
|
|
|
end
|
2016-04-15 21:56:10 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# ignore if no changes has been done
|
|
|
|
changes = human_changes(ticket)
|
|
|
|
return if @item[:type] == 'update' && !article && (!changes || changes.empty?)
|
|
|
|
|
|
|
|
# get user based notification template
|
|
|
|
# if create, send create message / block update messages
|
|
|
|
template = nil
|
2016-04-18 07:48:21 +00:00
|
|
|
sent_value = nil
|
2016-04-15 21:56:10 +00:00
|
|
|
if @item[:type] == 'create'
|
|
|
|
template = 'ticket_create'
|
|
|
|
elsif @item[:type] == 'update'
|
|
|
|
template = 'ticket_update'
|
|
|
|
elsif @item[:type] == 'reminder_reached'
|
|
|
|
template = 'ticket_reminder_reached'
|
2016-04-18 07:48:21 +00:00
|
|
|
sent_value = ticket.pending_time
|
2016-04-15 21:56:10 +00:00
|
|
|
elsif @item[:type] == 'escalation'
|
|
|
|
template = 'ticket_escalation'
|
2016-04-18 07:48:21 +00:00
|
|
|
sent_value = ticket.escalation_time
|
2016-04-15 21:56:10 +00:00
|
|
|
elsif @item[:type] == 'escalation_warning'
|
|
|
|
template = 'ticket_escalation_warning'
|
2016-04-18 07:48:21 +00:00
|
|
|
sent_value = ticket.escalation_time
|
2016-04-15 21:56:10 +00:00
|
|
|
else
|
|
|
|
raise "unknown type for notification #{@item[:type]}"
|
|
|
|
end
|
|
|
|
|
|
|
|
user = User.find(1)
|
2016-04-27 09:21:07 +00:00
|
|
|
|
|
|
|
current_user = User.lookup(id: @item[:user_id])
|
|
|
|
if !current_user
|
|
|
|
current_user = User.lookup(id: 1)
|
|
|
|
end
|
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
result = NotificationFactory::Slack.template(
|
|
|
|
template: template,
|
|
|
|
locale: user[:preferences][:locale],
|
|
|
|
objects: {
|
|
|
|
ticket: ticket,
|
|
|
|
article: article,
|
2016-04-27 09:21:07 +00:00
|
|
|
current_user: current_user,
|
2016-04-15 21:56:10 +00:00
|
|
|
changes: changes,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
# good, warning, danger
|
|
|
|
color = '#000000'
|
|
|
|
ticket_state_type = ticket.state.state_type.name
|
2016-04-18 08:33:14 +00:00
|
|
|
if ticket.escalation_time && ticket.escalation_time < Time.zone.now
|
2016-04-15 21:56:10 +00:00
|
|
|
color = '#f35912'
|
|
|
|
elsif ticket_state_type == 'pending reminder'
|
|
|
|
if ticket.pending_time && ticket.pending_time < Time.zone.now
|
|
|
|
color = '#faab00'
|
|
|
|
end
|
|
|
|
elsif ticket_state_type =~ /^(new|open)$/
|
|
|
|
color = '#faab00'
|
|
|
|
elsif ticket_state_type == 'closed'
|
|
|
|
color = '#38ad69'
|
|
|
|
end
|
|
|
|
|
2016-06-30 20:04:48 +00:00
|
|
|
config['items'].each { |local_config|
|
2016-04-18 08:01:20 +00:00
|
|
|
next if local_config['webhook'].empty?
|
2016-04-15 21:56:10 +00:00
|
|
|
|
2016-04-18 07:48:21 +00:00
|
|
|
# check if reminder_reached/escalation/escalation_warning is already sent today
|
2016-04-18 08:01:20 +00:00
|
|
|
md5_webhook = Digest::MD5.hexdigest(local_config['webhook'])
|
2016-04-18 08:28:21 +00:00
|
|
|
cache_key = "slack::backend::#{@item[:type]}::#{ticket.id}::#{md5_webhook}"
|
2016-04-18 07:48:21 +00:00
|
|
|
if sent_value
|
|
|
|
value = Cache.get(cache_key)
|
|
|
|
if value == sent_value
|
2016-04-18 08:01:20 +00:00
|
|
|
Rails.logger.debug "did not send webhook, already sent (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})"
|
2016-04-18 07:48:21 +00:00
|
|
|
next
|
|
|
|
end
|
|
|
|
Cache.write(
|
|
|
|
cache_key,
|
|
|
|
sent_value,
|
|
|
|
{
|
|
|
|
expires_in: 24.hours
|
|
|
|
},
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
# check action
|
2016-04-18 08:01:20 +00:00
|
|
|
if local_config['types'].class == Array
|
2016-04-18 00:08:11 +00:00
|
|
|
hit = false
|
2016-06-30 20:04:48 +00:00
|
|
|
local_config['types'].each { |type|
|
2016-04-18 00:08:11 +00:00
|
|
|
next if type.to_s != @item[:type].to_s
|
|
|
|
hit = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
next if !hit
|
2016-04-18 08:01:20 +00:00
|
|
|
elsif local_config['types']
|
|
|
|
next if local_config['types'].to_s != @item[:type].to_s
|
2016-04-15 21:56:10 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# check group
|
2016-04-18 08:01:20 +00:00
|
|
|
if local_config['group_ids'].class == Array
|
2016-04-18 00:08:11 +00:00
|
|
|
hit = false
|
2016-06-30 20:04:48 +00:00
|
|
|
local_config['group_ids'].each { |group_id|
|
2016-04-18 00:08:11 +00:00
|
|
|
next if group_id.to_s != ticket.group_id.to_s
|
|
|
|
hit = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
next if !hit
|
2016-04-18 08:01:20 +00:00
|
|
|
elsif local_config['group_ids']
|
|
|
|
next if local_config['group_ids'].to_s != ticket.group_id.to_s
|
2016-04-15 21:56:10 +00:00
|
|
|
end
|
|
|
|
|
2016-04-18 06:38:19 +00:00
|
|
|
logo_url = 'https://zammad.com/assets/images/logo-200x200.png'
|
2016-04-18 08:01:20 +00:00
|
|
|
if !local_config['logo_url'].empty?
|
|
|
|
logo_url = local_config['logo_url']
|
2016-04-18 06:38:19 +00:00
|
|
|
end
|
|
|
|
|
2016-04-18 08:01:20 +00:00
|
|
|
Rails.logger.debug "sent webhook (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})"
|
2016-04-18 06:38:19 +00:00
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
notifier = Slack::Notifier.new(
|
2016-04-18 08:01:20 +00:00
|
|
|
local_config['webhook'],
|
|
|
|
channel: local_config['channel'],
|
|
|
|
username: local_config['username'],
|
2016-04-18 06:38:19 +00:00
|
|
|
icon_url: logo_url,
|
2016-04-15 21:56:10 +00:00
|
|
|
mrkdwn: true,
|
2016-04-18 00:02:31 +00:00
|
|
|
http_client: Transaction::Slack::Client,
|
2016-04-15 21:56:10 +00:00
|
|
|
)
|
2016-04-18 08:01:20 +00:00
|
|
|
if local_config['expand']
|
2016-04-15 21:56:10 +00:00
|
|
|
body = "#{result[:subject]}\n#{result[:body]}"
|
|
|
|
result = notifier.ping body
|
|
|
|
else
|
|
|
|
attachment = {
|
|
|
|
text: result[:body],
|
|
|
|
mrkdwn_in: ['text'],
|
|
|
|
color: color,
|
|
|
|
}
|
|
|
|
result = notifier.ping result[:subject],
|
|
|
|
attachments: [attachment]
|
|
|
|
end
|
2016-04-18 00:02:31 +00:00
|
|
|
if !result.success?
|
2016-04-18 07:48:21 +00:00
|
|
|
if sent_value
|
|
|
|
Cache.delete(cache_key)
|
|
|
|
end
|
2016-04-18 08:01:20 +00:00
|
|
|
Rails.logger.error "Unable to post webhook: #{local_config['webhook']}: #{result.inspect}"
|
2016-04-18 00:02:31 +00:00
|
|
|
next
|
2016-04-15 21:56:10 +00:00
|
|
|
end
|
2016-04-18 08:01:20 +00:00
|
|
|
Rails.logger.debug "sent webhook (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})"
|
2016-04-15 21:56:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
def human_changes(record)
|
|
|
|
|
|
|
|
return {} if !@item[:changes]
|
|
|
|
user = User.find(1)
|
|
|
|
locale = user.preferences[:locale] || 'en-us'
|
|
|
|
|
|
|
|
# only show allowed attributes
|
|
|
|
attribute_list = ObjectManager::Attribute.by_object_as_hash('Ticket', user)
|
|
|
|
#puts "AL #{attribute_list.inspect}"
|
|
|
|
user_related_changes = {}
|
2016-06-30 20:04:48 +00:00
|
|
|
@item[:changes].each { |key, value|
|
2016-04-15 21:56:10 +00:00
|
|
|
|
|
|
|
# if no config exists, use all attributes
|
|
|
|
if !attribute_list || attribute_list.empty?
|
|
|
|
user_related_changes[key] = value
|
|
|
|
|
|
|
|
# if config exists, just use existing attributes for user
|
|
|
|
elsif attribute_list[key.to_s]
|
|
|
|
user_related_changes[key] = value
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
changes = {}
|
2016-06-30 20:04:48 +00:00
|
|
|
user_related_changes.each { |key, value|
|
2016-04-15 21:56:10 +00:00
|
|
|
|
|
|
|
# get attribute name
|
|
|
|
attribute_name = key.to_s
|
|
|
|
object_manager_attribute = attribute_list[attribute_name]
|
|
|
|
if attribute_name[-3, 3] == '_id'
|
|
|
|
attribute_name = attribute_name[ 0, attribute_name.length - 3 ].to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
# add item to changes hash
|
|
|
|
if key.to_s == attribute_name
|
|
|
|
changes[attribute_name] = value
|
|
|
|
end
|
|
|
|
|
|
|
|
# if changed item is an _id field/reference, do an lookup for the realy values
|
|
|
|
value_id = []
|
|
|
|
value_str = [ value[0], value[1] ]
|
|
|
|
if key.to_s[-3, 3] == '_id'
|
|
|
|
value_id[0] = value[0]
|
|
|
|
value_id[1] = value[1]
|
|
|
|
|
|
|
|
if record.respond_to?(attribute_name) && record.send(attribute_name)
|
|
|
|
relation_class = record.send(attribute_name).class
|
|
|
|
if relation_class && value_id[0]
|
|
|
|
relation_model = relation_class.lookup(id: value_id[0])
|
|
|
|
if relation_model
|
|
|
|
if relation_model['name']
|
|
|
|
value_str[0] = relation_model['name']
|
|
|
|
elsif relation_model.respond_to?('fullname')
|
|
|
|
value_str[0] = relation_model.send('fullname')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if relation_class && value_id[1]
|
|
|
|
relation_model = relation_class.lookup(id: value_id[1])
|
|
|
|
if relation_model
|
|
|
|
if relation_model['name']
|
|
|
|
value_str[1] = relation_model['name']
|
|
|
|
elsif relation_model.respond_to?('fullname')
|
|
|
|
value_str[1] = relation_model.send('fullname')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# check if we have an dedcated display name for it
|
|
|
|
display = attribute_name
|
|
|
|
if object_manager_attribute && object_manager_attribute[:display]
|
|
|
|
|
|
|
|
# delete old key
|
|
|
|
changes.delete(display)
|
|
|
|
|
|
|
|
# set new key
|
|
|
|
display = object_manager_attribute[:display].to_s
|
|
|
|
end
|
|
|
|
changes[display] = if object_manager_attribute && object_manager_attribute[:translate]
|
|
|
|
from = Translation.translate(locale, value_str[0])
|
|
|
|
to = Translation.translate(locale, value_str[1])
|
|
|
|
[from, to]
|
|
|
|
else
|
|
|
|
[value_str[0].to_s, value_str[1].to_s]
|
|
|
|
end
|
|
|
|
}
|
|
|
|
changes
|
|
|
|
end
|
|
|
|
|
2016-04-18 00:02:31 +00:00
|
|
|
class Transaction::Slack::Client
|
|
|
|
def self.post(uri, params = {})
|
|
|
|
UserAgent.post(
|
|
|
|
uri.to_s,
|
|
|
|
params,
|
|
|
|
{
|
|
|
|
open_timeout: 4,
|
|
|
|
read_timeout: 10,
|
|
|
|
total_timeout: 20,
|
|
|
|
log: {
|
|
|
|
facility: 'slack_webhook',
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-15 21:56:10 +00:00
|
|
|
end
|