FEATURE: add topic tags changed trigger to chat integration (#208)
* FEATURE: add topic tags changed trigger to chat integration * FEATURE: add placeholder for reply to topic trigger add description on how to use the placeholder * DEV: Move slack message creation to provider Add tests to new method * FEATURE: add ${URL} to placeholder replacements and added tags link If triggered when a topic tag is changed, message behavior will follow what user defined in message. * DEV: Update tests with tags * DEV: add post to topic for testing * DEV: update test strings * DEV: add early return for topic tags changed trigger * DEV: move early return to use try/catch * DEV: update `create_slack_message` to not send a tuple of values * DEV: refactor method to be more readable * FEATURE: add `${ADDED_AND_REMOVED}` for default texts * DEV: Update typo in test * DEV: Add tests to check when if `create_slack_message` raises an error * DEV: Remove the `tag_added` from chat-integration filter Added migration to handle the migration of the `tag_added` filter from the chat-integration plugin. Only removed the logic from the plugin, data removal will happen in a future PR * DEV: lint migration file * DEV: update chat-integration to not show "tag_added" rules * DEV: update added and missing tags logic * DEV: update context variable name * DEV: update migration to include `begin/rescue` block and added a list with available filters
This commit is contained in:
parent
ac44465a60
commit
57b460737c
|
@ -23,6 +23,8 @@ export default class Rule extends RestModel {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
possible_filters_id = ["thread", "watch", "follow", "mute"];
|
||||||
|
|
||||||
get available_filters() {
|
get available_filters() {
|
||||||
const available = [];
|
const available = [];
|
||||||
const provider = this.channel.provider;
|
const provider = this.channel.provider;
|
||||||
|
@ -46,11 +48,6 @@ export default class Rule extends RestModel {
|
||||||
name: I18n.t("chat_integration.filter.follow"),
|
name: I18n.t("chat_integration.filter.follow"),
|
||||||
icon: "circle",
|
icon: "circle",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "tag_added",
|
|
||||||
name: I18n.t("chat_integration.filter.tag_added"),
|
|
||||||
icon: "tag",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "mute",
|
id: "mute",
|
||||||
name: I18n.t("chat_integration.filter.mute"),
|
name: I18n.t("chat_integration.filter.mute"),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
|
import { getOwner } from "@ember/owner";
|
||||||
import Group from "discourse/models/group";
|
import Group from "discourse/models/group";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
|
@ -13,14 +14,18 @@ export default class AdminPluginsChatIntegrationProvider extends DiscourseRoute
|
||||||
Group.findAll(),
|
Group.findAll(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const enabledFilters =
|
||||||
|
getOwner(this).lookup("model:rule").possible_filters_id;
|
||||||
channels.forEach((channel) => {
|
channels.forEach((channel) => {
|
||||||
channel.set(
|
channel.set(
|
||||||
"rules",
|
"rules",
|
||||||
channel.rules.map((rule) => {
|
channel.rules
|
||||||
rule = this.store.createRecord("rule", rule);
|
.filter((rule) => enabledFilters.includes(rule.filter))
|
||||||
rule.set("channel", channel);
|
.map((rule) => {
|
||||||
return rule;
|
rule = this.store.createRecord("rule", rule);
|
||||||
})
|
rule.set("channel", channel);
|
||||||
|
return rule;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ module DiscourseChatIntegration
|
||||||
# Abort if the post is blank
|
# Abort if the post is blank
|
||||||
return if post.blank?
|
return if post.blank?
|
||||||
|
|
||||||
# Abort if post is not either regular, or a 'tags_changed'/'category_changed' small action
|
# Abort if post is not either regular or a 'category_changed' small action
|
||||||
if (post.post_type != Post.types[:regular]) &&
|
if (post.post_type != Post.types[:regular]) &&
|
||||||
!(
|
!(
|
||||||
post.post_type == Post.types[:small_action] &&
|
post.post_type == Post.types[:small_action] &&
|
||||||
%w[tags_changed category_changed].include?(post.action_code)
|
%w[category_changed].include?(post.action_code)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -55,34 +55,7 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if post.action_code == "tags_changed"
|
matching_rules = matching_rules.select { |rule| rule.filter != "tag_added" } # ignore tag_added rules, now uses Automation
|
||||||
# Post is a small_action post regarding tags changing for the topic. Check if any tags were _added_
|
|
||||||
# and if so, corresponding rules with `filter: tag_added`
|
|
||||||
tags_added = post.custom_fields["tags_added"]
|
|
||||||
tags_added = [tags_added].compact if !tags_added.is_a?(Array)
|
|
||||||
return if tags_added.blank?
|
|
||||||
|
|
||||||
tags_removed = post.custom_fields["tags_removed"]
|
|
||||||
tags_removed = [tags_removed].compact if !tags_removed.is_a?(Array)
|
|
||||||
|
|
||||||
unchanged_tags = topic.tags.map(&:name) - tags_added - tags_removed
|
|
||||||
|
|
||||||
matching_rules =
|
|
||||||
matching_rules.select do |rule|
|
|
||||||
# Only rules that match this post, are ones where the filter is "tag_added"
|
|
||||||
next false if rule.filter != "tag_added"
|
|
||||||
next true if rule.tags.blank?
|
|
||||||
|
|
||||||
# Skip if the topic already has one of the tags in the rule, applied
|
|
||||||
next false if unchanged_tags.any? && (unchanged_tags & rule.tags).any?
|
|
||||||
|
|
||||||
# We don't need to do any additional filtering here because topics are filtered
|
|
||||||
# by tag later
|
|
||||||
true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
matching_rules = matching_rules.select { |rule| rule.filter != "tag_added" }
|
|
||||||
end
|
|
||||||
|
|
||||||
# If tagging is enabled, thow away rules that don't apply to this topic
|
# If tagging is enabled, thow away rules that don't apply to this topic
|
||||||
if SiteSetting.tagging_enabled
|
if SiteSetting.tagging_enabled
|
||||||
|
@ -97,7 +70,7 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
# Sort by order of precedence
|
# Sort by order of precedence
|
||||||
t_prec = { "group_message" => 0, "group_mention" => 1, "normal" => 2 } # Group things win
|
t_prec = { "group_message" => 0, "group_mention" => 1, "normal" => 2 } # Group things win
|
||||||
f_prec = { "mute" => 0, "thread" => 1, "watch" => 2, "follow" => 3, "tag_added" => 4 } #(mute always wins; thread beats watch beats follow)
|
f_prec = { "mute" => 0, "thread" => 1, "watch" => 2, "follow" => 3 } #(mute always wins; thread beats watch beats follow)
|
||||||
sort_func =
|
sort_func =
|
||||||
proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
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)
|
matching_rules = matching_rules.sort(&sort_func)
|
||||||
|
|
|
@ -37,7 +37,6 @@ en:
|
||||||
mute: 'Mute'
|
mute: 'Mute'
|
||||||
follow: 'First post only'
|
follow: 'First post only'
|
||||||
watch: 'All posts and replies'
|
watch: 'All posts and replies'
|
||||||
tag_added: 'Tag added to topic'
|
|
||||||
thread: 'All posts with threaded replies'
|
thread: 'All posts with threaded replies'
|
||||||
rule_table:
|
rule_table:
|
||||||
filter: "Filter"
|
filter: "Filter"
|
||||||
|
@ -256,7 +255,11 @@ en:
|
||||||
title: Send Slack message
|
title: Send Slack message
|
||||||
fields:
|
fields:
|
||||||
message:
|
message:
|
||||||
label: Message
|
label: Message.
|
||||||
|
description: >-
|
||||||
|
Use ${TOPIC} for topic name, ${URL} for used URL, ${REMOVED_TAGS} for removed tags, ${ADDED_TAGS} for added tags,
|
||||||
|
${ADDED_AND_REMOVED} for default text.
|
||||||
|
only available if trigger is set to Topic tags changed.
|
||||||
url:
|
url:
|
||||||
label: URL
|
label: URL
|
||||||
channel:
|
channel:
|
||||||
|
|
|
@ -33,7 +33,7 @@ en:
|
||||||
chat_integration_discord_enabled: "Enable the Discord chat-integration provider"
|
chat_integration_discord_enabled: "Enable the Discord chat-integration provider"
|
||||||
chat_integration_discord_message_content: "The message to include above the summary when sending a notification to Discord"
|
chat_integration_discord_message_content: "The message to include above the summary when sending a notification to Discord"
|
||||||
chat_integration_discord_excerpt_length: "Discord post excerpt length"
|
chat_integration_discord_excerpt_length: "Discord post excerpt length"
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
########## GUILDED SETTINGS ###########
|
########## GUILDED SETTINGS ###########
|
||||||
#######################################
|
#######################################
|
||||||
|
@ -187,6 +187,11 @@ en:
|
||||||
error_users: "Error: unable to fetch users from Slack"
|
error_users: "Error: unable to fetch users from Slack"
|
||||||
error_history: "Error: unable to fetch channel history from Slack"
|
error_history: "Error: unable to fetch channel history from Slack"
|
||||||
error_ts: "Error: unable to fetch requested message from Slack"
|
error_ts: "Error: unable to fetch requested message from Slack"
|
||||||
|
messaging:
|
||||||
|
topic_tag_changed:
|
||||||
|
added_and_removed: "Added %{added} and removed %{removed}"
|
||||||
|
added: "Added %{added}"
|
||||||
|
removed: "Removed %{removed}"
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
########## TELEGRAM STRINGS ###########
|
########## TELEGRAM STRINGS ###########
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class MigrateTagAddedFromFilterToAutomation < ActiveRecord::Migration[7.1]
|
||||||
|
def up
|
||||||
|
if defined?(DiscourseAutomation) &&
|
||||||
|
DiscourseChatIntegration::Channel.with_provider("slack").exists?
|
||||||
|
begin
|
||||||
|
DiscourseChatIntegration::Rule
|
||||||
|
.where("value::json->>'filter'=?", "tag_added")
|
||||||
|
.each do |rule|
|
||||||
|
channel_id = rule.value["channel_id"]
|
||||||
|
channel_name =
|
||||||
|
DiscourseChatIntegration::Channel.find(channel_id).value["data"]["identifier"] # it _must_ have a channel_id
|
||||||
|
|
||||||
|
category_id = rule.value["category_id"]
|
||||||
|
tags = rule.value["tags"]
|
||||||
|
|
||||||
|
automation =
|
||||||
|
DiscourseAutomation::Automation.new(
|
||||||
|
script: "send_slack_message",
|
||||||
|
trigger: "topic_tags_changed",
|
||||||
|
name: "When tags change in topic",
|
||||||
|
enabled: true,
|
||||||
|
last_updated_by_id: Discourse.system_user.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
automation.save!
|
||||||
|
|
||||||
|
# Triggers:
|
||||||
|
# Watching categories
|
||||||
|
|
||||||
|
metadata = (category_id ? { "value" => [category_id] } : {})
|
||||||
|
|
||||||
|
automation.upsert_field!(
|
||||||
|
"watching_categories",
|
||||||
|
"categories",
|
||||||
|
metadata,
|
||||||
|
target: "trigger",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Watching tags
|
||||||
|
|
||||||
|
metadata = (tags ? { "value" => tags } : {})
|
||||||
|
|
||||||
|
automation.upsert_field!("watching_tags", "tags", metadata, target: "trigger")
|
||||||
|
|
||||||
|
# Script options:
|
||||||
|
# Message
|
||||||
|
automation.upsert_field!(
|
||||||
|
"message",
|
||||||
|
"message",
|
||||||
|
{ "value" => "${ADDED_AND_REMOVED}" },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
|
||||||
|
# URL
|
||||||
|
automation.upsert_field!(
|
||||||
|
"url",
|
||||||
|
"text",
|
||||||
|
{ "value" => Discourse.current_hostname },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Channel
|
||||||
|
automation.upsert_field!(
|
||||||
|
"channel",
|
||||||
|
"text",
|
||||||
|
{ "value" => channel_name },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
rescue StandardError
|
||||||
|
Rails.logger.warn("Failed to migrate tag_added rules to automations")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
if defined?(DiscourseAutomation) &&
|
||||||
|
DiscourseChatIntegration::Channel.with_provider("slack").exists?
|
||||||
|
DiscourseAutomation::Automation
|
||||||
|
.where(script: "send_slack_message", trigger: "topic_tags_changed")
|
||||||
|
.each do |automation|
|
||||||
|
# if is the same name as created and message is the same
|
||||||
|
if automation.name == "When tags change in topic" &&
|
||||||
|
automation.fields.where(name: "message").first.metadata["value"] ==
|
||||||
|
"${ADDED_AND_REMOVED}"
|
||||||
|
automation.destroy!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -87,6 +87,74 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.create_slack_message(context:, content:, url:, channel_name:)
|
||||||
|
sender = ::DiscourseChatIntegration::Helper.formatted_display_name(Discourse.system_user)
|
||||||
|
|
||||||
|
content = replace_placehoders(content, context) if context["kind"] ==
|
||||||
|
DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED
|
||||||
|
|
||||||
|
full_content =
|
||||||
|
if context["kind"] == DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED
|
||||||
|
content
|
||||||
|
else
|
||||||
|
"#{content} - #{url}"
|
||||||
|
end
|
||||||
|
|
||||||
|
icon_url =
|
||||||
|
if SiteSetting.chat_integration_slack_icon_url.present?
|
||||||
|
"#{Discourse.base_url}#{SiteSetting.chat_integration_slack_icon_url}"
|
||||||
|
elsif (url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)).present?
|
||||||
|
"#{Discourse.base_url}#{url}"
|
||||||
|
end
|
||||||
|
|
||||||
|
slack_username =
|
||||||
|
if SiteSetting.chat_integration_slack_username.present?
|
||||||
|
SiteSetting.chat_integration_slack_username
|
||||||
|
else
|
||||||
|
SiteSetting.title || "Discourse"
|
||||||
|
end
|
||||||
|
|
||||||
|
message = {
|
||||||
|
channel: "##{channel_name}",
|
||||||
|
username: slack_username,
|
||||||
|
icon_url: icon_url,
|
||||||
|
attachments: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = {
|
||||||
|
fallback: content.truncate(100),
|
||||||
|
author_name: sender,
|
||||||
|
color: nil,
|
||||||
|
text: full_content,
|
||||||
|
mrkdwn_in: ["text"],
|
||||||
|
title: content.truncate(100),
|
||||||
|
title_link: url,
|
||||||
|
thumb_url: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if context["kind"] == DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED
|
||||||
|
topic = context["topic"]
|
||||||
|
category =
|
||||||
|
if topic.category&.uncategorized?
|
||||||
|
"[#{I18n.t("uncategorized_category_name")}]"
|
||||||
|
elsif topic.category
|
||||||
|
if (topic.category.parent_category)
|
||||||
|
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||||
|
else
|
||||||
|
"[#{topic.category.name}]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
summary[:title_link] = topic.posts.first.full_url
|
||||||
|
summary[
|
||||||
|
:title
|
||||||
|
] = "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}"
|
||||||
|
summary[:thumb_url]
|
||||||
|
end
|
||||||
|
|
||||||
|
message[:attachments].push(summary)
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
def self.send_via_api(post, channel, message)
|
def self.send_via_api(post, channel, message)
|
||||||
http = slack_api_http
|
http = slack_api_http
|
||||||
|
|
||||||
|
@ -214,6 +282,63 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
unique_by: :index_topic_custom_fields_on_topic_id_and_slack_thread_id,
|
unique_by: :index_topic_custom_fields_on_topic_id_and_slack_thread_id,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.replace_placehoders(content, context)
|
||||||
|
if context["topic"] && content.include?("${TOPIC}")
|
||||||
|
topic = context["topic"]
|
||||||
|
content = content.gsub("${TOPIC}", topic.title)
|
||||||
|
end
|
||||||
|
|
||||||
|
if content.include?("${REMOVED_TAGS}")
|
||||||
|
if context["removed_tags"].empty?
|
||||||
|
raise StandardError.new "No tags but content includes reference."
|
||||||
|
end
|
||||||
|
removed_tags_names = create_tag_list(context["removed_tags"])
|
||||||
|
content = content.gsub("${REMOVED_TAGS}", removed_tags_names)
|
||||||
|
end
|
||||||
|
|
||||||
|
if content.include?("${ADDED_TAGS}")
|
||||||
|
if context["added_tags"].empty?
|
||||||
|
raise StandardError.new "No tags but content includes reference."
|
||||||
|
end
|
||||||
|
added_tags_names = create_tag_list(context["added_tags"])
|
||||||
|
content = content.gsub("${ADDED_TAGS}", added_tags_names)
|
||||||
|
end
|
||||||
|
|
||||||
|
if content.include?("${ADDED_AND_REMOVED}")
|
||||||
|
added_tags = context["added_tags"]
|
||||||
|
missing_tags = context["removed_tags"]
|
||||||
|
|
||||||
|
text =
|
||||||
|
if !added_tags.empty? && !missing_tags.empty?
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.added_and_removed",
|
||||||
|
added: create_tag_list(added_tags),
|
||||||
|
removed: create_tag_list(missing_tags),
|
||||||
|
)
|
||||||
|
elsif !added_tags.empty?
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.added",
|
||||||
|
added: create_tag_list(added_tags),
|
||||||
|
)
|
||||||
|
elsif !missing_tags.empty?
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.removed",
|
||||||
|
removed: create_tag_list(missing_tags),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
content = content.gsub("${ADDED_AND_REMOVED}", text)
|
||||||
|
end
|
||||||
|
|
||||||
|
content = content.gsub("${URL}", url) if content.include?("${URL}")
|
||||||
|
|
||||||
|
content
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.create_tag_list(tag_list)
|
||||||
|
tag_list.map { |tag_name| "<#{Tag.find_by_name(tag_name).full_url}|#{tag_name}>" }.join(", ")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
require_relative "slack_message_formatter"
|
require_relative "slack_message_formatter"
|
||||||
|
|
56
plugin.rb
56
plugin.rb
|
@ -58,14 +58,9 @@ after_initialize do
|
||||||
|
|
||||||
version 1
|
version 1
|
||||||
|
|
||||||
triggerables %i[point_in_time recurring]
|
triggerables %i[point_in_time recurring topic_tags_changed]
|
||||||
|
|
||||||
script do |context, fields, automation|
|
script do |context, fields, automation|
|
||||||
sender = Discourse.system_user
|
|
||||||
|
|
||||||
content = fields.dig("message", "value")
|
|
||||||
url = fields.dig("url", "value")
|
|
||||||
full_content = "#{content} - #{url}"
|
|
||||||
channel_name = fields.dig("channel", "value")
|
channel_name = fields.dig("channel", "value")
|
||||||
channel =
|
channel =
|
||||||
DiscourseChatIntegration::Channel.new(
|
DiscourseChatIntegration::Channel.new(
|
||||||
|
@ -75,43 +70,18 @@ after_initialize do
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
icon_url =
|
begin
|
||||||
if SiteSetting.chat_integration_slack_icon_url.present?
|
message =
|
||||||
"#{Discourse.base_url}#{SiteSetting.chat_integration_slack_icon_url}"
|
DiscourseChatIntegration::Provider::SlackProvider.create_slack_message(
|
||||||
elsif (
|
context: context,
|
||||||
url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)
|
content: fields.dig("message", "value"),
|
||||||
).present?
|
url: fields.dig("url", "value"),
|
||||||
"#{Discourse.base_url}#{url}"
|
channel_name: channel_name,
|
||||||
end
|
)
|
||||||
|
DiscourseChatIntegration::Provider::SlackProvider.send_via_api(nil, channel, message)
|
||||||
slack_username =
|
rescue StandardError => _
|
||||||
if SiteSetting.chat_integration_slack_username.present?
|
# StandardError here is when there are no tags but content includes reference to them.
|
||||||
SiteSetting.chat_integration_slack_username
|
end
|
||||||
else
|
|
||||||
SiteSetting.title || "Discourse"
|
|
||||||
end
|
|
||||||
|
|
||||||
message = {
|
|
||||||
channel: "##{channel_name}",
|
|
||||||
username: slack_username,
|
|
||||||
icon_url: icon_url,
|
|
||||||
attachments: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = {
|
|
||||||
fallback: content.truncate(100),
|
|
||||||
author_name: sender,
|
|
||||||
color: nil,
|
|
||||||
text: full_content,
|
|
||||||
mrkdwn_in: ["text"],
|
|
||||||
title: content.truncate(100),
|
|
||||||
title_link: url,
|
|
||||||
thumb_url: nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
message[:attachments].push(summary)
|
|
||||||
|
|
||||||
DiscourseChatIntegration::Provider::SlackProvider.send_via_api(nil, channel, message)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -209,4 +209,141 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe ".create_slack_message" do
|
||||||
|
it "should work with a simple message" do
|
||||||
|
content = "Simple message"
|
||||||
|
url = "http://example.com"
|
||||||
|
message = { channel: "#general", username: "Discourse", content: "#{content} - #{url}" }
|
||||||
|
result =
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: url,
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
{
|
||||||
|
channel: result[:channel],
|
||||||
|
username: result[:username],
|
||||||
|
content: result[:attachments][0][:text],
|
||||||
|
},
|
||||||
|
).to eq(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should do the replacements" do
|
||||||
|
topic = Fabricate(:topic)
|
||||||
|
topic.posts << Fabricate(:post, topic: topic)
|
||||||
|
tag1, tag2, tag3, tag4 = [Fabricate(:tag), Fabricate(:tag), Fabricate(:tag), Fabricate(:tag)]
|
||||||
|
content =
|
||||||
|
"The topic title is: ${TOPIC}
|
||||||
|
removed tags: ${REMOVED_TAGS}
|
||||||
|
added tags: ${ADDED_TAGS}"
|
||||||
|
|
||||||
|
result =
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
"topic" => topic,
|
||||||
|
"removed_tags" => [tag1.name, tag2.name],
|
||||||
|
"added_tags" => [tag3.name, tag4.name],
|
||||||
|
"kind" => DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED,
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: "http://example.com",
|
||||||
|
)
|
||||||
|
text = result[:attachments][0][:text]
|
||||||
|
expect(text).to include(topic.title)
|
||||||
|
expect(text).to include("<#{tag1.full_url}|#{tag1.name}>, <#{tag2.full_url}|#{tag2.name}>")
|
||||||
|
expect(text).to include("<#{tag3.full_url}|#{tag3.name}>, <#{tag4.full_url}|#{tag4.name}>")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should do the replacements for ${ADDED_AND_REMOVED}" do
|
||||||
|
topic = Fabricate(:topic)
|
||||||
|
topic.posts << Fabricate(:post, topic: topic)
|
||||||
|
tag1, tag2 = [Fabricate(:tag), Fabricate(:tag)]
|
||||||
|
content = "${ADDED_AND_REMOVED}"
|
||||||
|
result =
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
"topic" => topic,
|
||||||
|
"added_tags" => [tag2.name],
|
||||||
|
"removed_tags" => [tag1.name],
|
||||||
|
"kind" => DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED,
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: "http://example.com",
|
||||||
|
)
|
||||||
|
text = result[:attachments][0][:text]
|
||||||
|
expect(text).to include(
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.added_and_removed",
|
||||||
|
added: "<#{tag2.full_url}|#{tag2.name}>",
|
||||||
|
removed: "<#{tag1.full_url}|#{tag1.name}>",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
result =
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
"topic" => topic,
|
||||||
|
"added_tags" => [],
|
||||||
|
"removed_tags" => [tag1.name],
|
||||||
|
"kind" => DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED,
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: "http://example.com",
|
||||||
|
)
|
||||||
|
text = result[:attachments][0][:text]
|
||||||
|
expect(text).to include(
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.removed",
|
||||||
|
removed: "<#{tag1.full_url}|#{tag1.name}>",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
result =
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
"topic" => topic,
|
||||||
|
"added_tags" => [tag2.name],
|
||||||
|
"removed_tags" => [],
|
||||||
|
"kind" => DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED,
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: "http://example.com",
|
||||||
|
)
|
||||||
|
text = result[:attachments][0][:text]
|
||||||
|
expect(text).to include(
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.slack.messaging.topic_tag_changed.added",
|
||||||
|
added: "<#{tag2.full_url}|#{tag2.name}>",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise errors if tags are not present but uses in content" do
|
||||||
|
topic = Fabricate(:topic)
|
||||||
|
topic.posts << Fabricate(:post, topic: topic)
|
||||||
|
content = "This should not work ${ADDED_TAGS}"
|
||||||
|
|
||||||
|
expect {
|
||||||
|
described_class.create_slack_message(
|
||||||
|
context: {
|
||||||
|
"topic" => topic,
|
||||||
|
"kind" => DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED,
|
||||||
|
"added_tags" => [],
|
||||||
|
"removed_tags" => [],
|
||||||
|
},
|
||||||
|
content: content,
|
||||||
|
channel_name: "general",
|
||||||
|
url: "http://example.com",
|
||||||
|
)
|
||||||
|
}.to raise_error(StandardError)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -377,82 +377,6 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "with create_small_action_post_for_tag_changes enabled" do
|
|
||||||
fab!(:admin) { Fabricate(:admin, refresh_auto_groups: true) }
|
|
||||||
fab!(:additional_tag) { Fabricate(:tag) }
|
|
||||||
|
|
||||||
before { SiteSetting.create_post_for_category_and_tag_changes = true }
|
|
||||||
|
|
||||||
def set_new_tags_and_return_small_action_post(tags)
|
|
||||||
PostRevisor.new(tagged_first_post).revise!(admin, tags: tags)
|
|
||||||
|
|
||||||
tagged_topic.ordered_posts.last
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should notify when rule is set up for tag additions for a category with no tag filter" do
|
|
||||||
post = set_new_tags_and_return_small_action_post([tag.name, additional_tag.name])
|
|
||||||
|
|
||||||
DiscourseChatIntegration::Rule.create!(
|
|
||||||
channel: chan1,
|
|
||||||
filter: "tag_added",
|
|
||||||
category_id: category.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
manager.trigger_notifications(post.id)
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "notifies when topic has a tag added that matches the rule" do
|
|
||||||
post = set_new_tags_and_return_small_action_post([tag.name, additional_tag.name])
|
|
||||||
|
|
||||||
DiscourseChatIntegration::Rule.create!(
|
|
||||||
channel: chan1,
|
|
||||||
filter: "tag_added",
|
|
||||||
category_id: category.id,
|
|
||||||
tags: [additional_tag.name],
|
|
||||||
)
|
|
||||||
|
|
||||||
manager.trigger_notifications(post.id)
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't notify when a new regular post is created" do
|
|
||||||
DiscourseChatIntegration::Rule.create!(
|
|
||||||
channel: chan1,
|
|
||||||
filter: "tag_added",
|
|
||||||
category_id: nil,
|
|
||||||
tags: [tag.name],
|
|
||||||
)
|
|
||||||
|
|
||||||
post = Fabricate(:post, topic: tagged_topic)
|
|
||||||
manager.trigger_notifications(post.id)
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't notify when topic has an unchanged tag present in the rule, even if a new tag is added" do
|
|
||||||
post = set_new_tags_and_return_small_action_post([tag.name, additional_tag.name])
|
|
||||||
|
|
||||||
DiscourseChatIntegration::Rule.create!(
|
|
||||||
channel: chan1,
|
|
||||||
filter: "tag_added",
|
|
||||||
category_id: category.id,
|
|
||||||
tags: [tag.name],
|
|
||||||
)
|
|
||||||
|
|
||||||
manager.trigger_notifications(post.id)
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't notify for small action 'tags_changed' posts unless a matching rule exists" do
|
|
||||||
post = set_new_tags_and_return_small_action_post([additional_tag.name])
|
|
||||||
|
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch", category_id: nil) # Wildcard watch
|
|
||||||
|
|
||||||
manager.trigger_notifications(post.id)
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue