mirror of
https://github.com/discourse/discourse-chat-integration.git
synced 2025-03-08 18:59:38 +00:00
When creating a new Discourse post from slack with the `post` feature, record the slack `ts` thread ID for the resulting topic post using an HTML comment to pass the `ts` through. When notifying slack of new Discourse posts, record the slack `ts` thread ID in the post's topic if it has not yet been recorded. (Normally, this will be done for the topic post, except where notifications are being posted for old topics before this feature was created.) Add a new rule filter `thread` which posts threaded responses to slack if there is a `ts` recorded for the post topic. Modify the `trigger_notifications` interface to enable other integrations to implement similar functionality. Present the `thread` rule in the help text and admin UI only for the slack providers. https://meta.discourse.org/t/optionally-threading-posts-to-parent-topic-in-slack-integration/150759
109 lines
4.5 KiB
Ruby
109 lines
4.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module DiscourseChat
|
|
module Manager
|
|
|
|
def self.guardian
|
|
Guardian.new(User.find_by(username: SiteSetting.chat_integration_discourse_username))
|
|
end
|
|
|
|
def self.trigger_notifications(post_id)
|
|
post = Post.find_by(id: post_id)
|
|
|
|
# Abort if the chat_user doesn't have permission to see the post
|
|
return if !guardian.can_see?(post)
|
|
|
|
# Abort if the post is blank, or is non-regular (e.g. a "topic closed" notification)
|
|
return if post.blank? || post.post_type != Post.types[:regular]
|
|
|
|
topic = post.topic
|
|
return if topic.blank?
|
|
|
|
# If it's a private message, filter rules by groups, otherwise filter rules by category
|
|
if topic.archetype == Archetype.private_message
|
|
group_ids_with_access = topic.topic_allowed_groups.pluck(:group_id)
|
|
return if group_ids_with_access.empty?
|
|
matching_rules = DiscourseChat::Rule.with_type('group_message').with_group_ids(group_ids_with_access)
|
|
else
|
|
matching_rules = DiscourseChat::Rule.with_type('normal').with_category_id(topic.category_id)
|
|
if topic.category # Also load the rules for the wildcard category
|
|
matching_rules += DiscourseChat::Rule.with_type('normal').with_category_id(nil)
|
|
end
|
|
|
|
# If groups are mentioned, check for any matching rules and append them
|
|
mentions = post.raw_mentions
|
|
if mentions && mentions.length > 0
|
|
groups = Group.where('LOWER(name) IN (?)', mentions)
|
|
if groups.exists?
|
|
matching_rules += DiscourseChat::Rule.with_type('group_mention').with_group_ids(groups.map(&:id))
|
|
end
|
|
end
|
|
end
|
|
|
|
# If tagging is enabled, thow away rules that don't apply to this topic
|
|
if SiteSetting.tagging_enabled
|
|
topic_tags = topic.tags.present? ? topic.tags.pluck(:name) : []
|
|
matching_rules = matching_rules.select do |rule|
|
|
next true if rule.tags.nil? || rule.tags.empty? # Filter has no tags specified
|
|
any_tags_match = !((rule.tags & topic_tags).empty?)
|
|
next any_tags_match # If any tags match, keep this filter, otherwise throw away
|
|
end
|
|
end
|
|
|
|
# Sort by order of precedence
|
|
t_prec = { 'group_message' => 0, 'group_mention' => 1, 'normal' => 2 } # Group things win
|
|
f_prec = { 'mute' => 0, 'thread' => 1, 'watch' => 2, 'follow' => 3 } #(mute always wins; thread beats watch beats follow)
|
|
sort_func = proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
|
matching_rules = matching_rules.sort(&sort_func)
|
|
|
|
# Take the first rule for each channel
|
|
uniq_func = proc { |rule| [rule.channel_id] }
|
|
matching_rules = matching_rules.uniq(&uniq_func)
|
|
|
|
# If a matching rule is set to mute, we can discard it now
|
|
matching_rules = matching_rules.select { |rule| rule.filter != "mute" }
|
|
|
|
# If this is not the first post, discard all "follow" rules
|
|
if !post.is_first_post?
|
|
matching_rules = matching_rules.select { |rule| rule.filter != "follow" }
|
|
end
|
|
|
|
# All remaining rules now require a notification to be sent
|
|
# If there are none left, abort
|
|
return false if matching_rules.empty?
|
|
|
|
# Loop through each rule, and trigger appropriate notifications
|
|
matching_rules.each do |rule|
|
|
# If there are any issues, skip to the next rule
|
|
next unless channel = rule.channel
|
|
next unless provider = ::DiscourseChat::Provider.get_by_name(channel.provider)
|
|
next unless is_enabled = ::DiscourseChat::Provider.is_enabled(provider)
|
|
|
|
begin
|
|
provider.trigger_notification(post, channel, rule)
|
|
channel.update_attribute('error_key', nil) if channel.error_key
|
|
rescue => e
|
|
if e.class == (DiscourseChat::ProviderError) && e.info.key?(:error_key) && !e.info[:error_key].nil?
|
|
channel.update_attribute('error_key', e.info[:error_key])
|
|
else
|
|
channel.update_attribute('error_key', 'chat_integration.channel_exception')
|
|
end
|
|
channel.update_attribute('error_info', JSON.pretty_generate(e.try(:info)))
|
|
|
|
# Log the error
|
|
# Discourse.handle_job_exception(e,
|
|
# message: "Triggering notifications failed",
|
|
# extra: { provider_name: provider::PROVIDER_NAME,
|
|
# channel: rule.channel,
|
|
# post_id: post.id,
|
|
# error_info: e.class == DiscourseChat::ProviderError ? e.info : nil }
|
|
# )
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
end
|