slack.rb 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Transaction::Slack
  3. include ChecksHumanChanges
  4. =begin
  5. backend = Transaction::Slack.new(
  6. object: 'Ticket',
  7. type: 'update',
  8. object_id: 123,
  9. interface_handle: 'application_server', # application_server|websocket|scheduler
  10. changes: {
  11. 'attribute1' => [before, now],
  12. 'attribute2' => [before, now],
  13. },
  14. created_at: Time.zone.now,
  15. user_id: 123,
  16. )
  17. backend.perform
  18. =end
  19. def initialize(item, params = {})
  20. @item = item
  21. @params = params
  22. end
  23. def perform
  24. # return if we run import mode
  25. return if Setting.get('import_mode')
  26. return if @item[:object] != 'Ticket'
  27. return if !Setting.get('slack_integration')
  28. config = Setting.get('slack_config')
  29. return if !config
  30. return if !config['items']
  31. ticket = Ticket.find_by(id: @item[:object_id])
  32. return if !ticket
  33. if @item[:article_id]
  34. article = Ticket::Article.find(@item[:article_id])
  35. # ignore notifications
  36. sender = Ticket::Article::Sender.lookup(id: article.sender_id)
  37. if sender&.name == 'System'
  38. return if @item[:changes].blank?
  39. article = nil
  40. end
  41. end
  42. # ignore if no changes has been done
  43. changes = human_changes(@item[:changes], ticket)
  44. return if @item[:type] == 'update' && !article && changes.blank?
  45. # get user based notification template
  46. # if create, send create message / block update messages
  47. template = nil
  48. sent_value = nil
  49. case @item[:type]
  50. when 'create'
  51. template = 'ticket_create'
  52. when 'update'
  53. template = 'ticket_update'
  54. when 'reminder_reached'
  55. template = 'ticket_reminder_reached'
  56. sent_value = ticket.pending_time
  57. when 'escalation'
  58. template = 'ticket_escalation'
  59. sent_value = ticket.escalation_at
  60. when 'escalation_warning'
  61. template = 'ticket_escalation_warning'
  62. sent_value = ticket.escalation_at
  63. else
  64. raise "unknown type for notification #{@item[:type]}"
  65. end
  66. user = User.find(1)
  67. current_user = User.lookup(id: @item[:user_id])
  68. if !current_user
  69. current_user = User.lookup(id: 1)
  70. end
  71. result = NotificationFactory::Messaging.template(
  72. template: template,
  73. locale: user.locale,
  74. timezone: Setting.get('timezone_default'),
  75. objects: {
  76. ticket: ticket,
  77. article: article,
  78. current_user: current_user,
  79. changes: changes,
  80. },
  81. )
  82. config['items'].each do |local_config|
  83. next if local_config['webhook'].blank?
  84. # check if reminder_reached/escalation/escalation_warning is already sent today
  85. md5_webhook = Digest::MD5.hexdigest(local_config['webhook'])
  86. cache_key = "slack::backend::#{@item[:type]}::#{ticket.id}::#{md5_webhook}"
  87. if sent_value
  88. value = Rails.cache.read(cache_key)
  89. if value == sent_value
  90. Rails.logger.debug { "did not send webhook, already sent (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})" }
  91. next
  92. end
  93. Rails.cache.write(
  94. cache_key,
  95. sent_value,
  96. {
  97. expires_in: 24.hours
  98. },
  99. )
  100. end
  101. # check action
  102. if local_config['types'].instance_of?(Array)
  103. hit = false
  104. local_config['types'].each do |type|
  105. next if type.to_s != @item[:type].to_s
  106. hit = true
  107. break
  108. end
  109. next if !hit
  110. elsif local_config['types']
  111. next if local_config['types'].to_s != @item[:type].to_s
  112. end
  113. # check group
  114. if local_config['group_ids'].instance_of?(Array)
  115. hit = false
  116. local_config['group_ids'].each do |group_id|
  117. next if group_id.to_s != ticket.group_id.to_s
  118. hit = true
  119. break
  120. end
  121. next if !hit
  122. elsif local_config['group_ids']
  123. next if local_config['group_ids'].to_s != ticket.group_id.to_s
  124. end
  125. icon_url = 'https://zammad.com/assets/images/logo-200x200.png'
  126. if local_config['icon_url'].present?
  127. icon_url = local_config['icon_url']
  128. end
  129. Rails.logger.debug { "sent webhook (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})" }
  130. require 'slack-notifier' # Only load this gem when it is really used.
  131. notifier = Slack::Notifier.new(
  132. local_config['webhook'],
  133. channel: local_config['channel'],
  134. username: local_config['username'],
  135. icon_url: icon_url,
  136. mrkdwn: true,
  137. http_client: Transaction::Slack::Client,
  138. )
  139. if local_config['expand']
  140. body = "#{result[:subject]}\n#{result[:body]}"
  141. result_ping = notifier.ping body
  142. else
  143. attachment = {
  144. text: result[:body],
  145. mrkdwn_in: ['text'],
  146. color: ticket.current_state_color,
  147. }
  148. result_ping = notifier.ping result[:subject],
  149. attachments: [attachment]
  150. end
  151. if !result_ping.empty? && !result_ping[0].success?
  152. if sent_value
  153. Rails.cache.delete(cache_key)
  154. end
  155. Rails.logger.error "Unable to post webhook: #{local_config['webhook']}: #{result_ping.inspect}"
  156. next
  157. end
  158. Rails.logger.debug { "sent webhook (#{@item[:type]}/#{ticket.id}/#{local_config['webhook']})" }
  159. end
  160. end
  161. class Transaction::Slack::Client
  162. def self.post(uri, params = {})
  163. UserAgent.post(
  164. uri.to_s,
  165. params,
  166. {
  167. open_timeout: 4,
  168. read_timeout: 10,
  169. total_timeout: 20,
  170. log: {
  171. facility: 'slack_webhook',
  172. },
  173. verify_ssl: true,
  174. },
  175. )
  176. end
  177. end
  178. end