mirror of
https://github.com/discourse/discourse-chat-integration.git
synced 2025-03-06 17:59:29 +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
201 lines
6.8 KiB
Ruby
201 lines
6.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module DiscourseChat
|
|
module Helper
|
|
|
|
def self.process_command(channel, tokens)
|
|
guardian = DiscourseChat::Manager.guardian
|
|
|
|
provider = channel.provider
|
|
|
|
cmd = tokens.shift if tokens.size >= 1
|
|
|
|
error_text = I18n.t("chat_integration.provider.#{provider}.parse_error")
|
|
|
|
case cmd
|
|
when "thread", "watch", "follow", "mute"
|
|
return error_text if tokens.empty?
|
|
# If the first token in the command is a tag, this rule applies to all categories
|
|
category_name = tokens[0].start_with?('tag:') ? nil : tokens.shift
|
|
|
|
if category_name
|
|
category = Category.find_by(slug: category_name)
|
|
unless category
|
|
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(', ')
|
|
return I18n.t("chat_integration.provider.#{provider}.not_found.category", name: category_name, list: cat_list)
|
|
end
|
|
else
|
|
category = nil # All categories
|
|
end
|
|
|
|
tags = []
|
|
# Every remaining token must be a tag. If not, abort and send help text
|
|
while tokens.size > 0
|
|
token = tokens.shift
|
|
if token.start_with?('tag:')
|
|
tag_name = token.sub(/^tag:/, '')
|
|
else
|
|
return error_text
|
|
end
|
|
|
|
tag = Tag.find_by(name: tag_name)
|
|
|
|
unless tag
|
|
return I18n.t("chat_integration.provider.#{provider}.not_found.tag", name: tag_name)
|
|
end
|
|
tags.push(tag.name)
|
|
end
|
|
|
|
category_id = category.nil? ? nil : category.id
|
|
case DiscourseChat::Helper.smart_create_rule(channel: channel, filter: cmd, category_id: category_id, tags: tags)
|
|
when :created
|
|
I18n.t("chat_integration.provider.#{provider}.create.created")
|
|
when :updated
|
|
I18n.t("chat_integration.provider.#{provider}.create.updated")
|
|
else
|
|
I18n.t("chat_integration.provider.#{provider}.create.error")
|
|
end
|
|
when "remove"
|
|
return error_text unless tokens.size == 1
|
|
|
|
rule_number = tokens[0].to_i
|
|
return error_text unless rule_number.to_s == tokens[0] # Check we were given a number
|
|
|
|
if DiscourseChat::Helper.delete_by_index(channel, rule_number)
|
|
I18n.t("chat_integration.provider.#{provider}.delete.success")
|
|
else
|
|
I18n.t("chat_integration.provider.#{provider}.delete.error")
|
|
end
|
|
when "status"
|
|
DiscourseChat::Helper.status_for_channel(channel)
|
|
when "help"
|
|
I18n.t("chat_integration.provider.#{provider}.help")
|
|
else
|
|
error_text
|
|
end
|
|
end
|
|
|
|
# Produce a string with a list of all rules associated with a channel
|
|
def self.status_for_channel(channel)
|
|
rules = channel.rules.order_by_precedence
|
|
provider = channel.provider
|
|
|
|
text = I18n.t("chat_integration.provider.#{provider}.status.header") + "\n"
|
|
|
|
i = 1
|
|
rules.each do |rule|
|
|
category_id = rule.category_id
|
|
|
|
case rule.type
|
|
when "normal"
|
|
if category_id.nil?
|
|
category_name = I18n.t("chat_integration.all_categories")
|
|
else
|
|
category = Category.find_by(id: category_id)
|
|
if category
|
|
category_name = category.slug
|
|
else
|
|
category_name = I18n.t("chat_integration.deleted_category")
|
|
end
|
|
end
|
|
when "group_mention", "group_message"
|
|
group = Group.find_by(id: rule.group_id)
|
|
if group
|
|
category_name = I18n.t("chat_integration.#{rule.type}_template", name: group.name)
|
|
else
|
|
category_name = I18n.t("chat_integration.deleted_group")
|
|
end
|
|
end
|
|
|
|
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string",
|
|
index: i,
|
|
filter: rule.filter,
|
|
category: category_name
|
|
)
|
|
|
|
if SiteSetting.tagging_enabled && (!rule.tags.nil?)
|
|
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string_tags_suffix", tags: rule.tags.join(', '))
|
|
end
|
|
|
|
text << "\n"
|
|
i += 1
|
|
end
|
|
|
|
if rules.size == 0
|
|
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules")
|
|
end
|
|
|
|
text
|
|
end
|
|
|
|
# Delete a rule based on its (1 based) index as seen in the
|
|
# status_for_channel function
|
|
def self.delete_by_index(channel, index)
|
|
rules = channel.rules.order_by_precedence
|
|
return false if index < (1) || index > (rules.size)
|
|
return :deleted if rules[index - 1].destroy
|
|
end
|
|
|
|
# Create a rule for a specific channel
|
|
# Designed to be used by provider's "Slash commands"
|
|
# Will intelligently adjust existing rules to avoid duplicates
|
|
# Returns
|
|
# :updated if an existing rule has been updated
|
|
# :created if a new rule has been created
|
|
# false if there was an error
|
|
def self.smart_create_rule(channel:, filter:, category_id: nil, tags: nil)
|
|
existing_rules = DiscourseChat::Rule.with_channel(channel).with_type('normal')
|
|
|
|
# Select the ones that have the same category
|
|
same_category = existing_rules.select { |rule| rule.category_id == category_id }
|
|
|
|
same_category_and_tags = same_category.select { |rule| (rule.tags.nil? ? [] : rule.tags.sort) == (tags.nil? ? [] : tags.sort) }
|
|
|
|
if same_category_and_tags.size > 0
|
|
# These rules have exactly the same criteria as what we're trying to create
|
|
the_rule = same_category_and_tags.shift # Take out the first one
|
|
|
|
same_category_and_tags.each do |rule| # Destroy all the others - they're duplicates
|
|
rule.destroy
|
|
end
|
|
|
|
return :updated if the_rule.update(filter: filter) # Update the filter
|
|
return false # Error, probably validation
|
|
end
|
|
|
|
same_category_and_filters = same_category.select { |rule| rule.filter == filter }
|
|
|
|
if same_category_and_filters.size > 0
|
|
# These rules are exactly the same, except for tags. Let's combine the tags together
|
|
tags = [] if tags.nil?
|
|
same_category_and_filters.each do |rule|
|
|
tags = tags | rule.tags unless rule.tags.nil? # Append the tags together, avoiding duplicates by magic
|
|
end
|
|
|
|
the_rule = same_category_and_filters.shift # Take out the first one
|
|
|
|
if the_rule.update(tags: tags) # Update the tags
|
|
same_category_and_filters.each do |rule| # Destroy all the others - they're duplicates
|
|
rule.destroy
|
|
end
|
|
return :updated
|
|
end
|
|
|
|
return false # Error
|
|
end
|
|
|
|
# This rule is unique! Create a new one:
|
|
return :created if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save
|
|
false
|
|
end
|
|
|
|
def self.save_transcript(transcript)
|
|
secret = SecureRandom.hex
|
|
redis_key = "chat_integration:transcript:#{secret}"
|
|
Discourse.redis.setex(redis_key, 3600, transcript)
|
|
secret
|
|
end
|
|
|
|
end
|
|
end
|