DEV: Introduce syntax_tree for ruby formatting (#149)
This commit is contained in:
parent
f6dde41cba
commit
49956bf829
|
@ -20,7 +20,7 @@ jobs:
|
|||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
cache: yarn
|
||||
|
||||
- name: Yarn install
|
||||
|
@ -33,15 +33,15 @@ jobs:
|
|||
bundler-cache: true
|
||||
|
||||
- name: ESLint
|
||||
if: ${{ always() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
|
||||
if: ${{ !cancelled() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets,admin/assets}/javascripts
|
||||
|
||||
- name: Prettier
|
||||
if: ${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
shell: bash
|
||||
run: |
|
||||
yarn prettier -v
|
||||
if [ 0 -lt $(find assets -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
if [ 0 -lt $(find assets admin/assets -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
yarn prettier --list-different "assets/**/*.{scss,js,es6}"
|
||||
fi
|
||||
if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
|
@ -49,9 +49,18 @@ jobs:
|
|||
fi
|
||||
|
||||
- name: Ember template lint
|
||||
if: ${{ always() }}
|
||||
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts
|
||||
if: ${{ !cancelled() }}
|
||||
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts admin/assets/javascripts
|
||||
|
||||
- name: Rubocop
|
||||
if: ${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: bundle exec rubocop .
|
||||
|
||||
- name: Syntax Tree
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
if test -f .streerc; then
|
||||
bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake')
|
||||
else
|
||||
echo "Stree config not detected for this repository. Skipping."
|
||||
fi
|
||||
|
|
|
@ -80,7 +80,7 @@ jobs:
|
|||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/cache@v3
|
||||
|
@ -130,7 +130,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/spec -type f -name "*.rb" 2> /dev/null | wc -l) ]; then
|
||||
echo "::set-output name=files_exist::true"
|
||||
echo "files_exist=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Plugin RSpec
|
||||
|
@ -142,7 +142,7 @@ jobs:
|
|||
shell: bash
|
||||
run: |
|
||||
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/test/javascripts -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
echo "::set-output name=files_exist::true"
|
||||
echo "files_exist=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Plugin QUnit
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
inherit_gem:
|
||||
rubocop-discourse: default.yml
|
||||
rubocop-discourse: stree-compat.yml
|
||||
|
|
7
Gemfile
7
Gemfile
|
@ -1,8 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
source "https://rubygems.org"
|
||||
|
||||
group :development do
|
||||
gem 'translations-manager', git: 'https://github.com/discourse/translations-manager.git'
|
||||
gem 'rubocop-discourse'
|
||||
gem "translations-manager", git: "https://github.com/discourse/translations-manager.git"
|
||||
gem "rubocop-discourse"
|
||||
gem "syntax_tree"
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ GEM
|
|||
parallel (1.22.1)
|
||||
parser (3.1.2.1)
|
||||
ast (~> 2.4.1)
|
||||
prettier_print (1.2.0)
|
||||
rainbow (3.1.1)
|
||||
regexp_parser (2.6.0)
|
||||
rexml (3.2.5)
|
||||
|
@ -33,6 +34,8 @@ GEM
|
|||
rubocop-rspec (2.13.2)
|
||||
rubocop (~> 1.33)
|
||||
ruby-progressbar (1.11.0)
|
||||
syntax_tree (5.1.0)
|
||||
prettier_print (>= 1.2.0)
|
||||
unicode-display_width (2.3.0)
|
||||
|
||||
PLATFORMS
|
||||
|
@ -40,6 +43,7 @@ PLATFORMS
|
|||
|
||||
DEPENDENCIES
|
||||
rubocop-discourse
|
||||
syntax_tree
|
||||
translations-manager!
|
||||
|
||||
BUNDLED WITH
|
||||
|
|
|
@ -8,15 +8,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
end
|
||||
|
||||
def list_providers
|
||||
providers = ::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
||||
{
|
||||
name: x::PROVIDER_NAME,
|
||||
id: x::PROVIDER_NAME,
|
||||
channel_parameters: (defined? x::CHANNEL_PARAMETERS) ? x::CHANNEL_PARAMETERS : []
|
||||
}
|
||||
end
|
||||
providers =
|
||||
::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
||||
{
|
||||
name: x::PROVIDER_NAME,
|
||||
id: x::PROVIDER_NAME,
|
||||
channel_parameters: (defined?(x::CHANNEL_PARAMETERS)) ? x::CHANNEL_PARAMETERS : [],
|
||||
}
|
||||
end
|
||||
|
||||
render json: providers, root: 'providers'
|
||||
render json: providers, root: "providers"
|
||||
end
|
||||
|
||||
def test
|
||||
|
@ -28,9 +29,7 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
|
||||
provider = ::DiscourseChatIntegration::Provider.get_by_name(channel.provider)
|
||||
|
||||
if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
raise Discourse::NotFound if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
||||
|
||||
post = Topic.find(topic_id.to_i).posts.first
|
||||
|
||||
|
@ -56,34 +55,36 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
raise Discourse::InvalidParameters if !providers.include?(requested_provider)
|
||||
|
||||
channels = DiscourseChatIntegration::Channel.with_provider(requested_provider)
|
||||
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: 'channels'
|
||||
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: "channels"
|
||||
end
|
||||
|
||||
def create_channel
|
||||
begin
|
||||
providers = ::DiscourseChatIntegration::Provider.enabled_providers.map { |x| x::PROVIDER_NAME }
|
||||
providers =
|
||||
::DiscourseChatIntegration::Provider.enabled_providers.map { |x| x::PROVIDER_NAME }
|
||||
|
||||
if !defined?(params[:channel]) && defined?(params[:channel][:provider])
|
||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
||||
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||
end
|
||||
|
||||
requested_provider = params[:channel][:provider]
|
||||
|
||||
if !providers.include?(requested_provider)
|
||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
||||
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||
end
|
||||
|
||||
allowed_keys = DiscourseChatIntegration::Provider.get_by_name(requested_provider)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
allowed_keys =
|
||||
DiscourseChatIntegration::Provider.get_by_name(
|
||||
requested_provider,
|
||||
)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
|
||||
hash = params.require(:channel).permit(:provider, data: allowed_keys)
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.new(hash)
|
||||
|
||||
if !channel.save
|
||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.save
|
||||
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
|
@ -94,15 +95,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
channel = DiscourseChatIntegration::Channel.find(params[:id].to_i)
|
||||
channel.error_key = nil # Reset any error on the rule
|
||||
|
||||
allowed_keys = DiscourseChatIntegration::Provider.get_by_name(channel.provider)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
allowed_keys =
|
||||
DiscourseChatIntegration::Provider.get_by_name(
|
||||
channel.provider,
|
||||
)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
|
||||
hash = params.require(:channel).permit(data: allowed_keys)
|
||||
|
||||
if !channel.update(hash)
|
||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.update(hash)
|
||||
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
|
@ -118,14 +120,13 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
|
||||
def create_rule
|
||||
begin
|
||||
hash = params.require(:rule).permit(:channel_id, :type, :filter, :group_id, :category_id, tags: [])
|
||||
hash =
|
||||
params.require(:rule).permit(:channel_id, :type, :filter, :group_id, :category_id, tags: [])
|
||||
rule = DiscourseChatIntegration::Rule.new(hash)
|
||||
|
||||
if !rule.save
|
||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.save
|
||||
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
|
@ -136,11 +137,9 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
|||
rule = DiscourseChatIntegration::Rule.find(params[:id].to_i)
|
||||
hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: [])
|
||||
|
||||
if !rule.update(hash)
|
||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.update(hash)
|
||||
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
module DiscourseChatIntegration
|
||||
module Helper
|
||||
|
||||
def self.process_command(channel, tokens)
|
||||
guardian = DiscourseChatIntegration::Manager.guardian
|
||||
|
||||
|
@ -16,13 +15,19 @@ module DiscourseChatIntegration
|
|||
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
|
||||
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)
|
||||
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
|
||||
|
@ -32,8 +37,8 @@ module DiscourseChatIntegration
|
|||
# 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:/, '')
|
||||
if token.start_with?("tag:")
|
||||
tag_name = token.sub(/^tag:/, "")
|
||||
else
|
||||
return error_text
|
||||
end
|
||||
|
@ -47,7 +52,12 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
category_id = category.nil? ? nil : category.id
|
||||
case DiscourseChatIntegration::Helper.smart_create_rule(channel: channel, filter: cmd, category_id: category_id, tags: tags)
|
||||
case DiscourseChatIntegration::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
|
||||
|
@ -107,23 +117,25 @@ module DiscourseChatIntegration
|
|||
end
|
||||
end
|
||||
|
||||
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string",
|
||||
text << I18n.t(
|
||||
"chat_integration.provider.#{provider}.status.rule_string",
|
||||
index: i,
|
||||
filter: rule.filter,
|
||||
category: category_name
|
||||
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(', '))
|
||||
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 << I18n.t("chat_integration.provider.#{provider}.status.no_rules") if rules.size == 0
|
||||
|
||||
text
|
||||
end
|
||||
|
@ -144,12 +156,15 @@ module DiscourseChatIntegration
|
|||
# :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 = DiscourseChatIntegration::Rule.with_channel(channel).with_type('normal')
|
||||
existing_rules = DiscourseChatIntegration::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) }
|
||||
same_category_and_tags =
|
||||
same_category.select do |rule|
|
||||
(rule.tags.nil? ? [] : rule.tags.sort) == (tags.nil? ? [] : tags.sort)
|
||||
end
|
||||
|
||||
if same_category_and_tags.size > 0
|
||||
# These rules have exactly the same criteria as what we're trying to create
|
||||
|
@ -185,7 +200,9 @@ module DiscourseChatIntegration
|
|||
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
|
||||
if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save
|
||||
return :created
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
|
@ -197,13 +214,13 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
def self.formatted_display_name(user)
|
||||
if !SiteSetting.enable_names || user.name.blank?
|
||||
return "@#{user.username}"
|
||||
end
|
||||
return "@#{user.username}" if !SiteSetting.enable_names || user.name.blank?
|
||||
|
||||
full_name = user.name
|
||||
full_name_normalized = User.normalize_username(full_name.strip)
|
||||
similar = full_name_normalized.gsub(' ', '_') == user.username_lower || full_name_normalized.gsub(' ', '') == user.username_lower
|
||||
similar =
|
||||
full_name_normalized.gsub(" ", "_") == user.username_lower ||
|
||||
full_name_normalized.gsub(" ", "") == user.username_lower
|
||||
if similar && SiteSetting.prioritize_username_in_ux?
|
||||
"@#{user.username}"
|
||||
elsif similar
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
module Jobs
|
||||
class DiscourseChatAddTypeField < ::Jobs::Onceoff
|
||||
def execute_onceoff(args)
|
||||
DiscourseChatIntegration::Rule.find_each do |rule|
|
||||
rule.save(validate: false)
|
||||
end
|
||||
DiscourseChatIntegration::Rule.find_each { |rule| rule.save(validate: false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,26 +3,26 @@
|
|||
module Jobs
|
||||
class DiscourseChatMigrateFromSlackOfficial < ::Jobs::Onceoff
|
||||
def execute_onceoff(args)
|
||||
slack_installed = PluginStoreRow.where(plugin_name: 'discourse-slack-official').exists?
|
||||
slack_installed = PluginStoreRow.where(plugin_name: "discourse-slack-official").exists?
|
||||
|
||||
if slack_installed
|
||||
already_setup_rules = DiscourseChatIntegration::Channel.with_provider('slack').exists?
|
||||
already_setup_rules = DiscourseChatIntegration::Channel.with_provider("slack").exists?
|
||||
|
||||
already_setup_sitesettings =
|
||||
SiteSetting.chat_integration_slack_enabled ||
|
||||
SiteSetting.chat_integration_slack_access_token.present? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
||||
SiteSetting.chat_integration_slack_access_token.present? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
||||
|
||||
if !already_setup_rules && !already_setup_sitesettings
|
||||
ActiveRecord::Base.transaction do
|
||||
migrate_settings
|
||||
migrate_data
|
||||
is_slack_enabled = site_settings_value('slack_enabled')
|
||||
is_slack_enabled = site_settings_value("slack_enabled")
|
||||
|
||||
if is_slack_enabled
|
||||
slack_enabled = SiteSetting.find_by(name: 'slack_enabled')
|
||||
slack_enabled.update!(value: 'f')
|
||||
slack_enabled = SiteSetting.find_by(name: "slack_enabled")
|
||||
slack_enabled.update!(value: "f")
|
||||
|
||||
SiteSetting.chat_integration_slack_enabled = true
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
|
@ -30,44 +30,55 @@ module Jobs
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def migrate_data
|
||||
rows = []
|
||||
PluginStoreRow.where(plugin_name: 'discourse-slack-official')
|
||||
PluginStoreRow
|
||||
.where(plugin_name: "discourse-slack-official")
|
||||
.where("key ~* :pat", pat: "^category_.*")
|
||||
.each do |row|
|
||||
PluginStore
|
||||
.cast_value(row.type_name, row.value)
|
||||
.each do |rule|
|
||||
category_id =
|
||||
if row.key == "category_*"
|
||||
nil
|
||||
else
|
||||
row.key.gsub!("category_", "")
|
||||
row.key.to_i
|
||||
end
|
||||
|
||||
PluginStore.cast_value(row.type_name, row.value).each do |rule|
|
||||
category_id =
|
||||
if row.key == 'category_*'
|
||||
nil
|
||||
else
|
||||
row.key.gsub!('category_', '')
|
||||
row.key.to_i
|
||||
next if !category_id.nil? && !Category.exists?(id: category_id)
|
||||
|
||||
valid_tags = []
|
||||
valid_tags = Tag.where(name: rule[:tags]).pluck(:name) if rule[:tags]
|
||||
|
||||
rows << {
|
||||
category_id: category_id,
|
||||
channel: rule[:channel],
|
||||
filter: rule[:filter],
|
||||
tags: valid_tags,
|
||||
}
|
||||
end
|
||||
|
||||
next if !category_id.nil? && !Category.exists?(id: category_id)
|
||||
|
||||
valid_tags = []
|
||||
valid_tags = Tag.where(name: rule[:tags]).pluck(:name) if rule[:tags]
|
||||
|
||||
rows << {
|
||||
category_id: category_id,
|
||||
channel: rule[:channel],
|
||||
filter: rule[:filter],
|
||||
tags: valid_tags
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
rows.each do |row|
|
||||
# Load an existing channel with this identifier. If none, create it
|
||||
row[:channel] = "##{row[:channel]}" unless row[:channel].start_with?("#")
|
||||
channel = DiscourseChatIntegration::Channel.with_provider('slack').with_data_value('identifier', row[:channel]).first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider("slack")
|
||||
.with_data_value("identifier", row[:channel])
|
||||
.first
|
||||
if !channel
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: 'slack', data: { identifier: row[:channel] })
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel.create(
|
||||
provider: "slack",
|
||||
data: {
|
||||
identifier: row[:channel],
|
||||
},
|
||||
)
|
||||
if !channel.id
|
||||
Rails.logger.warn("Error creating channel for #{row}")
|
||||
next
|
||||
|
@ -75,50 +86,58 @@ module Jobs
|
|||
end
|
||||
|
||||
# Create the rule, with clever logic for avoiding duplicates
|
||||
success = DiscourseChatIntegration::Helper.smart_create_rule(channel: channel, filter: row[:filter], category_id: row[:category_id], tags: row[:tags])
|
||||
success =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: channel,
|
||||
filter: row[:filter],
|
||||
category_id: row[:category_id],
|
||||
tags: row[:tags],
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def migrate_settings
|
||||
if !(slack_access_token = site_settings_value('slack_access_token')).nil?
|
||||
if !(slack_access_token = site_settings_value("slack_access_token")).nil?
|
||||
SiteSetting.chat_integration_slack_access_token = slack_access_token
|
||||
end
|
||||
|
||||
if !(slack_incoming_webhook_token = site_settings_value('slack_incoming_webhook_token')).nil?
|
||||
if !(slack_incoming_webhook_token = site_settings_value("slack_incoming_webhook_token")).nil?
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = slack_incoming_webhook_token
|
||||
end
|
||||
|
||||
if !(slack_discourse_excerpt_length = site_settings_value('slack_discourse_excerpt_length')).nil?
|
||||
if !(
|
||||
slack_discourse_excerpt_length = site_settings_value("slack_discourse_excerpt_length")
|
||||
).nil?
|
||||
SiteSetting.chat_integration_slack_excerpt_length = slack_discourse_excerpt_length
|
||||
end
|
||||
|
||||
if !(slack_outbound_webhook_url = site_settings_value('slack_outbound_webhook_url')).nil?
|
||||
if !(slack_outbound_webhook_url = site_settings_value("slack_outbound_webhook_url")).nil?
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url = slack_outbound_webhook_url
|
||||
end
|
||||
|
||||
if !(slack_icon_url = site_settings_value('slack_icon_url')).nil?
|
||||
if !(slack_icon_url = site_settings_value("slack_icon_url")).nil?
|
||||
SiteSetting.chat_integration_slack_icon_url = slack_icon_url
|
||||
end
|
||||
|
||||
if !(post_to_slack_window_secs = site_settings_value('post_to_slack_window_secs')).nil?
|
||||
if !(post_to_slack_window_secs = site_settings_value("post_to_slack_window_secs")).nil?
|
||||
SiteSetting.chat_integration_delay_seconds = post_to_slack_window_secs
|
||||
end
|
||||
|
||||
if !(slack_discourse_username = site_settings_value('slack_discourse_username')).nil?
|
||||
if !(slack_discourse_username = site_settings_value("slack_discourse_username")).nil?
|
||||
username = User.find_by(username: slack_discourse_username.downcase)&.username
|
||||
SiteSetting.chat_integration_discourse_username = (username || Discourse.system_user.username)
|
||||
SiteSetting.chat_integration_discourse_username =
|
||||
(username || Discourse.system_user.username)
|
||||
end
|
||||
end
|
||||
|
||||
def site_settings_value(name)
|
||||
value = SiteSetting.find_by(name: name)&.value
|
||||
|
||||
if value == 't'
|
||||
if value == "t"
|
||||
value = true
|
||||
elsif value == 'f'
|
||||
elsif value == "f"
|
||||
value = false
|
||||
end
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
||||
store :value, accessors: [ :provider, :error_key, :error_info, :data ], coder: JSON
|
||||
store :value, accessors: %i[provider error_key error_info data], coder: JSON
|
||||
|
||||
scope :with_provider, ->(provider) { where("value::json->>'provider'=?", provider) }
|
||||
scope :with_data_value, ->(key, value) { where("(value::json->>'data')::json->>?=?", key.to_s, value.to_s) }
|
||||
scope :with_data_value,
|
||||
->(key, value) { where("(value::json->>'data')::json->>?=?", key.to_s, value.to_s) }
|
||||
|
||||
after_initialize :init_data
|
||||
after_destroy :destroy_rules
|
||||
|
@ -13,7 +14,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
|||
validate :provider_valid?, :data_valid?
|
||||
|
||||
def self.key_prefix
|
||||
'channel:'.freeze
|
||||
"channel:".freeze
|
||||
end
|
||||
|
||||
def rules
|
||||
|
@ -52,9 +53,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
|||
|
||||
data.each do |key, value|
|
||||
regex_string = params.find { |p| p[:key] == key }[:regex]
|
||||
if !Regexp.new(regex_string).match(value)
|
||||
errors.add(:data, "data.#{key} is invalid")
|
||||
end
|
||||
errors.add(:data, "data.#{key} is invalid") if !Regexp.new(regex_string).match(value)
|
||||
|
||||
unique = params.find { |p| p[:key] == key }[:unique]
|
||||
if unique
|
||||
|
@ -63,8 +62,6 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
|||
end
|
||||
end
|
||||
|
||||
if check_unique && matching_channels.exists?
|
||||
errors.add(:data, "matches an existing channel")
|
||||
end
|
||||
errors.add(:data, "matches an existing channel") if check_unique && matching_channels.exists?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||
PLUGIN_NAME = 'discourse-chat-integration'
|
||||
PLUGIN_NAME = "discourse-chat-integration"
|
||||
|
||||
default_scope { self.default_scope }
|
||||
|
||||
|
@ -9,13 +9,14 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
|||
before_save :set_key
|
||||
|
||||
def self.default_scope
|
||||
where(type_name: 'JSON')
|
||||
.where(plugin_name: self::PLUGIN_NAME)
|
||||
.where("key LIKE ?", "#{self.key_prefix}%")
|
||||
where(type_name: "JSON").where(plugin_name: self::PLUGIN_NAME).where(
|
||||
"key LIKE ?",
|
||||
"#{self.key_prefix}%",
|
||||
)
|
||||
end
|
||||
|
||||
def self.key_prefix
|
||||
raise 'Not implemented'
|
||||
raise "Not implemented"
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -25,7 +26,7 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
|||
end
|
||||
|
||||
def init_plugin_model
|
||||
self.type_name ||= 'JSON'
|
||||
self.type_name ||= "JSON"
|
||||
self.plugin_name ||= PLUGIN_NAME
|
||||
end
|
||||
|
||||
|
@ -37,5 +38,4 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
|||
"#{self.key_prefix}#{max_id}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,54 +2,65 @@
|
|||
|
||||
class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
||||
store :value, accessors: [ :channel_id, :type, :group_id, :category_id, :tags, :filter ], coder: JSON
|
||||
store :value, accessors: %i[channel_id type group_id category_id tags filter], coder: JSON
|
||||
|
||||
scope :with_type, ->(type) { where("value::json->>'type'=?", type.to_s) }
|
||||
scope :with_channel, ->(channel) { with_channel_id(channel.id) }
|
||||
scope :with_channel_id, ->(channel_id) { where("value::json->>'channel_id'=?", channel_id.to_s) }
|
||||
|
||||
scope :with_category_id, ->(category_id) do
|
||||
if category_id.nil?
|
||||
where("(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'")
|
||||
else
|
||||
where("value::json->>'category_id'=?", category_id.to_s)
|
||||
end
|
||||
end
|
||||
scope :with_category_id,
|
||||
->(category_id) {
|
||||
if category_id.nil?
|
||||
where(
|
||||
"(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'",
|
||||
)
|
||||
else
|
||||
where("value::json->>'category_id'=?", category_id.to_s)
|
||||
end
|
||||
}
|
||||
|
||||
scope :with_group_ids, ->(group_id) do
|
||||
where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s))
|
||||
end
|
||||
scope :with_group_ids,
|
||||
->(group_id) { where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s)) }
|
||||
|
||||
scope :order_by_precedence, -> {
|
||||
order("
|
||||
scope :order_by_precedence,
|
||||
-> {
|
||||
order(
|
||||
"
|
||||
CASE
|
||||
WHEN value::json->>'type' = 'group_mention' THEN 1
|
||||
WHEN value::json->>'type' = 'group_message' THEN 2
|
||||
ELSE 3
|
||||
END
|
||||
",
|
||||
"
|
||||
"
|
||||
CASE
|
||||
WHEN value::json->>'filter' = 'mute' THEN 1
|
||||
WHEN value::json->>'filter' = 'thread' THEN 2
|
||||
WHEN value::json->>'filter' = 'watch' THEN 3
|
||||
WHEN value::json->>'filter' = 'follow' THEN 4
|
||||
END
|
||||
")
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
after_initialize :init_filter
|
||||
|
||||
validates :filter, inclusion: { in: %w(thread watch follow mute),
|
||||
message: "%{value} is not a valid filter" }
|
||||
validates :filter,
|
||||
inclusion: {
|
||||
in: %w[thread watch follow mute],
|
||||
message: "%{value} is not a valid filter",
|
||||
}
|
||||
|
||||
validates :type, inclusion: { in: %w(normal group_message group_mention),
|
||||
message: "%{value} is not a valid filter" }
|
||||
validates :type,
|
||||
inclusion: {
|
||||
in: %w[normal group_message group_mention],
|
||||
message: "%{value} is not a valid filter",
|
||||
}
|
||||
|
||||
validate :channel_valid?, :category_valid?, :group_valid?, :tags_valid?
|
||||
|
||||
def self.key_prefix
|
||||
'rule:'.freeze
|
||||
"rule:".freeze
|
||||
end
|
||||
|
||||
# We never want an empty array, set it to nil instead
|
||||
|
@ -62,7 +73,7 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
|||
end
|
||||
|
||||
# These are only allowed to be integers
|
||||
%w(channel_id category_id group_id).each do |name|
|
||||
%w[channel_id category_id group_id].each do |name|
|
||||
define_method "#{name}=" do |val|
|
||||
if val.nil? || val.blank?
|
||||
super(nil)
|
||||
|
@ -91,11 +102,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
|||
end
|
||||
|
||||
def category_valid?
|
||||
if type != 'normal' && !category_id.nil?
|
||||
if type != "normal" && !category_id.nil?
|
||||
errors.add(:category_id, "cannot be specified for that type of rule")
|
||||
end
|
||||
|
||||
return unless type == 'normal'
|
||||
return unless type == "normal"
|
||||
|
||||
if !(category_id.nil? || Category.where(id: category_id).exists?)
|
||||
errors.add(:category_id, "#{category_id} is not a valid category id")
|
||||
|
@ -103,11 +114,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
|||
end
|
||||
|
||||
def group_valid?
|
||||
if type == 'normal' && !group_id.nil?
|
||||
if type == "normal" && !group_id.nil?
|
||||
errors.add(:group_id, "cannot be specified for that type of rule")
|
||||
end
|
||||
|
||||
return if type == 'normal'
|
||||
return if type == "normal"
|
||||
|
||||
if !Group.where(id: group_id).exists?
|
||||
errors.add(:group_id, "#{group_id} is not a valid group id")
|
||||
|
@ -118,14 +129,12 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
|||
return if tags.nil?
|
||||
|
||||
tags.each do |tag|
|
||||
if !Tag.where(name: tag).exists?
|
||||
errors.add(:tags, "#{tag} is not a valid tag")
|
||||
end
|
||||
errors.add(:tags, "#{tag} is not a valid tag") if !Tag.where(name: tag).exists?
|
||||
end
|
||||
end
|
||||
|
||||
def init_filter
|
||||
self.filter ||= 'watch'
|
||||
self.type ||= 'normal'
|
||||
self.filter ||= "watch"
|
||||
self.type ||= "normal"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Discourse::Application.routes.append do
|
||||
mount ::DiscourseChatIntegration::AdminEngine, at: '/admin/plugins/chat-integration', constraints: AdminConstraint.new
|
||||
mount ::DiscourseChatIntegration::PublicEngine, at: '/chat-transcript/', as: 'chat-transcript'
|
||||
mount ::DiscourseChatIntegration::Provider::HookEngine, at: '/chat-integration/'
|
||||
mount ::DiscourseChatIntegration::AdminEngine,
|
||||
at: "/admin/plugins/chat-integration",
|
||||
constraints: AdminConstraint.new
|
||||
mount ::DiscourseChatIntegration::PublicEngine, at: "/chat-transcript/", as: "chat-transcript"
|
||||
mount ::DiscourseChatIntegration::Provider::HookEngine, at: "/chat-integration/"
|
||||
|
||||
# For backwards compatibility with Slack plugin
|
||||
post "/slack/command" => "discourse_chat_integration/provider/slack_provider/slack_command#command"
|
||||
post "/slack/command" =>
|
||||
"discourse_chat_integration/provider/slack_provider/slack_command#command"
|
||||
end
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_dependency 'admin_constraint'
|
||||
require_dependency "admin_constraint"
|
||||
|
||||
module ::DiscourseChatIntegration
|
||||
AdminEngine.routes.draw do
|
||||
get "" => "chat#respond"
|
||||
get '/providers' => "chat#list_providers"
|
||||
post '/test' => "chat#test"
|
||||
get "/providers" => "chat#list_providers"
|
||||
post "/test" => "chat#test"
|
||||
|
||||
get '/channels' => "chat#list_channels"
|
||||
post '/channels' => "chat#create_channel"
|
||||
put '/channels/:id' => "chat#update_channel"
|
||||
delete '/channels/:id' => "chat#destroy_channel"
|
||||
get "/channels" => "chat#list_channels"
|
||||
post "/channels" => "chat#create_channel"
|
||||
put "/channels/:id" => "chat#update_channel"
|
||||
delete "/channels/:id" => "chat#destroy_channel"
|
||||
|
||||
post '/rules' => "chat#create_rule"
|
||||
put '/rules/:id' => "chat#update_rule"
|
||||
delete '/rules/:id' => "chat#destroy_rule"
|
||||
post "/rules" => "chat#create_rule"
|
||||
put "/rules/:id" => "chat#update_rule"
|
||||
delete "/rules/:id" => "chat#destroy_rule"
|
||||
|
||||
get "/:provider" => "chat#respond"
|
||||
end
|
||||
|
||||
PublicEngine.routes.draw do
|
||||
get '/:secret' => "public#post_transcript"
|
||||
end
|
||||
PublicEngine.routes.draw { get "/:secret" => "public#post_transcript" }
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative './rule_serializer'
|
||||
require_relative "./rule_serializer"
|
||||
|
||||
class DiscourseChatIntegration::ChannelSerializer < ApplicationSerializer
|
||||
attributes :id, :provider, :error_key, :error_info, :data, :rules
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
module DiscourseChatIntegration
|
||||
module Manager
|
||||
|
||||
def self.guardian
|
||||
Guardian.new(User.find_by(username: SiteSetting.chat_integration_discourse_username))
|
||||
end
|
||||
|
@ -23,19 +22,26 @@ module DiscourseChatIntegration
|
|||
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 = DiscourseChatIntegration::Rule.with_type('group_message').with_group_ids(group_ids_with_access)
|
||||
matching_rules =
|
||||
DiscourseChatIntegration::Rule.with_type("group_message").with_group_ids(
|
||||
group_ids_with_access,
|
||||
)
|
||||
else
|
||||
matching_rules = DiscourseChatIntegration::Rule.with_type('normal').with_category_id(topic.category_id)
|
||||
matching_rules =
|
||||
DiscourseChatIntegration::Rule.with_type("normal").with_category_id(topic.category_id)
|
||||
if topic.category # Also load the rules for the wildcard category
|
||||
matching_rules += DiscourseChatIntegration::Rule.with_type('normal').with_category_id(nil)
|
||||
matching_rules += DiscourseChatIntegration::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)
|
||||
groups = Group.where("LOWER(name) IN (?)", mentions)
|
||||
if groups.exists?
|
||||
matching_rules += DiscourseChatIntegration::Rule.with_type('group_mention').with_group_ids(groups.map(&:id))
|
||||
matching_rules +=
|
||||
DiscourseChatIntegration::Rule.with_type("group_mention").with_group_ids(
|
||||
groups.map(&:id),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -43,17 +49,19 @@ module DiscourseChatIntegration
|
|||
# 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
|
||||
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]] }
|
||||
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
|
||||
|
@ -81,14 +89,15 @@ module DiscourseChatIntegration
|
|||
|
||||
begin
|
||||
provider.trigger_notification(post, channel, rule)
|
||||
channel.update_attribute('error_key', nil) if channel.error_key
|
||||
channel.update_attribute("error_key", nil) if channel.error_key
|
||||
rescue => e
|
||||
if e.class == (DiscourseChatIntegration::ProviderError) && e.info.key?(:error_key) && !e.info[:error_key].nil?
|
||||
channel.update_attribute('error_key', e.info[:error_key])
|
||||
if e.class == (DiscourseChatIntegration::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')
|
||||
channel.update_attribute("error_key", "chat_integration.channel_exception")
|
||||
end
|
||||
channel.update_attribute('error_info', JSON.pretty_generate(e.try(:info)))
|
||||
channel.update_attribute("error_info", JSON.pretty_generate(e.try(:info)))
|
||||
|
||||
# Log the error
|
||||
# Discourse.handle_job_exception(e,
|
||||
|
@ -99,10 +108,7 @@ module DiscourseChatIntegration
|
|||
# error_info: e.class == DiscourseChatIntegration::ProviderError ? e.info : nil }
|
||||
# )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
class AddUniqueIndexToSlackThreadTs < ActiveRecord::Migration[6.1]
|
||||
def up
|
||||
add_index :topic_custom_fields, [:topic_id, :name],
|
||||
unique: true,
|
||||
where: "(name LIKE 'slack_thread_id_%')",
|
||||
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
||||
add_index :topic_custom_fields,
|
||||
%i[topic_id name],
|
||||
unique: true,
|
||||
where: "(name LIKE 'slack_thread_id_%')",
|
||||
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,15 +12,11 @@ module DiscourseChatIntegration
|
|||
|
||||
module Provider
|
||||
def self.providers
|
||||
constants.select do |constant|
|
||||
constant.to_s =~ /Provider$/
|
||||
end.map(&method(:const_get))
|
||||
constants.select { |constant| constant.to_s =~ /Provider$/ }.map(&method(:const_get))
|
||||
end
|
||||
|
||||
def self.enabled_providers
|
||||
self.providers.select do |provider|
|
||||
self.is_enabled(provider)
|
||||
end
|
||||
self.providers.select { |provider| self.is_enabled(provider) }
|
||||
end
|
||||
|
||||
def self.provider_names
|
||||
|
@ -36,7 +32,7 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
def self.is_enabled(provider)
|
||||
if defined? provider::PROVIDER_ENABLED_SETTING
|
||||
if defined?(provider::PROVIDER_ENABLED_SETTING)
|
||||
SiteSetting.public_send(provider::PROVIDER_ENABLED_SETTING)
|
||||
else
|
||||
false
|
||||
|
@ -51,9 +47,10 @@ module DiscourseChatIntegration
|
|||
class HookController < ::ApplicationController
|
||||
requires_plugin DiscourseChatIntegration::PLUGIN_NAME
|
||||
|
||||
class ProviderDisabled < StandardError; end
|
||||
class ProviderDisabled < StandardError
|
||||
end
|
||||
|
||||
rescue_from ProviderDisabled do
|
||||
rescue_from ProviderDisabled do
|
||||
rescue_discourse_actions(:not_found, 404)
|
||||
end
|
||||
|
||||
|
@ -72,22 +69,20 @@ module DiscourseChatIntegration
|
|||
def self.mount_engines
|
||||
engines = []
|
||||
DiscourseChatIntegration::Provider.providers.each do |provider|
|
||||
engine = provider.constants.select do |constant|
|
||||
constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine")
|
||||
end.map(&provider.method(:const_get)).first
|
||||
engine =
|
||||
provider
|
||||
.constants
|
||||
.select { |constant| constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine") }
|
||||
.map(&provider.method(:const_get))
|
||||
.first
|
||||
|
||||
if engine
|
||||
engines.push(engine: engine, name: provider::PROVIDER_NAME)
|
||||
end
|
||||
engines.push(engine: engine, name: provider::PROVIDER_NAME) if engine
|
||||
end
|
||||
|
||||
DiscourseChatIntegration::Provider::HookEngine.routes.draw do
|
||||
engines.each do |engine|
|
||||
mount engine[:engine], at: engine[:name]
|
||||
end
|
||||
engines.each { |engine| mount engine[:engine], at: engine[:name] }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,7 +8,12 @@ module DiscourseChatIntegration
|
|||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "webhook_url", regex: '^https:\/\/discord\.com\/api\/webhooks\/', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/discord\.com\/api\/webhooks\/',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
].freeze
|
||||
|
||||
def self.send_message(url, message)
|
||||
|
@ -17,7 +22,7 @@ module DiscourseChatIntegration
|
|||
|
||||
uri = URI(url)
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
|
@ -25,7 +30,7 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
def self.ensure_protocol(url)
|
||||
return url if !url.start_with?('//')
|
||||
return url if !url.start_with?("//")
|
||||
"http:#{url}"
|
||||
end
|
||||
|
||||
|
@ -34,24 +39,40 @@ module DiscourseChatIntegration
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
message = {
|
||||
content: SiteSetting.chat_integration_discord_message_content,
|
||||
embeds: [{
|
||||
title: "#{topic.title} #{(category == '[uncategorized]') ? '' : category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
color: topic.category ? topic.category.color.to_i(16) : nil,
|
||||
description: post.excerpt(SiteSetting.chat_integration_discord_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
url: post.full_url,
|
||||
author: {
|
||||
name: display_name,
|
||||
url: Discourse.base_url + "/u/" + post.user.username,
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url)
|
||||
}
|
||||
}]
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
color: topic.category ? topic.category.color.to_i(16) : nil,
|
||||
description:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_discord_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
url: post.full_url,
|
||||
author: {
|
||||
name: display_name,
|
||||
url: Discourse.base_url + "/u/" + post.user.username,
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -59,17 +80,20 @@ module DiscourseChatIntegration
|
|||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
# Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously
|
||||
webhook_url = "#{channel.data['webhook_url']}?wait=true"
|
||||
webhook_url = "#{channel.data["webhook_url"]}?wait=true"
|
||||
message = generate_discord_message(post)
|
||||
response = send_message(webhook_url, message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
||||
error_key: nil, message: message, response_body: response.body
|
||||
})
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||
info: {
|
||||
error_key: nil,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||
|
||||
PROVIDER_NAME = "flowdock".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_flowdock_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "flow_token", regex: '^\S+', unique: true, hidden: true },
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "flow_token", regex: '^\S+', unique: true, hidden: true }]
|
||||
|
||||
def self.send_message(url, message)
|
||||
uri = URI(url)
|
||||
|
@ -14,7 +11,7 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
|||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
|
@ -29,15 +26,21 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
|||
event: "discussion",
|
||||
author: {
|
||||
name: display_name,
|
||||
avatar: post.user.small_avatar_url
|
||||
avatar: post.user.small_avatar_url,
|
||||
},
|
||||
title: I18n.t("chat_integration.provider.flowdock.message_title"),
|
||||
external_thread_id: post.topic.id,
|
||||
body: post.excerpt(SiteSetting.chat_integration_flowdock_excerpt_length, text_entities: true, strip_links: false, remap_emoji: true),
|
||||
body:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_flowdock_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: false,
|
||||
remap_emoji: true,
|
||||
),
|
||||
thread: {
|
||||
title: post.topic.title,
|
||||
external_url: post.full_url
|
||||
}
|
||||
external_url: post.full_url,
|
||||
},
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -50,7 +53,11 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
|||
|
||||
unless response.kind_of?(Net::HTTPSuccess)
|
||||
error_key = nil
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,19 +3,28 @@
|
|||
module DiscourseChatIntegration
|
||||
module Provider
|
||||
module GitterProvider
|
||||
PROVIDER_NAME = 'gitter'.freeze
|
||||
PROVIDER_NAME = "gitter".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_gitter_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url", regex: '^https://webhooks\.gitter\.im/e/\S+$', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https://webhooks\.gitter\.im/e/\S+$',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = gitter_message(post)
|
||||
response = Net::HTTP.post_form(URI(channel.data['webhook_url']), message: message)
|
||||
response = Net::HTTP.post_form(URI(channel.data["webhook_url"]), message: message)
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
error_key = nil
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -23,7 +32,14 @@ module DiscourseChatIntegration
|
|||
display_name = post.user.username
|
||||
topic = post.topic
|
||||
parent_category = topic.category.try :parent_category
|
||||
category_name = parent_category ? "[#{parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category_name =
|
||||
(
|
||||
if parent_category
|
||||
"[#{parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
|
||||
"[__#{display_name}__ - #{topic.title} - #{category_name}](#{post.full_url})"
|
||||
end
|
||||
|
|
|
@ -3,26 +3,35 @@
|
|||
module DiscourseChatIntegration
|
||||
module Provider
|
||||
module GoogleProvider
|
||||
PROVIDER_NAME = 'google'.freeze
|
||||
PROVIDER_NAME = "google".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_google_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url", regex: '^https:\/\/chat.googleapis.com\/v1\/\S+$', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/chat.googleapis.com\/v1\/\S+$',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = get_message(post)
|
||||
uri = URI(channel.data['webhook_url'])
|
||||
uri = URI(channel.data["webhook_url"])
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -35,49 +44,67 @@ module DiscourseChatIntegration
|
|||
widgets: [
|
||||
{
|
||||
keyValue: {
|
||||
"topLabel": I18n.t("chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}", site_title: SiteSetting.title),
|
||||
"content": post.topic.title,
|
||||
"contentMultiline": "false",
|
||||
"bottomLabel": I18n.t("chat_integration.provider.google.author", username: post.user.username),
|
||||
"onClick": {
|
||||
"openLink": {
|
||||
"url": post.full_url
|
||||
}
|
||||
}
|
||||
}
|
||||
topLabel:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}",
|
||||
site_title: SiteSetting.title,
|
||||
),
|
||||
content: post.topic.title,
|
||||
contentMultiline: "false",
|
||||
bottomLabel:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.author",
|
||||
username: post.user.username,
|
||||
),
|
||||
onClick: {
|
||||
openLink: {
|
||||
url: post.full_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
widgets: [
|
||||
{
|
||||
textParagraph: {
|
||||
text: post.excerpt(SiteSetting.chat_integration_google_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)
|
||||
}
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_google_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
widgets: [
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
"textButton": {
|
||||
"text": I18n.t("chat_integration.provider.google.link", site_title: SiteSetting.title),
|
||||
"onClick": {
|
||||
"openLink": {
|
||||
"url": post.full_url
|
||||
}
|
||||
}
|
||||
}
|
||||
textButton: {
|
||||
text:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.link",
|
||||
site_title: SiteSetting.title,
|
||||
),
|
||||
onClick: {
|
||||
openLink: {
|
||||
url: post.full_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,27 +2,32 @@
|
|||
module DiscourseChatIntegration::Provider::GroupmeProvider
|
||||
PROVIDER_NAME = "groupme".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_groupme_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }]
|
||||
|
||||
def self.generate_groupme_message(post)
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "#{I18n.t('uncategorized_category_name')}"
|
||||
category = "#{I18n.t("uncategorized_category_name")}"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "#{topic.category.parent_category.name}/#{topic.category.name}" : "#{topic.category.name}"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"#{topic.category.parent_category.name}/#{topic.category.name}"
|
||||
else
|
||||
"#{topic.category.name}"
|
||||
end
|
||||
)
|
||||
end
|
||||
pre_post_text = "#{display_name} Posted to #{SiteSetting.title}\n\nTopic: #{topic.title} [#{category}]"
|
||||
pre_post_text =
|
||||
"#{display_name} Posted to #{SiteSetting.title}\n\nTopic: #{topic.title} [#{category}]"
|
||||
read_more = "(Read More: #{post.full_url})"
|
||||
post_excerpt = "#{post.excerpt(SiteSetting.chat_integration_groupme_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)}"
|
||||
data = {
|
||||
text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}"
|
||||
}
|
||||
post_excerpt =
|
||||
"#{post.excerpt(SiteSetting.chat_integration_groupme_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)}"
|
||||
data = { text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}" }
|
||||
data
|
||||
end
|
||||
|
||||
|
@ -35,33 +40,39 @@ module DiscourseChatIntegration::Provider::GroupmeProvider
|
|||
instance_names = SiteSetting.chat_integration_groupme_instance_names.split(/\s*,\s*/)
|
||||
|
||||
unless instance_names.length() == bot_ids.length()
|
||||
instance_names = [I18n.t('chat_integration.provider.groupme.errors.instance_names_issue')] * bot_ids.length()
|
||||
instance_names =
|
||||
[I18n.t("chat_integration.provider.groupme.errors.instance_names_issue")] * bot_ids.length()
|
||||
end
|
||||
|
||||
name_to_id = Hash[instance_names.zip(bot_ids)]
|
||||
user_input_channel = channel.data['groupme_instance_name'].strip
|
||||
unless user_input_channel.eql? 'all'
|
||||
instance_names = [user_input_channel]
|
||||
end
|
||||
instance_names.each { |instance_name|
|
||||
user_input_channel = channel.data["groupme_instance_name"].strip
|
||||
instance_names = [user_input_channel] unless user_input_channel.eql? "all"
|
||||
instance_names.each do |instance_name|
|
||||
bot_id = name_to_id["#{instance_name}"]
|
||||
uri = URI("https://api.groupme.com/v3/bots/post")
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
message[:bot_id] = bot_id
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
num_errors += 1
|
||||
if response.code.to_s == '404'
|
||||
error_key = 'chat_integration.provider.groupme.errors.not_found'
|
||||
if response.code.to_s == "404"
|
||||
error_key = "chat_integration.provider.groupme.errors.not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
last_error_raised = { error_key: error_key, groupme_name: instance_name, bot_id: bot_id, request: req.body, response_code: response.code, response_body: response.body }
|
||||
last_error_raised = {
|
||||
error_key: error_key,
|
||||
groupme_name: instance_name,
|
||||
bot_id: bot_id,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
if last_error_raised
|
||||
successfully_sent = instance_names.length() - num_errors
|
||||
last_error_raised[:success_rate] = "#{successfully_sent}/#{instance_names.length()}"
|
||||
|
|
|
@ -7,27 +7,43 @@ module DiscourseChatIntegration
|
|||
PROVIDER_ENABLED_SETTING = :chat_integration_guilded_enabled
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "webhook_url", regex: '^https:\/\/media\.guilded\.gg\/webhooks\/', unique: true, hidden: true }
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/media\.guilded\.gg\/webhooks\/',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
].freeze
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
webhook_url = channel.data['webhook_url']
|
||||
webhook_url = channel.data["webhook_url"]
|
||||
message = generate_guilded_message(post)
|
||||
response = send_message(webhook_url, message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
||||
error_key: nil, message: message, response_body: response.body
|
||||
})
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||
info: {
|
||||
error_key: nil,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_guilded_message(post)
|
||||
topic = post.topic
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
|
@ -37,15 +53,24 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
message = {
|
||||
embeds: [{
|
||||
title: "#{topic.title} #{(category == '[uncategorized]') ? '' : category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
url: post.full_url,
|
||||
description: post.excerpt(SiteSetting.chat_integration_guilded_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
footer: {
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
text: "#{display_name} | #{post.created_at}"
|
||||
}
|
||||
}]
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
url: post.full_url,
|
||||
description:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_guilded_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
footer: {
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
text: "#{display_name} | #{post.created_at}",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -54,9 +79,9 @@ module DiscourseChatIntegration
|
|||
def self.send_message(url, message)
|
||||
uri = URI(url)
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
|
@ -64,10 +89,9 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
def self.ensure_protocol(url)
|
||||
return url if !url.start_with?('//')
|
||||
return url if !url.start_with?("//")
|
||||
"http:#{url}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,25 +6,27 @@ module DiscourseChatIntegration
|
|||
PROVIDER_NAME = "matrix".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_matrix_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true }
|
||||
]
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true },
|
||||
]
|
||||
|
||||
def self.send_message(room_id, message)
|
||||
homeserver = SiteSetting.chat_integration_matrix_homeserver
|
||||
event_type = 'm.room.message'
|
||||
event_type = "m.room.message"
|
||||
uid = Time.now.to_i
|
||||
|
||||
url_params = URI.encode_www_form(access_token: SiteSetting.chat_integration_matrix_access_token)
|
||||
url_params =
|
||||
URI.encode_www_form(access_token: SiteSetting.chat_integration_matrix_access_token)
|
||||
|
||||
url = "#{homeserver}/_matrix/client/r0/rooms/#{CGI::escape(room_id)}/send/#{event_type}/#{uid}"
|
||||
url =
|
||||
"#{homeserver}/_matrix/client/r0/rooms/#{CGI.escape(room_id)}/send/#{event_type}/#{uid}"
|
||||
|
||||
uri = URI([url, url_params].join('?'))
|
||||
uri = URI([url, url_params].join("?"))
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
|
||||
req = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Put.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
|
@ -35,16 +37,29 @@ module DiscourseChatIntegration
|
|||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
message = {
|
||||
msgtype: SiteSetting.chat_integration_matrix_use_notice ? 'm.notice' : 'm.text',
|
||||
body: I18n.t('chat_integration.provider.matrix.text_message', user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title),
|
||||
format: 'org.matrix.custom.html',
|
||||
formatted_body: I18n.t('chat_integration.provider.matrix.formatted_message', user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt: post.excerpt(SiteSetting.chat_integration_matrix_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true))
|
||||
|
||||
msgtype: SiteSetting.chat_integration_matrix_use_notice ? "m.notice" : "m.text",
|
||||
body:
|
||||
I18n.t(
|
||||
"chat_integration.provider.matrix.text_message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
),
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body:
|
||||
I18n.t(
|
||||
"chat_integration.provider.matrix.formatted_message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_matrix_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -53,24 +68,26 @@ module DiscourseChatIntegration
|
|||
def self.trigger_notification(post, channel, rule)
|
||||
message = generate_matrix_message(post)
|
||||
|
||||
response = send_message(channel.data['room_id'], message)
|
||||
response = send_message(channel.data["room_id"], message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
error_key = nil
|
||||
begin
|
||||
responseData = JSON.parse(response.body)
|
||||
if responseData['errcode'] == "M_UNKNOWN_TOKEN"
|
||||
error_key = 'chat_integration.provider.matrix.errors.unknown_token'
|
||||
elsif responseData['errcode'] == "M_UNKNOWN"
|
||||
error_key = 'chat_integration.provider.matrix.errors.unknown_room'
|
||||
if responseData["errcode"] == "M_UNKNOWN_TOKEN"
|
||||
error_key = "chat_integration.provider.matrix.errors.unknown_token"
|
||||
elsif responseData["errcode"] == "M_UNKNOWN"
|
||||
error_key = "chat_integration.provider.matrix.errors.unknown_room"
|
||||
end
|
||||
ensure
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,22 +15,18 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
|||
def command
|
||||
text = process_command(params)
|
||||
|
||||
render json: {
|
||||
response_type: 'ephemeral',
|
||||
text: text
|
||||
}
|
||||
render json: { response_type: "ephemeral", text: text }
|
||||
end
|
||||
|
||||
def process_command(params)
|
||||
|
||||
tokens = params[:text].split(" ")
|
||||
|
||||
# channel name fix
|
||||
channel_id =
|
||||
case params[:channel_name]
|
||||
when 'directmessage'
|
||||
when "directmessage"
|
||||
"@#{params[:user_name]}"
|
||||
when 'privategroup'
|
||||
when "privategroup"
|
||||
params[:channel_id]
|
||||
else
|
||||
"##{params[:channel_name]}"
|
||||
|
@ -38,21 +34,29 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
|||
|
||||
provider = DiscourseChatIntegration::Provider::MattermostProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.with_provider(provider).with_data_value('identifier', channel_id).first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider(provider)
|
||||
.with_data_value("identifier", channel_id)
|
||||
.first
|
||||
|
||||
# Create channel if doesn't exist
|
||||
channel ||= DiscourseChatIntegration::Channel.create!(provider: provider, data: { identifier: channel_id })
|
||||
channel ||=
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: {
|
||||
identifier: channel_id,
|
||||
},
|
||||
)
|
||||
|
||||
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
||||
|
||||
end
|
||||
|
||||
def mattermost_token_valid?
|
||||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_mattermost_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token != params[:token]
|
||||
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token != params[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
|
@ -63,8 +67,5 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
|||
isolate_namespace DiscourseChatIntegration::Provider::MattermostProvider
|
||||
end
|
||||
|
||||
MattermostEngine.routes.draw do
|
||||
post "command" => "mattermost_command#command"
|
||||
end
|
||||
|
||||
MattermostEngine.routes.draw { post "command" => "mattermost_command#command" }
|
||||
end
|
||||
|
|
|
@ -5,29 +5,30 @@ module DiscourseChatIntegration
|
|||
module MattermostProvider
|
||||
PROVIDER_NAME = "mattermost".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_mattermost_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||
|
||||
def self.send_via_webhook(message)
|
||||
|
||||
uri = URI(SiteSetting.chat_integration_mattermost_webhook_url)
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include? "Couldn't find the channel"
|
||||
error_key = 'chat_integration.provider.mattermost.errors.channel_not_found'
|
||||
error_key = "chat_integration.provider.mattermost.errors.channel_not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.mattermost_message(post, channel)
|
||||
|
@ -35,17 +36,26 @@ module DiscourseChatIntegration
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
icon_url =
|
||||
if SiteSetting.chat_integration_mattermost_icon_url.present?
|
||||
UrlHelper.absolute(SiteSetting.chat_integration_mattermost_icon_url)
|
||||
elsif (url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)).present?
|
||||
elsif (
|
||||
url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)
|
||||
).present?
|
||||
UrlHelper.absolute(url)
|
||||
end
|
||||
|
||||
|
@ -53,7 +63,7 @@ module DiscourseChatIntegration
|
|||
channel: channel,
|
||||
username: SiteSetting.title || "Discourse",
|
||||
icon_url: icon_url,
|
||||
attachments: []
|
||||
attachments: [],
|
||||
}
|
||||
|
||||
summary = {
|
||||
|
@ -61,8 +71,15 @@ module DiscourseChatIntegration
|
|||
author_name: display_name,
|
||||
author_icon: post.user.small_avatar_url,
|
||||
color: topic.category ? "##{topic.category.color}" : nil,
|
||||
text: post.excerpt(SiteSetting.chat_integration_mattermost_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_mattermost_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
title:
|
||||
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
title_link: post.full_url,
|
||||
}
|
||||
|
||||
|
@ -71,12 +88,11 @@ module DiscourseChatIntegration
|
|||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
channel_id = channel.data["identifier"]
|
||||
message = mattermost_message(post, channel_id)
|
||||
|
||||
self.send_via_webhook(message)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,36 +5,45 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
|||
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_rocketchat_enabled
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||
|
||||
def self.rocketchat_message(post, channel)
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
message = {
|
||||
channel: channel,
|
||||
attachments: []
|
||||
}
|
||||
message = { channel: channel, attachments: [] }
|
||||
|
||||
summary = {
|
||||
fallback: "#{topic.title} - #{display_name}",
|
||||
author_name: display_name,
|
||||
author_icon: post.user.small_avatar_url,
|
||||
color: topic.category ? "##{topic.category.color}" : nil,
|
||||
text: post.excerpt(SiteSetting.chat_integration_rocketchat_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_rocketchat_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
mrkdwn_in: ["text"],
|
||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
title_link: post.full_url
|
||||
title:
|
||||
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
title_link: post.full_url,
|
||||
}
|
||||
|
||||
message[:attachments].push(summary)
|
||||
|
@ -46,25 +55,29 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
|||
uri = URI(SiteSetting.chat_integration_rocketchat_webhook_url)
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include?('invalid-channel')
|
||||
error_key = 'chat_integration.provider.rocketchat.errors.invalid_channel'
|
||||
if response.body.include?("invalid-channel")
|
||||
error_key = "chat_integration.provider.rocketchat.errors.invalid_channel"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
channel_id = channel.data["identifier"]
|
||||
message = rocketchat_message(post, channel_id)
|
||||
|
||||
self.send_via_webhook(message)
|
||||
|
|
|
@ -11,7 +11,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
:preload_json,
|
||||
:verify_authenticity_token,
|
||||
:redirect_to_login_if_required,
|
||||
only: [:command, :interactive]
|
||||
only: %i[command interactive]
|
||||
|
||||
def command
|
||||
message = process_command(params)
|
||||
|
@ -28,15 +28,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
private
|
||||
|
||||
def process_command(params)
|
||||
|
||||
tokens = params[:text].split(" ")
|
||||
|
||||
# channel name fix
|
||||
channel_id =
|
||||
case params[:channel_name]
|
||||
when 'directmessage'
|
||||
when "directmessage"
|
||||
"@#{params[:user_name]}"
|
||||
when 'privategroup'
|
||||
when "privategroup"
|
||||
params[:channel_id]
|
||||
else
|
||||
"##{params[:channel_name]}"
|
||||
|
@ -44,17 +43,28 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
provider = DiscourseChatIntegration::Provider::SlackProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.with_provider(provider)
|
||||
.with_data_value('identifier', channel_id)
|
||||
.first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider(provider)
|
||||
.with_data_value("identifier", channel_id)
|
||||
.first
|
||||
|
||||
channel ||= DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: { identifier: channel_id }
|
||||
)
|
||||
channel ||=
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: {
|
||||
identifier: channel_id,
|
||||
},
|
||||
)
|
||||
|
||||
if tokens[0] == 'post'
|
||||
process_post_request(channel, tokens, params[:channel_id], channel_id, params[:response_url])
|
||||
if tokens[0] == "post"
|
||||
process_post_request(
|
||||
channel,
|
||||
tokens,
|
||||
params[:channel_id],
|
||||
channel_id,
|
||||
params[:response_url],
|
||||
)
|
||||
else
|
||||
{ text: ::DiscourseChatIntegration::Helper.process_command(channel, tokens) }
|
||||
end
|
||||
|
@ -66,9 +76,10 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
end
|
||||
|
||||
Scheduler::Defer.later "Processing slack transcript request" do
|
||||
response = build_post_request_response(channel, tokens, slack_channel_id, channel_name, response_url)
|
||||
response =
|
||||
build_post_request_response(channel, tokens, slack_channel_id, channel_name, response_url)
|
||||
http = DiscourseChatIntegration::Provider::SlackProvider.slack_api_http
|
||||
req = Net::HTTP::Post.new(URI(response_url), 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(URI(response_url), "Content-Type" => "application/json")
|
||||
req.body = response.to_json
|
||||
http.request(req)
|
||||
end
|
||||
|
@ -81,15 +92,16 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
first_message_ts = nil
|
||||
requested_thread_ts = nil
|
||||
|
||||
thread_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p[0-9]{16}\?thread_ts=([0-9]{10}.[0-9]{6})\S*$/
|
||||
thread_url_regex =
|
||||
/^https:\/\/\S+\.slack\.com\/archives\/\S+\/p[0-9]{16}\?thread_ts=([0-9]{10}.[0-9]{6})\S*$/
|
||||
slack_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p([0-9]{16})\/?$/
|
||||
|
||||
if tokens.size > 2 && tokens[1] == "thread" && match = slack_url_regex.match(tokens[2])
|
||||
requested_thread_ts = match.captures[0].insert(10, '.')
|
||||
requested_thread_ts = match.captures[0].insert(10, ".")
|
||||
elsif tokens.size > 1 && match = thread_url_regex.match(tokens[1])
|
||||
requested_thread_ts = match.captures[0]
|
||||
elsif tokens.size > 1 && match = slack_url_regex.match(tokens[1])
|
||||
first_message_ts = match.captures[0].insert(10, '.')
|
||||
first_message_ts = match.captures[0].insert(10, ".")
|
||||
elsif tokens.size > 1
|
||||
begin
|
||||
requested_messages = Integer(tokens[1], 10)
|
||||
|
@ -100,12 +112,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
error_key = "chat_integration.provider.slack.transcript.error"
|
||||
|
||||
return { text: I18n.t(error_key) } unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id, requested_thread_ts: requested_thread_ts)
|
||||
unless transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: channel_name,
|
||||
channel_id: slack_channel_id,
|
||||
requested_thread_ts: requested_thread_ts,
|
||||
)
|
||||
return { text: I18n.t(error_key) }
|
||||
end
|
||||
return { text: I18n.t("#{error_key}_users") } unless transcript.load_user_data
|
||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||
|
||||
if first_message_ts
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_first_message_by_ts(first_message_ts)
|
||||
unless transcript.set_first_message_by_ts(first_message_ts)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
elsif requested_messages
|
||||
transcript.set_first_message_by_index(-requested_messages)
|
||||
else
|
||||
|
@ -123,22 +144,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
# Do nothing
|
||||
elsif json[:type] == "message_action" && json[:message][:thread_ts]
|
||||
# Context menu used on a threaded message
|
||||
transcript = SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: json[:message][:thread_ts]
|
||||
)
|
||||
transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: json[:message][:thread_ts],
|
||||
)
|
||||
|
||||
# Send a loading modal within 3 seconds:
|
||||
req = Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.open",
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
||||
)
|
||||
req.body = {
|
||||
"trigger_id": json[:trigger_id],
|
||||
"view": transcript.build_modal_ui
|
||||
}.to_json
|
||||
req =
|
||||
Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.open",
|
||||
"Content-Type" => "application/json",
|
||||
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||
)
|
||||
req.body = { trigger_id: json[:trigger_id], view: transcript.build_modal_ui }.to_json
|
||||
response = http.request(req)
|
||||
view_id = JSON.parse(response.body).dig("view", "id")
|
||||
|
||||
|
@ -147,19 +167,17 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
error_view = generate_error_view("history") unless transcript.load_chat_history
|
||||
|
||||
# Then update the modal with the transcript link:
|
||||
req = Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.update",
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
||||
)
|
||||
req.body = {
|
||||
"view_id": view_id,
|
||||
"view": error_view || transcript.build_modal_ui
|
||||
}.to_json
|
||||
req =
|
||||
Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.update",
|
||||
"Content-Type" => "application/json",
|
||||
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||
)
|
||||
req.body = { view_id: view_id, view: error_view || transcript.build_modal_ui }.to_json
|
||||
response = http.request(req)
|
||||
else
|
||||
# Button clicked in one of our interactive messages
|
||||
req = Net::HTTP::Post.new(URI(json[:response_url]), 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(URI(json[:response_url]), "Content-Type" => "application/json")
|
||||
req.body = build_interactive_response(json).to_json
|
||||
response = http.request(req)
|
||||
end
|
||||
|
@ -177,26 +195,33 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
constant_val = json[:callback_id]
|
||||
changed_val = json[:actions][0][:selected_options][0][:value]
|
||||
|
||||
first_message = (action_name == 'first_message') ? changed_val : constant_val
|
||||
last_message = (action_name == 'first_message') ? constant_val : changed_val
|
||||
first_message = (action_name == "first_message") ? changed_val : constant_val
|
||||
last_message = (action_name == "first_message") ? constant_val : changed_val
|
||||
end
|
||||
|
||||
error_key = "chat_integration.provider.slack.transcript.error"
|
||||
|
||||
return { text: I18n.t(error_key) } unless transcript = SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: requested_thread
|
||||
)
|
||||
unless transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: requested_thread,
|
||||
)
|
||||
return { text: I18n.t(error_key) }
|
||||
end
|
||||
return { text: I18n.t("#{error_key}_users") } unless transcript.load_user_data
|
||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||
|
||||
if first_message
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_first_message_by_ts(first_message)
|
||||
unless transcript.set_first_message_by_ts(first_message)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
end
|
||||
|
||||
if last_message
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_last_message_by_ts(last_message)
|
||||
unless transcript.set_last_message_by_ts(last_message)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
end
|
||||
|
||||
transcript.build_slack_ui
|
||||
|
@ -210,17 +235,11 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
type: "modal",
|
||||
title: {
|
||||
type: "plain_text",
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title")
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title"),
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: ":warning: *#{I18n.t(error_key)}*"
|
||||
}
|
||||
}
|
||||
]
|
||||
{ type: "section", text: { type: "mrkdwn", text: ":warning: *#{I18n.t(error_key)}*" } },
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -228,8 +247,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != params[:token]
|
||||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != params[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
|
@ -240,8 +258,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
json = JSON.parse(params[:payload], symbolize_names: true)
|
||||
|
||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != json[:token]
|
||||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != json[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
|
@ -256,5 +273,4 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
post "command" => "slack_command#command"
|
||||
post "interactive" => "slack_command#interactive"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,13 +6,15 @@ class ChatIntegrationSlackEnabledSettingValidator
|
|||
end
|
||||
|
||||
def valid_value?(val)
|
||||
return true if val == ('f') || val == (false)
|
||||
return false if SiteSetting.chat_integration_slack_outbound_webhook_url.blank? && SiteSetting.chat_integration_slack_access_token.blank?
|
||||
return true if val == ("f") || val == (false)
|
||||
if SiteSetting.chat_integration_slack_outbound_webhook_url.blank? &&
|
||||
SiteSetting.chat_integration_slack_access_token.blank?
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def error_message
|
||||
I18n.t('site_settings.errors.chat_integration_slack_api_configs_are_empty')
|
||||
I18n.t("site_settings.errors.chat_integration_slack_api_configs_are_empty")
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
user["_transcript_username"]
|
||||
elsif @raw.key?("username")
|
||||
# This is for bot messages
|
||||
@raw["username"].gsub(' ', '_')
|
||||
@raw["username"].gsub(" ", "_")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -22,74 +22,69 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
def url
|
||||
channel_id = @transcript.channel_id
|
||||
ts = @raw['ts'].gsub('.', '')
|
||||
ts = @raw["ts"].gsub(".", "")
|
||||
"https://slack.com/archives/#{channel_id}/p#{ts}"
|
||||
end
|
||||
|
||||
def text
|
||||
text = @raw['text'].nil? ? "" : @raw['text']
|
||||
text = @raw["text"].nil? ? "" : @raw["text"]
|
||||
|
||||
pre = {}
|
||||
|
||||
# Extract code blocks and replace with placeholder
|
||||
text = text.gsub(/```(.*?)```/m) do |match|
|
||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||
pre[key] = HTMLEntities.new.decode $1
|
||||
"\n```\n#{key}\n```\n"
|
||||
end
|
||||
text =
|
||||
text.gsub(/```(.*?)```/m) do |match|
|
||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||
pre[key] = HTMLEntities.new.decode $1
|
||||
"\n```\n#{key}\n```\n"
|
||||
end
|
||||
|
||||
# # Extract inline code and replace with placeholder
|
||||
text = text.gsub(/(?<!`)`([^`]+?)`(?!`)/) do |match|
|
||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||
pre[key] = HTMLEntities.new.decode $1
|
||||
"`#{key}`"
|
||||
end
|
||||
text =
|
||||
text.gsub(/(?<!`)`([^`]+?)`(?!`)/) do |match|
|
||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||
pre[key] = HTMLEntities.new.decode $1
|
||||
"`#{key}`"
|
||||
end
|
||||
|
||||
# Format links (don't worry about special cases @ # !)
|
||||
text = text.gsub(/<(.*?)>/) do |match|
|
||||
group = $1
|
||||
parts = group.split('|')
|
||||
link = parts[0].start_with?('@', '#', '!') ? nil : parts[0]
|
||||
text = parts.length > 1 ? parts[1] : parts[0]
|
||||
text =
|
||||
text.gsub(/<(.*?)>/) do |match|
|
||||
group = $1
|
||||
parts = group.split("|")
|
||||
link = parts[0].start_with?("@", "#", "!") ? nil : parts[0]
|
||||
text = parts.length > 1 ? parts[1] : parts[0]
|
||||
|
||||
if parts[0].start_with?('@')
|
||||
user_id = parts[0][1..-1]
|
||||
if user = @transcript.users[user_id]
|
||||
user_name = user['_transcript_username']
|
||||
else
|
||||
user_name = user_id
|
||||
if parts[0].start_with?("@")
|
||||
user_id = parts[0][1..-1]
|
||||
if user = @transcript.users[user_id]
|
||||
user_name = user["_transcript_username"]
|
||||
else
|
||||
user_name = user_id
|
||||
end
|
||||
next "@#{user_name}"
|
||||
end
|
||||
next "@#{user_name}"
|
||||
end
|
||||
|
||||
if link.nil?
|
||||
text
|
||||
elsif link == text
|
||||
"<#{link}>"
|
||||
else
|
||||
"[#{text}](#{link})"
|
||||
if link.nil?
|
||||
text
|
||||
elsif link == text
|
||||
"<#{link}>"
|
||||
else
|
||||
"[#{text}](#{link})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add an extra * to each side for bold
|
||||
text = text.gsub(/\*.*?\*/) do |match|
|
||||
"*#{match}*"
|
||||
end
|
||||
text = text.gsub(/\*.*?\*/) { |match| "*#{match}*" }
|
||||
|
||||
# Add an extra ~ to each side for strikethrough
|
||||
text = text.gsub(/~.*?~/) do |match|
|
||||
"~#{match}~"
|
||||
end
|
||||
text = text.gsub(/~.*?~/) { |match| "~#{match}~" }
|
||||
|
||||
# Replace emoji - with _
|
||||
text = text.gsub(/:[a-z0-9_-]+:/) do |match|
|
||||
match.gsub("-") { "_" }
|
||||
end
|
||||
text = text.gsub(/:[a-z0-9_-]+:/) { |match| match.gsub("-") { "_" } }
|
||||
|
||||
# Restore pre-formatted code block content
|
||||
pre.each do |key, value|
|
||||
text = text.gsub(key) { value }
|
||||
end
|
||||
pre.each { |key, value| text = text.gsub(key) { value } }
|
||||
|
||||
text
|
||||
end
|
||||
|
@ -97,9 +92,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
def attachments_string
|
||||
string = ""
|
||||
string += "\n" if !attachments.empty?
|
||||
attachments.each do |attachment|
|
||||
string += " - #{attachment}\n"
|
||||
end
|
||||
attachments.each { |attachment| string += " - #{attachment}\n" }
|
||||
string
|
||||
end
|
||||
|
||||
|
@ -108,7 +101,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
end
|
||||
|
||||
def raw_text
|
||||
raw_text = @raw['text'].nil? ? "" : @raw['text']
|
||||
raw_text = @raw["text"].nil? ? "" : @raw["text"]
|
||||
raw_text += attachments_string
|
||||
raw_text
|
||||
end
|
||||
|
@ -116,7 +109,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
def attachments
|
||||
attachments = []
|
||||
|
||||
return attachments unless @raw.key?('attachments')
|
||||
return attachments unless @raw.key?("attachments")
|
||||
|
||||
@raw["attachments"].each do |attachment|
|
||||
next unless attachment.key?("fallback")
|
||||
|
|
|
@ -8,7 +8,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
@excerpt = +""
|
||||
end
|
||||
|
||||
def self.format(html = '')
|
||||
def self.format(html = "")
|
||||
me = self.new
|
||||
parser = Nokogiri::HTML::SAX::Parser.new(me)
|
||||
parser.parse(html)
|
||||
|
@ -20,7 +20,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
when "a"
|
||||
attributes = Hash[*attributes.flatten]
|
||||
@in_a = true
|
||||
@excerpt << "<#{absolute_url(attributes['href'])}|"
|
||||
@excerpt << "<#{absolute_url(attributes["href"])}|"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -40,13 +40,18 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
private
|
||||
|
||||
def absolute_url(url)
|
||||
uri = URI(url) rescue nil
|
||||
uri =
|
||||
begin
|
||||
URI(url)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
return Discourse.current_hostname unless uri
|
||||
return uri.to_s if uri.scheme == "mailto"
|
||||
|
||||
uri.host = Discourse.current_hostname if !uri.host
|
||||
uri.scheme = (SiteSetting.force_https ? 'https' : 'http') if !uri.scheme
|
||||
uri.scheme = (SiteSetting.force_https ? "https" : "http") if !uri.scheme
|
||||
uri.to_s
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,18 +13,19 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "identifier", regex: '^[@#]?\S*$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]?\S*$', unique: true }]
|
||||
|
||||
require_dependency 'topic'
|
||||
::Topic.register_custom_field_type(DiscourseChatIntegration::Provider::SlackProvider::THREAD_LEGACY, :string)
|
||||
require_dependency "topic"
|
||||
::Topic.register_custom_field_type(
|
||||
DiscourseChatIntegration::Provider::SlackProvider::THREAD_LEGACY,
|
||||
:string,
|
||||
)
|
||||
|
||||
def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length)
|
||||
doc = Nokogiri::HTML5.fragment(post.excerpt(max_length,
|
||||
remap_emoji: true,
|
||||
keep_onebox_source: true
|
||||
))
|
||||
doc =
|
||||
Nokogiri::HTML5.fragment(
|
||||
post.excerpt(max_length, remap_emoji: true, keep_onebox_source: true),
|
||||
)
|
||||
|
||||
SlackMessageFormatter.format(doc.to_html)
|
||||
end
|
||||
|
@ -34,11 +35,18 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
icon_url =
|
||||
|
@ -55,12 +63,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
SiteSetting.title || "Discourse"
|
||||
end
|
||||
|
||||
message = {
|
||||
channel: channel,
|
||||
username: slack_username,
|
||||
icon_url: icon_url,
|
||||
attachments: []
|
||||
}
|
||||
message = { channel: channel, username: slack_username, icon_url: icon_url, attachments: [] }
|
||||
|
||||
if filter == "thread" && thread_ts = get_slack_thread_ts(topic, channel)
|
||||
message[:thread_ts] = thread_ts
|
||||
|
@ -73,9 +76,10 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
color: topic.category ? "##{topic.category.color}" : nil,
|
||||
text: excerpt(post),
|
||||
mrkdwn_in: ["text"],
|
||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
title:
|
||||
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
title_link: post.full_url,
|
||||
thumb_url: post.full_url
|
||||
thumb_url: post.full_url,
|
||||
}
|
||||
|
||||
message[:attachments].push(summary)
|
||||
|
@ -92,14 +96,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
# <!--SLACK_CHANNEL_ID=#{@channel_id};SLACK_TS=#{@requested_thread_ts}-->
|
||||
slack_thread_regex = /<!--SLACK_CHANNEL_ID=([^;.]+);SLACK_TS=([0-9]{10}.[0-9]{6})-->/
|
||||
|
||||
req = Net::HTTP::Post.new(URI('https://slack.com/api/chat.postMessage'))
|
||||
req = Net::HTTP::Post.new(URI("https://slack.com/api/chat.postMessage"))
|
||||
|
||||
data = {
|
||||
token: SiteSetting.chat_integration_slack_access_token,
|
||||
username: message[:username],
|
||||
icon_url: message[:icon_url],
|
||||
channel: message[:channel].gsub('#', ''),
|
||||
attachments: message[:attachments].to_json
|
||||
channel: message[:channel].gsub("#", ""),
|
||||
attachments: message[:attachments].to_json,
|
||||
}
|
||||
|
||||
if post
|
||||
|
@ -116,18 +120,28 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { request: uri, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
request: uri,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
unless json["ok"] == true
|
||||
if json.key?("error") && (json["error"] == ('channel_not_found') || json["error"] == ('is_archived'))
|
||||
error_key = 'chat_integration.provider.slack.errors.channel_not_found'
|
||||
if json.key?("error") &&
|
||||
(json["error"] == ("channel_not_found") || json["error"] == ("is_archived"))
|
||||
error_key = "chat_integration.provider.slack.errors.channel_not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: uri, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: uri,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
ts = json["ts"]
|
||||
|
@ -139,25 +153,33 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
def self.send_via_webhook(message)
|
||||
http = FinalDestination::HTTP.new("hooks.slack.com", 443)
|
||||
http.use_ssl = true
|
||||
req = Net::HTTP::Post.new(URI(SiteSetting.chat_integration_slack_outbound_webhook_url), 'Content-Type' => 'application/json')
|
||||
req =
|
||||
Net::HTTP::Post.new(
|
||||
URI(SiteSetting.chat_integration_slack_outbound_webhook_url),
|
||||
"Content-Type" => "application/json",
|
||||
)
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.code.to_s == '403'
|
||||
error_key = 'chat_integration.provider.slack.errors.action_prohibited'
|
||||
elsif response.body == ('channel_not_found') || response.body == ('channel_is_archived')
|
||||
error_key = 'chat_integration.provider.slack.errors.channel_not_found'
|
||||
if response.code.to_s == "403"
|
||||
error_key = "chat_integration.provider.slack.errors.action_prohibited"
|
||||
elsif response.body == ("channel_not_found") || response.body == ("channel_is_archived")
|
||||
error_key = "chat_integration.provider.slack.errors.channel_not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
channel_id = channel.data["identifier"]
|
||||
filter = rule.nil? ? "" : rule.filter
|
||||
message = slack_message(post, channel_id, filter)
|
||||
|
||||
|
@ -166,7 +188,6 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
else
|
||||
self.send_via_api(post, channel_id, message)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.slack_api_http
|
||||
|
@ -182,13 +203,16 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
end
|
||||
|
||||
def self.set_slack_thread_ts(topic, channel, value)
|
||||
TopicCustomField.upsert({
|
||||
TopicCustomField.upsert(
|
||||
{
|
||||
topic_id: topic.id,
|
||||
name: "#{THREAD_CUSTOM_FIELD_PREFIX}#{channel}",
|
||||
value: value,
|
||||
created_at: Time.zone.now,
|
||||
updated_at: Time.zone.now
|
||||
}, unique_by: [:topic_id, :name])
|
||||
updated_at: Time.zone.now,
|
||||
},
|
||||
unique_by: %i[topic_id name],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
module DiscourseChatIntegration::Provider::SlackProvider
|
||||
class SlackTranscript
|
||||
class UserFetchError < RuntimeError; end
|
||||
class UserFetchError < RuntimeError
|
||||
end
|
||||
|
||||
attr_reader :users, :channel_id, :messages
|
||||
|
||||
|
@ -42,17 +43,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
# Work through the messages in order. If a gap is found, this could be the first message
|
||||
new_first_message_index = nil
|
||||
previous_message_ts = @messages[-skip_messages].ts.split('.').first.to_i
|
||||
previous_message_ts = @messages[-skip_messages].ts.split(".").first.to_i
|
||||
possible_first_messages.each_with_index do |message, index|
|
||||
|
||||
# Calculate the time since the last message
|
||||
this_ts = message.ts.split('.').first.to_i
|
||||
this_ts = message.ts.split(".").first.to_i
|
||||
time_since_previous_message = this_ts - previous_message_ts
|
||||
|
||||
# If greater than 3 minutes, this could be the first message
|
||||
if time_since_previous_message > 3.minutes
|
||||
new_first_message_index = index
|
||||
end
|
||||
new_first_message_index = index if time_since_previous_message > 3.minutes
|
||||
|
||||
previous_message_ts = this_ts
|
||||
end
|
||||
|
@ -84,11 +82,11 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
def build_transcript
|
||||
post_content = +""
|
||||
post_content << "[quote]\n" if SiteSetting.chat_integration_slack_transcript_quote
|
||||
post_content << "[**#{I18n.t('chat_integration.provider.slack.transcript.view_on_slack', name: @channel_name)}**](#{first_message.url})\n"
|
||||
post_content << "[**#{I18n.t("chat_integration.provider.slack.transcript.view_on_slack", name: @channel_name)}**](#{first_message.url})\n"
|
||||
|
||||
all_avatars = {}
|
||||
|
||||
last_username = ''
|
||||
last_username = ""
|
||||
|
||||
transcript_messages = @messages[@first_message_index..@last_message_index]
|
||||
|
||||
|
@ -108,9 +106,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
|
||||
post_content << m.text
|
||||
|
||||
m.attachments.each do |attachment|
|
||||
post_content << "\n> #{attachment}\n"
|
||||
end
|
||||
m.attachments.each { |attachment| post_content << "\n> #{attachment}\n" }
|
||||
|
||||
post_content << "\n"
|
||||
end
|
||||
|
@ -118,9 +114,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
post_content << "[/quote]" if SiteSetting.chat_integration_slack_transcript_quote
|
||||
post_content << "\n\n"
|
||||
|
||||
all_avatars.each do |username, url|
|
||||
post_content << "[#{username}]: #{url}\n"
|
||||
end
|
||||
all_avatars.each { |username, url| post_content << "[#{username}]: #{url}\n" }
|
||||
|
||||
if not @requested_thread_ts.nil?
|
||||
post_content << "<!--SLACK_CHANNEL_ID=#{@channel_name};SLACK_TS=#{@requested_thread_ts}-->"
|
||||
|
@ -134,17 +128,17 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
type: "modal",
|
||||
title: {
|
||||
type: "plain_text",
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title")
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title"),
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": I18n.t("chat_integration.provider.slack.transcript.modal_description")
|
||||
}
|
||||
}
|
||||
]
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_description"),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if @messages
|
||||
|
@ -153,30 +147,31 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
|
||||
|
||||
data[:blocks] << {
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": ":writing_hand: *#{I18n.t("chat_integration.provider.slack.transcript.transcript_ready")}*"
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text:
|
||||
":writing_hand: *#{I18n.t("chat_integration.provider.slack.transcript.transcript_ready")}*",
|
||||
},
|
||||
"accessory": {
|
||||
"type": "button",
|
||||
"text": {
|
||||
"type": "plain_text",
|
||||
"text": I18n.t("chat_integration.provider.slack.transcript.continue_on_discourse"),
|
||||
"emoji": true
|
||||
accessory: {
|
||||
type: "button",
|
||||
text: {
|
||||
type: "plain_text",
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.continue_on_discourse"),
|
||||
emoji: true,
|
||||
},
|
||||
"style": "primary",
|
||||
"url": link,
|
||||
"action_id": "null_action"
|
||||
}
|
||||
style: "primary",
|
||||
url: link,
|
||||
action_id: "null_action",
|
||||
},
|
||||
}
|
||||
else
|
||||
data[:blocks] << {
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": ":writing_hand: #{I18n.t("chat_integration.provider.slack.transcript.loading")}"
|
||||
}
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: ":writing_hand: #{I18n.t("chat_integration.provider.slack.transcript.loading")}",
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -188,76 +183,81 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
secret = DiscourseChatIntegration::Helper.save_transcript(post_content)
|
||||
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
|
||||
|
||||
return { text: "<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>" } if @requested_thread_ts
|
||||
if @requested_thread_ts
|
||||
return(
|
||||
{
|
||||
text:
|
||||
"<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>",
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
{
|
||||
text: "<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>",
|
||||
attachments: [
|
||||
{
|
||||
pretext: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.first_message_pretext",
|
||||
n: @messages.length - first_message_number
|
||||
),
|
||||
pretext:
|
||||
I18n.t(
|
||||
"chat_integration.provider.slack.transcript.first_message_pretext",
|
||||
n: @messages.length - first_message_number,
|
||||
),
|
||||
fallback: "#{first_message.username} - #{first_message.raw_text}",
|
||||
color: "#007AB8",
|
||||
author_name: first_message.username,
|
||||
author_icon: first_message.avatar,
|
||||
text: first_message.raw_text,
|
||||
footer: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.posted_in",
|
||||
name: @channel_name
|
||||
),
|
||||
footer:
|
||||
I18n.t("chat_integration.provider.slack.transcript.posted_in", name: @channel_name),
|
||||
ts: first_message.ts,
|
||||
callback_id: last_message.ts,
|
||||
actions: [
|
||||
{
|
||||
name: "first_message",
|
||||
text: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.change_first_message"
|
||||
),
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.change_first_message"),
|
||||
type: "select",
|
||||
options: first_message_options = @messages[ [(first_message_number - 20), 0].max .. last_message_number]
|
||||
.map { |m| { text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts } }
|
||||
}
|
||||
options:
|
||||
first_message_options =
|
||||
@messages[[(first_message_number - 20), 0].max..last_message_number].map do |m|
|
||||
{ text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts }
|
||||
end,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pretext: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.last_message_pretext",
|
||||
n: @messages.length - last_message_number
|
||||
),
|
||||
pretext:
|
||||
I18n.t(
|
||||
"chat_integration.provider.slack.transcript.last_message_pretext",
|
||||
n: @messages.length - last_message_number,
|
||||
),
|
||||
fallback: "#{last_message.username} - #{last_message.raw_text}",
|
||||
color: "#007AB8",
|
||||
author_name: last_message.username,
|
||||
author_icon: last_message.avatar,
|
||||
text: last_message.raw_text,
|
||||
footer: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.posted_in",
|
||||
name: @channel_name
|
||||
),
|
||||
footer:
|
||||
I18n.t("chat_integration.provider.slack.transcript.posted_in", name: @channel_name),
|
||||
ts: last_message.ts,
|
||||
callback_id: first_message.ts,
|
||||
actions: [
|
||||
{
|
||||
name: "last_message",
|
||||
text: I18n.t(
|
||||
"chat_integration.provider.slack.transcript.change_last_message"
|
||||
),
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.change_last_message"),
|
||||
type: "select",
|
||||
options: @messages[first_message_number..(last_message_number + 20)]
|
||||
.map { |m| { text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts } }
|
||||
}
|
||||
options:
|
||||
@messages[first_message_number..(last_message_number + 20)].map do |m|
|
||||
{ text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts }
|
||||
end,
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
def load_user_data
|
||||
key = "slack_user_info_#{Digest::SHA1.hexdigest(SiteSetting.chat_integration_slack_access_token)}"
|
||||
@users = Discourse.cache.fetch(key, expires_in: 10.minutes) do
|
||||
fetch_user_data
|
||||
end
|
||||
key =
|
||||
"slack_user_info_#{Digest::SHA1.hexdigest(SiteSetting.chat_integration_slack_access_token)}"
|
||||
@users = Discourse.cache.fetch(key, expires_in: 10.minutes) { fetch_user_data }
|
||||
true
|
||||
rescue UserFetchError
|
||||
false
|
||||
|
@ -267,26 +267,30 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
http = ::DiscourseChatIntegration::Provider::SlackProvider.slack_api_http
|
||||
|
||||
cursor = nil
|
||||
req = Net::HTTP::Post.new(URI('https://slack.com/api/users.list'))
|
||||
req = Net::HTTP::Post.new(URI("https://slack.com/api/users.list"))
|
||||
|
||||
users = {}
|
||||
loop do
|
||||
break if cursor == ""
|
||||
req.set_form_data(token: SiteSetting.chat_integration_slack_access_token, limit: 200, cursor: cursor)
|
||||
req.set_form_data(
|
||||
token: SiteSetting.chat_integration_slack_access_token,
|
||||
limit: 200,
|
||||
cursor: cursor,
|
||||
)
|
||||
response = http.request(req)
|
||||
raise UserFetchError.new unless response.kind_of? Net::HTTPSuccess
|
||||
json = JSON.parse(response.body)
|
||||
raise UserFetchError.new unless json['ok']
|
||||
cursor = json['response_metadata']['next_cursor']
|
||||
json['members'].each do |user|
|
||||
raise UserFetchError.new unless json["ok"]
|
||||
cursor = json["response_metadata"]["next_cursor"]
|
||||
json["members"].each do |user|
|
||||
# Slack uses display_name and falls back to real_name if it is not set
|
||||
if user['profile']['display_name'].blank?
|
||||
user['_transcript_username'] = user['profile']['real_name']
|
||||
if user["profile"]["display_name"].blank?
|
||||
user["_transcript_username"] = user["profile"]["real_name"]
|
||||
else
|
||||
user['_transcript_username'] = user['profile']['display_name']
|
||||
user["_transcript_username"] = user["profile"]["display_name"]
|
||||
end
|
||||
user['_transcript_username'] = user['_transcript_username'].gsub(' ', '_')
|
||||
users[user['id']] = user
|
||||
user["_transcript_username"] = user["_transcript_username"].gsub(" ", "_")
|
||||
users[user["id"]] = user
|
||||
end
|
||||
end
|
||||
users
|
||||
|
@ -302,7 +306,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
data = {
|
||||
token: SiteSetting.chat_integration_slack_access_token,
|
||||
channel: @channel_id,
|
||||
limit: count
|
||||
limit: count,
|
||||
}
|
||||
|
||||
data[:ts] = @requested_thread_ts if @requested_thread_ts
|
||||
|
@ -311,9 +315,9 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
response = http.request(req)
|
||||
return false unless response.kind_of? Net::HTTPSuccess
|
||||
json = JSON.parse(response.body)
|
||||
return false unless json['ok']
|
||||
return false unless json["ok"]
|
||||
|
||||
raw_messages = json['messages']
|
||||
raw_messages = json["messages"]
|
||||
raw_messages = raw_messages.reverse unless @requested_thread_ts
|
||||
|
||||
# Build some message objects
|
||||
|
@ -323,13 +327,13 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
|||
next unless message["type"] == "message"
|
||||
|
||||
# Don't load responses to threads unless specifically requested (if ts==thread_ts then it's the thread parent)
|
||||
next if !@requested_thread_ts && message["thread_ts"] && message["thread_ts"] != message["ts"]
|
||||
if !@requested_thread_ts && message["thread_ts"] && message["thread_ts"] != message["ts"]
|
||||
next
|
||||
end
|
||||
|
||||
this_message = SlackMessage.new(message, self)
|
||||
@messages << this_message
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,29 +5,33 @@ module DiscourseChatIntegration::Provider::TeamsProvider
|
|||
PROVIDER_ENABLED_SETTING = :chat_integration_teams_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url", regex: '^https:\/\/\S+$', unique: true, hidden: true }
|
||||
{ key: "webhook_url", regex: '^https:\/\/\S+$', unique: true, hidden: true },
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = get_message(post)
|
||||
uri = URI(channel.data['webhook_url'])
|
||||
uri = URI(channel.data["webhook_url"])
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include?('Invalid webhook URL')
|
||||
error_key = 'chat_integration.provider.teams.errors.invalid_channel'
|
||||
if response.body.include?("Invalid webhook URL")
|
||||
error_key = "chat_integration.provider.teams.errors.invalid_channel"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.get_message(post)
|
||||
|
@ -41,29 +45,41 @@ module DiscourseChatIntegration::Provider::TeamsProvider
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
message = {
|
||||
"@type": "MessageCard",
|
||||
"summary": topic.title,
|
||||
"sections": [{
|
||||
"activityTitle": "[#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}](#{post.full_url})",
|
||||
"activitySubtitle": post.excerpt(SiteSetting.chat_integration_teams_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
"activityImage": post.user.small_avatar_url,
|
||||
"facts": [{
|
||||
"name": full_name,
|
||||
"value": display_name
|
||||
}],
|
||||
"markdown": true
|
||||
}],
|
||||
summary: topic.title,
|
||||
sections: [
|
||||
{
|
||||
activityTitle:
|
||||
"[#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}](#{post.full_url})",
|
||||
activitySubtitle:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_teams_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
activityImage: post.user.small_avatar_url,
|
||||
facts: [{ name: full_name, value: display_name }],
|
||||
markdown: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
message
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,12 +13,11 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
|||
only: :command
|
||||
|
||||
def command
|
||||
|
||||
# If it's a new message (telegram also sends hooks for other reasons that we don't care about)
|
||||
if params.key?('message')
|
||||
chat_id = params['message']['chat']['id']
|
||||
if params.key?("message")
|
||||
chat_id = params["message"]["chat"]["id"]
|
||||
|
||||
message_text = process_command(params['message'])
|
||||
message_text = process_command(params["message"])
|
||||
|
||||
if message_text.present?
|
||||
message = {
|
||||
|
@ -30,15 +29,15 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
|||
|
||||
DiscourseChatIntegration::Provider::TelegramProvider.sendMessage(message)
|
||||
end
|
||||
elsif params.dig("channel_post", "text")&.include?("/getchatid")
|
||||
chat_id = params["channel_post"]["chat"]["id"]
|
||||
|
||||
elsif params.dig('channel_post', 'text')&.include?('/getchatid')
|
||||
chat_id = params['channel_post']['chat']['id']
|
||||
|
||||
message_text = I18n.t(
|
||||
"chat_integration.provider.telegram.unknown_chat",
|
||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
)
|
||||
message_text =
|
||||
I18n.t(
|
||||
"chat_integration.provider.telegram.unknown_chat",
|
||||
site_title: CGI.escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
)
|
||||
|
||||
message = {
|
||||
chat_id: chat_id,
|
||||
|
@ -51,43 +50,49 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
|||
end
|
||||
|
||||
# Always give telegram a success message, otherwise we'll stop receiving webhooks
|
||||
data = {
|
||||
success: true
|
||||
}
|
||||
data = { success: true }
|
||||
render json: data
|
||||
end
|
||||
|
||||
def process_command(message)
|
||||
return unless message['text'] # No command to be processed
|
||||
return unless message["text"] # No command to be processed
|
||||
|
||||
chat_id = params['message']['chat']['id']
|
||||
chat_id = params["message"]["chat"]["id"]
|
||||
|
||||
provider = DiscourseChatIntegration::Provider::TelegramProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.with_provider(provider).with_data_value('chat_id', chat_id).first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider(provider)
|
||||
.with_data_value("chat_id", chat_id)
|
||||
.first
|
||||
|
||||
text_key = if channel.nil?
|
||||
"unknown_chat"
|
||||
elsif !SiteSetting.chat_integration_telegram_enable_slash_commands || !message['text'].start_with?('/')
|
||||
"silent"
|
||||
else
|
||||
""
|
||||
end
|
||||
text_key =
|
||||
if channel.nil?
|
||||
"unknown_chat"
|
||||
elsif !SiteSetting.chat_integration_telegram_enable_slash_commands ||
|
||||
!message["text"].start_with?("/")
|
||||
"silent"
|
||||
else
|
||||
""
|
||||
end
|
||||
|
||||
return "" if text_key == "silent"
|
||||
|
||||
if text_key.present?
|
||||
return I18n.t(
|
||||
"chat_integration.provider.telegram.#{text_key}",
|
||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
return(
|
||||
I18n.t(
|
||||
"chat_integration.provider.telegram.#{text_key}",
|
||||
site_title: CGI.escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
tokens = message['text'].split(" ")
|
||||
tokens = message["text"].split(" ")
|
||||
|
||||
tokens[0][0] = '' # Remove the slash from the first token
|
||||
tokens[0] = tokens[0].split('@')[0] # Remove the bot name from the command (necessary for group chats)
|
||||
tokens[0][0] = "" # Remove the slash from the first token
|
||||
tokens[0] = tokens[0].split("@")[0] # Remove the bot name from the command (necessary for group chats)
|
||||
|
||||
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
||||
end
|
||||
|
@ -96,8 +101,7 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
|||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_telegram_secret.blank? ||
|
||||
SiteSetting.chat_integration_telegram_secret != params[:token]
|
||||
|
||||
SiteSetting.chat_integration_telegram_secret != params[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
|
@ -108,7 +112,5 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
|||
isolate_namespace DiscourseChatIntegration::Provider::TelegramProvider
|
||||
end
|
||||
|
||||
TelegramEngine.routes.draw do
|
||||
post "command/:token" => "telegram_command#command"
|
||||
end
|
||||
TelegramEngine.routes.draw { post "command/:token" => "telegram_command#command" }
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
DiscourseEvent.on(:site_setting_changed) do |setting_name, old_value, new_value|
|
||||
isEnabledSetting = setting_name == :chat_integration_telegram_enabled
|
||||
isAccessToken = setting_name == :chat_integration_telegram_access_token
|
||||
isAccessToken = setting_name == :chat_integration_telegram_access_token
|
||||
|
||||
if (isEnabledSetting || isAccessToken)
|
||||
enabled = isEnabledSetting ? new_value == true : SiteSetting.chat_integration_telegram_enabled
|
||||
|
|
|
@ -6,30 +6,30 @@ module DiscourseChatIntegration
|
|||
PROVIDER_NAME = "telegram".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "chat_id", regex: '^(-?[0-9]+|@\S+)$', unique: true }
|
||||
]
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "chat_id", regex: '^(-?[0-9]+|@\S+)$', unique: true },
|
||||
]
|
||||
|
||||
def self.setup_webhook
|
||||
newSecret = SecureRandom.hex
|
||||
SiteSetting.chat_integration_telegram_secret = newSecret
|
||||
|
||||
message = {
|
||||
url: Discourse.base_url + '/chat-integration/telegram/command/' + newSecret,
|
||||
}
|
||||
message = { url: Discourse.base_url + "/chat-integration/telegram/command/" + newSecret }
|
||||
|
||||
response = self.do_api_request('setWebhook', message)
|
||||
response = self.do_api_request("setWebhook", message)
|
||||
|
||||
if response['ok'] != true
|
||||
if response["ok"] != true
|
||||
# If setting up webhook failed, disable provider
|
||||
SiteSetting.chat_integration_telegram_enabled = false
|
||||
Rails.logger.error("Failed to setup telegram webhook. Message data= " + message.to_json + " response=" + response.to_json)
|
||||
Rails.logger.error(
|
||||
"Failed to setup telegram webhook. Message data= " + message.to_json + " response=" +
|
||||
response.to_json,
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.sendMessage(message)
|
||||
self.do_api_request('sendMessage', message)
|
||||
self.do_api_request("sendMessage", message)
|
||||
end
|
||||
|
||||
def self.do_api_request(methodName, message)
|
||||
|
@ -40,7 +40,7 @@ module DiscourseChatIntegration
|
|||
|
||||
uri = URI("https://api.telegram.org/bot#{access_token}/#{methodName}")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
|
@ -54,28 +54,38 @@ module DiscourseChatIntegration
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
tags = ''
|
||||
if topic.tags.present?
|
||||
tags = topic.tags.map(&:name).join(', ')
|
||||
end
|
||||
tags = ""
|
||||
tags = topic.tags.map(&:name).join(", ") if topic.tags.present?
|
||||
|
||||
I18n.t(
|
||||
"chat_integration.provider.telegram.message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: CGI::escapeHTML(topic.title),
|
||||
post_excerpt: post.excerpt(SiteSetting.chat_integration_telegram_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
)
|
||||
|
||||
"chat_integration.provider.telegram.message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: CGI.escapeHTML(topic.title),
|
||||
post_excerpt:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_telegram_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
chat_id = channel.data['chat_id']
|
||||
chat_id = channel.data["chat_id"]
|
||||
|
||||
message = {
|
||||
chat_id: chat_id,
|
||||
|
@ -86,18 +96,20 @@ module DiscourseChatIntegration
|
|||
|
||||
response = sendMessage(message)
|
||||
|
||||
if response['ok'] != true
|
||||
if response["ok"] != true
|
||||
error_key = nil
|
||||
if response['description'].include? 'chat not found'
|
||||
error_key = 'chat_integration.provider.telegram.errors.channel_not_found'
|
||||
elsif response['description'].include? 'Forbidden'
|
||||
error_key = 'chat_integration.provider.telegram.errors.forbidden'
|
||||
if response["description"].include? "chat not found"
|
||||
error_key = "chat_integration.provider.telegram.errors.channel_not_found"
|
||||
elsif response["description"].include? "Forbidden"
|
||||
error_key = "chat_integration.provider.telegram.errors.forbidden"
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,35 +5,38 @@ module DiscourseChatIntegration::Provider::WebexProvider
|
|||
PROVIDER_ENABLED_SETTING = :chat_integration_webex_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url",
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/webexapis\.com\/v1\/webhooks\/incoming\/[A-Za-z0-9\-@\/]+\S+$',
|
||||
unique: true,
|
||||
hidden: true }
|
||||
hidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = get_message(post)
|
||||
uri = URI(channel.data['webhook_url'])
|
||||
uri = URI(channel.data["webhook_url"])
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include?('Invalid webhook URL')
|
||||
error_key = 'chat_integration.provider.webex.errors.invalid_channel'
|
||||
if response.body.include?("Invalid webhook URL")
|
||||
error_key = "chat_integration.provider.webex.errors.invalid_channel"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.get_message(post)
|
||||
|
@ -41,24 +44,31 @@ module DiscourseChatIntegration::Provider::WebexProvider
|
|||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ?
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
markdown = "**#{topic.title}**: #{category}"
|
||||
markdown += " #{topic.tags.map(&:name).join(', ')} " if topic.tags.present?
|
||||
markdown += " #{topic.tags.map(&:name).join(", ")} " if topic.tags.present?
|
||||
markdown += "(#{post.full_url}) from #{display_name}:\n"
|
||||
markdown += post.excerpt(SiteSetting.chat_integration_webex_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true
|
||||
)
|
||||
markdown +=
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_webex_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
)
|
||||
|
||||
{ "markdown": markdown }
|
||||
{ markdown: markdown }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,18 +6,21 @@ module DiscourseChatIntegration
|
|||
PROVIDER_NAME = "zulip".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_zulip_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "stream", unique: true, regex: '^\S+' },
|
||||
{ key: "subject", unique: true, regex: '^\S+' },
|
||||
]
|
||||
{ key: "stream", unique: true, regex: '^\S+' },
|
||||
{ key: "subject", unique: true, regex: '^\S+' },
|
||||
]
|
||||
|
||||
def self.send_message(message)
|
||||
uri = URI("#{SiteSetting.chat_integration_zulip_server}/api/v1/messages")
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri)
|
||||
req.basic_auth(SiteSetting.chat_integration_zulip_bot_email_address, SiteSetting.chat_integration_zulip_bot_api_key)
|
||||
req.basic_auth(
|
||||
SiteSetting.chat_integration_zulip_bot_email_address,
|
||||
SiteSetting.chat_integration_zulip_bot_api_key,
|
||||
)
|
||||
req.set_form_data(message)
|
||||
|
||||
response = http.request(req)
|
||||
|
@ -28,23 +31,27 @@ module DiscourseChatIntegration
|
|||
def self.generate_zulip_message(post, stream, subject)
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
message = I18n.t('chat_integration.provider.zulip.message', user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt: post.excerpt(SiteSetting.chat_integration_zulip_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true))
|
||||
message =
|
||||
I18n.t(
|
||||
"chat_integration.provider.zulip.message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_zulip_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
)
|
||||
|
||||
data = {
|
||||
type: 'stream',
|
||||
to: stream,
|
||||
subject: subject,
|
||||
content: message
|
||||
}
|
||||
data = { type: "stream", to: stream, subject: subject, content: message }
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
|
||||
stream = channel.data['stream']
|
||||
subject = channel.data['subject']
|
||||
stream = channel.data["stream"]
|
||||
subject = channel.data["subject"]
|
||||
|
||||
message = self.generate_zulip_message(post, stream, subject)
|
||||
|
||||
|
@ -52,12 +59,18 @@ module DiscourseChatIntegration
|
|||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
error_key = nil
|
||||
error_key = 'chat_integration.provider.zulip.errors.does_not_exist' if response.body.include?('does not exist')
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_code: response.code, response_body: response.body }
|
||||
error_key =
|
||||
"chat_integration.provider.zulip.errors.does_not_exist" if response.body.include?(
|
||||
"does not exist",
|
||||
)
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
26
plugin.rb
26
plugin.rb
|
@ -26,7 +26,7 @@ after_initialize do
|
|||
Jobs.enqueue_in(time, :notify_chats, post_id: post.id)
|
||||
end
|
||||
|
||||
add_admin_route 'chat_integration.menu_title', 'chat-integration'
|
||||
add_admin_route "chat_integration.menu_title", "chat-integration"
|
||||
|
||||
AdminDashboardData.add_problem_check do
|
||||
next if !SiteSetting.chat_integration_enabled
|
||||
|
@ -47,7 +47,7 @@ after_initialize do
|
|||
DiscourseChatIntegration::Provider.mount_engines
|
||||
|
||||
if defined?(DiscourseAutomation)
|
||||
add_automation_scriptable('send_slack_message') do
|
||||
add_automation_scriptable("send_slack_message") do
|
||||
field :message, component: :message, required: true, accepts_placeholders: true
|
||||
field :url, component: :text, required: true
|
||||
field :channel, component: :text, required: true
|
||||
|
@ -59,16 +59,24 @@ after_initialize do
|
|||
script do |context, fields, automation|
|
||||
sender = Discourse.system_user
|
||||
|
||||
content = fields.dig('message', 'value')
|
||||
url = fields.dig('url', 'value')
|
||||
content = fields.dig("message", "value")
|
||||
url = fields.dig("url", "value")
|
||||
full_content = "#{content} - #{url}"
|
||||
channel_name = fields.dig('channel', 'value')
|
||||
channel = DiscourseChatIntegration::Channel.new(provider: "slack", data: { identifier: "##{channel_name}" })
|
||||
channel_name = fields.dig("channel", "value")
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel.new(
|
||||
provider: "slack",
|
||||
data: {
|
||||
identifier: "##{channel_name}",
|
||||
},
|
||||
)
|
||||
|
||||
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?
|
||||
elsif (
|
||||
url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)
|
||||
).present?
|
||||
"#{Discourse.base_url}#{url}"
|
||||
end
|
||||
|
||||
|
@ -83,7 +91,7 @@ after_initialize do
|
|||
channel: "##{channel_name}",
|
||||
username: slack_username,
|
||||
icon_url: icon_url,
|
||||
attachments: []
|
||||
attachments: [],
|
||||
}
|
||||
|
||||
summary = {
|
||||
|
@ -94,7 +102,7 @@ after_initialize do
|
|||
mrkdwn_in: ["text"],
|
||||
title: content.truncate(100),
|
||||
title_link: url,
|
||||
thumb_url: nil
|
||||
thumb_url: nil,
|
||||
}
|
||||
|
||||
message[:attachments].push(summary)
|
||||
|
|
|
@ -11,9 +11,7 @@ RSpec.shared_context "with dummy provider" do
|
|||
@@raise_exception = nil
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
if @@raise_exception
|
||||
raise @@raise_exception
|
||||
end
|
||||
raise @@raise_exception if @@raise_exception
|
||||
|
||||
@@sent_messages.push(post: post.id, channel: channel)
|
||||
end
|
||||
|
@ -32,9 +30,7 @@ RSpec.shared_context "with dummy provider" do
|
|||
end
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
::DiscourseChatIntegration::Provider.send(:remove_const, :DummyProvider)
|
||||
end
|
||||
after(:each) { ::DiscourseChatIntegration::Provider.send(:remove_const, :DummyProvider) }
|
||||
|
||||
let(:provider) { ::DiscourseChatIntegration::Provider::DummyProvider }
|
||||
end
|
||||
|
@ -44,9 +40,7 @@ RSpec.shared_context "with validated dummy provider" do
|
|||
module ::DiscourseChatIntegration::Provider::Dummy2Provider
|
||||
PROVIDER_NAME = "dummy2".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "val", regex: '^\S+$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "val", regex: '^\S+$', unique: true }]
|
||||
|
||||
@@sent_messages = []
|
||||
|
||||
|
@ -58,10 +52,7 @@ RSpec.shared_context "with validated dummy provider" do
|
|||
@@sent_messages
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
::DiscourseChatIntegration::Provider.send(:remove_const, :Dummy2Provider)
|
||||
end
|
||||
after(:each) { ::DiscourseChatIntegration::Provider.send(:remove_const, :Dummy2Provider) }
|
||||
end
|
||||
|
|
|
@ -1,229 +1,279 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require_relative '../dummy_provider'
|
||||
require "rails_helper"
|
||||
require_relative "../dummy_provider"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Manager do
|
||||
include_context "with dummy provider"
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:tag1) { Fabricate(:tag) }
|
||||
let(:tag2) { Fabricate(:tag) }
|
||||
let(:tag3) { Fabricate(:tag) }
|
||||
|
||||
describe '.process_command' do
|
||||
|
||||
describe 'add new rule' do
|
||||
describe ".process_command" do
|
||||
describe "add new rule" do
|
||||
# Not testing how filters are merged here, that's done in .smart_create_rule
|
||||
# We just want to make sure the commands are being interpretted correctly
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug])
|
||||
it "should add a new rule correctly" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["watch", category.slug])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
it 'should work with all four filter types' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['thread', category.slug])
|
||||
it "should work with all four filter types" do
|
||||
response =
|
||||
DiscourseChatIntegration::Helper.process_command(chan1, ["thread", category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq("thread")
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["watch", category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq("watch")
|
||||
|
||||
response =
|
||||
DiscourseChatIntegration::Helper.process_command(chan1, ["follow", category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq("follow")
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["mute", category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq("mute")
|
||||
end
|
||||
|
||||
it "errors on incorrect categories" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, %w[watch blah])
|
||||
|
||||
expect(response).to eq(
|
||||
I18n.t(
|
||||
"chat_integration.provider.dummy.not_found.category",
|
||||
name: "blah",
|
||||
list: "uncategorized",
|
||||
),
|
||||
)
|
||||
end
|
||||
|
||||
context "with tags enabled" do
|
||||
before { SiteSetting.tagging_enabled = true }
|
||||
|
||||
it "should add a new tag rule correctly" do
|
||||
response =
|
||||
DiscourseChatIntegration::Helper.process_command(chan1, ["watch", "tag:#{tag1.name}"])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq('thread')
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq('watch')
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['follow', category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq('follow')
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['mute', category.slug])
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.filter).to eq('mute')
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(nil)
|
||||
expect(rule.tags).to eq([tag1.name])
|
||||
end
|
||||
|
||||
it 'errors on incorrect categories' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', 'blah'])
|
||||
it "should work with a category and multiple tags" do
|
||||
response =
|
||||
DiscourseChatIntegration::Helper.process_command(
|
||||
chan1,
|
||||
["watch", category.slug, "tag:#{tag1.name}", "tag:#{tag2.name}"],
|
||||
)
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.not_found.category", name: 'blah', list: 'uncategorized'))
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to contain_exactly(tag1.name, tag2.name)
|
||||
end
|
||||
|
||||
context 'with tags enabled' do
|
||||
before do
|
||||
SiteSetting.tagging_enabled = true
|
||||
end
|
||||
|
||||
it 'should add a new tag rule correctly' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', "tag:#{tag1.name}"])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.category_id).to eq(nil)
|
||||
expect(rule.tags).to eq([tag1.name])
|
||||
end
|
||||
|
||||
it 'should work with a category and multiple tags' do
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug, "tag:#{tag1.name}", "tag:#{tag2.name}"])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to contain_exactly(tag1.name, tag2.name)
|
||||
end
|
||||
|
||||
it 'errors on incorrect tags' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug, "tag:blah"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.not_found.tag", name: "blah"))
|
||||
end
|
||||
it "errors on incorrect tags" do
|
||||
response =
|
||||
DiscourseChatIntegration::Helper.process_command(
|
||||
chan1,
|
||||
["watch", category.slug, "tag:blah"],
|
||||
)
|
||||
expect(response).to eq(
|
||||
I18n.t("chat_integration.provider.dummy.not_found.tag", name: "blah"),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remove rule' do
|
||||
it 'removes the rule' do
|
||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
describe "remove rule" do
|
||||
it "removes the rule" do
|
||||
rule1 =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['remove', '1'])
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, %w[remove 1])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.success"))
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.success"))
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(0)
|
||||
end
|
||||
|
||||
it 'errors correctly' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['remove', '1'])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.error"))
|
||||
end
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(0)
|
||||
end
|
||||
|
||||
describe 'help command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["help"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.help"))
|
||||
end
|
||||
it "errors correctly" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, %w[remove 1])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.error"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['status'])
|
||||
expect(response).to eq(DiscourseChatIntegration::Helper.status_for_channel(chan1))
|
||||
end
|
||||
describe "help command" do
|
||||
it "should return the right response" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["help"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.help"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'unknown command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['somerandomtext'])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.parse_error"))
|
||||
end
|
||||
describe "status command" do
|
||||
it "should return the right response" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["status"])
|
||||
expect(response).to eq(DiscourseChatIntegration::Helper.status_for_channel(chan1))
|
||||
end
|
||||
end
|
||||
|
||||
describe "unknown command" do
|
||||
it "should return the right response" do
|
||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["somerandomtext"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.parse_error"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.status_for_channel' do
|
||||
|
||||
context 'with no rules' do
|
||||
it 'includes the heading' do
|
||||
describe ".status_for_channel" do
|
||||
context "with no rules" do
|
||||
it "includes the heading" do
|
||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||
expect(string).to include('dummy.status.header')
|
||||
expect(string).to include("dummy.status.header")
|
||||
end
|
||||
|
||||
it 'includes the no_rules string' do
|
||||
it "includes the no_rules string" do
|
||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||
expect(string).to include('dummy.status.no_rules')
|
||||
expect(string).to include("dummy.status.no_rules")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with some rules' do
|
||||
context "with some rules" do
|
||||
let(:group) { Fabricate(:group) }
|
||||
before do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id, tags: nil)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'mute', category_id: nil, tags: nil)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil, tags: [tag1.name])
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', type: 'group_message', group_id: group.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'watch', category_id: 1, tags: nil)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: nil,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "mute",
|
||||
category_id: nil,
|
||||
tags: nil,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "follow",
|
||||
category_id: nil,
|
||||
tags: [tag1.name],
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
type: "group_message",
|
||||
group_id: group.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
filter: "watch",
|
||||
category_id: 1,
|
||||
tags: nil,
|
||||
)
|
||||
|
||||
SiteSetting.tagging_enabled = false
|
||||
end
|
||||
|
||||
it 'displays the correct rules' do
|
||||
it "displays the correct rules" do
|
||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||
expect(string.scan('status.rule_string').size).to eq(4)
|
||||
expect(string.scan("status.rule_string").size).to eq(4)
|
||||
end
|
||||
|
||||
it 'only displays tags for rules with tags' do
|
||||
it "only displays tags for rules with tags" do
|
||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||
expect(string.scan('rule_string_tags_suffix').size).to eq(0)
|
||||
expect(string.scan("rule_string_tags_suffix").size).to eq(0)
|
||||
|
||||
SiteSetting.tagging_enabled = true
|
||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||
expect(string.scan('rule_string_tags_suffix').size).to eq(1)
|
||||
expect(string.scan("rule_string_tags_suffix").size).to eq(1)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '.delete_by_index' do
|
||||
describe ".delete_by_index" do
|
||||
let(:category2) { Fabricate(:category) }
|
||||
let(:category3) { Fabricate(:category) }
|
||||
|
||||
it 'deletes the correct rule' do
|
||||
it "deletes the correct rule" do
|
||||
# Three identical rules, with different filters
|
||||
# Status will be sorted by precedence
|
||||
# be in this order
|
||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'mute',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category2.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
rule3 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'follow',
|
||||
category_id: category3.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
rule1 =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
rule2 =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category2.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
rule3 =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "follow",
|
||||
category_id: category3.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(3)
|
||||
|
||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 2)).to eq(:deleted)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(2)
|
||||
expect(DiscourseChatIntegration::Rule.all.map(&:category_id)).to contain_exactly(category.id, category3.id)
|
||||
expect(DiscourseChatIntegration::Rule.all.map(&:category_id)).to contain_exactly(
|
||||
category.id,
|
||||
category3.id,
|
||||
)
|
||||
end
|
||||
|
||||
it 'fails gracefully for out of range indexes' do
|
||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
it "fails gracefully for out of range indexes" do
|
||||
rule1 =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, -1)).to eq(false)
|
||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 0)).to eq(false)
|
||||
|
@ -231,87 +281,100 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
|
||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 1)).to eq(:deleted)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '.smart_create_rule' do
|
||||
|
||||
it 'creates a rule when there are none' do
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name]
|
||||
)
|
||||
describe ".smart_create_rule" do
|
||||
it "creates a rule when there are none" do
|
||||
val =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name],
|
||||
)
|
||||
expect(val).to eq(:created)
|
||||
|
||||
record = DiscourseChatIntegration::Rule.all.first
|
||||
expect(record.channel).to eq(chan1)
|
||||
expect(record.filter).to eq('watch')
|
||||
expect(record.filter).to eq("watch")
|
||||
expect(record.category_id).to eq(category.id)
|
||||
expect(record.tags).to eq([tag1.name])
|
||||
end
|
||||
|
||||
it 'updates a rule when it has the same category and tags' do
|
||||
existing = DiscourseChatIntegration::Rule.create!(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag2.name, tag1.name]
|
||||
)
|
||||
it "updates a rule when it has the same category and tags" do
|
||||
existing =
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag2.name, tag1.name],
|
||||
)
|
||||
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
||||
filter: 'mute',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
val =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: chan1,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
|
||||
expect(val).to eq(:updated)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.all.first.filter).to eq('mute')
|
||||
expect(DiscourseChatIntegration::Rule.all.first.filter).to eq("mute")
|
||||
end
|
||||
|
||||
it 'updates a rule when it has the same category and filter' do
|
||||
existing = DiscourseChatIntegration::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
it "updates a rule when it has the same category and filter" do
|
||||
existing =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag3.name]
|
||||
)
|
||||
val =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag3.name],
|
||||
)
|
||||
|
||||
expect(val).to eq(:updated)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.all.first.tags).to contain_exactly(tag1.name, tag2.name, tag3.name)
|
||||
expect(DiscourseChatIntegration::Rule.all.first.tags).to contain_exactly(
|
||||
tag1.name,
|
||||
tag2.name,
|
||||
tag3.name,
|
||||
)
|
||||
end
|
||||
|
||||
it 'destroys duplicate rules on save' do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch')
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch')
|
||||
it "destroys duplicate rules on save" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch")
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch")
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(2)
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: nil,
|
||||
tags: nil
|
||||
)
|
||||
val =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: nil,
|
||||
tags: nil,
|
||||
)
|
||||
expect(val).to eq(:updated)
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||
end
|
||||
|
||||
it 'returns false on error' do
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1, filter: 'blah')
|
||||
it "returns false on error" do
|
||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1, filter: "blah")
|
||||
|
||||
expect(val).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.save_transcript' do
|
||||
|
||||
it 'saves a transcript to redis' do
|
||||
describe ".save_transcript" do
|
||||
it "saves a transcript to redis" do
|
||||
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
||||
|
||||
expect(Discourse.redis.get("chat_integration:transcript:#{key}")).to eq("Some content here")
|
||||
|
@ -325,19 +388,25 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
describe ".formatted_display_name" do
|
||||
let(:user) { Fabricate(:user, name: "John Smith", username: 'js1') }
|
||||
let(:user) { Fabricate(:user, name: "John Smith", username: "js1") }
|
||||
|
||||
it "prioritizes correctly" do
|
||||
SiteSetting.prioritize_username_in_ux = true
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("@#{user.username} (John Smith)")
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq(
|
||||
"@#{user.username} (John Smith)",
|
||||
)
|
||||
SiteSetting.prioritize_username_in_ux = false
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("John Smith (@#{user.username})")
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq(
|
||||
"John Smith (@#{user.username})",
|
||||
)
|
||||
end
|
||||
|
||||
it "only displays one when name/username are similar" do
|
||||
user.update!(username: "john_smith")
|
||||
SiteSetting.prioritize_username_in_ux = true
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("@#{user.username}")
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq(
|
||||
"@#{user.username}",
|
||||
)
|
||||
SiteSetting.prioritize_username_in_ux = false
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("John Smith")
|
||||
end
|
||||
|
@ -346,10 +415,13 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
SiteSetting.enable_names = false
|
||||
|
||||
SiteSetting.prioritize_username_in_ux = true
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("@#{user.username}")
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq(
|
||||
"@#{user.username}",
|
||||
)
|
||||
SiteSetting.prioritize_username_in_ux = false
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("@#{user.username}")
|
||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq(
|
||||
"@#{user.username}",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,42 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
||||
let(:category) { Fabricate(:category) }
|
||||
|
||||
describe 'site settings' do
|
||||
describe "site settings" do
|
||||
before do
|
||||
PluginStoreRow.create!(
|
||||
plugin_name: 'discourse-slack-official',
|
||||
plugin_name: "discourse-slack-official",
|
||||
key: "category_#{category.id}",
|
||||
type_name: "JSON",
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]"
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]",
|
||||
)
|
||||
|
||||
SiteSetting.create!(value: 't', data_type: 5, name: 'slack_enabled')
|
||||
SiteSetting.create!(value: 'token', data_type: 1, name: 'slack_access_token')
|
||||
SiteSetting.create!(value: 'token2', data_type: 1, name: 'slack_incoming_webhook_token')
|
||||
SiteSetting.create!(value: 300, data_type: 3, name: 'slack_discourse_excerpt_length')
|
||||
SiteSetting.create!(value: "https://hooks.slack.com/services/something", data_type: 1, name: 'slack_outbound_webhook_url')
|
||||
SiteSetting.create!(value: "http://outbound2.com", data_type: 1, name: 'slack_icon_url')
|
||||
SiteSetting.create!(value: 100, data_type: 3, name: 'post_to_slack_window_secs')
|
||||
SiteSetting.create!(value: User.last.username, data_type: 1, name: 'slack_discourse_username')
|
||||
SiteSetting.create!(value: "t", data_type: 5, name: "slack_enabled")
|
||||
SiteSetting.create!(value: "token", data_type: 1, name: "slack_access_token")
|
||||
SiteSetting.create!(value: "token2", data_type: 1, name: "slack_incoming_webhook_token")
|
||||
SiteSetting.create!(value: 300, data_type: 3, name: "slack_discourse_excerpt_length")
|
||||
SiteSetting.create!(
|
||||
value: "https://hooks.slack.com/services/something",
|
||||
data_type: 1,
|
||||
name: "slack_outbound_webhook_url",
|
||||
)
|
||||
SiteSetting.create!(value: "http://outbound2.com", data_type: 1, name: "slack_icon_url")
|
||||
SiteSetting.create!(value: 100, data_type: 3, name: "post_to_slack_window_secs")
|
||||
SiteSetting.create!(value: User.last.username, data_type: 1, name: "slack_discourse_username")
|
||||
end
|
||||
|
||||
it 'should migrate the site settings correctly' do
|
||||
it "should migrate the site settings correctly" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
expect(SiteSetting.find_by(name: 'slack_enabled').value).to eq('f')
|
||||
expect(SiteSetting.chat_integration_slack_access_token).to eq('token')
|
||||
expect(SiteSetting.chat_integration_slack_incoming_webhook_token).to eq('token2')
|
||||
expect(SiteSetting.find_by(name: "slack_enabled").value).to eq("f")
|
||||
expect(SiteSetting.chat_integration_slack_access_token).to eq("token")
|
||||
expect(SiteSetting.chat_integration_slack_incoming_webhook_token).to eq("token2")
|
||||
expect(SiteSetting.chat_integration_slack_excerpt_length).to eq(300)
|
||||
|
||||
expect(SiteSetting.chat_integration_slack_outbound_webhook_url)
|
||||
.to eq("https://hooks.slack.com/services/something")
|
||||
expect(SiteSetting.chat_integration_slack_outbound_webhook_url).to eq(
|
||||
"https://hooks.slack.com/services/something",
|
||||
)
|
||||
|
||||
expect(SiteSetting.chat_integration_slack_icon_url)
|
||||
.to eq("http://outbound2.com")
|
||||
expect(SiteSetting.chat_integration_slack_icon_url).to eq("http://outbound2.com")
|
||||
|
||||
expect(SiteSetting.chat_integration_delay_seconds).to eq(100)
|
||||
expect(SiteSetting.chat_integration_discourse_username).to eq(User.last.username)
|
||||
|
@ -44,31 +48,31 @@ RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
|||
expect(SiteSetting.chat_integration_enabled).to eq(true)
|
||||
end
|
||||
|
||||
describe 'when slack_discourse_username is not valid' do
|
||||
before do
|
||||
SiteSetting.find_by(name: 'slack_discourse_username').update!(value: 'someguy')
|
||||
end
|
||||
describe "when slack_discourse_username is not valid" do
|
||||
before { SiteSetting.find_by(name: "slack_discourse_username").update!(value: "someguy") }
|
||||
|
||||
it 'should default to the system user' do
|
||||
it "should default to the system user" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
expect(SiteSetting.chat_integration_discourse_username)
|
||||
.to eq(Discourse.system_user.username)
|
||||
expect(SiteSetting.chat_integration_discourse_username).to eq(
|
||||
Discourse.system_user.username,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a uncategorized filter is present' do
|
||||
describe "when a uncategorized filter is present" do
|
||||
before do
|
||||
PluginStoreRow.create!(
|
||||
plugin_name: 'discourse-slack-official',
|
||||
plugin_name: "discourse-slack-official",
|
||||
key: "category_*",
|
||||
type_name: "JSON",
|
||||
value: "[{\"channel\":\"#channel1\",\"filter\":\"watch\"},{\"channel\":\"channel2\",\"filter\":\"follow\"},{\"channel\":\"#channel1\",\"filter\":\"mute\"}]"
|
||||
value:
|
||||
"[{\"channel\":\"#channel1\",\"filter\":\"watch\"},{\"channel\":\"channel2\",\"filter\":\"follow\"},{\"channel\":\"#channel1\",\"filter\":\"mute\"}]",
|
||||
)
|
||||
end
|
||||
|
||||
it 'should create the right channels and rules' do
|
||||
it "should create the right channels and rules" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.count).to eq(2)
|
||||
|
@ -76,86 +80,87 @@ RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
|||
|
||||
channel = DiscourseChatIntegration::Channel.first
|
||||
|
||||
expect(channel.value['provider']).to eq("slack")
|
||||
expect(channel.value['data']['identifier']).to eq("#channel1")
|
||||
expect(channel.value["provider"]).to eq("slack")
|
||||
expect(channel.value["data"]["identifier"]).to eq("#channel1")
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.first
|
||||
|
||||
expect(rule.value['channel_id']).to eq(channel.id)
|
||||
expect(rule.value['filter']).to eq('mute')
|
||||
expect(rule.value['category_id']).to eq(nil)
|
||||
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||
expect(rule.value["filter"]).to eq("mute")
|
||||
expect(rule.value["category_id"]).to eq(nil)
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.last
|
||||
|
||||
expect(channel.value['provider']).to eq("slack")
|
||||
expect(channel.value['data']['identifier']).to eq("#channel2")
|
||||
expect(channel.value["provider"]).to eq("slack")
|
||||
expect(channel.value["data"]["identifier"]).to eq("#channel2")
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.last
|
||||
|
||||
expect(rule.value['channel_id']).to eq(channel.id)
|
||||
expect(rule.value['filter']).to eq('follow')
|
||||
expect(rule.value['category_id']).to eq(nil)
|
||||
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||
expect(rule.value["filter"]).to eq("follow")
|
||||
expect(rule.value["category_id"]).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when filter contains an invalid tag' do
|
||||
describe "when filter contains an invalid tag" do
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
|
||||
before do
|
||||
PluginStoreRow.create!(
|
||||
plugin_name: 'discourse-slack-official',
|
||||
plugin_name: "discourse-slack-official",
|
||||
key: "category_#{category.id}",
|
||||
type_name: "JSON",
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\",\"tags\":[\"#{tag.name}\",\"random-tag\"]}]"
|
||||
value:
|
||||
"[{\"channel\":\"#slack-channel\",\"filter\":\"mute\",\"tags\":[\"#{tag.name}\",\"random-tag\"]}]",
|
||||
)
|
||||
end
|
||||
|
||||
it 'should discard invalid tags' do
|
||||
it "should discard invalid tags" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.first
|
||||
|
||||
expect(rule.value['tags']).to eq([tag.name])
|
||||
expect(rule.value["tags"]).to eq([tag.name])
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a category filter is present' do
|
||||
describe "when a category filter is present" do
|
||||
before do
|
||||
PluginStoreRow.create!(
|
||||
plugin_name: 'discourse-slack-official',
|
||||
plugin_name: "discourse-slack-official",
|
||||
key: "category_#{category.id}",
|
||||
type_name: "JSON",
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]"
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]",
|
||||
)
|
||||
end
|
||||
|
||||
it 'should migrate the settings correctly' do
|
||||
it "should migrate the settings correctly" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.first
|
||||
|
||||
expect(channel.value['provider']).to eq("slack")
|
||||
expect(channel.value['data']['identifier']).to eq("#slack-channel")
|
||||
expect(channel.value["provider"]).to eq("slack")
|
||||
expect(channel.value["data"]["identifier"]).to eq("#slack-channel")
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.first
|
||||
|
||||
expect(rule.value['channel_id']).to eq(channel.id)
|
||||
expect(rule.value['filter']).to eq('mute')
|
||||
expect(rule.value['category_id']).to eq(category.id)
|
||||
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||
expect(rule.value["filter"]).to eq("mute")
|
||||
expect(rule.value["category_id"]).to eq(category.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when a category has been deleted' do
|
||||
describe "when a category has been deleted" do
|
||||
before do
|
||||
PluginStoreRow.create!(
|
||||
plugin_name: 'discourse-slack-official',
|
||||
key: 'category_9999',
|
||||
plugin_name: "discourse-slack-official",
|
||||
key: "category_9999",
|
||||
type_name: "JSON",
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]"
|
||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]",
|
||||
)
|
||||
end
|
||||
|
||||
it 'should not migrate the settings' do
|
||||
it "should not migrate the settings" do
|
||||
described_class.new.execute_onceoff({})
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.count).to eq(0)
|
||||
|
|
|
@ -1,48 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe PostCreator do
|
||||
let(:topic) { Fabricate(:post).topic }
|
||||
|
||||
before do
|
||||
Jobs::NotifyChats.jobs.clear
|
||||
end
|
||||
before { Jobs::NotifyChats.jobs.clear }
|
||||
|
||||
describe 'when a post is created' do
|
||||
describe 'when plugin is enabled' do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
end
|
||||
describe "when a post is created" do
|
||||
describe "when plugin is enabled" do
|
||||
before { SiteSetting.chat_integration_enabled = true }
|
||||
|
||||
it 'should schedule a chat notification job' do
|
||||
it "should schedule a chat notification job" do
|
||||
freeze_time Time.now.beginning_of_day
|
||||
|
||||
post = PostCreator.new(topic.user,
|
||||
raw: 'Some post content',
|
||||
topic_id: topic.id
|
||||
).create!
|
||||
post = PostCreator.new(topic.user, raw: "Some post content", topic_id: topic.id).create!
|
||||
|
||||
job = Jobs::NotifyChats.jobs.last
|
||||
|
||||
expect(job['at'])
|
||||
.to eq(Time.now.to_f + SiteSetting.chat_integration_delay_seconds.seconds.to_f)
|
||||
|
||||
expect(job['args'].first['post_id']).to eq(post.id)
|
||||
expect(job["at"]).to eq(
|
||||
Time.now.to_f + SiteSetting.chat_integration_delay_seconds.seconds.to_f,
|
||||
)
|
||||
|
||||
expect(job["args"].first["post_id"]).to eq(post.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when plugin is not enabled' do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = false
|
||||
end
|
||||
describe "when plugin is not enabled" do
|
||||
before { SiteSetting.chat_integration_enabled = false }
|
||||
|
||||
it 'should not schedule a job for chat notifications' do
|
||||
PostCreator.new(topic.user,
|
||||
raw: 'Some post content',
|
||||
topic_id: topic.id
|
||||
).create!
|
||||
it "should not schedule a job for chat notifications" do
|
||||
PostCreator.new(topic.user, raw: "Some post content", topic_id: topic.id).create!
|
||||
|
||||
expect(Jobs::NotifyChats.jobs).to eq([])
|
||||
end
|
||||
|
|
|
@ -1,38 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::DiscordProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_discord_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_discord_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "discord",
|
||||
data: {
|
||||
name: "Awesome Channel",
|
||||
webhook_url: "https://discord.com/api/webhooks/1234/abcd",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'discord', data: { name: "Awesome Channel", webhook_url: 'https://discord.com/api/webhooks/1234/abcd' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://discord.com/api/webhooks/1234/abcd?wait=true').to_return(status: 200)
|
||||
it "sends a webhook request" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").to_return(
|
||||
status: 200,
|
||||
)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'includes the protocol in the avatar URL' do
|
||||
stub1 = stub_request(:post, 'https://discord.com/api/webhooks/1234/abcd?wait=true')
|
||||
.with(body: hash_including(embeds: [hash_including(author: hash_including(url: /^https?:\/\//))]))
|
||||
.to_return(status: 200)
|
||||
it "includes the protocol in the avatar URL" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").with(
|
||||
body:
|
||||
hash_including(embeds: [hash_including(author: hash_including(url: %r{^https?://}))]),
|
||||
).to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").to_return(status: 400)
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").to_return(
|
||||
status: 400,
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::FlowdockProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_flowdock_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_flowdock_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "flowdock",
|
||||
data: {
|
||||
flow_token: "5d1fe04cf66e078d6a2b579ddb8a465b",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'flowdock', data: { flow_token: '5d1fe04cf66e078d6a2b579ddb8a465b' }) }
|
||||
|
||||
it 'sends a request' do
|
||||
it "sends a request" do
|
||||
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://api.flowdock.com/messages").to_return(
|
||||
status: 404,
|
||||
body: "{ \"error\": \"Not Found\"}",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,27 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::GitterProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_gitter_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_gitter_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "gitter",
|
||||
data: {
|
||||
name: "gitterHQ/services",
|
||||
webhook_url: "https://webhooks.gitter.im/e/a1e2i3o4u5",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'gitter', data: { name: 'gitterHQ/services', webhook_url: 'https://webhooks.gitter.im/e/a1e2i3o4u5' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(body: "OK")
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(body: "OK")
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, chan1.data["webhook_url"]).to_return(
|
||||
status: 404,
|
||||
body: "{ \"error\": \"Not Found\"}",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::GoogleProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_google_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_google_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "google",
|
||||
data: {
|
||||
name: "discourse",
|
||||
webhook_url: "https://chat.googleapis.com/v1/abcdefg",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'google', data: { name: 'discourse', webhook_url: 'https://chat.googleapis.com/v1/abcdefg' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(body: "1")
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(body: "1")
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
||||
it "handles errors correctly" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,28 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::GroupmeProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_groupme_enabled = true
|
||||
SiteSetting.chat_integration_groupme_bot_ids = '1a2b3c4d5e6f7g'
|
||||
SiteSetting.chat_integration_groupme_bot_ids = "1a2b3c4d5e6f7g"
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'groupme', data: { groupme_bot_id: '1a2b3c4d5e6f7g' }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "groupme",
|
||||
data: {
|
||||
groupme_bot_id: "1a2b3c4d5e6f7g",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends a request' do
|
||||
it "sends a request" do
|
||||
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(
|
||||
status: 404,
|
||||
body: "{ \"error\": \"Not Found\"}",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::GuildedProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_guilded_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_guilded_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "guilded",
|
||||
data: {
|
||||
name: "Awesome Channel",
|
||||
webhook_url: "https://media.guilded.gg/webhooks/1234/abcd",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'guilded', data: { name: "Awesome Channel", webhook_url: 'https://media.guilded.gg/webhooks/1234/abcd' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://media.guilded.gg/webhooks/1234/abcd').to_return(status: 200)
|
||||
it "sends a webhook request" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 400)
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 400)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,31 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::MatrixProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_matrix_enabled = true
|
||||
SiteSetting.chat_integration_matrix_access_token = 'abcd'
|
||||
SiteSetting.chat_integration_matrix_access_token = "abcd"
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'matrix', data: { name: "Awesome Channel", room_id: '!blah:matrix.org' }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "matrix",
|
||||
data: {
|
||||
name: "Awesome Channel",
|
||||
room_id: "!blah:matrix.org",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends the message' do
|
||||
stub1 = stub_request(:put, %r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*}).to_return(status: 200)
|
||||
it "sends the message" do
|
||||
stub1 =
|
||||
stub_request(
|
||||
:put,
|
||||
%r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*},
|
||||
).to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:put, %r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*}).to_return(status: 400, body: '{"errmsg":"M_UNKNOWN"}')
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(
|
||||
:put,
|
||||
%r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*},
|
||||
).to_return(status: 400, body: '{"errmsg":"M_UNKNOWN"}')
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,88 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
describe 'Mattermost Command Controller', type: :request do
|
||||
describe "Mattermost Command Controller", type: :request do
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
let(:tag2) { Fabricate(:tag) }
|
||||
let!(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'mattermost', data: { identifier: '#welcome' }) }
|
||||
let!(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "mattermost",
|
||||
data: {
|
||||
identifier: "#welcome",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
describe 'with plugin disabled' do
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/mattermost/command.json'
|
||||
describe "with plugin disabled" do
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/mattermost/command.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with plugin enabled and provider disabled' do
|
||||
describe "with plugin enabled and provider disabled" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_mattermost_enabled = false
|
||||
end
|
||||
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/mattermost/command.json'
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/mattermost/command.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'slash commands endpoint' do
|
||||
describe "slash commands endpoint" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_mattermost_webhook_url = "https://hooks.mattermost.com/services/abcde"
|
||||
SiteSetting.chat_integration_mattermost_webhook_url =
|
||||
"https://hooks.mattermost.com/services/abcde"
|
||||
SiteSetting.chat_integration_mattermost_enabled = true
|
||||
end
|
||||
|
||||
describe 'when forum is private' do
|
||||
it 'should not redirect to login page' do
|
||||
describe "when forum is private" do
|
||||
it "should not redirect to login page" do
|
||||
SiteSetting.login_required = true
|
||||
token = 'sometoken'
|
||||
token = "sometoken"
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token = token
|
||||
|
||||
post '/chat-integration/mattermost/command.json', params: {
|
||||
text: 'help', token: token
|
||||
}
|
||||
post "/chat-integration/mattermost/command.json", params: { text: "help", token: token }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the token is invalid' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/mattermost/command.json', params: { text: 'help' }
|
||||
describe "when the token is invalid" do
|
||||
it "should raise the right error" do
|
||||
post "/chat-integration/mattermost/command.json", params: { text: "help" }
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when incoming webhook token has not been set' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/mattermost/command.json', params: {
|
||||
text: 'help', token: 'some token'
|
||||
}
|
||||
describe "when incoming webhook token has not been set" do
|
||||
it "should raise the right error" do
|
||||
post "/chat-integration/mattermost/command.json",
|
||||
params: {
|
||||
text: "help",
|
||||
token: "some token",
|
||||
}
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token is valid' do
|
||||
describe "when token is valid" do
|
||||
let(:token) { "Secret Sauce" }
|
||||
|
||||
# No need to test every single command here, that's tested
|
||||
# by helper_spec upstream
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token = token
|
||||
end
|
||||
before { SiteSetting.chat_integration_mattermost_incoming_webhook_token = token }
|
||||
|
||||
describe 'add new rule' do
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
post "/chat-integration/mattermost/command.json", params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
}
|
||||
describe "add new rule" do
|
||||
it "should add a new rule correctly" do
|
||||
post "/chat-integration/mattermost/command.json",
|
||||
params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: "welcome",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
|
@ -90,34 +96,40 @@ describe 'Mattermost Command Controller', type: :request do
|
|||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
describe 'from an unknown channel' do
|
||||
it 'creates the channel' do
|
||||
post "/chat-integration/mattermost/command.json", params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: 'general',
|
||||
token: token
|
||||
}
|
||||
describe "from an unknown channel" do
|
||||
it "creates the channel" do
|
||||
post "/chat-integration/mattermost/command.json",
|
||||
params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: "general",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.mattermost.create.created"))
|
||||
expect(json["text"]).to eq(
|
||||
I18n.t("chat_integration.provider.mattermost.create.created"),
|
||||
)
|
||||
|
||||
chan = DiscourseChatIntegration::Channel.with_provider('mattermost').with_data_value('identifier', '#general').first
|
||||
expect(chan.provider).to eq('mattermost')
|
||||
chan =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider("mattermost")
|
||||
.with_data_value("identifier", "#general")
|
||||
.first
|
||||
expect(chan.provider).to eq("mattermost")
|
||||
|
||||
rule = chan.rules.first
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::MattermostProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
let(:upload) { Fabricate(:upload) }
|
||||
|
||||
before do
|
||||
|
@ -14,36 +14,47 @@ RSpec.describe DiscourseChatIntegration::Provider::MattermostProvider do
|
|||
SiteSetting.logo_small = upload
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'mattermost', data: { identifier: "#awesomechannel" }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "mattermost",
|
||||
data: {
|
||||
identifier: "#awesomechannel",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://mattermost.blah/hook/abcd').to_return(status: 200)
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
describe 'when mattermost icon is not configured' do
|
||||
it 'defaults to the right icon' do
|
||||
describe "when mattermost icon is not configured" do
|
||||
it "defaults to the right icon" do
|
||||
message = described_class.mattermost_message(post, chan1)
|
||||
expect(message[:icon_url]).to eq(UrlHelper.absolute(upload.url))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when mattermost icon has been configured' do
|
||||
it 'should use the right icon' do
|
||||
describe "when mattermost icon has been configured" do
|
||||
it "should use the right icon" do
|
||||
SiteSetting.chat_integration_mattermost_icon_url = "https://specific_logo"
|
||||
message = described_class.mattermost_message(post, chan1)
|
||||
expect(message[:icon_url]).to eq(SiteSetting.chat_integration_mattermost_icon_url)
|
||||
end
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(status: 500, body: "error")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(
|
||||
status: 500,
|
||||
body: "error",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,31 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::RocketchatProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_rocketchat_enabled = true
|
||||
SiteSetting.chat_integration_rocketchat_webhook_url = "https://example.com/abcd"
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'rocketchat', data: { identifier: "#general" }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "rocketchat",
|
||||
data: {
|
||||
identifier: "#general",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(body: "{\"success\":true}")
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, "https://example.com/abcd").to_return(body: "{\"success\":true}")
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(status: 400, body: "{}")
|
||||
it "handles errors correctly" do
|
||||
stub1 = stub_request(:post, "https://example.com/abcd").to_return(status: 400, body: "{}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,106 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
describe 'Slack Command Controller', type: :request do
|
||||
before do
|
||||
Discourse.cache.clear
|
||||
end
|
||||
describe "Slack Command Controller", type: :request do
|
||||
before { Discourse.cache.clear }
|
||||
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
let(:tag2) { Fabricate(:tag) }
|
||||
let!(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'slack', data: { identifier: '#welcome' }) }
|
||||
let!(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(provider: "slack", data: { identifier: "#welcome" })
|
||||
end
|
||||
|
||||
describe 'with plugin disabled' do
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/slack/command.json'
|
||||
describe "with plugin disabled" do
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/slack/command.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with plugin enabled and provider disabled' do
|
||||
describe "with plugin enabled and provider disabled" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_slack_enabled = false
|
||||
end
|
||||
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/slack/command.json'
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/slack/command.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'slash commands endpoint' do
|
||||
describe "slash commands endpoint" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url = "https://hooks.slack.com/services/abcde"
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url =
|
||||
"https://hooks.slack.com/services/abcde"
|
||||
SiteSetting.chat_integration_slack_enabled = true
|
||||
end
|
||||
|
||||
describe 'when forum is private' do
|
||||
it 'should not redirect to login page' do
|
||||
describe "when forum is private" do
|
||||
it "should not redirect to login page" do
|
||||
SiteSetting.login_required = true
|
||||
token = 'sometoken'
|
||||
token = "sometoken"
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||
|
||||
post '/chat-integration/slack/command.json', params: {
|
||||
text: 'help', token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json", params: { text: "help", token: token }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the token is invalid' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/slack/command.json', params: { text: 'help' }
|
||||
describe "when the token is invalid" do
|
||||
it "should raise the right error" do
|
||||
post "/chat-integration/slack/command.json", params: { text: "help" }
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'backwards compatibility with discourse-slack-official' do
|
||||
it 'should return the right response' do
|
||||
token = 'secret sauce'
|
||||
describe "backwards compatibility with discourse-slack-official" do
|
||||
it "should return the right response" do
|
||||
token = "secret sauce"
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||
|
||||
post '/slack/command.json', params: {
|
||||
text: 'help', token: token
|
||||
}
|
||||
post "/slack/command.json", params: { text: "help", token: token }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["text"]).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when incoming webhook token has not been set' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/slack/command.json', params: {
|
||||
text: 'help', token: 'some token'
|
||||
}
|
||||
describe "when incoming webhook token has not been set" do
|
||||
it "should raise the right error" do
|
||||
post "/chat-integration/slack/command.json", params: { text: "help", token: "some token" }
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token is valid' do
|
||||
describe "when token is valid" do
|
||||
let(:token) { "Secret Sauce" }
|
||||
|
||||
# No need to test every single command here, that's tested
|
||||
# by helper_spec upstream
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||
end
|
||||
before { SiteSetting.chat_integration_slack_incoming_webhook_token = token }
|
||||
|
||||
describe 'add new rule' do
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
}
|
||||
describe "add new rule" do
|
||||
it "should add a new rule correctly" do
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: "welcome",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
|
@ -108,277 +101,314 @@ describe 'Slack Command Controller', type: :request do
|
|||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
describe 'from an unknown channel' do
|
||||
it 'creates the channel' do
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: 'general',
|
||||
token: token
|
||||
}
|
||||
describe "from an unknown channel" do
|
||||
it "creates the channel" do
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: "general",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.create.created"))
|
||||
|
||||
chan = DiscourseChatIntegration::Channel.with_provider('slack').with_data_value('identifier', '#general').first
|
||||
expect(chan.provider).to eq('slack')
|
||||
chan =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider("slack")
|
||||
.with_data_value("identifier", "#general")
|
||||
.first
|
||||
expect(chan.provider).to eq("slack")
|
||||
|
||||
rule = chan.rules.first
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'post transcript' do
|
||||
let(:messages_fixture) {
|
||||
describe "post transcript" do
|
||||
let(:messages_fixture) do
|
||||
[
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6JSSESES",
|
||||
"text": "Yeah, should make posting slack transcripts much easier",
|
||||
"ts": "1501801665.062694"
|
||||
type: "message",
|
||||
user: "U6JSSESES",
|
||||
text: "Yeah, should make posting slack transcripts much easier",
|
||||
ts: "1501801665.062694",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U5Z773QLS",
|
||||
"text": "Oooh a new discourse plugin???",
|
||||
"ts": "1501801643.056375"
|
||||
type: "message",
|
||||
user: "U5Z773QLS",
|
||||
text: "Oooh a new discourse plugin???",
|
||||
ts: "1501801643.056375",
|
||||
},
|
||||
{ type: "message", user: "U6E2W7R8C", text: "Which one?", ts: "1501801634.053761" },
|
||||
{
|
||||
type: "message",
|
||||
user: "U6JSSESES",
|
||||
text:
|
||||
"So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
||||
ts: "1501801629.052212",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6E2W7R8C",
|
||||
"text": "Which one?",
|
||||
"ts": "1501801634.053761"
|
||||
text: "",
|
||||
username: "Test Community",
|
||||
bot_id: "B6C6JNUDN",
|
||||
attachments: [
|
||||
{
|
||||
author_name: "@david",
|
||||
fallback: "Discourse can now be integrated with Mattermost! - @david",
|
||||
text: "Hey <http://localhost/groups/team|@team>, what do you think about this?",
|
||||
title: "Discourse can now be integrated with Mattermost! [Announcements] ",
|
||||
id: 1,
|
||||
title_link:
|
||||
"http://localhost:3000/t/discourse-can-now-be-integrated-with-mattermost/51/4",
|
||||
color: "283890",
|
||||
mrkdwn_in: ["text"],
|
||||
},
|
||||
],
|
||||
type: "message",
|
||||
subtype: "bot_message",
|
||||
ts: "1501615820.949638",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6JSSESES",
|
||||
"text": "So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
||||
"ts": "1501801629.052212"
|
||||
type: "message",
|
||||
user: "U5Z773QLS",
|
||||
text: "Let’s try some *bold text*",
|
||||
ts: "1501093331.439776",
|
||||
},
|
||||
{
|
||||
"text": "",
|
||||
"username": "Test Community",
|
||||
"bot_id": "B6C6JNUDN",
|
||||
"attachments": [
|
||||
{
|
||||
"author_name": "@david",
|
||||
"fallback": "Discourse can now be integrated with Mattermost! - @david",
|
||||
"text": "Hey <http://localhost/groups/team|@team>, what do you think about this?",
|
||||
"title": "Discourse can now be integrated with Mattermost! [Announcements] ",
|
||||
"id": 1,
|
||||
"title_link": "http://localhost:3000/t/discourse-can-now-be-integrated-with-mattermost/51/4",
|
||||
"color": "283890",
|
||||
"mrkdwn_in": [
|
||||
"text"
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "message",
|
||||
"subtype": "bot_message",
|
||||
"ts": "1501615820.949638"
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U5Z773QLS",
|
||||
"text": "Let’s try some *bold text*",
|
||||
"ts": "1501093331.439776"
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_access_token = 'abcde'
|
||||
end
|
||||
|
||||
before { SiteSetting.chat_integration_slack_access_token = "abcde" }
|
||||
|
||||
context "with valid slack responses" do
|
||||
before do
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(body: '{"ok":true,"members":[{"id":"U5Z773QLS","profile":{"display_name":"david","real_name":"david","icon_24":"https://example.com/avatar"}}],"response_metadata":{"next_cursor":""}}')
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(
|
||||
body:
|
||||
'{"ok":true,"members":[{"id":"U5Z773QLS","profile":{"display_name":"david","real_name":"david","icon_24":"https://example.com/avatar"}}],"response_metadata":{"next_cursor":""}}',
|
||||
)
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").to_return(
|
||||
body: { ok: true, messages: messages_fixture }.to_json,
|
||||
)
|
||||
end
|
||||
|
||||
it 'generates the transcript UI properly' do
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.with(body: /attachments/)
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
it "generates the transcript UI properly" do
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||
body: /attachments/,
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "post",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'can select by url' do
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.with(body: /1501801629\.052212/)
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
it "can select by url" do
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||
body: /1501801629\.052212/,
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text:
|
||||
"post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'can select by url with thread parameter' do
|
||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
||||
.with(body: /1501801629\.052212/)
|
||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
it "can select by url with thread parameter" do
|
||||
replies_stub =
|
||||
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||
body: /1501801629\.052212/,
|
||||
).to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").to_return(
|
||||
body: { ok: true }.to_json,
|
||||
)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501201669054212?thread_ts=1501801629.052212",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text:
|
||||
"post https://sometestslack.slack.com/archives/C6029G78F/p1501201669054212?thread_ts=1501801629.052212",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
expect(replies_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'can select by thread' do
|
||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
||||
.with(body: /1501801629\.052212/)
|
||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
it "can select by thread" do
|
||||
replies_stub =
|
||||
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||
body: /1501801629\.052212/,
|
||||
).to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").to_return(
|
||||
body: { ok: true }.to_json,
|
||||
)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post thread https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text:
|
||||
"post thread https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
expect(replies_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'can select by count' do
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.with(body: /1501801629\.052212/)
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
it "can select by count" do
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||
body: /1501801629\.052212/,
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post 4",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "post 4",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'can auto select' do
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.with(body: /1501615820\.949638/)
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
it "can auto select" do
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||
body: /1501615820\.949638/,
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "post",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it "supports using shortcuts to create a thread transcript" do
|
||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
||||
.with(body: /1501801629\.052212/)
|
||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
replies_stub =
|
||||
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||
body: /1501801629\.052212/,
|
||||
).to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||
|
||||
view_open_stub = stub_request(:post, "https://slack.com/api/views.open")
|
||||
.with(body: /TRIGGERID/)
|
||||
.to_return(body: { ok: true, view: { id: "VIEWID" } }.to_json)
|
||||
view_open_stub =
|
||||
stub_request(:post, "https://slack.com/api/views.open").with(
|
||||
body: /TRIGGERID/,
|
||||
).to_return(body: { ok: true, view: { id: "VIEWID" } }.to_json)
|
||||
|
||||
view_update_stub = stub_request(:post, "https://slack.com/api/views.update")
|
||||
.with(body: /VIEWID/)
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
view_update_stub =
|
||||
stub_request(:post, "https://slack.com/api/views.update").with(
|
||||
body: /VIEWID/,
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
post "/chat-integration/slack/interactive.json", params: {
|
||||
payload: {
|
||||
type: "message_action",
|
||||
channel: { name: 'general', id: 'C6029G78F' },
|
||||
trigger_id: "TRIGGERID",
|
||||
message: { thread_ts: "1501801629.052212" },
|
||||
token: token
|
||||
}.to_json
|
||||
}
|
||||
post "/chat-integration/slack/interactive.json",
|
||||
params: {
|
||||
payload: {
|
||||
type: "message_action",
|
||||
channel: {
|
||||
name: "general",
|
||||
id: "C6029G78F",
|
||||
},
|
||||
trigger_id: "TRIGGERID",
|
||||
message: {
|
||||
thread_ts: "1501801629.052212",
|
||||
},
|
||||
token: token,
|
||||
}.to_json,
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
expect(view_open_stub).to have_been_requested
|
||||
expect(view_update_stub).to have_been_requested
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it 'deals with failed API calls correctly' do
|
||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
||||
.with(body: { text: I18n.t("chat_integration.provider.slack.transcript.error_users") })
|
||||
.to_return(body: { ok: true }.to_json)
|
||||
it "deals with failed API calls correctly" do
|
||||
command_stub =
|
||||
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||
body: {
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.error_users"),
|
||||
},
|
||||
).to_return(body: { ok: true }.to_json)
|
||||
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(status: 403)
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post 2",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "post 2",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["text"]).to include(I18n.t("chat_integration.provider.slack.transcript.loading"))
|
||||
expect(json["text"]).to include(
|
||||
I18n.t("chat_integration.provider.slack.transcript.loading"),
|
||||
)
|
||||
|
||||
expect(command_stub).to have_been_requested
|
||||
end
|
||||
|
||||
it 'errors correctly if there is no api key' do
|
||||
SiteSetting.chat_integration_slack_access_token = ''
|
||||
it "errors correctly if there is no api key" do
|
||||
SiteSetting.chat_integration_slack_access_token = ""
|
||||
|
||||
post "/chat-integration/slack/command.json", params: {
|
||||
text: "post 2",
|
||||
response_url: 'https://hooks.slack.com/commands/1234',
|
||||
channel_name: 'general',
|
||||
channel_id: 'C6029G78F',
|
||||
token: token
|
||||
}
|
||||
post "/chat-integration/slack/command.json",
|
||||
params: {
|
||||
text: "post 2",
|
||||
response_url: "https://hooks.slack.com/commands/1234",
|
||||
channel_name: "general",
|
||||
channel_id: "C6029G78F",
|
||||
token: token,
|
||||
}
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["text"]).to include(I18n.t("chat_integration.provider.slack.transcript.api_required"))
|
||||
expect(json["text"]).to include(
|
||||
I18n.t("chat_integration.provider.slack.transcript.api_required"),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackMessageFormatter do
|
||||
describe '.format' do
|
||||
context 'with links' do
|
||||
it 'should return the right message' do
|
||||
expect(described_class.format("<a href='http://somepath.com'>test</a>"))
|
||||
.to eq('<http://somepath.com|test>')
|
||||
describe ".format" do
|
||||
context "with links" do
|
||||
it "should return the right message" do
|
||||
expect(described_class.format("<a href='http://somepath.com'>test</a>")).to eq(
|
||||
"<http://somepath.com|test>",
|
||||
)
|
||||
end
|
||||
|
||||
describe 'when text contains a link with an incomplete URL' do
|
||||
it 'should return the right message' do
|
||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>"))
|
||||
.to eq("test <http://localhost:3000/some/path|>")
|
||||
describe "when text contains a link with an incomplete URL" do
|
||||
it "should return the right message" do
|
||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>")).to eq(
|
||||
"test <http://localhost:3000/some/path|>",
|
||||
)
|
||||
|
||||
SiteSetting.force_https = true
|
||||
|
||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>"))
|
||||
.to eq("test <https://localhost:3000/some/path|>")
|
||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>")).to eq(
|
||||
"test <https://localhost:3000/some/path|>",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it "should not raise an error with unparseable urls" do
|
||||
expect(described_class.format("<a>test</a>")).to eq("<test.localhost|test>")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.excerpt' do
|
||||
describe 'when post contains emoijs' do
|
||||
before do
|
||||
post.update!(raw: ':slight_smile: This is a test')
|
||||
end
|
||||
describe ".excerpt" do
|
||||
describe "when post contains emoijs" do
|
||||
before { post.update!(raw: ":slight_smile: This is a test") }
|
||||
|
||||
it 'should return the right excerpt' do
|
||||
expect(described_class.excerpt(post)).to eq('🙂 This is a test')
|
||||
it "should return the right excerpt" do
|
||||
expect(described_class.excerpt(post)).to eq("🙂 This is a test")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when post contains onebox' do
|
||||
it 'should return the right excerpt' do
|
||||
post.update!(cooked: <<~COOKED
|
||||
describe "when post contains onebox" do
|
||||
it "should return the right excerpt" do
|
||||
post.update!(cooked: <<~COOKED)
|
||||
<aside class=\"onebox whitelistedgeneric\">
|
||||
<header class=\"source\">
|
||||
<a href=\"http://somesource.com\">
|
||||
|
@ -45,59 +43,97 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
|||
<div style=\"clear: both\"></div>
|
||||
</aside>
|
||||
COOKED
|
||||
)
|
||||
|
||||
expect(described_class.excerpt(post))
|
||||
.to eq('<http://somesource.com|meta.discourse.org>')
|
||||
expect(described_class.excerpt(post)).to eq("<http://somesource.com|meta.discourse.org>")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when post contains an email' do
|
||||
it 'should return the right excerpt' do
|
||||
post.update!(cooked: <<~COOKED
|
||||
describe "when post contains an email" do
|
||||
it "should return the right excerpt" do
|
||||
post.update!(cooked: <<~COOKED)
|
||||
The address is <a href=\"mailto:someone@domain.com\">my email</a>
|
||||
COOKED
|
||||
)
|
||||
|
||||
expect(described_class.excerpt(post))
|
||||
.to eq('The address is <mailto:someone@domain.com|my email>')
|
||||
expect(described_class.excerpt(post)).to eq(
|
||||
"The address is <mailto:someone@domain.com|my email>",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url = "https://hooks.slack.com/services/abcde"
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url =
|
||||
"https://hooks.slack.com/services/abcde"
|
||||
SiteSetting.chat_integration_slack_enabled = true
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'slack', data: { identifier: '#general' }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(provider: "slack", data: { identifier: "#general" })
|
||||
end
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
||||
it "sends a webhook request" do
|
||||
stub1 =
|
||||
stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(
|
||||
body: "success",
|
||||
)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(status: 400, body: "error")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(
|
||||
status: 400,
|
||||
body: "error",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
describe 'with api token' do
|
||||
describe "with api token" do
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_access_token = "magic"
|
||||
@ts = "#{Time.now.to_i}.012345"
|
||||
@ts2 = "#{Time.now.to_i}.012346"
|
||||
@stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
||||
@stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(body: "{\"ok\":true, \"ts\": \"#{@ts}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@thread_stub = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).with(body: hash_including("thread_ts" => @ts)).to_return(body: "{\"ok\":true, \"ts\": \"#{@ts}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@thread_stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).with(body: hash_including("thread_ts" => @ts2)).to_return(body: "{\"ok\":true, \"ts\": \"#{@ts2}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@stub1 =
|
||||
stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(
|
||||
body: "success",
|
||||
)
|
||||
@stub2 =
|
||||
stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(
|
||||
body:
|
||||
"{\"ok\":true, \"ts\": \"#{@ts}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }",
|
||||
headers: {
|
||||
"Content-Type" => "application/json",
|
||||
},
|
||||
)
|
||||
@thread_stub =
|
||||
stub_request(:post, %r{https://slack.com/api/chat.postMessage}).with(
|
||||
body: hash_including("thread_ts" => @ts),
|
||||
).to_return(
|
||||
body:
|
||||
"{\"ok\":true, \"ts\": \"#{@ts}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }",
|
||||
headers: {
|
||||
"Content-Type" => "application/json",
|
||||
},
|
||||
)
|
||||
@thread_stub2 =
|
||||
stub_request(:post, %r{https://slack.com/api/chat.postMessage}).with(
|
||||
body: hash_including("thread_ts" => @ts2),
|
||||
).to_return(
|
||||
body:
|
||||
"{\"ok\":true, \"ts\": \"#{@ts2}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }",
|
||||
headers: {
|
||||
"Content-Type" => "application/json",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends an api request' do
|
||||
it "sends an api request" do
|
||||
expect(@stub2).to have_been_requested.times(0)
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
|
||||
|
@ -108,7 +144,7 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
|||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
end
|
||||
|
||||
it 'sends thread id for thread' do
|
||||
it "sends thread id for thread" do
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
||||
|
@ -118,9 +154,15 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
|||
expect(@thread_stub).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'tracks threading in different channels separately' do
|
||||
it "tracks threading in different channels separately" do
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
chan2 = DiscourseChatIntegration::Channel.create(provider: 'dummy2', data: { "identifier" => "#random" })
|
||||
chan2 =
|
||||
DiscourseChatIntegration::Channel.create(
|
||||
provider: "dummy2",
|
||||
data: {
|
||||
"identifier" => "#random",
|
||||
},
|
||||
)
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: chan2, filter: "thread")
|
||||
|
@ -137,12 +179,11 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
|||
expect(described_class.get_slack_thread_ts(post.topic, "#random")).to eq(@ts2)
|
||||
end
|
||||
|
||||
it 'recognizes slack thread ts in comment' do
|
||||
post.update!(cooked: "cooked", raw: <<~RAW
|
||||
it "recognizes slack thread ts in comment" do
|
||||
post.update!(cooked: "cooked", raw: <<~RAW)
|
||||
My fingers are typing words that improve `raw_quality`
|
||||
<!--SLACK_CHANNEL_ID=#general;SLACK_TS=#{@ts}-->
|
||||
RAW
|
||||
)
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
||||
|
||||
|
@ -152,14 +193,19 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
|||
expect(@thread_stub).to have_been_requested.times(1)
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
@stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(body: "{\"ok\":false }", headers: { 'Content-Type' => 'application/json' })
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
it "handles errors correctly" do
|
||||
@stub2 =
|
||||
stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(
|
||||
body: "{\"ok\":false }",
|
||||
headers: {
|
||||
"Content-Type" => "application/json",
|
||||
},
|
||||
)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(@stub2).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,77 +1,69 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscript do
|
||||
before do
|
||||
Discourse.cache.clear
|
||||
end
|
||||
before { Discourse.cache.clear }
|
||||
|
||||
let(:messages_fixture) {
|
||||
let(:messages_fixture) do
|
||||
[
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6JSSESES",
|
||||
"text": "Yeah, should make posting slack transcripts much easier",
|
||||
"ts": "1501801665.062694"
|
||||
type: "message",
|
||||
user: "U6JSSESES",
|
||||
text: "Yeah, should make posting slack transcripts much easier",
|
||||
ts: "1501801665.062694",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U5Z773QLZ",
|
||||
"text": "Oooh a new discourse plugin <@U5Z773QLS> ???",
|
||||
"ts": "1501801643.056375"
|
||||
type: "message",
|
||||
user: "U5Z773QLZ",
|
||||
text: "Oooh a new discourse plugin <@U5Z773QLS> ???",
|
||||
ts: "1501801643.056375",
|
||||
},
|
||||
{ type: "message", user: "U6E2W7R8C", text: "Which one?", ts: "1501801635.053761" },
|
||||
{
|
||||
type: "message",
|
||||
user: "U6JSSESES",
|
||||
text: "So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
||||
ts: "1501801629.052212",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6E2W7R8C",
|
||||
"text": "Which one?",
|
||||
"ts": "1501801635.053761"
|
||||
type: "message",
|
||||
user: "U820GH3LA",
|
||||
text: "I'm interested!!",
|
||||
ts: "1501801634.053761",
|
||||
thread_ts: "1501801629.052212",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U6JSSESES",
|
||||
"text": "So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
||||
"ts": "1501801629.052212"
|
||||
text: "Check this out!",
|
||||
username: "Test Community",
|
||||
bot_id: "B6C6JNUDN",
|
||||
attachments: [
|
||||
{
|
||||
author_name: "@david",
|
||||
fallback: "Discourse can now be integrated with Mattermost! - @david",
|
||||
text: "Hey <http://localhost/groups/team|@team>, what do you think about this?",
|
||||
title: "Discourse can now be integrated with Mattermost! [Announcements] ",
|
||||
id: 1,
|
||||
title_link:
|
||||
"http://localhost:3000/t/discourse-can-now-be-integrated-with-mattermost/51/4",
|
||||
color: "283890",
|
||||
mrkdwn_in: ["text"],
|
||||
},
|
||||
],
|
||||
type: "message",
|
||||
subtype: "bot_message",
|
||||
ts: "1501615820.949638",
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U820GH3LA",
|
||||
"text": "I'm interested!!",
|
||||
"ts": "1501801634.053761",
|
||||
"thread_ts": "1501801629.052212"
|
||||
type: "message",
|
||||
user: "U5Z773QLS",
|
||||
text: "Let’s try some *bold text* <@U5Z773QLZ> <@someotheruser>",
|
||||
ts: "1501093331.439776",
|
||||
},
|
||||
{
|
||||
"text": "Check this out!",
|
||||
"username": "Test Community",
|
||||
"bot_id": "B6C6JNUDN",
|
||||
"attachments": [
|
||||
{
|
||||
"author_name": "@david",
|
||||
"fallback": "Discourse can now be integrated with Mattermost! - @david",
|
||||
"text": "Hey <http://localhost/groups/team|@team>, what do you think about this?",
|
||||
"title": "Discourse can now be integrated with Mattermost! [Announcements] ",
|
||||
"id": 1,
|
||||
"title_link": "http://localhost:3000/t/discourse-can-now-be-integrated-with-mattermost/51/4",
|
||||
"color": "283890",
|
||||
"mrkdwn_in": [
|
||||
"text"
|
||||
]
|
||||
}
|
||||
],
|
||||
"type": "message",
|
||||
"subtype": "bot_message",
|
||||
"ts": "1501615820.949638"
|
||||
},
|
||||
{
|
||||
"type": "message",
|
||||
"user": "U5Z773QLS",
|
||||
"text": "Let’s try some *bold text* <@U5Z773QLZ> <@someotheruser>",
|
||||
"ts": "1501093331.439776"
|
||||
},
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let(:users_fixture) {
|
||||
let(:users_fixture) do
|
||||
[
|
||||
{
|
||||
id: "U6JSSESES",
|
||||
|
@ -79,8 +71,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
profile: {
|
||||
image_24: "https://example.com/avatar",
|
||||
display_name: "Threader",
|
||||
real_name: "A. Threader"
|
||||
}
|
||||
real_name: "A. Threader",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "U820GH3LA",
|
||||
|
@ -88,8 +80,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
profile: {
|
||||
image_24: "https://example.com/avatar",
|
||||
display_name: "Responder",
|
||||
real_name: "A. Responder"
|
||||
}
|
||||
real_name: "A. Responder",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "U5Z773QLS",
|
||||
|
@ -97,8 +89,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
profile: {
|
||||
image_24: "https://example.com/avatar",
|
||||
display_name: "awesomeguy",
|
||||
real_name: "actually just a guy"
|
||||
}
|
||||
real_name: "actually just a guy",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "U5Z773QLZ",
|
||||
|
@ -106,196 +98,221 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
profile: {
|
||||
image_24: "https://example.com/avatar",
|
||||
display_name: "",
|
||||
real_name: "another guy"
|
||||
}
|
||||
}
|
||||
real_name: "another guy",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let(:transcript) { described_class.new(channel_name: "#general", channel_id: "G1234") }
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_access_token = "abcde"
|
||||
end
|
||||
before { SiteSetting.chat_integration_slack_access_token = "abcde" }
|
||||
|
||||
it "doesn't raise an error when there are no messages to guess" do
|
||||
transcript.instance_variable_set(:@messages, [])
|
||||
expect(transcript.guess_first_message(skip_messages: 1)).to eq(false)
|
||||
end
|
||||
|
||||
describe 'loading users' do
|
||||
it 'loads users correctly' do
|
||||
stub_request(:post, "https://slack.com/api/users.list")
|
||||
.with(body: { token: "abcde", "cursor": nil, "limit": "200" })
|
||||
.to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
|
||||
describe "loading users" do
|
||||
it "loads users correctly" do
|
||||
stub_request(:post, "https://slack.com/api/users.list").with(
|
||||
body: {
|
||||
token: "abcde",
|
||||
cursor: nil,
|
||||
limit: "200",
|
||||
},
|
||||
).to_return(
|
||||
status: 200,
|
||||
body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json,
|
||||
)
|
||||
|
||||
expect(transcript.load_user_data).to be_truthy
|
||||
end
|
||||
|
||||
it 'handles failed connection' do
|
||||
stub_request(:post, "https://slack.com/api/users.list")
|
||||
.to_return(status: 500, body: '')
|
||||
it "handles failed connection" do
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(status: 500, body: "")
|
||||
|
||||
expect(transcript.load_user_data).to eq(false)
|
||||
end
|
||||
|
||||
it 'handles slack failure' do
|
||||
stub_request(:post, "https://slack.com/api/users.list")
|
||||
.to_return(status: 200, body: { ok: false }.to_json)
|
||||
it "handles slack failure" do
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(
|
||||
status: 200,
|
||||
body: { ok: false }.to_json,
|
||||
)
|
||||
|
||||
expect(transcript.load_user_data).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with loaded users' do
|
||||
context "with loaded users" do
|
||||
before do
|
||||
stub_request(:post, "https://slack.com/api/users.list")
|
||||
.to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
|
||||
stub_request(:post, "https://slack.com/api/users.list").to_return(
|
||||
status: 200,
|
||||
body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json,
|
||||
)
|
||||
transcript.load_user_data
|
||||
end
|
||||
|
||||
describe 'loading history' do
|
||||
it 'loads messages correctly' do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
||||
.with(body: hash_including(token: "abcde", channel: 'G1234'))
|
||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
||||
describe "loading history" do
|
||||
it "loads messages correctly" do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").with(
|
||||
body: hash_including(token: "abcde", channel: "G1234"),
|
||||
).to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
||||
|
||||
expect(transcript.load_chat_history).to be_truthy
|
||||
expect(transcript.load_chat_history).to be_truthy
|
||||
end
|
||||
|
||||
it 'handles failed connection' do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
||||
.to_return(status: 500, body: {}.to_json)
|
||||
it "handles failed connection" do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").to_return(
|
||||
status: 500,
|
||||
body: {}.to_json,
|
||||
)
|
||||
|
||||
expect(transcript.load_chat_history).to be_falsey
|
||||
expect(transcript.load_chat_history).to be_falsey
|
||||
end
|
||||
|
||||
it 'handles slack failure' do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
||||
.to_return(status: 200, body: { ok: false }.to_json)
|
||||
it "handles slack failure" do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").to_return(
|
||||
status: 200,
|
||||
body: { ok: false }.to_json,
|
||||
)
|
||||
|
||||
expect(transcript.load_chat_history).to be_falsey
|
||||
expect(transcript.load_chat_history).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'with thread_ts specified' do
|
||||
let(:thread_transcript) { described_class.new(channel_name: "#general", channel_id: "G1234", requested_thread_ts: "1501801629.052212") }
|
||||
context "with thread_ts specified" do
|
||||
let(:thread_transcript) do
|
||||
described_class.new(
|
||||
channel_name: "#general",
|
||||
channel_id: "G1234",
|
||||
requested_thread_ts: "1501801629.052212",
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
thread_transcript.load_user_data
|
||||
stub_request(:post, "https://slack.com/api/conversations.replies")
|
||||
.with(body: hash_including(token: "abcde", channel: 'G1234', ts: "1501801629.052212"))
|
||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture[3..4] }.to_json)
|
||||
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||
body: hash_including(token: "abcde", channel: "G1234", ts: "1501801629.052212"),
|
||||
).to_return(status: 200, body: { ok: true, messages: messages_fixture[3..4] }.to_json)
|
||||
thread_transcript.load_chat_history
|
||||
end
|
||||
|
||||
it 'includes messages in a thread' do
|
||||
it "includes messages in a thread" do
|
||||
expect(thread_transcript.messages.length).to eq(2)
|
||||
end
|
||||
|
||||
it 'loads in chronological order' do # replies API presents messages in actual chronological order
|
||||
expect(thread_transcript.messages.first.ts).to eq('1501801629.052212')
|
||||
it "loads in chronological order" do # replies API presents messages in actual chronological order
|
||||
expect(thread_transcript.messages.first.ts).to eq("1501801629.052212")
|
||||
end
|
||||
|
||||
it 'includes slack thread identifiers in body' do
|
||||
it "includes slack thread identifiers in body" do
|
||||
text = thread_transcript.build_transcript
|
||||
expect(text).to include("<!--SLACK_CHANNEL_ID=#general;SLACK_TS=1501801629.052212-->")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'with loaded messages' do
|
||||
context "with loaded messages" do
|
||||
before do
|
||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
||||
.with(body: hash_including(token: "abcde", channel: 'G1234'))
|
||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
||||
stub_request(:post, "https://slack.com/api/conversations.history").with(
|
||||
body: hash_including(token: "abcde", channel: "G1234"),
|
||||
).to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
||||
transcript.load_chat_history
|
||||
end
|
||||
|
||||
it 'ignores messages in a thread' do
|
||||
it "ignores messages in a thread" do
|
||||
expect(transcript.messages.length).to eq(6)
|
||||
end
|
||||
|
||||
it 'loads in chronological order' do # API presents in reverse chronological
|
||||
expect(transcript.messages.first.ts).to eq('1501093331.439776')
|
||||
it "loads in chronological order" do # API presents in reverse chronological
|
||||
expect(transcript.messages.first.ts).to eq("1501093331.439776")
|
||||
end
|
||||
|
||||
it 'handles bold text' do
|
||||
it "handles bold text" do
|
||||
expect(transcript.messages.first.text).to start_with("Let’s try some **bold text** ")
|
||||
end
|
||||
|
||||
it 'handles links' do
|
||||
expect(transcript.messages[2].text).to eq("So, who's interested in the new [discourse plugin](https://meta.discourse.org)?")
|
||||
it "handles links" do
|
||||
expect(transcript.messages[2].text).to eq(
|
||||
"So, who's interested in the new [discourse plugin](https://meta.discourse.org)?",
|
||||
)
|
||||
end
|
||||
|
||||
it 'includes attachments' do
|
||||
expect(transcript.messages[1].attachments.first).to eq("Discourse can now be integrated with Mattermost! - @david")
|
||||
it "includes attachments" do
|
||||
expect(transcript.messages[1].attachments.first).to eq(
|
||||
"Discourse can now be integrated with Mattermost! - @david",
|
||||
)
|
||||
end
|
||||
|
||||
it 'can generate URL' do
|
||||
expect(transcript.messages.first.url).to eq("https://slack.com/archives/G1234/p1501093331439776")
|
||||
it "can generate URL" do
|
||||
expect(transcript.messages.first.url).to eq(
|
||||
"https://slack.com/archives/G1234/p1501093331439776",
|
||||
)
|
||||
end
|
||||
|
||||
it 'includes attachments in raw text' do
|
||||
transcript.set_first_message_by_ts('1501615820.949638')
|
||||
expect(transcript.first_message.raw_text).to eq("Check this out!\n - Discourse can now be integrated with Mattermost! - @david\n")
|
||||
it "includes attachments in raw text" do
|
||||
transcript.set_first_message_by_ts("1501615820.949638")
|
||||
expect(transcript.first_message.raw_text).to eq(
|
||||
"Check this out!\n - Discourse can now be integrated with Mattermost! - @david\n",
|
||||
)
|
||||
end
|
||||
|
||||
it 'gives correct first and last messages' do
|
||||
it "gives correct first and last messages" do
|
||||
expect(transcript.first_message_number).to eq(0)
|
||||
expect(transcript.last_message_number).to eq(transcript.messages.length - 1)
|
||||
|
||||
expect(transcript.first_message.ts).to eq('1501093331.439776')
|
||||
expect(transcript.last_message.ts).to eq('1501801665.062694')
|
||||
expect(transcript.first_message.ts).to eq("1501093331.439776")
|
||||
expect(transcript.last_message.ts).to eq("1501801665.062694")
|
||||
end
|
||||
|
||||
it 'can change first and last messages by index' do
|
||||
it "can change first and last messages by index" do
|
||||
expect(transcript.set_first_message_by_index(999)).to be_falsey
|
||||
expect(transcript.set_first_message_by_index(1)).to be_truthy
|
||||
|
||||
expect(transcript.set_last_message_by_index(-2)).to be_truthy
|
||||
|
||||
expect(transcript.first_message.ts).to eq('1501615820.949638')
|
||||
expect(transcript.last_message.ts).to eq('1501801643.056375')
|
||||
expect(transcript.first_message.ts).to eq("1501615820.949638")
|
||||
expect(transcript.last_message.ts).to eq("1501801643.056375")
|
||||
end
|
||||
|
||||
it 'can change first and last messages by ts' do
|
||||
expect(transcript.set_first_message_by_ts('blah')).to be_falsey
|
||||
expect(transcript.set_first_message_by_ts('1501615820.949638')).to be_truthy
|
||||
it "can change first and last messages by ts" do
|
||||
expect(transcript.set_first_message_by_ts("blah")).to be_falsey
|
||||
expect(transcript.set_first_message_by_ts("1501615820.949638")).to be_truthy
|
||||
|
||||
expect(transcript.set_last_message_by_ts('1501801629.052212')).to be_truthy
|
||||
expect(transcript.set_last_message_by_ts("1501801629.052212")).to be_truthy
|
||||
|
||||
expect(transcript.first_message_number).to eq(1)
|
||||
expect(transcript.last_message_number).to eq(2)
|
||||
end
|
||||
|
||||
it 'can guess the first message' do
|
||||
it "can guess the first message" do
|
||||
expect(transcript.guess_first_message(skip_messages: 1)).to eq(true)
|
||||
expect(transcript.first_message.ts).to eq('1501801629.052212')
|
||||
expect(transcript.first_message.ts).to eq("1501801629.052212")
|
||||
end
|
||||
|
||||
it 'handles usernames correctly' do
|
||||
expect(transcript.first_message.username).to eq('awesomeguy') # Normal user
|
||||
expect(transcript.messages[1].username).to eq('Test_Community') # Bot user
|
||||
it "handles usernames correctly" do
|
||||
expect(transcript.first_message.username).to eq("awesomeguy") # Normal user
|
||||
expect(transcript.messages[1].username).to eq("Test_Community") # Bot user
|
||||
expect(transcript.messages[3].username).to eq(nil) # Unknown normal user
|
||||
# Normal user, display_name not set (fall back to real_name)
|
||||
expect(transcript.messages[4].username).to eq('another_guy')
|
||||
expect(transcript.messages[4].username).to eq("another_guy")
|
||||
end
|
||||
|
||||
it 'handles user mentions correctly' do
|
||||
it "handles user mentions correctly" do
|
||||
# User with display_name not set, unrecognized user
|
||||
expect(transcript.first_message.text).to \
|
||||
eq('Let’s try some **bold text** @another_guy @someotheruser')
|
||||
expect(transcript.first_message.text).to eq(
|
||||
"Let’s try some **bold text** @another_guy @someotheruser",
|
||||
)
|
||||
# Normal user
|
||||
expect(transcript.messages[4].text).to \
|
||||
eq('Oooh a new discourse plugin @awesomeguy ???')
|
||||
expect(transcript.messages[4].text).to eq("Oooh a new discourse plugin @awesomeguy ???")
|
||||
end
|
||||
|
||||
it 'handles avatars correctly' do
|
||||
it "handles avatars correctly" do
|
||||
expect(transcript.first_message.avatar).to eq("https://example.com/avatar") # Normal user
|
||||
expect(transcript.messages[1].avatar).to eq(nil) # Bot user
|
||||
end
|
||||
|
||||
it 'creates a transcript correctly' do
|
||||
it "creates a transcript correctly" do
|
||||
transcript.set_last_message_by_index(1)
|
||||
|
||||
text = transcript.build_transcript
|
||||
|
@ -317,7 +334,7 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
expect(text).to eq(expected)
|
||||
end
|
||||
|
||||
it 'omits quote tags when disabled' do
|
||||
it "omits quote tags when disabled" do
|
||||
transcript.set_last_message_by_index(1)
|
||||
|
||||
text = transcript.build_transcript
|
||||
|
@ -331,7 +348,7 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
expect(text).not_to include("[/quote]")
|
||||
end
|
||||
|
||||
it 'creates the slack UI correctly' do
|
||||
it "creates the slack UI correctly" do
|
||||
transcript.set_last_message_by_index(1)
|
||||
ui = transcript.build_slack_ui
|
||||
|
||||
|
@ -352,16 +369,17 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
end
|
||||
|
||||
describe "message formatting" do
|
||||
it 'handles code block newlines' do
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "Here is some code```my code\nwith newline```",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
)
|
||||
it "handles code block newlines" do
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "Here is some code```my code\nwith newline```",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq(<<~MD)
|
||||
Here is some code
|
||||
```
|
||||
|
@ -371,16 +389,18 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
MD
|
||||
end
|
||||
|
||||
it 'handles multiple code blocks' do
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "Here is some code```my code\nwith newline```and another```some more code```",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
)
|
||||
it "handles multiple code blocks" do
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" =>
|
||||
"Here is some code```my code\nwith newline```and another```some more code```",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq(<<~MD)
|
||||
Here is some code
|
||||
```
|
||||
|
@ -394,73 +414,83 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
|||
MD
|
||||
end
|
||||
|
||||
it 'handles strikethrough' do
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "Some ~strikethrough~",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
)
|
||||
it "handles strikethrough" do
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "Some ~strikethrough~",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq("Some ~~strikethrough~~")
|
||||
end
|
||||
|
||||
it 'handles slack links' do
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "A link to <https://google.com|google>, <https://autolinked.com|https://autolinked.com>, <https://notext.com>, <#channel>, <@user>",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
it "handles slack links" do
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" =>
|
||||
"A link to <https://google.com|google>, <https://autolinked.com|https://autolinked.com>, <https://notext.com>, <#channel>, <@user>",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq(
|
||||
"A link to [google](https://google.com), <https://autolinked.com>, <https://notext.com>, #channel, @user",
|
||||
)
|
||||
expect(message.text).to eq("A link to [google](https://google.com), <https://autolinked.com>, <https://notext.com>, #channel, @user")
|
||||
end
|
||||
|
||||
it 'does not format things inside backticks' do
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
it "does not format things inside backticks" do
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" =>
|
||||
"You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq(
|
||||
"You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
|
||||
)
|
||||
expect(message.text).to eq("You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`")
|
||||
end
|
||||
|
||||
it 'unescapes html in backticks' do
|
||||
it "unescapes html in backticks" do
|
||||
# Because Slack escapes HTML entities, even in backticks
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "The code is `<stuff>`",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
)
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "The code is `<stuff>`",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq("The code is `<stuff>`")
|
||||
end
|
||||
|
||||
it 'updates emoji dashes to underscores' do
|
||||
it "updates emoji dashes to underscores" do
|
||||
# Discourse does not allow dashes in emoji names, so this helps communities have matching custom emojis
|
||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "This is :my-emoji:",
|
||||
"ts" => "1501093331.439776"
|
||||
},
|
||||
transcript
|
||||
)
|
||||
message =
|
||||
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||
{
|
||||
"type" => "message",
|
||||
"user" => "U5Z773QLS",
|
||||
"text" => "This is :my-emoji:",
|
||||
"ts" => "1501093331.439776",
|
||||
},
|
||||
transcript,
|
||||
)
|
||||
expect(message.text).to eq("This is :my_emoji:")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,42 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::TeamsProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_teams_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_teams_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "teams",
|
||||
data: {
|
||||
name: "discourse",
|
||||
webhook_url:
|
||||
"https://outlook.office.com/webhook/677980e4-e03b-4a5e-ad29-dc1ee0c32a80@9e9b5238-5ab2-496a-8e6a-e9cf05c7eb5c/IncomingWebhook/e7a1006ded44478992769d0c4f391e34/e028ca8a-e9c8-4c6c-a4d8-578f881a3cff",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'teams', data: { name: 'discourse', webhook_url: 'https://outlook.office.com/webhook/677980e4-e03b-4a5e-ad29-dc1ee0c32a80@9e9b5238-5ab2-496a-8e6a-e9cf05c7eb5c/IncomingWebhook/e7a1006ded44478992769d0c4f391e34/e028ca8a-e9c8-4c6c-a4d8-578f881a3cff' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(body: "1")
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(body: "1")
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
||||
it "handles errors correctly" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
describe 'with nil user.name' do
|
||||
before do
|
||||
post.user.update!(name: nil)
|
||||
end
|
||||
describe "with nil user.name" do
|
||||
before { post.user.update!(name: nil) }
|
||||
|
||||
it 'handles nil username correctly' do
|
||||
it "handles nil username correctly" do
|
||||
message = described_class.get_message(post)
|
||||
name = message[:sections].first[:facts].first[:name]
|
||||
expect(name).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,32 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
describe 'Telegram Command Controller', type: :request do
|
||||
describe "Telegram Command Controller", type: :request do
|
||||
let(:category) { Fabricate(:category) }
|
||||
let!(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'telegram', data: { name: 'Amazing Channel', chat_id: '123' }) }
|
||||
let!(:webhook_stub) { stub_request(:post, 'https://api.telegram.org/botTOKEN/setWebhook').to_return(body: "{\"ok\":true}") }
|
||||
let!(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "telegram",
|
||||
data: {
|
||||
name: "Amazing Channel",
|
||||
chat_id: "123",
|
||||
},
|
||||
)
|
||||
end
|
||||
let!(:webhook_stub) do
|
||||
stub_request(:post, "https://api.telegram.org/botTOKEN/setWebhook").to_return(
|
||||
body: "{\"ok\":true}",
|
||||
)
|
||||
end
|
||||
|
||||
describe 'with plugin disabled' do
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/telegram/command/abcd.json'
|
||||
describe "with plugin disabled" do
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/telegram/command/abcd.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with plugin enabled and provider disabled' do
|
||||
describe "with plugin enabled and provider disabled" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_telegram_enabled = false
|
||||
end
|
||||
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/telegram/command/abcd.json'
|
||||
it "should return a 404" do
|
||||
post "/chat-integration/telegram/command/abcd.json"
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'slash commands endpoint' do
|
||||
describe "slash commands endpoint" do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||
|
@ -34,85 +46,122 @@ describe 'Telegram Command Controller', type: :request do
|
|||
SiteSetting.chat_integration_telegram_secret = "shhh"
|
||||
end
|
||||
|
||||
let!(:stub) { stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":true}") }
|
||||
let!(:stub) do
|
||||
stub_request(:post, "https://api.telegram.org/botTOKEN/sendMessage").to_return(
|
||||
body: "{\"ok\":true}",
|
||||
)
|
||||
end
|
||||
|
||||
describe 'when forum is private' do
|
||||
it 'should not redirect to login page' do
|
||||
describe "when forum is private" do
|
||||
it "should not redirect to login page" do
|
||||
SiteSetting.login_required = true
|
||||
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
message: { chat: { id: 123 }, text: '/help' }
|
||||
}
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/help",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the token is invalid' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/telegram/command/blah.json', params: {
|
||||
message: { chat: { id: 123 }, text: '/help' }
|
||||
}
|
||||
describe "when the token is invalid" do
|
||||
it "should raise the right error" do
|
||||
post "/chat-integration/telegram/command/blah.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/help",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token has not been set' do
|
||||
it 'should raise the right error' do
|
||||
describe "when token has not been set" do
|
||||
it "should raise the right error" do
|
||||
SiteSetting.chat_integration_telegram_access_token = ""
|
||||
post '/chat-integration/telegram/command/blah.json', params: {
|
||||
message: { chat: { id: 123 }, text: '/help' }
|
||||
}
|
||||
post "/chat-integration/telegram/command/blah.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/help",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token is valid' do
|
||||
describe "when token is valid" do
|
||||
let(:token) { "TOKEN" }
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_telegram_enable_slash_commands = true
|
||||
end
|
||||
before { SiteSetting.chat_integration_telegram_enable_slash_commands = true }
|
||||
|
||||
describe 'add new rule' do
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
message: { chat: { id: 123 }, text: "/watch #{category.slug}" }
|
||||
}
|
||||
describe "add new rule" do
|
||||
it "should add a new rule correctly" do
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/watch #{category.slug}",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(stub).to have_been_requested.once
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
it 'should add a new rule correctly using group chat syntax' do
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
message: { chat: { id: 123 }, text: "/watch@my-awesome-bot #{category.slug}" }
|
||||
}
|
||||
it "should add a new rule correctly using group chat syntax" do
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/watch@my-awesome-bot #{category.slug}",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(stub).to have_been_requested.once
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
expect(rule.channel).to eq(chan1)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
describe 'from an unknown channel' do
|
||||
it 'does nothing' do
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
message: { chat: { id: 456 }, text: "/watch #{category.slug}" }
|
||||
}
|
||||
describe "from an unknown channel" do
|
||||
it "does nothing" do
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 456,
|
||||
},
|
||||
text: "/watch #{category.slug}",
|
||||
},
|
||||
}
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(0)
|
||||
expect(DiscourseChatIntegration::Channel.all.size).to eq(1)
|
||||
|
@ -121,16 +170,28 @@ describe 'Telegram Command Controller', type: :request do
|
|||
end
|
||||
|
||||
it "should respond only to a specific command in a broadcast channel" do
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
channel_post: { chat: { id: 123 }, text: "something" }
|
||||
}
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
channel_post: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "something",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(stub).to have_been_requested.times(0)
|
||||
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
channel_post: { chat: { id: 123 }, text: "/getchatid" }
|
||||
}
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
channel_post: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
text: "/getchatid",
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(stub).to have_been_requested.times(1)
|
||||
|
@ -138,9 +199,14 @@ describe 'Telegram Command Controller', type: :request do
|
|||
|
||||
context "when 'text' is missing" do
|
||||
it "does not break" do
|
||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
||||
message: { chat: { id: 123 } }
|
||||
}
|
||||
post "/chat-integration/telegram/command/shhh.json",
|
||||
params: {
|
||||
message: {
|
||||
chat: {
|
||||
id: 123,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(response).to have_http_status :ok
|
||||
expect(DiscourseChatIntegration::Rule.count).to eq(0)
|
||||
|
|
|
@ -1,33 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::TelegramProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
let!(:webhook_stub) { stub_request(:post, 'https://api.telegram.org/botTOKEN/setWebhook').to_return(body: "{\"ok\":true}") }
|
||||
let!(:webhook_stub) do
|
||||
stub_request(:post, "https://api.telegram.org/botTOKEN/setWebhook").to_return(
|
||||
body: "{\"ok\":true}",
|
||||
)
|
||||
end
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||
SiteSetting.chat_integration_telegram_enabled = true
|
||||
SiteSetting.chat_integration_telegram_secret = 'shhh'
|
||||
SiteSetting.chat_integration_telegram_secret = "shhh"
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'telegram', data: { name: "Awesome Channel", chat_id: '123' }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "telegram",
|
||||
data: {
|
||||
name: "Awesome Channel",
|
||||
chat_id: "123",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":true}")
|
||||
it "sends a webhook request" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://api.telegram.org/botTOKEN/sendMessage").to_return(
|
||||
body: "{\"ok\":true}",
|
||||
)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":false, \"description\":\"chat not found\"}")
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://api.telegram.org/botTOKEN/sendMessage").to_return(
|
||||
body: "{\"ok\":false, \"description\":\"chat not found\"}",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,30 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::WebexProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_webex_enabled = true
|
||||
describe ".trigger_notifications" do
|
||||
before { SiteSetting.chat_integration_webex_enabled = true }
|
||||
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "webex",
|
||||
data: {
|
||||
name: "discourse",
|
||||
webhook_url:
|
||||
"https://webexapis.com/v1/webhooks/incoming/jAHJjVVQ1cgEwb4ikQQawIrGdUtlocKA9fSNvIyADQoYo0mI70pztWUDOu22gDRPJOEJtCsc688zi1RMa",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'webex', data: { name: 'discourse', webhook_url: 'https://webexapis.com/v1/webhooks/incoming/jAHJjVVQ1cgEwb4ikQQawIrGdUtlocKA9fSNvIyADQoYo0mI70pztWUDOu22gDRPJOEJtCsc688zi1RMa' }) }
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(body: "1")
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(body: "1")
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
||||
it "handles errors correctly" do
|
||||
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Provider::ZulipProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
before do
|
||||
SiteSetting.chat_integration_zulip_enabled = true
|
||||
SiteSetting.chat_integration_zulip_server = "https://hello.world"
|
||||
|
@ -13,21 +13,33 @@ RSpec.describe DiscourseChatIntegration::Provider::ZulipProvider do
|
|||
SiteSetting.chat_integration_zulip_bot_api_key = "secret"
|
||||
end
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'zulip', data: { stream: "general", subject: "Discourse Notifications" }) }
|
||||
let(:chan1) do
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: "zulip",
|
||||
data: {
|
||||
stream: "general",
|
||||
subject: "Discourse Notifications",
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://hello.world/api/v1/messages').to_return(status: 200)
|
||||
it "sends a webhook request" do
|
||||
stub1 = stub_request(:post, "https://hello.world/api/v1/messages").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, 'https://hello.world/api/v1/messages').to_return(status: 400, body: '{}')
|
||||
it "handles errors correctly" do
|
||||
stub1 =
|
||||
stub_request(:post, "https://hello.world/api/v1/messages").to_return(
|
||||
status: 400,
|
||||
body: "{}",
|
||||
)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(
|
||||
::DiscourseChatIntegration::ProviderError,
|
||||
)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require_relative '../dummy_provider'
|
||||
require "rails_helper"
|
||||
require_relative "../dummy_provider"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Channel do
|
||||
include_context "with dummy provider"
|
||||
include_context "with validated dummy provider"
|
||||
|
||||
it 'should save and load successfully' do
|
||||
it "should save and load successfully" do
|
||||
expect(DiscourseChatIntegration::Channel.all.length).to eq(0)
|
||||
|
||||
chan = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||
|
@ -16,49 +16,53 @@ RSpec.describe DiscourseChatIntegration::Channel do
|
|||
|
||||
loadedChan = DiscourseChatIntegration::Channel.find(chan.id)
|
||||
|
||||
expect(loadedChan.provider).to eq('dummy')
|
||||
|
||||
expect(loadedChan.provider).to eq("dummy")
|
||||
end
|
||||
|
||||
it 'should edit successfully' do
|
||||
it "should edit successfully" do
|
||||
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "hello" })
|
||||
expect(channel.valid?).to eq(true)
|
||||
channel.save!
|
||||
end
|
||||
|
||||
it 'can be filtered by provider' do
|
||||
channel1 = DiscourseChatIntegration::Channel.create!(provider: 'dummy')
|
||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah" })
|
||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah2" })
|
||||
it "can be filtered by provider" do
|
||||
channel1 = DiscourseChatIntegration::Channel.create!(provider: "dummy")
|
||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah" })
|
||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah2" })
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.all.length).to eq(3)
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.with_provider('dummy2').length).to eq(2)
|
||||
expect(DiscourseChatIntegration::Channel.with_provider('dummy').length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Channel.with_provider("dummy2").length).to eq(2)
|
||||
expect(DiscourseChatIntegration::Channel.with_provider("dummy").length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be filtered by data value' do
|
||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "foo" })
|
||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah" })
|
||||
it "can be filtered by data value" do
|
||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "foo" })
|
||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah" })
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.all.length).to eq(2)
|
||||
|
||||
for_provider = DiscourseChatIntegration::Channel.with_provider('dummy2')
|
||||
for_provider = DiscourseChatIntegration::Channel.with_provider("dummy2")
|
||||
expect(for_provider.length).to eq(2)
|
||||
|
||||
expect(DiscourseChatIntegration::Channel.with_provider('dummy2').with_data_value('val', 'blah').length).to eq(1)
|
||||
expect(
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider("dummy2")
|
||||
.with_data_value("val", "blah")
|
||||
.length,
|
||||
).to eq(1)
|
||||
end
|
||||
|
||||
it 'can find its own rules' do
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
||||
it "can find its own rules" do
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||
expect(channel.rules.size).to eq(0)
|
||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
expect(channel.rules.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'destroys its rules on destroy' do
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
||||
it "destroys its rules on destroy" do
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||
expect(channel.rules.size).to eq(0)
|
||||
rule1 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
|
@ -68,42 +72,48 @@ RSpec.describe DiscourseChatIntegration::Channel do
|
|||
expect(DiscourseChatIntegration::Rule.with_channel(channel).exists?).to eq(false)
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
|
||||
it 'validates provider correctly' do
|
||||
describe "validations" do
|
||||
it "validates provider correctly" do
|
||||
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy")
|
||||
expect(channel.valid?).to eq(true)
|
||||
channel.provider = 'somerandomprovider'
|
||||
channel.provider = "somerandomprovider"
|
||||
expect(channel.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'succeeds with valid data' do
|
||||
it "succeeds with valid data" do
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
||||
expect(channel2.valid?).to eq(true)
|
||||
end
|
||||
|
||||
it 'disallows invalid data' do
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: ' ' })
|
||||
it "disallows invalid data" do
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: " " })
|
||||
expect(channel2.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'disallows unknown keys' do
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello", unknown: "world" })
|
||||
it "disallows unknown keys" do
|
||||
channel2 =
|
||||
DiscourseChatIntegration::Channel.new(
|
||||
provider: "dummy2",
|
||||
data: {
|
||||
val: "hello",
|
||||
unknown: "world",
|
||||
},
|
||||
)
|
||||
expect(channel2.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'requires all keys' do
|
||||
it "requires all keys" do
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: {})
|
||||
expect(channel2.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'disallows duplicate channels' do
|
||||
channel1 = DiscourseChatIntegration::Channel.create(provider: "dummy2", data: { val: "hello" })
|
||||
it "disallows duplicate channels" do
|
||||
channel1 =
|
||||
DiscourseChatIntegration::Channel.create(provider: "dummy2", data: { val: "hello" })
|
||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
||||
expect(channel2.valid?).to eq(false)
|
||||
channel2.data[:val] = "hello2"
|
||||
expect(channel2.valid?).to eq(true)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require_relative '../dummy_provider'
|
||||
require "rails_helper"
|
||||
require_relative "../dummy_provider"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Rule do
|
||||
include_context "with dummy provider"
|
||||
|
@ -9,31 +9,34 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
let(:tag1) { Fabricate(:tag) }
|
||||
let(:tag2) { Fabricate(:tag) }
|
||||
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy') }
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: "dummy") }
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:group) { Fabricate(:group) }
|
||||
|
||||
describe '.alloc_key' do
|
||||
it 'should return sequential numbers' do
|
||||
describe ".alloc_key" do
|
||||
it "should return sequential numbers" do
|
||||
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:1")
|
||||
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:2")
|
||||
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:3")
|
||||
end
|
||||
end
|
||||
|
||||
it 'should convert between channel and channel_id successfully' do
|
||||
it "should convert between channel and channel_id successfully" do
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
expect(rule.channel_id).to eq(channel.id)
|
||||
expect(rule.channel.id).to eq(channel.id)
|
||||
end
|
||||
|
||||
it 'should save and load successfully' do
|
||||
it "should save and load successfully" do
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(0)
|
||||
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: channel,
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
filter: 'watch')
|
||||
rule =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: channel,
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
filter: "watch",
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(1)
|
||||
|
||||
|
@ -42,18 +45,20 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(loadedRule.channel.id).to eq(channel.id)
|
||||
expect(loadedRule.category_id).to eq(category.id)
|
||||
expect(loadedRule.tags).to contain_exactly(tag1.name, tag2.name)
|
||||
expect(loadedRule.filter).to eq('watch')
|
||||
|
||||
expect(loadedRule.filter).to eq("watch")
|
||||
end
|
||||
|
||||
describe 'general operations' do
|
||||
describe "general operations" do
|
||||
before do
|
||||
rule = DiscourseChatIntegration::Rule.create(channel: channel,
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name])
|
||||
rule =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: channel,
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name],
|
||||
)
|
||||
end
|
||||
|
||||
it 'can be modified' do
|
||||
it "can be modified" do
|
||||
rule = DiscourseChatIntegration::Rule.all.first
|
||||
rule.tags = [tag1.name]
|
||||
|
||||
|
@ -63,7 +68,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(rule.tags).to contain_exactly(tag1.name)
|
||||
end
|
||||
|
||||
it 'can be deleted' do
|
||||
it "can be deleted" do
|
||||
DiscourseChatIntegration::Rule.new(channel: channel).save!
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(2)
|
||||
|
||||
|
@ -73,7 +78,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(DiscourseChatIntegration::Rule.all.length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can delete all' do
|
||||
it "can delete all" do
|
||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
|
@ -86,9 +91,9 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(DiscourseChatIntegration::Rule.all.length).to eq(0)
|
||||
end
|
||||
|
||||
it 'can be filtered by channel' do
|
||||
channel2 = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
||||
channel3 = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
||||
it "can be filtered by channel" do
|
||||
channel2 = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||
channel3 = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||
|
@ -101,7 +106,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(DiscourseChatIntegration::Rule.with_channel(channel2).length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be filtered by category' do
|
||||
it "can be filtered by category" do
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, category_id: category.id)
|
||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, category_id: nil)
|
||||
|
||||
|
@ -111,11 +116,21 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(DiscourseChatIntegration::Rule.with_category_id(nil).length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be filtered by group' do
|
||||
it "can be filtered by group" do
|
||||
group1 = Fabricate(:group)
|
||||
group2 = Fabricate(:group)
|
||||
rule2 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group1.id)
|
||||
rule3 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group2.id)
|
||||
rule2 =
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: channel,
|
||||
type: "group_message",
|
||||
group_id: group1.id,
|
||||
)
|
||||
rule3 =
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: channel,
|
||||
type: "group_message",
|
||||
group_id: group2.id,
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(3)
|
||||
|
||||
|
@ -125,42 +140,55 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(DiscourseChatIntegration::Rule.with_group_ids([group2.id]).length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be filtered by type' do
|
||||
it "can be filtered by type" do
|
||||
group1 = Fabricate(:group)
|
||||
|
||||
rule2 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group1.id)
|
||||
rule3 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_mention', group_id: group1.id)
|
||||
rule2 =
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: channel,
|
||||
type: "group_message",
|
||||
group_id: group1.id,
|
||||
)
|
||||
rule3 =
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: channel,
|
||||
type: "group_mention",
|
||||
group_id: group1.id,
|
||||
)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(3)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.with_type('group_message').length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.with_type('group_mention').length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.with_type('normal').length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.with_type("group_message").length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.with_type("group_mention").length).to eq(1)
|
||||
expect(DiscourseChatIntegration::Rule.with_type("normal").length).to eq(1)
|
||||
end
|
||||
|
||||
it 'can be sorted by precedence' do
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'mute')
|
||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'follow')
|
||||
rule4 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'thread')
|
||||
rule5 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'mute')
|
||||
it "can be sorted by precedence" do
|
||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "mute")
|
||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "follow")
|
||||
rule4 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "thread")
|
||||
rule5 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "mute")
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(5)
|
||||
|
||||
expect(DiscourseChatIntegration::Rule.all.order_by_precedence.map(&:filter)).to eq(["mute", "mute", "thread", "watch", "follow"])
|
||||
expect(DiscourseChatIntegration::Rule.all.order_by_precedence.map(&:filter)).to eq(
|
||||
%w[mute mute thread watch follow],
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
|
||||
describe "validations" do
|
||||
let(:rule) do
|
||||
DiscourseChatIntegration::Rule.create(filter: 'watch',
|
||||
channel: channel,
|
||||
category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
filter: "watch",
|
||||
channel: channel,
|
||||
category_id: category.id,
|
||||
)
|
||||
end
|
||||
|
||||
it 'validates channel correctly' do
|
||||
it "validates channel correctly" do
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.channel_id = 'blahblahblah'
|
||||
rule.channel_id = "blahblahblah"
|
||||
expect(rule.valid?).to eq(false)
|
||||
rule.channel_id = -1
|
||||
expect(rule.valid?).to eq(false)
|
||||
|
@ -175,7 +203,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(rule.valid?).to eq(true)
|
||||
end
|
||||
|
||||
it 'validates group correctly' do
|
||||
it "validates group correctly" do
|
||||
rule.category_id = nil
|
||||
rule.group_id = group.id
|
||||
rule.type = "group_message"
|
||||
|
@ -184,42 +212,41 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
|||
expect(rule.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'validates category correctly' do
|
||||
it "validates category correctly" do
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.category_id = -99
|
||||
expect(rule.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'validates filter correctly' do
|
||||
it "validates filter correctly" do
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.filter = 'thread'
|
||||
rule.filter = "thread"
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.filter = 'follow'
|
||||
rule.filter = "follow"
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.filter = 'mute'
|
||||
rule.filter = "mute"
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.filter = ''
|
||||
rule.filter = ""
|
||||
expect(rule.valid?).to eq(false)
|
||||
rule.filter = 'somerandomstring'
|
||||
rule.filter = "somerandomstring"
|
||||
expect(rule.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it 'validates tags correctly' do
|
||||
it "validates tags correctly" do
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.tags = []
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.tags = [tag1.name]
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.tags = [tag1.name, 'blah']
|
||||
rule.tags = [tag1.name, "blah"]
|
||||
expect(rule.valid?).to eq(false)
|
||||
end
|
||||
|
||||
it "doesn't allow save when invalid" do
|
||||
expect(rule.valid?).to eq(true)
|
||||
rule.filter = 'somerandomfilter'
|
||||
rule.filter = "somerandomfilter"
|
||||
expect(rule.valid?).to eq(false)
|
||||
expect(rule.save).to eq(false)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,34 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require_relative '../dummy_provider'
|
||||
|
||||
describe 'Chat Controller', type: :request do
|
||||
require "rails_helper"
|
||||
require_relative "../dummy_provider"
|
||||
|
||||
describe "Chat Controller", type: :request do
|
||||
let(:topic) { Fabricate(:post).topic }
|
||||
let(:admin) { Fabricate(:admin) }
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:category2) { Fabricate(:category) }
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy') }
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: "dummy") }
|
||||
|
||||
include_context "with dummy provider"
|
||||
include_context "with validated dummy provider"
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
end
|
||||
before { SiteSetting.chat_integration_enabled = true }
|
||||
|
||||
shared_examples 'admin constraints' do |action, route|
|
||||
context 'when user is not signed in' do
|
||||
it 'should raise the right error' do
|
||||
shared_examples "admin constraints" do |action, route|
|
||||
context "when user is not signed in" do
|
||||
it "should raise the right error" do
|
||||
public_send(action, route)
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not an admin' do
|
||||
it 'should raise the right error' do
|
||||
context "when user is not an admin" do
|
||||
it "should raise the right error" do
|
||||
sign_in(Fabricate(:user))
|
||||
public_send(action, route)
|
||||
expect(response.status).to eq(404)
|
||||
|
@ -36,154 +33,168 @@ describe 'Chat Controller', type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'listing providers' do
|
||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/providers.json'
|
||||
describe "listing providers" do
|
||||
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/providers.json"
|
||||
|
||||
context 'when signed in as an admin' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
context "when signed in as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
it 'should return the right response' do
|
||||
get '/admin/plugins/chat-integration/providers.json'
|
||||
it "should return the right response" do
|
||||
get "/admin/plugins/chat-integration/providers.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json['providers'].size).to eq(2)
|
||||
expect(json["providers"].size).to eq(2)
|
||||
|
||||
expect(json['providers'].find { |h| h['name'] == 'dummy' }).to eq(
|
||||
'name' => 'dummy',
|
||||
'id' => 'dummy',
|
||||
'channel_parameters' => []
|
||||
expect(json["providers"].find { |h| h["name"] == "dummy" }).to eq(
|
||||
"name" => "dummy",
|
||||
"id" => "dummy",
|
||||
"channel_parameters" => [],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'testing channels' do
|
||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/test.json'
|
||||
describe "testing channels" do
|
||||
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/test.json"
|
||||
|
||||
context 'when signed in as an admin' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
context "when signed in as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
it 'should return the right response' do
|
||||
post '/admin/plugins/chat-integration/test.json', params: {
|
||||
channel_id: channel.id, topic_id: topic.id
|
||||
}
|
||||
it "should return the right response" do
|
||||
post "/admin/plugins/chat-integration/test.json",
|
||||
params: {
|
||||
channel_id: channel.id,
|
||||
topic_id: topic.id,
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it 'should fail for invalid channel' do
|
||||
post '/admin/plugins/chat-integration/test.json', params: {
|
||||
channel_id: 999, topic_id: topic.id
|
||||
}
|
||||
it "should fail for invalid channel" do
|
||||
post "/admin/plugins/chat-integration/test.json",
|
||||
params: {
|
||||
channel_id: 999,
|
||||
topic_id: topic.id,
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'viewing channels' do
|
||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/channels.json'
|
||||
describe "viewing channels" do
|
||||
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/channels.json"
|
||||
|
||||
context 'when signed in as an admin' do
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
context "when signed in as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
it 'should return the right response' do
|
||||
rule = DiscourseChatIntegration::Rule.create(
|
||||
channel: channel,
|
||||
filter: 'follow',
|
||||
category_id: category.id,
|
||||
tags: [tag.name]
|
||||
)
|
||||
it "should return the right response" do
|
||||
rule =
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: channel,
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
tags: [tag.name],
|
||||
)
|
||||
|
||||
get '/admin/plugins/chat-integration/channels.json', params: { provider: 'dummy' }
|
||||
get "/admin/plugins/chat-integration/channels.json", params: { provider: "dummy" }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
channels = response.parsed_body['channels']
|
||||
channels = response.parsed_body["channels"]
|
||||
|
||||
expect(channels.count).to eq(1)
|
||||
|
||||
expect(channels.first).to eq(
|
||||
"id" => channel.id,
|
||||
"provider" => 'dummy',
|
||||
"data" => {},
|
||||
"provider" => "dummy",
|
||||
"data" => {
|
||||
},
|
||||
"error_key" => nil,
|
||||
"error_info" => nil,
|
||||
"rules" => [{ "id" => rule.id, "type" => 'normal', "group_name" => nil, "group_id" => nil, "filter" => "follow", "channel_id" => channel.id, "category_id" => category.id, "tags" => [tag.name] }]
|
||||
"rules" => [
|
||||
{
|
||||
"id" => rule.id,
|
||||
"type" => "normal",
|
||||
"group_name" => nil,
|
||||
"group_id" => nil,
|
||||
"filter" => "follow",
|
||||
"channel_id" => channel.id,
|
||||
"category_id" => category.id,
|
||||
"tags" => [tag.name],
|
||||
},
|
||||
],
|
||||
)
|
||||
end
|
||||
|
||||
it 'should fail for invalid provider' do
|
||||
get '/admin/plugins/chat-integration/channels.json', params: { provider: 'someprovider' }
|
||||
it "should fail for invalid provider" do
|
||||
get "/admin/plugins/chat-integration/channels.json", params: { provider: "someprovider" }
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe 'adding a channel' do
|
||||
include_examples 'admin constraints', 'post', '/admin/plugins/chat-integration/channels.json'
|
||||
describe "adding a channel" do
|
||||
include_examples "admin constraints", "post", "/admin/plugins/chat-integration/channels.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able to add a new channel' do
|
||||
post '/admin/plugins/chat-integration/channels.json', params: {
|
||||
channel: {
|
||||
provider: 'dummy',
|
||||
data: {}
|
||||
}
|
||||
}
|
||||
it "should be able to add a new channel" do
|
||||
post "/admin/plugins/chat-integration/channels.json",
|
||||
params: {
|
||||
channel: {
|
||||
provider: "dummy",
|
||||
data: {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.all.last
|
||||
|
||||
expect(channel.provider).to eq('dummy')
|
||||
expect(channel.provider).to eq("dummy")
|
||||
end
|
||||
|
||||
it 'should fail for invalid params' do
|
||||
post '/admin/plugins/chat-integration/channels.json', params: {
|
||||
channel: {
|
||||
provider: 'dummy2',
|
||||
data: { val: 'something with whitespace' }
|
||||
}
|
||||
}
|
||||
it "should fail for invalid params" do
|
||||
post "/admin/plugins/chat-integration/channels.json",
|
||||
params: {
|
||||
channel: {
|
||||
provider: "dummy2",
|
||||
data: {
|
||||
val: "something with whitespace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'updating a channel' do
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy2', data: { val: "something" }) }
|
||||
describe "updating a channel" do
|
||||
let(:channel) do
|
||||
DiscourseChatIntegration::Channel.create(provider: "dummy2", data: { val: "something" })
|
||||
end
|
||||
|
||||
include_examples 'admin constraints', 'put', "/admin/plugins/chat-integration/channels/1.json"
|
||||
include_examples "admin constraints", "put", "/admin/plugins/chat-integration/channels/1.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able update a channel' do
|
||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json", params: {
|
||||
channel: {
|
||||
data: { val: "something-else" }
|
||||
}
|
||||
}
|
||||
it "should be able update a channel" do
|
||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json",
|
||||
params: {
|
||||
channel: {
|
||||
data: {
|
||||
val: "something-else",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
|
@ -191,30 +202,32 @@ describe 'Chat Controller', type: :request do
|
|||
expect(channel.data).to eq("val" => "something-else")
|
||||
end
|
||||
|
||||
it 'should fail for invalid params' do
|
||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json", params: {
|
||||
channel: {
|
||||
data: { val: "something with whitespace" }
|
||||
}
|
||||
}
|
||||
it "should fail for invalid params" do
|
||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json",
|
||||
params: {
|
||||
channel: {
|
||||
data: {
|
||||
val: "something with whitespace",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'deleting a channel' do
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy', data: {}) }
|
||||
describe "deleting a channel" do
|
||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: "dummy", data: {}) }
|
||||
|
||||
include_examples 'admin constraints', 'delete', "/admin/plugins/chat-integration/channels/1.json"
|
||||
include_examples "admin constraints",
|
||||
"delete",
|
||||
"/admin/plugins/chat-integration/channels/1.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able delete a channel' do
|
||||
it "should be able delete a channel" do
|
||||
delete "/admin/plugins/chat-integration/channels/#{channel.id}.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
@ -223,24 +236,22 @@ describe 'Chat Controller', type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'adding a rule' do
|
||||
include_examples 'admin constraints', 'put', '/admin/plugins/chat-integration/rules.json'
|
||||
describe "adding a rule" do
|
||||
include_examples "admin constraints", "put", "/admin/plugins/chat-integration/rules.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able to add a new rule' do
|
||||
post '/admin/plugins/chat-integration/rules.json', params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: 'watch',
|
||||
tags: [tag.name]
|
||||
}
|
||||
}
|
||||
it "should be able to add a new rule" do
|
||||
post "/admin/plugins/chat-integration/rules.json",
|
||||
params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: "watch",
|
||||
tags: [tag.name],
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
|
@ -248,46 +259,51 @@ describe 'Chat Controller', type: :request do
|
|||
|
||||
expect(rule.channel_id).to eq(channel.id)
|
||||
expect(rule.category_id).to eq(category.id)
|
||||
expect(rule.filter).to eq('watch')
|
||||
expect(rule.filter).to eq("watch")
|
||||
expect(rule.tags).to eq([tag.name])
|
||||
|
||||
end
|
||||
|
||||
it 'should fail for invalid params' do
|
||||
post '/admin/plugins/chat-integration/rules.json', params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: 'watch',
|
||||
tags: ['somenonexistanttag']
|
||||
}
|
||||
}
|
||||
it "should fail for invalid params" do
|
||||
post "/admin/plugins/chat-integration/rules.json",
|
||||
params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: "watch",
|
||||
tags: ["somenonexistanttag"],
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'updating a rule' do
|
||||
let(:rule) { DiscourseChatIntegration::Rule.create(channel: channel, filter: 'follow', category_id: category.id, tags: [tag.name]) }
|
||||
describe "updating a rule" do
|
||||
let(:rule) do
|
||||
DiscourseChatIntegration::Rule.create(
|
||||
channel: channel,
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
tags: [tag.name],
|
||||
)
|
||||
end
|
||||
|
||||
include_examples 'admin constraints', 'put', "/admin/plugins/chat-integration/rules/1.json"
|
||||
include_examples "admin constraints", "put", "/admin/plugins/chat-integration/rules/1.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able update a rule' do
|
||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json", params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category2.id,
|
||||
filter: rule.filter,
|
||||
tags: rule.tags
|
||||
}
|
||||
}
|
||||
it "should be able update a rule" do
|
||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json",
|
||||
params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category2.id,
|
||||
filter: rule.filter,
|
||||
tags: rule.tags,
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
|
@ -295,40 +311,38 @@ describe 'Chat Controller', type: :request do
|
|||
expect(rule.category_id).to eq(category2.id)
|
||||
end
|
||||
|
||||
it 'should fail for invalid params' do
|
||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json", params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: 'watch',
|
||||
tags: ['somenonexistanttag']
|
||||
}
|
||||
}
|
||||
it "should fail for invalid params" do
|
||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json",
|
||||
params: {
|
||||
rule: {
|
||||
channel_id: channel.id,
|
||||
category_id: category.id,
|
||||
filter: "watch",
|
||||
tags: ["somenonexistanttag"],
|
||||
},
|
||||
}
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'deleting a rule' do
|
||||
describe "deleting a rule" do
|
||||
let(:rule) do
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel_id: channel.id,
|
||||
filter: 'follow',
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
tags: [tag.name]
|
||||
tags: [tag.name],
|
||||
)
|
||||
end
|
||||
|
||||
include_examples 'admin constraints', 'delete', "/admin/plugins/chat-integration/rules/1.json"
|
||||
include_examples "admin constraints", "delete", "/admin/plugins/chat-integration/rules/1.json"
|
||||
|
||||
context 'as an admin' do
|
||||
context "as an admin" do
|
||||
before { sign_in(admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
end
|
||||
|
||||
it 'should be able delete a rule' do
|
||||
it "should be able delete a rule" do
|
||||
delete "/admin/plugins/chat-integration/rules/#{rule.id}.json"
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
|
@ -336,5 +350,4 @@ describe 'Chat Controller', type: :request do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
describe 'Public Controller', type: :request do
|
||||
describe "Public Controller", type: :request do
|
||||
before { SiteSetting.chat_integration_enabled = true }
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
end
|
||||
|
||||
describe 'loading a transcript' do
|
||||
|
||||
it 'should be able to load a transcript' do
|
||||
describe "loading a transcript" do
|
||||
it "should be able to load a transcript" do
|
||||
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
||||
|
||||
get "/chat-transcript/#{key}.json"
|
||||
|
@ -20,13 +16,11 @@ describe 'Public Controller', type: :request do
|
|||
expect(response.body).to eq('{"content":"Some content here"}')
|
||||
end
|
||||
|
||||
it 'should 404 for non-existant transcript' do
|
||||
key = 'abcdefghijk'
|
||||
it "should 404 for non-existant transcript" do
|
||||
key = "abcdefghijk"
|
||||
get "/chat-transcript/#{key}.json"
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require_dependency 'post_creator'
|
||||
require_relative '../dummy_provider'
|
||||
require "rails_helper"
|
||||
require_dependency "post_creator"
|
||||
require_relative "../dummy_provider"
|
||||
|
||||
RSpec.describe DiscourseChatIntegration::Manager do
|
||||
|
||||
let(:manager) { ::DiscourseChatIntegration::Manager }
|
||||
let(:category) { Fabricate(:category) }
|
||||
let(:group) { Fabricate(:group) }
|
||||
|
@ -14,31 +13,37 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
let(:first_post) { Fabricate(:post, topic: topic) }
|
||||
let(:second_post) { Fabricate(:post, topic: topic, post_number: 2) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
describe ".trigger_notifications" do
|
||||
include_context "with dummy provider"
|
||||
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
||||
let(:chan3) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||
let(:chan3) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
end
|
||||
before { SiteSetting.chat_integration_enabled = true }
|
||||
|
||||
it "should fail gracefully when a provider throws an exception" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
)
|
||||
|
||||
# Triggering a ProviderError should set the error_key to the error message
|
||||
provider.set_raise_exception(DiscourseChatIntegration::ProviderError.new info: { error_key: "hello" })
|
||||
provider.set_raise_exception(
|
||||
DiscourseChatIntegration::ProviderError.new info: { error_key: "hello" }
|
||||
)
|
||||
manager.trigger_notifications(first_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq('hello')
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq("hello")
|
||||
|
||||
# Triggering a different error should set the error_key to a generic message
|
||||
provider.set_raise_exception(StandardError.new "hello")
|
||||
manager.trigger_notifications(first_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq('chat_integration.channel_exception')
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq(
|
||||
"chat_integration.channel_exception",
|
||||
)
|
||||
|
||||
provider.set_raise_exception(nil)
|
||||
|
||||
|
@ -48,17 +53,33 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
|
||||
it "should not send notifications when provider is disabled" do
|
||||
SiteSetting.chat_integration_enabled = false
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
)
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
end
|
||||
|
||||
it "should send a notification to watched and following channels for new topic" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'follow', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan3, filter: 'mute', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan3,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
)
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
|
||||
|
@ -66,9 +87,21 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should send a notification only to watched for reply" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'follow', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan3, filter: 'mute', category_id: category.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan3,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
)
|
||||
|
||||
manager.trigger_notifications(second_post.id)
|
||||
|
||||
|
@ -76,7 +109,7 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should respect wildcard category settings" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: nil)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch", category_id: nil)
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
|
||||
|
@ -84,17 +117,25 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should respect mute over watch" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'mute', category_id: category.id) # Specific mute
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch", category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
) # Specific mute
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
end
|
||||
|
||||
it "should respect watch over follow" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil) # Wildcard follow
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id) # Specific watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "follow", category_id: nil) # Wildcard follow
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
category_id: category.id,
|
||||
) # Specific watch
|
||||
|
||||
manager.trigger_notifications(second_post.id)
|
||||
|
||||
|
@ -102,8 +143,12 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should respect thread over watch" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'thread', category_id: category.id) # Specific thread
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch", category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "thread",
|
||||
category_id: category.id,
|
||||
) # Specific thread
|
||||
|
||||
manager.trigger_notifications(second_post.id)
|
||||
|
||||
|
@ -111,18 +156,23 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should not notify about private messages" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "follow", category_id: nil) # Wildcard watch
|
||||
|
||||
private_post = Fabricate(:private_message_post)
|
||||
|
||||
manager.trigger_notifications(private_post.id)
|
||||
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
end
|
||||
|
||||
it "should work for group pms" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch') # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group.id) # Group watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch") # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
type: "group_message",
|
||||
filter: "watch",
|
||||
group_id: group.id,
|
||||
) # Group watch
|
||||
|
||||
private_post = Fabricate(:private_message_post)
|
||||
private_post.topic.invite_group(Fabricate(:user), group)
|
||||
|
@ -133,8 +183,18 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should work for pms with multiple groups" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, type: 'group_message', filter: 'watch', group_id: group.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group2.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
type: "group_message",
|
||||
filter: "watch",
|
||||
group_id: group.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
type: "group_message",
|
||||
filter: "watch",
|
||||
group_id: group2.id,
|
||||
)
|
||||
|
||||
private_post = Fabricate(:private_message_post)
|
||||
private_post.topic.invite_group(Fabricate(:user), group)
|
||||
|
@ -146,40 +206,77 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should work for group mentions" do
|
||||
third_post = Fabricate(:post, topic: topic, post_number: 3, raw: "let's mention @#{group.name}")
|
||||
third_post =
|
||||
Fabricate(:post, topic: topic, post_number: 3, raw: "let's mention @#{group.name}")
|
||||
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch') # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan3, type: 'group_mention', filter: 'watch', group_id: group.id)
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch") # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
type: "group_message",
|
||||
filter: "watch",
|
||||
group_id: group.id,
|
||||
)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan3,
|
||||
type: "group_mention",
|
||||
filter: "watch",
|
||||
group_id: group.id,
|
||||
)
|
||||
|
||||
manager.trigger_notifications(third_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id, chan3.id)
|
||||
end
|
||||
|
||||
it "should give group rule precedence over normal rules" do
|
||||
third_post = Fabricate(:post, topic: topic, post_number: 3, raw: "let's mention @#{group.name}")
|
||||
third_post =
|
||||
Fabricate(:post, topic: topic, post_number: 3, raw: "let's mention @#{group.name}")
|
||||
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'mute', category_id: category.id) # Mute category
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "mute",
|
||||
category_id: category.id,
|
||||
) # Mute category
|
||||
manager.trigger_notifications(third_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', type: 'group_mention', group_id: group.id) # Watch mentions
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
type: "group_mention",
|
||||
group_id: group.id,
|
||||
) # Watch mentions
|
||||
manager.trigger_notifications(third_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||
end
|
||||
|
||||
it "should not notify about mentions in private messages" do
|
||||
# Group 1 watching for messages on channel 1
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', type: 'group_message', group_id: group.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "watch",
|
||||
type: "group_message",
|
||||
group_id: group.id,
|
||||
)
|
||||
# Group 2 watching for mentions on channel 2
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'watch', type: 'group_mention', group_id: group2.id)
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan2,
|
||||
filter: "watch",
|
||||
type: "group_mention",
|
||||
group_id: group2.id,
|
||||
)
|
||||
|
||||
# Make a private message only accessible to group 1
|
||||
private_message = Fabricate(:private_message_post)
|
||||
private_message.topic.invite_group(Fabricate(:user), group)
|
||||
|
||||
# Mention group 2 in the message
|
||||
mention_post = Fabricate(:post, topic: private_message.topic, post_number: 2, raw: "let's mention @#{group2.name}")
|
||||
mention_post =
|
||||
Fabricate(
|
||||
:post,
|
||||
topic: private_message.topic,
|
||||
post_number: 2,
|
||||
raw: "let's mention @#{group2.name}",
|
||||
)
|
||||
|
||||
# We expect that only group 1 receives a notification
|
||||
manager.trigger_notifications(mention_post.id)
|
||||
|
@ -187,15 +284,15 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
end
|
||||
|
||||
it "should not notify about posts the chat_user cannot see" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil) # Wildcard watch
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "follow", category_id: nil) # Wildcard watch
|
||||
|
||||
# Create a group & user
|
||||
group = Fabricate(:group, name: "friends")
|
||||
user = Fabricate(:user, username: 'david')
|
||||
user = Fabricate(:user, username: "david")
|
||||
group.add(user)
|
||||
|
||||
# Set the chat_user to the newly created non-admin user
|
||||
SiteSetting.chat_integration_discourse_username = 'david'
|
||||
SiteSetting.chat_integration_discourse_username = "david"
|
||||
|
||||
# Create a category
|
||||
category = Fabricate(:category, name: "Test category")
|
||||
|
@ -208,7 +305,7 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
|
||||
# Check no notification sent
|
||||
manager.trigger_notifications(first_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||
|
||||
# Now expose category to new user
|
||||
category.set_permissions(Group[:friends] => :full)
|
||||
|
@ -217,20 +314,17 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
# Check notification sent
|
||||
manager.trigger_notifications(first_post.id)
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||
|
||||
end
|
||||
|
||||
describe 'with tags enabled' do
|
||||
let(:tag) { Fabricate(:tag, name: 'gsoc') }
|
||||
describe "with tags enabled" do
|
||||
let(:tag) { Fabricate(:tag, name: "gsoc") }
|
||||
let(:tagged_topic) { Fabricate(:topic, category_id: category.id, tags: [tag]) }
|
||||
let(:tagged_first_post) { Fabricate(:post, topic: tagged_topic) }
|
||||
|
||||
before(:each) do
|
||||
SiteSetting.tagging_enabled = true
|
||||
end
|
||||
before(:each) { SiteSetting.tagging_enabled = true }
|
||||
|
||||
it 'should still work for rules without any tags specified' do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil) # Wildcard watch
|
||||
it "should still work for rules without any tags specified" do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "follow", category_id: nil) # Wildcard watch
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
manager.trigger_notifications(tagged_first_post.id)
|
||||
|
@ -238,16 +332,19 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
|||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id, chan1.id)
|
||||
end
|
||||
|
||||
it 'should only match tagged topics when rule has tags' do
|
||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: category.id, tags: [tag.name])
|
||||
it "should only match tagged topics when rule has tags" do
|
||||
DiscourseChatIntegration::Rule.create!(
|
||||
channel: chan1,
|
||||
filter: "follow",
|
||||
category_id: category.id,
|
||||
tags: [tag.name],
|
||||
)
|
||||
|
||||
manager.trigger_notifications(first_post.id)
|
||||
manager.trigger_notifications(tagged_first_post.id)
|
||||
|
||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue