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
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
|
||||||
- name: Yarn install
|
- name: Yarn install
|
||||||
|
@ -33,15 +33,15 @@ jobs:
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
|
||||||
- name: ESLint
|
- name: ESLint
|
||||||
if: ${{ always() }}
|
if: ${{ !cancelled() }}
|
||||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
|
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets,admin/assets}/javascripts
|
||||||
|
|
||||||
- name: Prettier
|
- name: Prettier
|
||||||
if: ${{ always() }}
|
if: ${{ !cancelled() }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
yarn prettier -v
|
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}"
|
yarn prettier --list-different "assets/**/*.{scss,js,es6}"
|
||||||
fi
|
fi
|
||||||
if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||||
|
@ -49,9 +49,18 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Ember template lint
|
- name: Ember template lint
|
||||||
if: ${{ always() }}
|
if: ${{ !cancelled() }}
|
||||||
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts
|
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts admin/assets/javascripts
|
||||||
|
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
if: ${{ always() }}
|
if: ${{ !cancelled() }}
|
||||||
run: bundle exec rubocop .
|
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
|
- name: Get yarn cache directory
|
||||||
id: yarn-cache-dir
|
id: yarn-cache-dir
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Yarn cache
|
- name: Yarn cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
@ -130,7 +130,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/spec -type f -name "*.rb" 2> /dev/null | wc -l) ]; then
|
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
|
fi
|
||||||
|
|
||||||
- name: Plugin RSpec
|
- name: Plugin RSpec
|
||||||
|
@ -142,7 +142,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
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
|
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
|
fi
|
||||||
|
|
||||||
- name: Plugin QUnit
|
- name: Plugin QUnit
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
inherit_gem:
|
inherit_gem:
|
||||||
rubocop-discourse: default.yml
|
rubocop-discourse: stree-compat.yml
|
||||||
|
|
7
Gemfile
7
Gemfile
|
@ -1,8 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source "https://rubygems.org"
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'translations-manager', git: 'https://github.com/discourse/translations-manager.git'
|
gem "translations-manager", git: "https://github.com/discourse/translations-manager.git"
|
||||||
gem 'rubocop-discourse'
|
gem "rubocop-discourse"
|
||||||
|
gem "syntax_tree"
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,7 @@ GEM
|
||||||
parallel (1.22.1)
|
parallel (1.22.1)
|
||||||
parser (3.1.2.1)
|
parser (3.1.2.1)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
|
prettier_print (1.2.0)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
regexp_parser (2.6.0)
|
regexp_parser (2.6.0)
|
||||||
rexml (3.2.5)
|
rexml (3.2.5)
|
||||||
|
@ -33,6 +34,8 @@ GEM
|
||||||
rubocop-rspec (2.13.2)
|
rubocop-rspec (2.13.2)
|
||||||
rubocop (~> 1.33)
|
rubocop (~> 1.33)
|
||||||
ruby-progressbar (1.11.0)
|
ruby-progressbar (1.11.0)
|
||||||
|
syntax_tree (5.1.0)
|
||||||
|
prettier_print (>= 1.2.0)
|
||||||
unicode-display_width (2.3.0)
|
unicode-display_width (2.3.0)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
|
@ -40,6 +43,7 @@ PLATFORMS
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
rubocop-discourse
|
rubocop-discourse
|
||||||
|
syntax_tree
|
||||||
translations-manager!
|
translations-manager!
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
|
|
|
@ -8,15 +8,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_providers
|
def list_providers
|
||||||
providers = ::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
providers =
|
||||||
{
|
::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
||||||
name: x::PROVIDER_NAME,
|
{
|
||||||
id: x::PROVIDER_NAME,
|
name: x::PROVIDER_NAME,
|
||||||
channel_parameters: (defined? x::CHANNEL_PARAMETERS) ? x::CHANNEL_PARAMETERS : []
|
id: x::PROVIDER_NAME,
|
||||||
}
|
channel_parameters: (defined?(x::CHANNEL_PARAMETERS)) ? x::CHANNEL_PARAMETERS : [],
|
||||||
end
|
}
|
||||||
|
end
|
||||||
|
|
||||||
render json: providers, root: 'providers'
|
render json: providers, root: "providers"
|
||||||
end
|
end
|
||||||
|
|
||||||
def test
|
def test
|
||||||
|
@ -28,9 +29,7 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||||
|
|
||||||
provider = ::DiscourseChatIntegration::Provider.get_by_name(channel.provider)
|
provider = ::DiscourseChatIntegration::Provider.get_by_name(channel.provider)
|
||||||
|
|
||||||
if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
raise Discourse::NotFound if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
||||||
raise Discourse::NotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
post = Topic.find(topic_id.to_i).posts.first
|
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)
|
raise Discourse::InvalidParameters if !providers.include?(requested_provider)
|
||||||
|
|
||||||
channels = DiscourseChatIntegration::Channel.with_provider(requested_provider)
|
channels = DiscourseChatIntegration::Channel.with_provider(requested_provider)
|
||||||
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: 'channels'
|
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: "channels"
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_channel
|
def create_channel
|
||||||
begin
|
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])
|
if !defined?(params[:channel]) && defined?(params[:channel][:provider])
|
||||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||||
end
|
end
|
||||||
|
|
||||||
requested_provider = params[:channel][:provider]
|
requested_provider = params[:channel][:provider]
|
||||||
|
|
||||||
if !providers.include?(requested_provider)
|
if !providers.include?(requested_provider)
|
||||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||||
end
|
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)
|
hash = params.require(:channel).permit(:provider, data: allowed_keys)
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.new(hash)
|
channel = DiscourseChatIntegration::Channel.new(hash)
|
||||||
|
|
||||||
if !channel.save
|
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.save
|
||||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
|
||||||
end
|
|
||||||
|
|
||||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||||
rescue Discourse::InvalidParameters => e
|
rescue Discourse::InvalidParameters => e
|
||||||
render json: { errors: [e.message] }, status: 422
|
render json: { errors: [e.message] }, status: 422
|
||||||
end
|
end
|
||||||
|
@ -94,15 +95,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||||
channel = DiscourseChatIntegration::Channel.find(params[:id].to_i)
|
channel = DiscourseChatIntegration::Channel.find(params[:id].to_i)
|
||||||
channel.error_key = nil # Reset any error on the rule
|
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)
|
hash = params.require(:channel).permit(data: allowed_keys)
|
||||||
|
|
||||||
if !channel.update(hash)
|
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.update(hash)
|
||||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
|
||||||
end
|
|
||||||
|
|
||||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||||
rescue Discourse::InvalidParameters => e
|
rescue Discourse::InvalidParameters => e
|
||||||
render json: { errors: [e.message] }, status: 422
|
render json: { errors: [e.message] }, status: 422
|
||||||
end
|
end
|
||||||
|
@ -118,14 +120,13 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||||
|
|
||||||
def create_rule
|
def create_rule
|
||||||
begin
|
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)
|
rule = DiscourseChatIntegration::Rule.new(hash)
|
||||||
|
|
||||||
if !rule.save
|
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.save
|
||||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
|
||||||
end
|
|
||||||
|
|
||||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||||
rescue Discourse::InvalidParameters => e
|
rescue Discourse::InvalidParameters => e
|
||||||
render json: { errors: [e.message] }, status: 422
|
render json: { errors: [e.message] }, status: 422
|
||||||
end
|
end
|
||||||
|
@ -136,11 +137,9 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||||
rule = DiscourseChatIntegration::Rule.find(params[:id].to_i)
|
rule = DiscourseChatIntegration::Rule.find(params[:id].to_i)
|
||||||
hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: [])
|
hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: [])
|
||||||
|
|
||||||
if !rule.update(hash)
|
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.update(hash)
|
||||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
|
||||||
end
|
|
||||||
|
|
||||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||||
rescue Discourse::InvalidParameters => e
|
rescue Discourse::InvalidParameters => e
|
||||||
render json: { errors: [e.message] }, status: 422
|
render json: { errors: [e.message] }, status: 422
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module DiscourseChatIntegration
|
module DiscourseChatIntegration
|
||||||
module Helper
|
module Helper
|
||||||
|
|
||||||
def self.process_command(channel, tokens)
|
def self.process_command(channel, tokens)
|
||||||
guardian = DiscourseChatIntegration::Manager.guardian
|
guardian = DiscourseChatIntegration::Manager.guardian
|
||||||
|
|
||||||
|
@ -16,13 +15,19 @@ module DiscourseChatIntegration
|
||||||
when "thread", "watch", "follow", "mute"
|
when "thread", "watch", "follow", "mute"
|
||||||
return error_text if tokens.empty?
|
return error_text if tokens.empty?
|
||||||
# If the first token in the command is a tag, this rule applies to all categories
|
# 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
|
if category_name
|
||||||
category = Category.find_by(slug: category_name)
|
category = Category.find_by(slug: category_name)
|
||||||
unless category
|
unless category
|
||||||
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(', ')
|
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)
|
return(
|
||||||
|
I18n.t(
|
||||||
|
"chat_integration.provider.#{provider}.not_found.category",
|
||||||
|
name: category_name,
|
||||||
|
list: cat_list,
|
||||||
|
)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
category = nil # All categories
|
category = nil # All categories
|
||||||
|
@ -32,8 +37,8 @@ module DiscourseChatIntegration
|
||||||
# Every remaining token must be a tag. If not, abort and send help text
|
# Every remaining token must be a tag. If not, abort and send help text
|
||||||
while tokens.size > 0
|
while tokens.size > 0
|
||||||
token = tokens.shift
|
token = tokens.shift
|
||||||
if token.start_with?('tag:')
|
if token.start_with?("tag:")
|
||||||
tag_name = token.sub(/^tag:/, '')
|
tag_name = token.sub(/^tag:/, "")
|
||||||
else
|
else
|
||||||
return error_text
|
return error_text
|
||||||
end
|
end
|
||||||
|
@ -47,7 +52,12 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
category_id = category.nil? ? nil : category.id
|
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
|
when :created
|
||||||
I18n.t("chat_integration.provider.#{provider}.create.created")
|
I18n.t("chat_integration.provider.#{provider}.create.created")
|
||||||
when :updated
|
when :updated
|
||||||
|
@ -107,23 +117,25 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string",
|
text << I18n.t(
|
||||||
|
"chat_integration.provider.#{provider}.status.rule_string",
|
||||||
index: i,
|
index: i,
|
||||||
filter: rule.filter,
|
filter: rule.filter,
|
||||||
category: category_name
|
category: category_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
if SiteSetting.tagging_enabled && (!rule.tags.nil?)
|
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
|
end
|
||||||
|
|
||||||
text << "\n"
|
text << "\n"
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if rules.size == 0
|
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules") if rules.size == 0
|
||||||
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules")
|
|
||||||
end
|
|
||||||
|
|
||||||
text
|
text
|
||||||
end
|
end
|
||||||
|
@ -144,12 +156,15 @@ module DiscourseChatIntegration
|
||||||
# :created if a new rule has been created
|
# :created if a new rule has been created
|
||||||
# false if there was an error
|
# false if there was an error
|
||||||
def self.smart_create_rule(channel:, filter:, category_id: nil, tags: nil)
|
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
|
# Select the ones that have the same category
|
||||||
same_category = existing_rules.select { |rule| rule.category_id == category_id }
|
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
|
if same_category_and_tags.size > 0
|
||||||
# These rules have exactly the same criteria as what we're trying to create
|
# These rules have exactly the same criteria as what we're trying to create
|
||||||
|
@ -185,7 +200,9 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
# This rule is unique! Create a new one:
|
# 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
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -197,13 +214,13 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.formatted_display_name(user)
|
def self.formatted_display_name(user)
|
||||||
if !SiteSetting.enable_names || user.name.blank?
|
return "@#{user.username}" if !SiteSetting.enable_names || user.name.blank?
|
||||||
return "@#{user.username}"
|
|
||||||
end
|
|
||||||
|
|
||||||
full_name = user.name
|
full_name = user.name
|
||||||
full_name_normalized = User.normalize_username(full_name.strip)
|
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?
|
if similar && SiteSetting.prioritize_username_in_ux?
|
||||||
"@#{user.username}"
|
"@#{user.username}"
|
||||||
elsif similar
|
elsif similar
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
module Jobs
|
module Jobs
|
||||||
class DiscourseChatAddTypeField < ::Jobs::Onceoff
|
class DiscourseChatAddTypeField < ::Jobs::Onceoff
|
||||||
def execute_onceoff(args)
|
def execute_onceoff(args)
|
||||||
DiscourseChatIntegration::Rule.find_each do |rule|
|
DiscourseChatIntegration::Rule.find_each { |rule| rule.save(validate: false) }
|
||||||
rule.save(validate: false)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,26 +3,26 @@
|
||||||
module Jobs
|
module Jobs
|
||||||
class DiscourseChatMigrateFromSlackOfficial < ::Jobs::Onceoff
|
class DiscourseChatMigrateFromSlackOfficial < ::Jobs::Onceoff
|
||||||
def execute_onceoff(args)
|
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
|
if slack_installed
|
||||||
already_setup_rules = DiscourseChatIntegration::Channel.with_provider('slack').exists?
|
already_setup_rules = DiscourseChatIntegration::Channel.with_provider("slack").exists?
|
||||||
|
|
||||||
already_setup_sitesettings =
|
already_setup_sitesettings =
|
||||||
SiteSetting.chat_integration_slack_enabled ||
|
SiteSetting.chat_integration_slack_enabled ||
|
||||||
SiteSetting.chat_integration_slack_access_token.present? ||
|
SiteSetting.chat_integration_slack_access_token.present? ||
|
||||||
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
||||||
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
||||||
|
|
||||||
if !already_setup_rules && !already_setup_sitesettings
|
if !already_setup_rules && !already_setup_sitesettings
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
migrate_settings
|
migrate_settings
|
||||||
migrate_data
|
migrate_data
|
||||||
is_slack_enabled = site_settings_value('slack_enabled')
|
is_slack_enabled = site_settings_value("slack_enabled")
|
||||||
|
|
||||||
if is_slack_enabled
|
if is_slack_enabled
|
||||||
slack_enabled = SiteSetting.find_by(name: 'slack_enabled')
|
slack_enabled = SiteSetting.find_by(name: "slack_enabled")
|
||||||
slack_enabled.update!(value: 'f')
|
slack_enabled.update!(value: "f")
|
||||||
|
|
||||||
SiteSetting.chat_integration_slack_enabled = true
|
SiteSetting.chat_integration_slack_enabled = true
|
||||||
SiteSetting.chat_integration_enabled = true
|
SiteSetting.chat_integration_enabled = true
|
||||||
|
@ -30,44 +30,55 @@ module Jobs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate_data
|
def migrate_data
|
||||||
rows = []
|
rows = []
|
||||||
PluginStoreRow.where(plugin_name: 'discourse-slack-official')
|
PluginStoreRow
|
||||||
|
.where(plugin_name: "discourse-slack-official")
|
||||||
.where("key ~* :pat", pat: "^category_.*")
|
.where("key ~* :pat", pat: "^category_.*")
|
||||||
.each do |row|
|
.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|
|
next if !category_id.nil? && !Category.exists?(id: category_id)
|
||||||
category_id =
|
|
||||||
if row.key == 'category_*'
|
valid_tags = []
|
||||||
nil
|
valid_tags = Tag.where(name: rule[:tags]).pluck(:name) if rule[:tags]
|
||||||
else
|
|
||||||
row.key.gsub!('category_', '')
|
rows << {
|
||||||
row.key.to_i
|
category_id: category_id,
|
||||||
|
channel: rule[:channel],
|
||||||
|
filter: rule[:filter],
|
||||||
|
tags: valid_tags,
|
||||||
|
}
|
||||||
end
|
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
|
||||||
end
|
|
||||||
|
|
||||||
rows.each do |row|
|
rows.each do |row|
|
||||||
# Load an existing channel with this identifier. If none, create it
|
# Load an existing channel with this identifier. If none, create it
|
||||||
row[:channel] = "##{row[:channel]}" unless row[:channel].start_with?("#")
|
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
|
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
|
if !channel.id
|
||||||
Rails.logger.warn("Error creating channel for #{row}")
|
Rails.logger.warn("Error creating channel for #{row}")
|
||||||
next
|
next
|
||||||
|
@ -75,50 +86,58 @@ module Jobs
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create the rule, with clever logic for avoiding duplicates
|
# 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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def migrate_settings
|
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
|
SiteSetting.chat_integration_slack_access_token = slack_access_token
|
||||||
end
|
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
|
SiteSetting.chat_integration_slack_incoming_webhook_token = slack_incoming_webhook_token
|
||||||
end
|
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
|
SiteSetting.chat_integration_slack_excerpt_length = slack_discourse_excerpt_length
|
||||||
end
|
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
|
SiteSetting.chat_integration_slack_outbound_webhook_url = slack_outbound_webhook_url
|
||||||
end
|
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
|
SiteSetting.chat_integration_slack_icon_url = slack_icon_url
|
||||||
end
|
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
|
SiteSetting.chat_integration_delay_seconds = post_to_slack_window_secs
|
||||||
end
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
def site_settings_value(name)
|
def site_settings_value(name)
|
||||||
value = SiteSetting.find_by(name: name)&.value
|
value = SiteSetting.find_by(name: name)&.value
|
||||||
|
|
||||||
if value == 't'
|
if value == "t"
|
||||||
value = true
|
value = true
|
||||||
elsif value == 'f'
|
elsif value == "f"
|
||||||
value = false
|
value = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
# 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_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_initialize :init_data
|
||||||
after_destroy :destroy_rules
|
after_destroy :destroy_rules
|
||||||
|
@ -13,7 +14,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||||
validate :provider_valid?, :data_valid?
|
validate :provider_valid?, :data_valid?
|
||||||
|
|
||||||
def self.key_prefix
|
def self.key_prefix
|
||||||
'channel:'.freeze
|
"channel:".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def rules
|
def rules
|
||||||
|
@ -52,9 +53,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||||
|
|
||||||
data.each do |key, value|
|
data.each do |key, value|
|
||||||
regex_string = params.find { |p| p[:key] == key }[:regex]
|
regex_string = params.find { |p| p[:key] == key }[:regex]
|
||||||
if !Regexp.new(regex_string).match(value)
|
errors.add(:data, "data.#{key} is invalid") if !Regexp.new(regex_string).match(value)
|
||||||
errors.add(:data, "data.#{key} is invalid")
|
|
||||||
end
|
|
||||||
|
|
||||||
unique = params.find { |p| p[:key] == key }[:unique]
|
unique = params.find { |p| p[:key] == key }[:unique]
|
||||||
if unique
|
if unique
|
||||||
|
@ -63,8 +62,6 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if check_unique && matching_channels.exists?
|
errors.add(:data, "matches an existing channel") if check_unique && matching_channels.exists?
|
||||||
errors.add(:data, "matches an existing channel")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||||
PLUGIN_NAME = 'discourse-chat-integration'
|
PLUGIN_NAME = "discourse-chat-integration"
|
||||||
|
|
||||||
default_scope { self.default_scope }
|
default_scope { self.default_scope }
|
||||||
|
|
||||||
|
@ -9,13 +9,14 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||||
before_save :set_key
|
before_save :set_key
|
||||||
|
|
||||||
def self.default_scope
|
def self.default_scope
|
||||||
where(type_name: 'JSON')
|
where(type_name: "JSON").where(plugin_name: self::PLUGIN_NAME).where(
|
||||||
.where(plugin_name: self::PLUGIN_NAME)
|
"key LIKE ?",
|
||||||
.where("key LIKE ?", "#{self.key_prefix}%")
|
"#{self.key_prefix}%",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.key_prefix
|
def self.key_prefix
|
||||||
raise 'Not implemented'
|
raise "Not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -25,7 +26,7 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_plugin_model
|
def init_plugin_model
|
||||||
self.type_name ||= 'JSON'
|
self.type_name ||= "JSON"
|
||||||
self.plugin_name ||= PLUGIN_NAME
|
self.plugin_name ||= PLUGIN_NAME
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,5 +38,4 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||||
"#{self.key_prefix}#{max_id}"
|
"#{self.key_prefix}#{max_id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,54 +2,65 @@
|
||||||
|
|
||||||
class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
# 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_type, ->(type) { where("value::json->>'type'=?", type.to_s) }
|
||||||
scope :with_channel, ->(channel) { with_channel_id(channel.id) }
|
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_channel_id, ->(channel_id) { where("value::json->>'channel_id'=?", channel_id.to_s) }
|
||||||
|
|
||||||
scope :with_category_id, ->(category_id) do
|
scope :with_category_id,
|
||||||
if category_id.nil?
|
->(category_id) {
|
||||||
where("(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'")
|
if category_id.nil?
|
||||||
else
|
where(
|
||||||
where("value::json->>'category_id'=?", category_id.to_s)
|
"(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'",
|
||||||
end
|
)
|
||||||
end
|
else
|
||||||
|
where("value::json->>'category_id'=?", category_id.to_s)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
scope :with_group_ids, ->(group_id) do
|
scope :with_group_ids,
|
||||||
where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s))
|
->(group_id) { where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s)) }
|
||||||
end
|
|
||||||
|
|
||||||
scope :order_by_precedence, -> {
|
scope :order_by_precedence,
|
||||||
order("
|
-> {
|
||||||
|
order(
|
||||||
|
"
|
||||||
CASE
|
CASE
|
||||||
WHEN value::json->>'type' = 'group_mention' THEN 1
|
WHEN value::json->>'type' = 'group_mention' THEN 1
|
||||||
WHEN value::json->>'type' = 'group_message' THEN 2
|
WHEN value::json->>'type' = 'group_message' THEN 2
|
||||||
ELSE 3
|
ELSE 3
|
||||||
END
|
END
|
||||||
",
|
",
|
||||||
"
|
"
|
||||||
CASE
|
CASE
|
||||||
WHEN value::json->>'filter' = 'mute' THEN 1
|
WHEN value::json->>'filter' = 'mute' THEN 1
|
||||||
WHEN value::json->>'filter' = 'thread' THEN 2
|
WHEN value::json->>'filter' = 'thread' THEN 2
|
||||||
WHEN value::json->>'filter' = 'watch' THEN 3
|
WHEN value::json->>'filter' = 'watch' THEN 3
|
||||||
WHEN value::json->>'filter' = 'follow' THEN 4
|
WHEN value::json->>'filter' = 'follow' THEN 4
|
||||||
END
|
END
|
||||||
")
|
",
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
after_initialize :init_filter
|
after_initialize :init_filter
|
||||||
|
|
||||||
validates :filter, inclusion: { in: %w(thread watch follow mute),
|
validates :filter,
|
||||||
message: "%{value} is not a valid 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),
|
validates :type,
|
||||||
message: "%{value} is not a valid filter" }
|
inclusion: {
|
||||||
|
in: %w[normal group_message group_mention],
|
||||||
|
message: "%{value} is not a valid filter",
|
||||||
|
}
|
||||||
|
|
||||||
validate :channel_valid?, :category_valid?, :group_valid?, :tags_valid?
|
validate :channel_valid?, :category_valid?, :group_valid?, :tags_valid?
|
||||||
|
|
||||||
def self.key_prefix
|
def self.key_prefix
|
||||||
'rule:'.freeze
|
"rule:".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
# We never want an empty array, set it to nil instead
|
# We never want an empty array, set it to nil instead
|
||||||
|
@ -62,7 +73,7 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||||
end
|
end
|
||||||
|
|
||||||
# These are only allowed to be integers
|
# 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|
|
define_method "#{name}=" do |val|
|
||||||
if val.nil? || val.blank?
|
if val.nil? || val.blank?
|
||||||
super(nil)
|
super(nil)
|
||||||
|
@ -91,11 +102,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||||
end
|
end
|
||||||
|
|
||||||
def category_valid?
|
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")
|
errors.add(:category_id, "cannot be specified for that type of rule")
|
||||||
end
|
end
|
||||||
|
|
||||||
return unless type == 'normal'
|
return unless type == "normal"
|
||||||
|
|
||||||
if !(category_id.nil? || Category.where(id: category_id).exists?)
|
if !(category_id.nil? || Category.where(id: category_id).exists?)
|
||||||
errors.add(:category_id, "#{category_id} is not a valid category id")
|
errors.add(:category_id, "#{category_id} is not a valid category id")
|
||||||
|
@ -103,11 +114,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||||
end
|
end
|
||||||
|
|
||||||
def group_valid?
|
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")
|
errors.add(:group_id, "cannot be specified for that type of rule")
|
||||||
end
|
end
|
||||||
|
|
||||||
return if type == 'normal'
|
return if type == "normal"
|
||||||
|
|
||||||
if !Group.where(id: group_id).exists?
|
if !Group.where(id: group_id).exists?
|
||||||
errors.add(:group_id, "#{group_id} is not a valid group id")
|
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?
|
return if tags.nil?
|
||||||
|
|
||||||
tags.each do |tag|
|
tags.each do |tag|
|
||||||
if !Tag.where(name: tag).exists?
|
errors.add(:tags, "#{tag} is not a valid tag") if !Tag.where(name: tag).exists?
|
||||||
errors.add(:tags, "#{tag} is not a valid tag")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_filter
|
def init_filter
|
||||||
self.filter ||= 'watch'
|
self.filter ||= "watch"
|
||||||
self.type ||= 'normal'
|
self.type ||= "normal"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
Discourse::Application.routes.append do
|
Discourse::Application.routes.append do
|
||||||
mount ::DiscourseChatIntegration::AdminEngine, at: '/admin/plugins/chat-integration', constraints: AdminConstraint.new
|
mount ::DiscourseChatIntegration::AdminEngine,
|
||||||
mount ::DiscourseChatIntegration::PublicEngine, at: '/chat-transcript/', as: 'chat-transcript'
|
at: "/admin/plugins/chat-integration",
|
||||||
mount ::DiscourseChatIntegration::Provider::HookEngine, at: '/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
|
# 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
|
end
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_dependency 'admin_constraint'
|
require_dependency "admin_constraint"
|
||||||
|
|
||||||
module ::DiscourseChatIntegration
|
module ::DiscourseChatIntegration
|
||||||
AdminEngine.routes.draw do
|
AdminEngine.routes.draw do
|
||||||
get "" => "chat#respond"
|
get "" => "chat#respond"
|
||||||
get '/providers' => "chat#list_providers"
|
get "/providers" => "chat#list_providers"
|
||||||
post '/test' => "chat#test"
|
post "/test" => "chat#test"
|
||||||
|
|
||||||
get '/channels' => "chat#list_channels"
|
get "/channels" => "chat#list_channels"
|
||||||
post '/channels' => "chat#create_channel"
|
post "/channels" => "chat#create_channel"
|
||||||
put '/channels/:id' => "chat#update_channel"
|
put "/channels/:id" => "chat#update_channel"
|
||||||
delete '/channels/:id' => "chat#destroy_channel"
|
delete "/channels/:id" => "chat#destroy_channel"
|
||||||
|
|
||||||
post '/rules' => "chat#create_rule"
|
post "/rules" => "chat#create_rule"
|
||||||
put '/rules/:id' => "chat#update_rule"
|
put "/rules/:id" => "chat#update_rule"
|
||||||
delete '/rules/:id' => "chat#destroy_rule"
|
delete "/rules/:id" => "chat#destroy_rule"
|
||||||
|
|
||||||
get "/:provider" => "chat#respond"
|
get "/:provider" => "chat#respond"
|
||||||
end
|
end
|
||||||
|
|
||||||
PublicEngine.routes.draw do
|
PublicEngine.routes.draw { get "/:secret" => "public#post_transcript" }
|
||||||
get '/:secret' => "public#post_transcript"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative './rule_serializer'
|
require_relative "./rule_serializer"
|
||||||
|
|
||||||
class DiscourseChatIntegration::ChannelSerializer < ApplicationSerializer
|
class DiscourseChatIntegration::ChannelSerializer < ApplicationSerializer
|
||||||
attributes :id, :provider, :error_key, :error_info, :data, :rules
|
attributes :id, :provider, :error_key, :error_info, :data, :rules
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
module DiscourseChatIntegration
|
module DiscourseChatIntegration
|
||||||
module Manager
|
module Manager
|
||||||
|
|
||||||
def self.guardian
|
def self.guardian
|
||||||
Guardian.new(User.find_by(username: SiteSetting.chat_integration_discourse_username))
|
Guardian.new(User.find_by(username: SiteSetting.chat_integration_discourse_username))
|
||||||
end
|
end
|
||||||
|
@ -23,19 +22,26 @@ module DiscourseChatIntegration
|
||||||
if topic.archetype == Archetype.private_message
|
if topic.archetype == Archetype.private_message
|
||||||
group_ids_with_access = topic.topic_allowed_groups.pluck(:group_id)
|
group_ids_with_access = topic.topic_allowed_groups.pluck(:group_id)
|
||||||
return if group_ids_with_access.empty?
|
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
|
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
|
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
|
end
|
||||||
|
|
||||||
# If groups are mentioned, check for any matching rules and append them
|
# If groups are mentioned, check for any matching rules and append them
|
||||||
mentions = post.raw_mentions
|
mentions = post.raw_mentions
|
||||||
if mentions && mentions.length > 0
|
if mentions && mentions.length > 0
|
||||||
groups = Group.where('LOWER(name) IN (?)', mentions)
|
groups = Group.where("LOWER(name) IN (?)", mentions)
|
||||||
if groups.exists?
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -43,17 +49,19 @@ module DiscourseChatIntegration
|
||||||
# If tagging is enabled, thow away rules that don't apply to this topic
|
# If tagging is enabled, thow away rules that don't apply to this topic
|
||||||
if SiteSetting.tagging_enabled
|
if SiteSetting.tagging_enabled
|
||||||
topic_tags = topic.tags.present? ? topic.tags.pluck(:name) : []
|
topic_tags = topic.tags.present? ? topic.tags.pluck(:name) : []
|
||||||
matching_rules = matching_rules.select do |rule|
|
matching_rules =
|
||||||
next true if rule.tags.nil? || rule.tags.empty? # Filter has no tags specified
|
matching_rules.select do |rule|
|
||||||
any_tags_match = !((rule.tags & topic_tags).empty?)
|
next true if rule.tags.nil? || rule.tags.empty? # Filter has no tags specified
|
||||||
next any_tags_match # If any tags match, keep this filter, otherwise throw away
|
any_tags_match = !((rule.tags & topic_tags).empty?)
|
||||||
end
|
next any_tags_match # If any tags match, keep this filter, otherwise throw away
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sort by order of precedence
|
# Sort by order of precedence
|
||||||
t_prec = { 'group_message' => 0, 'group_mention' => 1, 'normal' => 2 } # Group things win
|
t_prec = { "group_message" => 0, "group_mention" => 1, "normal" => 2 } # Group things win
|
||||||
f_prec = { 'mute' => 0, 'thread' => 1, 'watch' => 2, 'follow' => 3 } #(mute always wins; thread beats watch beats follow)
|
f_prec = { "mute" => 0, "thread" => 1, "watch" => 2, "follow" => 3 } #(mute always wins; thread beats watch beats follow)
|
||||||
sort_func = proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
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)
|
matching_rules = matching_rules.sort(&sort_func)
|
||||||
|
|
||||||
# Take the first rule for each channel
|
# Take the first rule for each channel
|
||||||
|
@ -81,14 +89,15 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
begin
|
begin
|
||||||
provider.trigger_notification(post, channel, rule)
|
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
|
rescue => e
|
||||||
if e.class == (DiscourseChatIntegration::ProviderError) && e.info.key?(:error_key) && !e.info[:error_key].nil?
|
if e.class == (DiscourseChatIntegration::ProviderError) && e.info.key?(:error_key) &&
|
||||||
channel.update_attribute('error_key', e.info[:error_key])
|
!e.info[:error_key].nil?
|
||||||
|
channel.update_attribute("error_key", e.info[:error_key])
|
||||||
else
|
else
|
||||||
channel.update_attribute('error_key', 'chat_integration.channel_exception')
|
channel.update_attribute("error_key", "chat_integration.channel_exception")
|
||||||
end
|
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
|
# Log the error
|
||||||
# Discourse.handle_job_exception(e,
|
# Discourse.handle_job_exception(e,
|
||||||
|
@ -99,10 +108,7 @@ module DiscourseChatIntegration
|
||||||
# error_info: e.class == DiscourseChatIntegration::ProviderError ? e.info : nil }
|
# error_info: e.class == DiscourseChatIntegration::ProviderError ? e.info : nil }
|
||||||
# )
|
# )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
class AddUniqueIndexToSlackThreadTs < ActiveRecord::Migration[6.1]
|
class AddUniqueIndexToSlackThreadTs < ActiveRecord::Migration[6.1]
|
||||||
def up
|
def up
|
||||||
add_index :topic_custom_fields, [:topic_id, :name],
|
add_index :topic_custom_fields,
|
||||||
unique: true,
|
%i[topic_id name],
|
||||||
where: "(name LIKE 'slack_thread_id_%')",
|
unique: true,
|
||||||
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
where: "(name LIKE 'slack_thread_id_%')",
|
||||||
|
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,15 +12,11 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
def self.providers
|
def self.providers
|
||||||
constants.select do |constant|
|
constants.select { |constant| constant.to_s =~ /Provider$/ }.map(&method(:const_get))
|
||||||
constant.to_s =~ /Provider$/
|
|
||||||
end.map(&method(:const_get))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.enabled_providers
|
def self.enabled_providers
|
||||||
self.providers.select do |provider|
|
self.providers.select { |provider| self.is_enabled(provider) }
|
||||||
self.is_enabled(provider)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.provider_names
|
def self.provider_names
|
||||||
|
@ -36,7 +32,7 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.is_enabled(provider)
|
def self.is_enabled(provider)
|
||||||
if defined? provider::PROVIDER_ENABLED_SETTING
|
if defined?(provider::PROVIDER_ENABLED_SETTING)
|
||||||
SiteSetting.public_send(provider::PROVIDER_ENABLED_SETTING)
|
SiteSetting.public_send(provider::PROVIDER_ENABLED_SETTING)
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
@ -51,9 +47,10 @@ module DiscourseChatIntegration
|
||||||
class HookController < ::ApplicationController
|
class HookController < ::ApplicationController
|
||||||
requires_plugin DiscourseChatIntegration::PLUGIN_NAME
|
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)
|
rescue_discourse_actions(:not_found, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -72,22 +69,20 @@ module DiscourseChatIntegration
|
||||||
def self.mount_engines
|
def self.mount_engines
|
||||||
engines = []
|
engines = []
|
||||||
DiscourseChatIntegration::Provider.providers.each do |provider|
|
DiscourseChatIntegration::Provider.providers.each do |provider|
|
||||||
engine = provider.constants.select do |constant|
|
engine =
|
||||||
constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine")
|
provider
|
||||||
end.map(&provider.method(:const_get)).first
|
.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) if engine
|
||||||
engines.push(engine: engine, name: provider::PROVIDER_NAME)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
DiscourseChatIntegration::Provider::HookEngine.routes.draw do
|
DiscourseChatIntegration::Provider::HookEngine.routes.draw do
|
||||||
engines.each do |engine|
|
engines.each { |engine| mount engine[:engine], at: engine[:name] }
|
||||||
mount engine[:engine], at: engine[:name]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,12 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+' },
|
{ 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
|
].freeze
|
||||||
|
|
||||||
def self.send_message(url, message)
|
def self.send_message(url, message)
|
||||||
|
@ -17,7 +22,7 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
uri = URI(url)
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
|
@ -25,7 +30,7 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_protocol(url)
|
def self.ensure_protocol(url)
|
||||||
return url if !url.start_with?('//')
|
return url if !url.start_with?("//")
|
||||||
"http:#{url}"
|
"http:#{url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,24 +39,40 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.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
|
end
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
content: SiteSetting.chat_integration_discord_message_content,
|
content: SiteSetting.chat_integration_discord_message_content,
|
||||||
embeds: [{
|
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,
|
title:
|
||||||
description: post.excerpt(SiteSetting.chat_integration_discord_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||||
url: post.full_url,
|
color: topic.category ? topic.category.color.to_i(16) : nil,
|
||||||
author: {
|
description:
|
||||||
name: display_name,
|
post.excerpt(
|
||||||
url: Discourse.base_url + "/u/" + post.user.username,
|
SiteSetting.chat_integration_discord_excerpt_length,
|
||||||
icon_url: ensure_protocol(post.user.small_avatar_url)
|
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
|
message
|
||||||
|
@ -59,17 +80,20 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
# Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously
|
# 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)
|
message = generate_discord_message(post)
|
||||||
response = send_message(webhook_url, message)
|
response = send_message(webhook_url, message)
|
||||||
|
|
||||||
if !response.kind_of?(Net::HTTPSuccess)
|
if !response.kind_of?(Net::HTTPSuccess)
|
||||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||||
error_key: nil, message: message, response_body: response.body
|
info: {
|
||||||
})
|
error_key: nil,
|
||||||
|
message: message,
|
||||||
|
response_body: response.body,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module DiscourseChatIntegration::Provider::FlowdockProvider
|
module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||||
|
|
||||||
PROVIDER_NAME = "flowdock".freeze
|
PROVIDER_NAME = "flowdock".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_flowdock_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_flowdock_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "flow_token", regex: '^\S+', unique: true, hidden: true }]
|
||||||
{ key: "flow_token", regex: '^\S+', unique: true, hidden: true },
|
|
||||||
]
|
|
||||||
|
|
||||||
def self.send_message(url, message)
|
def self.send_message(url, message)
|
||||||
uri = URI(url)
|
uri = URI(url)
|
||||||
|
@ -14,7 +11,7 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||||
http.use_ssl = true
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
|
@ -29,15 +26,21 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||||
event: "discussion",
|
event: "discussion",
|
||||||
author: {
|
author: {
|
||||||
name: display_name,
|
name: display_name,
|
||||||
avatar: post.user.small_avatar_url
|
avatar: post.user.small_avatar_url,
|
||||||
},
|
},
|
||||||
title: I18n.t("chat_integration.provider.flowdock.message_title"),
|
title: I18n.t("chat_integration.provider.flowdock.message_title"),
|
||||||
external_thread_id: post.topic.id,
|
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: {
|
thread: {
|
||||||
title: post.topic.title,
|
title: post.topic.title,
|
||||||
external_url: post.full_url
|
external_url: post.full_url,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
message
|
message
|
||||||
|
@ -50,7 +53,11 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||||
|
|
||||||
unless response.kind_of?(Net::HTTPSuccess)
|
unless response.kind_of?(Net::HTTPSuccess)
|
||||||
error_key = nil
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,19 +3,28 @@
|
||||||
module DiscourseChatIntegration
|
module DiscourseChatIntegration
|
||||||
module Provider
|
module Provider
|
||||||
module GitterProvider
|
module GitterProvider
|
||||||
PROVIDER_NAME = 'gitter'.freeze
|
PROVIDER_NAME = "gitter".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_gitter_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_gitter_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+$', unique: true },
|
{ 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)
|
def self.trigger_notification(post, channel, rule)
|
||||||
message = gitter_message(post)
|
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
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
error_key = nil
|
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
|
end
|
||||||
|
|
||||||
|
@ -23,7 +32,14 @@ module DiscourseChatIntegration
|
||||||
display_name = post.user.username
|
display_name = post.user.username
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
parent_category = topic.category.try :parent_category
|
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})"
|
"[__#{display_name}__ - #{topic.title} - #{category_name}](#{post.full_url})"
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,26 +3,35 @@
|
||||||
module DiscourseChatIntegration
|
module DiscourseChatIntegration
|
||||||
module Provider
|
module Provider
|
||||||
module GoogleProvider
|
module GoogleProvider
|
||||||
PROVIDER_NAME = 'google'.freeze
|
PROVIDER_NAME = "google".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_google_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_google_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+$', unique: true },
|
{ 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)
|
def self.trigger_notification(post, channel, rule)
|
||||||
message = get_message(post)
|
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 = 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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,49 +44,67 @@ module DiscourseChatIntegration
|
||||||
widgets: [
|
widgets: [
|
||||||
{
|
{
|
||||||
keyValue: {
|
keyValue: {
|
||||||
"topLabel": I18n.t("chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}", site_title: SiteSetting.title),
|
topLabel:
|
||||||
"content": post.topic.title,
|
I18n.t(
|
||||||
"contentMultiline": "false",
|
"chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}",
|
||||||
"bottomLabel": I18n.t("chat_integration.provider.google.author", username: post.user.username),
|
site_title: SiteSetting.title,
|
||||||
"onClick": {
|
),
|
||||||
"openLink": {
|
content: post.topic.title,
|
||||||
"url": post.full_url
|
contentMultiline: "false",
|
||||||
}
|
bottomLabel:
|
||||||
}
|
I18n.t(
|
||||||
}
|
"chat_integration.provider.google.author",
|
||||||
|
username: post.user.username,
|
||||||
|
),
|
||||||
|
onClick: {
|
||||||
|
openLink: {
|
||||||
|
url: post.full_url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
widgets: [
|
widgets: [
|
||||||
{
|
{
|
||||||
textParagraph: {
|
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: [
|
widgets: [
|
||||||
{
|
{
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
"textButton": {
|
textButton: {
|
||||||
"text": I18n.t("chat_integration.provider.google.link", site_title: SiteSetting.title),
|
text:
|
||||||
"onClick": {
|
I18n.t(
|
||||||
"openLink": {
|
"chat_integration.provider.google.link",
|
||||||
"url": post.full_url
|
site_title: SiteSetting.title,
|
||||||
}
|
),
|
||||||
}
|
onClick: {
|
||||||
}
|
openLink: {
|
||||||
|
url: post.full_url,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,27 +2,32 @@
|
||||||
module DiscourseChatIntegration::Provider::GroupmeProvider
|
module DiscourseChatIntegration::Provider::GroupmeProvider
|
||||||
PROVIDER_NAME = "groupme".freeze
|
PROVIDER_NAME = "groupme".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_groupme_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_groupme_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }]
|
||||||
{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
def self.generate_groupme_message(post)
|
def self.generate_groupme_message(post)
|
||||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "#{I18n.t('uncategorized_category_name')}"
|
category = "#{I18n.t("uncategorized_category_name")}"
|
||||||
elsif topic.category
|
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
|
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})"
|
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)}"
|
post_excerpt =
|
||||||
data = {
|
"#{post.excerpt(SiteSetting.chat_integration_groupme_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)}"
|
||||||
text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}"
|
data = { text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}" }
|
||||||
}
|
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,33 +40,39 @@ module DiscourseChatIntegration::Provider::GroupmeProvider
|
||||||
instance_names = SiteSetting.chat_integration_groupme_instance_names.split(/\s*,\s*/)
|
instance_names = SiteSetting.chat_integration_groupme_instance_names.split(/\s*,\s*/)
|
||||||
|
|
||||||
unless instance_names.length() == bot_ids.length()
|
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
|
end
|
||||||
|
|
||||||
name_to_id = Hash[instance_names.zip(bot_ids)]
|
name_to_id = Hash[instance_names.zip(bot_ids)]
|
||||||
user_input_channel = channel.data['groupme_instance_name'].strip
|
user_input_channel = channel.data["groupme_instance_name"].strip
|
||||||
unless user_input_channel.eql? 'all'
|
instance_names = [user_input_channel] unless user_input_channel.eql? "all"
|
||||||
instance_names = [user_input_channel]
|
instance_names.each do |instance_name|
|
||||||
end
|
|
||||||
instance_names.each { |instance_name|
|
|
||||||
bot_id = name_to_id["#{instance_name}"]
|
bot_id = name_to_id["#{instance_name}"]
|
||||||
uri = URI("https://api.groupme.com/v3/bots/post")
|
uri = URI("https://api.groupme.com/v3/bots/post")
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
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")
|
||||||
message[:bot_id] = bot_id
|
message[:bot_id] = bot_id
|
||||||
req.body = message.to_json
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
num_errors += 1
|
num_errors += 1
|
||||||
if response.code.to_s == '404'
|
if response.code.to_s == "404"
|
||||||
error_key = 'chat_integration.provider.groupme.errors.not_found'
|
error_key = "chat_integration.provider.groupme.errors.not_found"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
||||||
}
|
end
|
||||||
if last_error_raised
|
if last_error_raised
|
||||||
successfully_sent = instance_names.length() - num_errors
|
successfully_sent = instance_names.length() - num_errors
|
||||||
last_error_raised[:success_rate] = "#{successfully_sent}/#{instance_names.length()}"
|
last_error_raised[:success_rate] = "#{successfully_sent}/#{instance_names.length()}"
|
||||||
|
|
|
@ -7,27 +7,43 @@ module DiscourseChatIntegration
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_guilded_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_guilded_enabled
|
||||||
|
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+' },
|
{ key: "name", regex: '^\S+' },
|
||||||
{ key: "webhook_url", regex: '^https:\/\/media\.guilded\.gg\/webhooks\/', unique: true, hidden: true }
|
{
|
||||||
|
key: "webhook_url",
|
||||||
|
regex: '^https:\/\/media\.guilded\.gg\/webhooks\/',
|
||||||
|
unique: true,
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
webhook_url = channel.data['webhook_url']
|
webhook_url = channel.data["webhook_url"]
|
||||||
message = generate_guilded_message(post)
|
message = generate_guilded_message(post)
|
||||||
response = send_message(webhook_url, message)
|
response = send_message(webhook_url, message)
|
||||||
|
|
||||||
if !response.kind_of?(Net::HTTPSuccess)
|
if !response.kind_of?(Net::HTTPSuccess)
|
||||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||||
error_key: nil, message: message, response_body: response.body
|
info: {
|
||||||
})
|
error_key: nil,
|
||||||
|
message: message,
|
||||||
|
response_body: response.body,
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_guilded_message(post)
|
def self.generate_guilded_message(post)
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
category = ''
|
category = ""
|
||||||
if topic.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
|
end
|
||||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||||
|
|
||||||
|
@ -37,15 +53,24 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
embeds: [{
|
embeds: [
|
||||||
title: "#{topic.title} #{(category == '[uncategorized]') ? '' : category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
{
|
||||||
url: post.full_url,
|
title:
|
||||||
description: post.excerpt(SiteSetting.chat_integration_guilded_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||||
footer: {
|
url: post.full_url,
|
||||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
description:
|
||||||
text: "#{display_name} | #{post.created_at}"
|
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
|
message
|
||||||
|
@ -54,9 +79,9 @@ module DiscourseChatIntegration
|
||||||
def self.send_message(url, message)
|
def self.send_message(url, message)
|
||||||
uri = URI(url)
|
uri = URI(url)
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
|
@ -64,10 +89,9 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_protocol(url)
|
def self.ensure_protocol(url)
|
||||||
return url if !url.start_with?('//')
|
return url if !url.start_with?("//")
|
||||||
"http:#{url}"
|
"http:#{url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,25 +6,27 @@ module DiscourseChatIntegration
|
||||||
PROVIDER_NAME = "matrix".freeze
|
PROVIDER_NAME = "matrix".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_matrix_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_matrix_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+' },
|
{ key: "name", regex: '^\S+' },
|
||||||
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true }
|
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.send_message(room_id, message)
|
def self.send_message(room_id, message)
|
||||||
homeserver = SiteSetting.chat_integration_matrix_homeserver
|
homeserver = SiteSetting.chat_integration_matrix_homeserver
|
||||||
event_type = 'm.room.message'
|
event_type = "m.room.message"
|
||||||
uid = Time.now.to_i
|
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 = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||||
http.use_ssl = true
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
|
@ -35,16 +37,29 @@ module DiscourseChatIntegration
|
||||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
msgtype: SiteSetting.chat_integration_matrix_use_notice ? 'm.notice' : 'm.text',
|
msgtype: SiteSetting.chat_integration_matrix_use_notice ? "m.notice" : "m.text",
|
||||||
body: I18n.t('chat_integration.provider.matrix.text_message', user: display_name,
|
body:
|
||||||
post_url: post.full_url,
|
I18n.t(
|
||||||
title: post.topic.title),
|
"chat_integration.provider.matrix.text_message",
|
||||||
format: 'org.matrix.custom.html',
|
user: display_name,
|
||||||
formatted_body: I18n.t('chat_integration.provider.matrix.formatted_message', user: display_name,
|
post_url: post.full_url,
|
||||||
post_url: post.full_url,
|
title: post.topic.title,
|
||||||
title: post.topic.title,
|
),
|
||||||
excerpt: post.excerpt(SiteSetting.chat_integration_matrix_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true))
|
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
|
message
|
||||||
|
@ -53,24 +68,26 @@ module DiscourseChatIntegration
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
message = generate_matrix_message(post)
|
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)
|
if !response.kind_of?(Net::HTTPSuccess)
|
||||||
error_key = nil
|
error_key = nil
|
||||||
begin
|
begin
|
||||||
responseData = JSON.parse(response.body)
|
responseData = JSON.parse(response.body)
|
||||||
if responseData['errcode'] == "M_UNKNOWN_TOKEN"
|
if responseData["errcode"] == "M_UNKNOWN_TOKEN"
|
||||||
error_key = 'chat_integration.provider.matrix.errors.unknown_token'
|
error_key = "chat_integration.provider.matrix.errors.unknown_token"
|
||||||
elsif responseData['errcode'] == "M_UNKNOWN"
|
elsif responseData["errcode"] == "M_UNKNOWN"
|
||||||
error_key = 'chat_integration.provider.matrix.errors.unknown_room'
|
error_key = "chat_integration.provider.matrix.errors.unknown_room"
|
||||||
end
|
end
|
||||||
ensure
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,22 +15,18 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||||
def command
|
def command
|
||||||
text = process_command(params)
|
text = process_command(params)
|
||||||
|
|
||||||
render json: {
|
render json: { response_type: "ephemeral", text: text }
|
||||||
response_type: 'ephemeral',
|
|
||||||
text: text
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_command(params)
|
def process_command(params)
|
||||||
|
|
||||||
tokens = params[:text].split(" ")
|
tokens = params[:text].split(" ")
|
||||||
|
|
||||||
# channel name fix
|
# channel name fix
|
||||||
channel_id =
|
channel_id =
|
||||||
case params[:channel_name]
|
case params[:channel_name]
|
||||||
when 'directmessage'
|
when "directmessage"
|
||||||
"@#{params[:user_name]}"
|
"@#{params[:user_name]}"
|
||||||
when 'privategroup'
|
when "privategroup"
|
||||||
params[:channel_id]
|
params[:channel_id]
|
||||||
else
|
else
|
||||||
"##{params[:channel_name]}"
|
"##{params[:channel_name]}"
|
||||||
|
@ -38,21 +34,29 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||||
|
|
||||||
provider = DiscourseChatIntegration::Provider::MattermostProvider::PROVIDER_NAME
|
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
|
# 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)
|
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mattermost_token_valid?
|
def mattermost_token_valid?
|
||||||
params.require(:token)
|
params.require(:token)
|
||||||
|
|
||||||
if SiteSetting.chat_integration_mattermost_incoming_webhook_token.blank? ||
|
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
|
raise Discourse::InvalidAccess.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -63,8 +67,5 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||||
isolate_namespace DiscourseChatIntegration::Provider::MattermostProvider
|
isolate_namespace DiscourseChatIntegration::Provider::MattermostProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
MattermostEngine.routes.draw do
|
MattermostEngine.routes.draw { post "command" => "mattermost_command#command" }
|
||||||
post "command" => "mattermost_command#command"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,29 +5,30 @@ module DiscourseChatIntegration
|
||||||
module MattermostProvider
|
module MattermostProvider
|
||||||
PROVIDER_NAME = "mattermost".freeze
|
PROVIDER_NAME = "mattermost".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_mattermost_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_mattermost_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
def self.send_via_webhook(message)
|
def self.send_via_webhook(message)
|
||||||
|
|
||||||
uri = URI(SiteSetting.chat_integration_mattermost_webhook_url)
|
uri = URI(SiteSetting.chat_integration_mattermost_webhook_url)
|
||||||
|
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
if response.body.include? "Couldn't find the channel"
|
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
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.mattermost_message(post, channel)
|
def self.mattermost_message(post, channel)
|
||||||
|
@ -35,17 +36,26 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||||
elsif topic.category
|
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
|
end
|
||||||
|
|
||||||
icon_url =
|
icon_url =
|
||||||
if SiteSetting.chat_integration_mattermost_icon_url.present?
|
if SiteSetting.chat_integration_mattermost_icon_url.present?
|
||||||
UrlHelper.absolute(SiteSetting.chat_integration_mattermost_icon_url)
|
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)
|
UrlHelper.absolute(url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -53,7 +63,7 @@ module DiscourseChatIntegration
|
||||||
channel: channel,
|
channel: channel,
|
||||||
username: SiteSetting.title || "Discourse",
|
username: SiteSetting.title || "Discourse",
|
||||||
icon_url: icon_url,
|
icon_url: icon_url,
|
||||||
attachments: []
|
attachments: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
summary = {
|
summary = {
|
||||||
|
@ -61,8 +71,15 @@ module DiscourseChatIntegration
|
||||||
author_name: display_name,
|
author_name: display_name,
|
||||||
author_icon: post.user.small_avatar_url,
|
author_icon: post.user.small_avatar_url,
|
||||||
color: topic.category ? "##{topic.category.color}" : nil,
|
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),
|
text:
|
||||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
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,
|
title_link: post.full_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,12 +88,11 @@ module DiscourseChatIntegration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
channel_id = channel.data['identifier']
|
channel_id = channel.data["identifier"]
|
||||||
message = mattermost_message(post, channel_id)
|
message = mattermost_message(post, channel_id)
|
||||||
|
|
||||||
self.send_via_webhook(message)
|
self.send_via_webhook(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,36 +5,45 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
||||||
|
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_rocketchat_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_rocketchat_enabled
|
||||||
|
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
def self.rocketchat_message(post, channel)
|
def self.rocketchat_message(post, channel)
|
||||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||||
elsif topic.category
|
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
|
end
|
||||||
|
|
||||||
message = {
|
message = { channel: channel, attachments: [] }
|
||||||
channel: channel,
|
|
||||||
attachments: []
|
|
||||||
}
|
|
||||||
|
|
||||||
summary = {
|
summary = {
|
||||||
fallback: "#{topic.title} - #{display_name}",
|
fallback: "#{topic.title} - #{display_name}",
|
||||||
author_name: display_name,
|
author_name: display_name,
|
||||||
author_icon: post.user.small_avatar_url,
|
author_icon: post.user.small_avatar_url,
|
||||||
color: topic.category ? "##{topic.category.color}" : nil,
|
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"],
|
mrkdwn_in: ["text"],
|
||||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
title:
|
||||||
title_link: post.full_url
|
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||||
|
title_link: post.full_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
message[:attachments].push(summary)
|
message[:attachments].push(summary)
|
||||||
|
@ -46,25 +55,29 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
||||||
uri = URI(SiteSetting.chat_integration_rocketchat_webhook_url)
|
uri = URI(SiteSetting.chat_integration_rocketchat_webhook_url)
|
||||||
|
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
if response.body.include?('invalid-channel')
|
if response.body.include?("invalid-channel")
|
||||||
error_key = 'chat_integration.provider.rocketchat.errors.invalid_channel'
|
error_key = "chat_integration.provider.rocketchat.errors.invalid_channel"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
channel_id = channel.data['identifier']
|
channel_id = channel.data["identifier"]
|
||||||
message = rocketchat_message(post, channel_id)
|
message = rocketchat_message(post, channel_id)
|
||||||
|
|
||||||
self.send_via_webhook(message)
|
self.send_via_webhook(message)
|
||||||
|
|
|
@ -11,7 +11,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
:preload_json,
|
:preload_json,
|
||||||
:verify_authenticity_token,
|
:verify_authenticity_token,
|
||||||
:redirect_to_login_if_required,
|
:redirect_to_login_if_required,
|
||||||
only: [:command, :interactive]
|
only: %i[command interactive]
|
||||||
|
|
||||||
def command
|
def command
|
||||||
message = process_command(params)
|
message = process_command(params)
|
||||||
|
@ -28,15 +28,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
private
|
private
|
||||||
|
|
||||||
def process_command(params)
|
def process_command(params)
|
||||||
|
|
||||||
tokens = params[:text].split(" ")
|
tokens = params[:text].split(" ")
|
||||||
|
|
||||||
# channel name fix
|
# channel name fix
|
||||||
channel_id =
|
channel_id =
|
||||||
case params[:channel_name]
|
case params[:channel_name]
|
||||||
when 'directmessage'
|
when "directmessage"
|
||||||
"@#{params[:user_name]}"
|
"@#{params[:user_name]}"
|
||||||
when 'privategroup'
|
when "privategroup"
|
||||||
params[:channel_id]
|
params[:channel_id]
|
||||||
else
|
else
|
||||||
"##{params[:channel_name]}"
|
"##{params[:channel_name]}"
|
||||||
|
@ -44,17 +43,28 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
provider = DiscourseChatIntegration::Provider::SlackProvider::PROVIDER_NAME
|
provider = DiscourseChatIntegration::Provider::SlackProvider::PROVIDER_NAME
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.with_provider(provider)
|
channel =
|
||||||
.with_data_value('identifier', channel_id)
|
DiscourseChatIntegration::Channel
|
||||||
.first
|
.with_provider(provider)
|
||||||
|
.with_data_value("identifier", channel_id)
|
||||||
|
.first
|
||||||
|
|
||||||
channel ||= DiscourseChatIntegration::Channel.create!(
|
channel ||=
|
||||||
provider: provider,
|
DiscourseChatIntegration::Channel.create!(
|
||||||
data: { identifier: channel_id }
|
provider: provider,
|
||||||
)
|
data: {
|
||||||
|
identifier: channel_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if tokens[0] == 'post'
|
if tokens[0] == "post"
|
||||||
process_post_request(channel, tokens, params[:channel_id], channel_id, params[:response_url])
|
process_post_request(
|
||||||
|
channel,
|
||||||
|
tokens,
|
||||||
|
params[:channel_id],
|
||||||
|
channel_id,
|
||||||
|
params[:response_url],
|
||||||
|
)
|
||||||
else
|
else
|
||||||
{ text: ::DiscourseChatIntegration::Helper.process_command(channel, tokens) }
|
{ text: ::DiscourseChatIntegration::Helper.process_command(channel, tokens) }
|
||||||
end
|
end
|
||||||
|
@ -66,9 +76,10 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
Scheduler::Defer.later "Processing slack transcript request" do
|
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
|
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
|
req.body = response.to_json
|
||||||
http.request(req)
|
http.request(req)
|
||||||
end
|
end
|
||||||
|
@ -81,15 +92,16 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
first_message_ts = nil
|
first_message_ts = nil
|
||||||
requested_thread_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})\/?$/
|
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])
|
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])
|
elsif tokens.size > 1 && match = thread_url_regex.match(tokens[1])
|
||||||
requested_thread_ts = match.captures[0]
|
requested_thread_ts = match.captures[0]
|
||||||
elsif tokens.size > 1 && match = slack_url_regex.match(tokens[1])
|
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
|
elsif tokens.size > 1
|
||||||
begin
|
begin
|
||||||
requested_messages = Integer(tokens[1], 10)
|
requested_messages = Integer(tokens[1], 10)
|
||||||
|
@ -100,12 +112,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
error_key = "chat_integration.provider.slack.transcript.error"
|
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}_users") } unless transcript.load_user_data
|
||||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||||
|
|
||||||
if first_message_ts
|
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
|
elsif requested_messages
|
||||||
transcript.set_first_message_by_index(-requested_messages)
|
transcript.set_first_message_by_index(-requested_messages)
|
||||||
else
|
else
|
||||||
|
@ -123,22 +144,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
# Do nothing
|
# Do nothing
|
||||||
elsif json[:type] == "message_action" && json[:message][:thread_ts]
|
elsif json[:type] == "message_action" && json[:message][:thread_ts]
|
||||||
# Context menu used on a threaded message
|
# Context menu used on a threaded message
|
||||||
transcript = SlackTranscript.new(
|
transcript =
|
||||||
channel_name: "##{json[:channel][:name]}",
|
SlackTranscript.new(
|
||||||
channel_id: json[:channel][:id],
|
channel_name: "##{json[:channel][:name]}",
|
||||||
requested_thread_ts: json[:message][:thread_ts]
|
channel_id: json[:channel][:id],
|
||||||
)
|
requested_thread_ts: json[:message][:thread_ts],
|
||||||
|
)
|
||||||
|
|
||||||
# Send a loading modal within 3 seconds:
|
# Send a loading modal within 3 seconds:
|
||||||
req = Net::HTTP::Post.new(
|
req =
|
||||||
"https://slack.com/api/views.open",
|
Net::HTTP::Post.new(
|
||||||
'Content-Type' => 'application/json',
|
"https://slack.com/api/views.open",
|
||||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
"Content-Type" => "application/json",
|
||||||
)
|
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||||
req.body = {
|
)
|
||||||
"trigger_id": json[:trigger_id],
|
req.body = { trigger_id: json[:trigger_id], view: transcript.build_modal_ui }.to_json
|
||||||
"view": transcript.build_modal_ui
|
|
||||||
}.to_json
|
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
view_id = JSON.parse(response.body).dig("view", "id")
|
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
|
error_view = generate_error_view("history") unless transcript.load_chat_history
|
||||||
|
|
||||||
# Then update the modal with the transcript link:
|
# Then update the modal with the transcript link:
|
||||||
req = Net::HTTP::Post.new(
|
req =
|
||||||
"https://slack.com/api/views.update",
|
Net::HTTP::Post.new(
|
||||||
'Content-Type' => 'application/json',
|
"https://slack.com/api/views.update",
|
||||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
"Content-Type" => "application/json",
|
||||||
)
|
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||||
req.body = {
|
)
|
||||||
"view_id": view_id,
|
req.body = { view_id: view_id, view: error_view || transcript.build_modal_ui }.to_json
|
||||||
"view": error_view || transcript.build_modal_ui
|
|
||||||
}.to_json
|
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
else
|
else
|
||||||
# Button clicked in one of our interactive messages
|
# 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
|
req.body = build_interactive_response(json).to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
end
|
end
|
||||||
|
@ -177,26 +195,33 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
constant_val = json[:callback_id]
|
constant_val = json[:callback_id]
|
||||||
changed_val = json[:actions][0][:selected_options][0][:value]
|
changed_val = json[:actions][0][:selected_options][0][:value]
|
||||||
|
|
||||||
first_message = (action_name == 'first_message') ? changed_val : constant_val
|
first_message = (action_name == "first_message") ? changed_val : constant_val
|
||||||
last_message = (action_name == 'first_message') ? constant_val : changed_val
|
last_message = (action_name == "first_message") ? constant_val : changed_val
|
||||||
end
|
end
|
||||||
|
|
||||||
error_key = "chat_integration.provider.slack.transcript.error"
|
error_key = "chat_integration.provider.slack.transcript.error"
|
||||||
|
|
||||||
return { text: I18n.t(error_key) } unless transcript = SlackTranscript.new(
|
unless transcript =
|
||||||
channel_name: "##{json[:channel][:name]}",
|
SlackTranscript.new(
|
||||||
channel_id: json[:channel][:id],
|
channel_name: "##{json[:channel][:name]}",
|
||||||
requested_thread_ts: requested_thread
|
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}_users") } unless transcript.load_user_data
|
||||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||||
|
|
||||||
if first_message
|
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
|
end
|
||||||
|
|
||||||
if last_message
|
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
|
end
|
||||||
|
|
||||||
transcript.build_slack_ui
|
transcript.build_slack_ui
|
||||||
|
@ -210,17 +235,11 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
type: "modal",
|
type: "modal",
|
||||||
title: {
|
title: {
|
||||||
type: "plain_text",
|
type: "plain_text",
|
||||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title")
|
text: I18n.t("chat_integration.provider.slack.transcript.modal_title"),
|
||||||
},
|
},
|
||||||
blocks: [
|
blocks: [
|
||||||
{
|
{ type: "section", text: { type: "mrkdwn", text: ":warning: *#{I18n.t(error_key)}*" } },
|
||||||
type: "section",
|
],
|
||||||
text: {
|
|
||||||
type: "mrkdwn",
|
|
||||||
text: ":warning: *#{I18n.t(error_key)}*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -228,8 +247,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
params.require(:token)
|
params.require(:token)
|
||||||
|
|
||||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
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
|
raise Discourse::InvalidAccess.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -240,8 +258,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
json = JSON.parse(params[:payload], symbolize_names: true)
|
json = JSON.parse(params[:payload], symbolize_names: true)
|
||||||
|
|
||||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
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
|
raise Discourse::InvalidAccess.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -256,5 +273,4 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
post "command" => "slack_command#command"
|
post "command" => "slack_command#command"
|
||||||
post "interactive" => "slack_command#interactive"
|
post "interactive" => "slack_command#interactive"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,13 +6,15 @@ class ChatIntegrationSlackEnabledSettingValidator
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_value?(val)
|
def valid_value?(val)
|
||||||
return true if val == ('f') || val == (false)
|
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?
|
if SiteSetting.chat_integration_slack_outbound_webhook_url.blank? &&
|
||||||
|
SiteSetting.chat_integration_slack_access_token.blank?
|
||||||
|
return false
|
||||||
|
end
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_message
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
user["_transcript_username"]
|
user["_transcript_username"]
|
||||||
elsif @raw.key?("username")
|
elsif @raw.key?("username")
|
||||||
# This is for bot messages
|
# This is for bot messages
|
||||||
@raw["username"].gsub(' ', '_')
|
@raw["username"].gsub(" ", "_")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -22,74 +22,69 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
def url
|
def url
|
||||||
channel_id = @transcript.channel_id
|
channel_id = @transcript.channel_id
|
||||||
ts = @raw['ts'].gsub('.', '')
|
ts = @raw["ts"].gsub(".", "")
|
||||||
"https://slack.com/archives/#{channel_id}/p#{ts}"
|
"https://slack.com/archives/#{channel_id}/p#{ts}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def text
|
def text
|
||||||
text = @raw['text'].nil? ? "" : @raw['text']
|
text = @raw["text"].nil? ? "" : @raw["text"]
|
||||||
|
|
||||||
pre = {}
|
pre = {}
|
||||||
|
|
||||||
# Extract code blocks and replace with placeholder
|
# Extract code blocks and replace with placeholder
|
||||||
text = text.gsub(/```(.*?)```/m) do |match|
|
text =
|
||||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
text.gsub(/```(.*?)```/m) do |match|
|
||||||
pre[key] = HTMLEntities.new.decode $1
|
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||||
"\n```\n#{key}\n```\n"
|
pre[key] = HTMLEntities.new.decode $1
|
||||||
end
|
"\n```\n#{key}\n```\n"
|
||||||
|
end
|
||||||
|
|
||||||
# # Extract inline code and replace with placeholder
|
# # Extract inline code and replace with placeholder
|
||||||
text = text.gsub(/(?<!`)`([^`]+?)`(?!`)/) do |match|
|
text =
|
||||||
key = "pre:" + SecureRandom.alphanumeric(50)
|
text.gsub(/(?<!`)`([^`]+?)`(?!`)/) do |match|
|
||||||
pre[key] = HTMLEntities.new.decode $1
|
key = "pre:" + SecureRandom.alphanumeric(50)
|
||||||
"`#{key}`"
|
pre[key] = HTMLEntities.new.decode $1
|
||||||
end
|
"`#{key}`"
|
||||||
|
end
|
||||||
|
|
||||||
# Format links (don't worry about special cases @ # !)
|
# Format links (don't worry about special cases @ # !)
|
||||||
text = text.gsub(/<(.*?)>/) do |match|
|
text =
|
||||||
group = $1
|
text.gsub(/<(.*?)>/) do |match|
|
||||||
parts = group.split('|')
|
group = $1
|
||||||
link = parts[0].start_with?('@', '#', '!') ? nil : parts[0]
|
parts = group.split("|")
|
||||||
text = parts.length > 1 ? parts[1] : parts[0]
|
link = parts[0].start_with?("@", "#", "!") ? nil : parts[0]
|
||||||
|
text = parts.length > 1 ? parts[1] : parts[0]
|
||||||
|
|
||||||
if parts[0].start_with?('@')
|
if parts[0].start_with?("@")
|
||||||
user_id = parts[0][1..-1]
|
user_id = parts[0][1..-1]
|
||||||
if user = @transcript.users[user_id]
|
if user = @transcript.users[user_id]
|
||||||
user_name = user['_transcript_username']
|
user_name = user["_transcript_username"]
|
||||||
else
|
else
|
||||||
user_name = user_id
|
user_name = user_id
|
||||||
|
end
|
||||||
|
next "@#{user_name}"
|
||||||
end
|
end
|
||||||
next "@#{user_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
if link.nil?
|
if link.nil?
|
||||||
text
|
text
|
||||||
elsif link == text
|
elsif link == text
|
||||||
"<#{link}>"
|
"<#{link}>"
|
||||||
else
|
else
|
||||||
"[#{text}](#{link})"
|
"[#{text}](#{link})"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Add an extra * to each side for bold
|
# Add an extra * to each side for bold
|
||||||
text = text.gsub(/\*.*?\*/) do |match|
|
text = text.gsub(/\*.*?\*/) { |match| "*#{match}*" }
|
||||||
"*#{match}*"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add an extra ~ to each side for strikethrough
|
# Add an extra ~ to each side for strikethrough
|
||||||
text = text.gsub(/~.*?~/) do |match|
|
text = text.gsub(/~.*?~/) { |match| "~#{match}~" }
|
||||||
"~#{match}~"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Replace emoji - with _
|
# Replace emoji - with _
|
||||||
text = text.gsub(/:[a-z0-9_-]+:/) do |match|
|
text = text.gsub(/:[a-z0-9_-]+:/) { |match| match.gsub("-") { "_" } }
|
||||||
match.gsub("-") { "_" }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Restore pre-formatted code block content
|
# Restore pre-formatted code block content
|
||||||
pre.each do |key, value|
|
pre.each { |key, value| text = text.gsub(key) { value } }
|
||||||
text = text.gsub(key) { value }
|
|
||||||
end
|
|
||||||
|
|
||||||
text
|
text
|
||||||
end
|
end
|
||||||
|
@ -97,9 +92,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
def attachments_string
|
def attachments_string
|
||||||
string = ""
|
string = ""
|
||||||
string += "\n" if !attachments.empty?
|
string += "\n" if !attachments.empty?
|
||||||
attachments.each do |attachment|
|
attachments.each { |attachment| string += " - #{attachment}\n" }
|
||||||
string += " - #{attachment}\n"
|
|
||||||
end
|
|
||||||
string
|
string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,7 +101,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
def raw_text
|
def raw_text
|
||||||
raw_text = @raw['text'].nil? ? "" : @raw['text']
|
raw_text = @raw["text"].nil? ? "" : @raw["text"]
|
||||||
raw_text += attachments_string
|
raw_text += attachments_string
|
||||||
raw_text
|
raw_text
|
||||||
end
|
end
|
||||||
|
@ -116,7 +109,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
def attachments
|
def attachments
|
||||||
attachments = []
|
attachments = []
|
||||||
|
|
||||||
return attachments unless @raw.key?('attachments')
|
return attachments unless @raw.key?("attachments")
|
||||||
|
|
||||||
@raw["attachments"].each do |attachment|
|
@raw["attachments"].each do |attachment|
|
||||||
next unless attachment.key?("fallback")
|
next unless attachment.key?("fallback")
|
||||||
|
|
|
@ -8,7 +8,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
@excerpt = +""
|
@excerpt = +""
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.format(html = '')
|
def self.format(html = "")
|
||||||
me = self.new
|
me = self.new
|
||||||
parser = Nokogiri::HTML::SAX::Parser.new(me)
|
parser = Nokogiri::HTML::SAX::Parser.new(me)
|
||||||
parser.parse(html)
|
parser.parse(html)
|
||||||
|
@ -20,7 +20,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
when "a"
|
when "a"
|
||||||
attributes = Hash[*attributes.flatten]
|
attributes = Hash[*attributes.flatten]
|
||||||
@in_a = true
|
@in_a = true
|
||||||
@excerpt << "<#{absolute_url(attributes['href'])}|"
|
@excerpt << "<#{absolute_url(attributes["href"])}|"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,13 +40,18 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
private
|
private
|
||||||
|
|
||||||
def absolute_url(url)
|
def absolute_url(url)
|
||||||
uri = URI(url) rescue nil
|
uri =
|
||||||
|
begin
|
||||||
|
URI(url)
|
||||||
|
rescue StandardError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
return Discourse.current_hostname unless uri
|
return Discourse.current_hostname unless uri
|
||||||
return uri.to_s if uri.scheme == "mailto"
|
return uri.to_s if uri.scheme == "mailto"
|
||||||
|
|
||||||
uri.host = Discourse.current_hostname if !uri.host
|
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
|
uri.to_s
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,18 +13,19 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled
|
||||||
|
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]?\S*$', unique: true }]
|
||||||
{ key: "identifier", regex: '^[@#]?\S*$', unique: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
require_dependency 'topic'
|
require_dependency "topic"
|
||||||
::Topic.register_custom_field_type(DiscourseChatIntegration::Provider::SlackProvider::THREAD_LEGACY, :string)
|
::Topic.register_custom_field_type(
|
||||||
|
DiscourseChatIntegration::Provider::SlackProvider::THREAD_LEGACY,
|
||||||
|
:string,
|
||||||
|
)
|
||||||
|
|
||||||
def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length)
|
def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length)
|
||||||
doc = Nokogiri::HTML5.fragment(post.excerpt(max_length,
|
doc =
|
||||||
remap_emoji: true,
|
Nokogiri::HTML5.fragment(
|
||||||
keep_onebox_source: true
|
post.excerpt(max_length, remap_emoji: true, keep_onebox_source: true),
|
||||||
))
|
)
|
||||||
|
|
||||||
SlackMessageFormatter.format(doc.to_html)
|
SlackMessageFormatter.format(doc.to_html)
|
||||||
end
|
end
|
||||||
|
@ -34,11 +35,18 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||||
elsif topic.category
|
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
|
end
|
||||||
|
|
||||||
icon_url =
|
icon_url =
|
||||||
|
@ -55,12 +63,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
SiteSetting.title || "Discourse"
|
SiteSetting.title || "Discourse"
|
||||||
end
|
end
|
||||||
|
|
||||||
message = {
|
message = { channel: channel, username: slack_username, icon_url: icon_url, attachments: [] }
|
||||||
channel: channel,
|
|
||||||
username: slack_username,
|
|
||||||
icon_url: icon_url,
|
|
||||||
attachments: []
|
|
||||||
}
|
|
||||||
|
|
||||||
if filter == "thread" && thread_ts = get_slack_thread_ts(topic, channel)
|
if filter == "thread" && thread_ts = get_slack_thread_ts(topic, channel)
|
||||||
message[:thread_ts] = thread_ts
|
message[:thread_ts] = thread_ts
|
||||||
|
@ -73,9 +76,10 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
color: topic.category ? "##{topic.category.color}" : nil,
|
color: topic.category ? "##{topic.category.color}" : nil,
|
||||||
text: excerpt(post),
|
text: excerpt(post),
|
||||||
mrkdwn_in: ["text"],
|
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,
|
title_link: post.full_url,
|
||||||
thumb_url: post.full_url
|
thumb_url: post.full_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
message[:attachments].push(summary)
|
message[:attachments].push(summary)
|
||||||
|
@ -92,14 +96,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
# <!--SLACK_CHANNEL_ID=#{@channel_id};SLACK_TS=#{@requested_thread_ts}-->
|
# <!--SLACK_CHANNEL_ID=#{@channel_id};SLACK_TS=#{@requested_thread_ts}-->
|
||||||
slack_thread_regex = /<!--SLACK_CHANNEL_ID=([^;.]+);SLACK_TS=([0-9]{10}.[0-9]{6})-->/
|
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 = {
|
data = {
|
||||||
token: SiteSetting.chat_integration_slack_access_token,
|
token: SiteSetting.chat_integration_slack_access_token,
|
||||||
username: message[:username],
|
username: message[:username],
|
||||||
icon_url: message[:icon_url],
|
icon_url: message[:icon_url],
|
||||||
channel: message[:channel].gsub('#', ''),
|
channel: message[:channel].gsub("#", ""),
|
||||||
attachments: message[:attachments].to_json
|
attachments: message[:attachments].to_json,
|
||||||
}
|
}
|
||||||
|
|
||||||
if post
|
if post
|
||||||
|
@ -116,18 +120,28 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
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
|
end
|
||||||
|
|
||||||
json = JSON.parse(response.body)
|
json = JSON.parse(response.body)
|
||||||
|
|
||||||
unless json["ok"] == true
|
unless json["ok"] == true
|
||||||
if json.key?("error") && (json["error"] == ('channel_not_found') || json["error"] == ('is_archived'))
|
if json.key?("error") &&
|
||||||
error_key = 'chat_integration.provider.slack.errors.channel_not_found'
|
(json["error"] == ("channel_not_found") || json["error"] == ("is_archived"))
|
||||||
|
error_key = "chat_integration.provider.slack.errors.channel_not_found"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
ts = json["ts"]
|
ts = json["ts"]
|
||||||
|
@ -139,25 +153,33 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
def self.send_via_webhook(message)
|
def self.send_via_webhook(message)
|
||||||
http = FinalDestination::HTTP.new("hooks.slack.com", 443)
|
http = FinalDestination::HTTP.new("hooks.slack.com", 443)
|
||||||
http.use_ssl = true
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
if response.code.to_s == '403'
|
if response.code.to_s == "403"
|
||||||
error_key = 'chat_integration.provider.slack.errors.action_prohibited'
|
error_key = "chat_integration.provider.slack.errors.action_prohibited"
|
||||||
elsif response.body == ('channel_not_found') || response.body == ('channel_is_archived')
|
elsif response.body == ("channel_not_found") || response.body == ("channel_is_archived")
|
||||||
error_key = 'chat_integration.provider.slack.errors.channel_not_found'
|
error_key = "chat_integration.provider.slack.errors.channel_not_found"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
channel_id = channel.data['identifier']
|
channel_id = channel.data["identifier"]
|
||||||
filter = rule.nil? ? "" : rule.filter
|
filter = rule.nil? ? "" : rule.filter
|
||||||
message = slack_message(post, channel_id, filter)
|
message = slack_message(post, channel_id, filter)
|
||||||
|
|
||||||
|
@ -166,7 +188,6 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
else
|
else
|
||||||
self.send_via_api(post, channel_id, message)
|
self.send_via_api(post, channel_id, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.slack_api_http
|
def self.slack_api_http
|
||||||
|
@ -182,13 +203,16 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.set_slack_thread_ts(topic, channel, value)
|
def self.set_slack_thread_ts(topic, channel, value)
|
||||||
TopicCustomField.upsert({
|
TopicCustomField.upsert(
|
||||||
|
{
|
||||||
topic_id: topic.id,
|
topic_id: topic.id,
|
||||||
name: "#{THREAD_CUSTOM_FIELD_PREFIX}#{channel}",
|
name: "#{THREAD_CUSTOM_FIELD_PREFIX}#{channel}",
|
||||||
value: value,
|
value: value,
|
||||||
created_at: Time.zone.now,
|
created_at: Time.zone.now,
|
||||||
updated_at: Time.zone.now
|
updated_at: Time.zone.now,
|
||||||
}, unique_by: [:topic_id, :name])
|
},
|
||||||
|
unique_by: %i[topic_id name],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
module DiscourseChatIntegration::Provider::SlackProvider
|
module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
class SlackTranscript
|
class SlackTranscript
|
||||||
class UserFetchError < RuntimeError; end
|
class UserFetchError < RuntimeError
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :users, :channel_id, :messages
|
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
|
# Work through the messages in order. If a gap is found, this could be the first message
|
||||||
new_first_message_index = nil
|
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|
|
possible_first_messages.each_with_index do |message, index|
|
||||||
|
|
||||||
# Calculate the time since the last message
|
# 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
|
time_since_previous_message = this_ts - previous_message_ts
|
||||||
|
|
||||||
# If greater than 3 minutes, this could be the first message
|
# If greater than 3 minutes, this could be the first message
|
||||||
if time_since_previous_message > 3.minutes
|
new_first_message_index = index if time_since_previous_message > 3.minutes
|
||||||
new_first_message_index = index
|
|
||||||
end
|
|
||||||
|
|
||||||
previous_message_ts = this_ts
|
previous_message_ts = this_ts
|
||||||
end
|
end
|
||||||
|
@ -84,11 +82,11 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
def build_transcript
|
def build_transcript
|
||||||
post_content = +""
|
post_content = +""
|
||||||
post_content << "[quote]\n" if SiteSetting.chat_integration_slack_transcript_quote
|
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 = {}
|
all_avatars = {}
|
||||||
|
|
||||||
last_username = ''
|
last_username = ""
|
||||||
|
|
||||||
transcript_messages = @messages[@first_message_index..@last_message_index]
|
transcript_messages = @messages[@first_message_index..@last_message_index]
|
||||||
|
|
||||||
|
@ -108,9 +106,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
|
|
||||||
post_content << m.text
|
post_content << m.text
|
||||||
|
|
||||||
m.attachments.each do |attachment|
|
m.attachments.each { |attachment| post_content << "\n> #{attachment}\n" }
|
||||||
post_content << "\n> #{attachment}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
post_content << "\n"
|
post_content << "\n"
|
||||||
end
|
end
|
||||||
|
@ -118,9 +114,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
post_content << "[/quote]" if SiteSetting.chat_integration_slack_transcript_quote
|
post_content << "[/quote]" if SiteSetting.chat_integration_slack_transcript_quote
|
||||||
post_content << "\n\n"
|
post_content << "\n\n"
|
||||||
|
|
||||||
all_avatars.each do |username, url|
|
all_avatars.each { |username, url| post_content << "[#{username}]: #{url}\n" }
|
||||||
post_content << "[#{username}]: #{url}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
if not @requested_thread_ts.nil?
|
if not @requested_thread_ts.nil?
|
||||||
post_content << "<!--SLACK_CHANNEL_ID=#{@channel_name};SLACK_TS=#{@requested_thread_ts}-->"
|
post_content << "<!--SLACK_CHANNEL_ID=#{@channel_name};SLACK_TS=#{@requested_thread_ts}-->"
|
||||||
|
@ -134,17 +128,17 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
type: "modal",
|
type: "modal",
|
||||||
title: {
|
title: {
|
||||||
type: "plain_text",
|
type: "plain_text",
|
||||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title")
|
text: I18n.t("chat_integration.provider.slack.transcript.modal_title"),
|
||||||
},
|
},
|
||||||
blocks: [
|
blocks: [
|
||||||
{
|
{
|
||||||
"type": "section",
|
type: "section",
|
||||||
"text": {
|
text: {
|
||||||
"type": "mrkdwn",
|
type: "mrkdwn",
|
||||||
"text": I18n.t("chat_integration.provider.slack.transcript.modal_description")
|
text: I18n.t("chat_integration.provider.slack.transcript.modal_description"),
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
if @messages
|
if @messages
|
||||||
|
@ -153,30 +147,31 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
|
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
|
||||||
|
|
||||||
data[:blocks] << {
|
data[:blocks] << {
|
||||||
"type": "section",
|
type: "section",
|
||||||
"text": {
|
text: {
|
||||||
"type": "mrkdwn",
|
type: "mrkdwn",
|
||||||
"text": ":writing_hand: *#{I18n.t("chat_integration.provider.slack.transcript.transcript_ready")}*"
|
text:
|
||||||
|
":writing_hand: *#{I18n.t("chat_integration.provider.slack.transcript.transcript_ready")}*",
|
||||||
},
|
},
|
||||||
"accessory": {
|
accessory: {
|
||||||
"type": "button",
|
type: "button",
|
||||||
"text": {
|
text: {
|
||||||
"type": "plain_text",
|
type: "plain_text",
|
||||||
"text": I18n.t("chat_integration.provider.slack.transcript.continue_on_discourse"),
|
text: I18n.t("chat_integration.provider.slack.transcript.continue_on_discourse"),
|
||||||
"emoji": true
|
emoji: true,
|
||||||
},
|
},
|
||||||
"style": "primary",
|
style: "primary",
|
||||||
"url": link,
|
url: link,
|
||||||
"action_id": "null_action"
|
action_id: "null_action",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
data[:blocks] << {
|
data[:blocks] << {
|
||||||
"type": "section",
|
type: "section",
|
||||||
"text": {
|
text: {
|
||||||
"type": "mrkdwn",
|
type: "mrkdwn",
|
||||||
"text": ":writing_hand: #{I18n.t("chat_integration.provider.slack.transcript.loading")}"
|
text: ":writing_hand: #{I18n.t("chat_integration.provider.slack.transcript.loading")}",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -188,76 +183,81 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
secret = DiscourseChatIntegration::Helper.save_transcript(post_content)
|
secret = DiscourseChatIntegration::Helper.save_transcript(post_content)
|
||||||
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
|
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")}>",
|
text: "<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>",
|
||||||
attachments: [
|
attachments: [
|
||||||
{
|
{
|
||||||
pretext: I18n.t(
|
pretext:
|
||||||
"chat_integration.provider.slack.transcript.first_message_pretext",
|
I18n.t(
|
||||||
n: @messages.length - first_message_number
|
"chat_integration.provider.slack.transcript.first_message_pretext",
|
||||||
),
|
n: @messages.length - first_message_number,
|
||||||
|
),
|
||||||
fallback: "#{first_message.username} - #{first_message.raw_text}",
|
fallback: "#{first_message.username} - #{first_message.raw_text}",
|
||||||
color: "#007AB8",
|
color: "#007AB8",
|
||||||
author_name: first_message.username,
|
author_name: first_message.username,
|
||||||
author_icon: first_message.avatar,
|
author_icon: first_message.avatar,
|
||||||
text: first_message.raw_text,
|
text: first_message.raw_text,
|
||||||
footer: I18n.t(
|
footer:
|
||||||
"chat_integration.provider.slack.transcript.posted_in",
|
I18n.t("chat_integration.provider.slack.transcript.posted_in", name: @channel_name),
|
||||||
name: @channel_name
|
|
||||||
),
|
|
||||||
ts: first_message.ts,
|
ts: first_message.ts,
|
||||||
callback_id: last_message.ts,
|
callback_id: last_message.ts,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
name: "first_message",
|
name: "first_message",
|
||||||
text: I18n.t(
|
text: I18n.t("chat_integration.provider.slack.transcript.change_first_message"),
|
||||||
"chat_integration.provider.slack.transcript.change_first_message"
|
|
||||||
),
|
|
||||||
type: "select",
|
type: "select",
|
||||||
options: first_message_options = @messages[ [(first_message_number - 20), 0].max .. last_message_number]
|
options:
|
||||||
.map { |m| { text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts } }
|
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(
|
pretext:
|
||||||
"chat_integration.provider.slack.transcript.last_message_pretext",
|
I18n.t(
|
||||||
n: @messages.length - last_message_number
|
"chat_integration.provider.slack.transcript.last_message_pretext",
|
||||||
),
|
n: @messages.length - last_message_number,
|
||||||
|
),
|
||||||
fallback: "#{last_message.username} - #{last_message.raw_text}",
|
fallback: "#{last_message.username} - #{last_message.raw_text}",
|
||||||
color: "#007AB8",
|
color: "#007AB8",
|
||||||
author_name: last_message.username,
|
author_name: last_message.username,
|
||||||
author_icon: last_message.avatar,
|
author_icon: last_message.avatar,
|
||||||
text: last_message.raw_text,
|
text: last_message.raw_text,
|
||||||
footer: I18n.t(
|
footer:
|
||||||
"chat_integration.provider.slack.transcript.posted_in",
|
I18n.t("chat_integration.provider.slack.transcript.posted_in", name: @channel_name),
|
||||||
name: @channel_name
|
|
||||||
),
|
|
||||||
ts: last_message.ts,
|
ts: last_message.ts,
|
||||||
callback_id: first_message.ts,
|
callback_id: first_message.ts,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
name: "last_message",
|
name: "last_message",
|
||||||
text: I18n.t(
|
text: I18n.t("chat_integration.provider.slack.transcript.change_last_message"),
|
||||||
"chat_integration.provider.slack.transcript.change_last_message"
|
|
||||||
),
|
|
||||||
type: "select",
|
type: "select",
|
||||||
options: @messages[first_message_number..(last_message_number + 20)]
|
options:
|
||||||
.map { |m| { text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts } }
|
@messages[first_message_number..(last_message_number + 20)].map do |m|
|
||||||
}
|
{ text: "#{m.username}: #{m.processed_text_with_attachments}", value: m.ts }
|
||||||
|
end,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_user_data
|
def load_user_data
|
||||||
key = "slack_user_info_#{Digest::SHA1.hexdigest(SiteSetting.chat_integration_slack_access_token)}"
|
key =
|
||||||
@users = Discourse.cache.fetch(key, expires_in: 10.minutes) do
|
"slack_user_info_#{Digest::SHA1.hexdigest(SiteSetting.chat_integration_slack_access_token)}"
|
||||||
fetch_user_data
|
@users = Discourse.cache.fetch(key, expires_in: 10.minutes) { fetch_user_data }
|
||||||
end
|
|
||||||
true
|
true
|
||||||
rescue UserFetchError
|
rescue UserFetchError
|
||||||
false
|
false
|
||||||
|
@ -267,26 +267,30 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
http = ::DiscourseChatIntegration::Provider::SlackProvider.slack_api_http
|
http = ::DiscourseChatIntegration::Provider::SlackProvider.slack_api_http
|
||||||
|
|
||||||
cursor = nil
|
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 = {}
|
users = {}
|
||||||
loop do
|
loop do
|
||||||
break if cursor == ""
|
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)
|
response = http.request(req)
|
||||||
raise UserFetchError.new unless response.kind_of? Net::HTTPSuccess
|
raise UserFetchError.new unless response.kind_of? Net::HTTPSuccess
|
||||||
json = JSON.parse(response.body)
|
json = JSON.parse(response.body)
|
||||||
raise UserFetchError.new unless json['ok']
|
raise UserFetchError.new unless json["ok"]
|
||||||
cursor = json['response_metadata']['next_cursor']
|
cursor = json["response_metadata"]["next_cursor"]
|
||||||
json['members'].each do |user|
|
json["members"].each do |user|
|
||||||
# Slack uses display_name and falls back to real_name if it is not set
|
# Slack uses display_name and falls back to real_name if it is not set
|
||||||
if user['profile']['display_name'].blank?
|
if user["profile"]["display_name"].blank?
|
||||||
user['_transcript_username'] = user['profile']['real_name']
|
user["_transcript_username"] = user["profile"]["real_name"]
|
||||||
else
|
else
|
||||||
user['_transcript_username'] = user['profile']['display_name']
|
user["_transcript_username"] = user["profile"]["display_name"]
|
||||||
end
|
end
|
||||||
user['_transcript_username'] = user['_transcript_username'].gsub(' ', '_')
|
user["_transcript_username"] = user["_transcript_username"].gsub(" ", "_")
|
||||||
users[user['id']] = user
|
users[user["id"]] = user
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
users
|
users
|
||||||
|
@ -302,7 +306,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
data = {
|
data = {
|
||||||
token: SiteSetting.chat_integration_slack_access_token,
|
token: SiteSetting.chat_integration_slack_access_token,
|
||||||
channel: @channel_id,
|
channel: @channel_id,
|
||||||
limit: count
|
limit: count,
|
||||||
}
|
}
|
||||||
|
|
||||||
data[:ts] = @requested_thread_ts if @requested_thread_ts
|
data[:ts] = @requested_thread_ts if @requested_thread_ts
|
||||||
|
@ -311,9 +315,9 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
return false unless response.kind_of? Net::HTTPSuccess
|
return false unless response.kind_of? Net::HTTPSuccess
|
||||||
json = JSON.parse(response.body)
|
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
|
raw_messages = raw_messages.reverse unless @requested_thread_ts
|
||||||
|
|
||||||
# Build some message objects
|
# Build some message objects
|
||||||
|
@ -323,13 +327,13 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||||
next unless message["type"] == "message"
|
next unless message["type"] == "message"
|
||||||
|
|
||||||
# Don't load responses to threads unless specifically requested (if ts==thread_ts then it's the thread parent)
|
# 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)
|
this_message = SlackMessage.new(message, self)
|
||||||
@messages << this_message
|
@messages << this_message
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,29 +5,33 @@ module DiscourseChatIntegration::Provider::TeamsProvider
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_teams_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_teams_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+$', unique: true },
|
{ 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)
|
def self.trigger_notification(post, channel, rule)
|
||||||
message = get_message(post)
|
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 = 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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
if response.body.include?('Invalid webhook URL')
|
if response.body.include?("Invalid webhook URL")
|
||||||
error_key = 'chat_integration.provider.teams.errors.invalid_channel'
|
error_key = "chat_integration.provider.teams.errors.invalid_channel"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_message(post)
|
def self.get_message(post)
|
||||||
|
@ -41,29 +45,41 @@ module DiscourseChatIntegration::Provider::TeamsProvider
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||||
elsif topic.category
|
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
|
end
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"@type": "MessageCard",
|
"@type": "MessageCard",
|
||||||
"summary": topic.title,
|
summary: topic.title,
|
||||||
"sections": [{
|
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),
|
activityTitle:
|
||||||
"activityImage": post.user.small_avatar_url,
|
"[#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}](#{post.full_url})",
|
||||||
"facts": [{
|
activitySubtitle:
|
||||||
"name": full_name,
|
post.excerpt(
|
||||||
"value": display_name
|
SiteSetting.chat_integration_teams_excerpt_length,
|
||||||
}],
|
text_entities: true,
|
||||||
"markdown": true
|
strip_links: true,
|
||||||
}],
|
remap_emoji: true,
|
||||||
|
),
|
||||||
|
activityImage: post.user.small_avatar_url,
|
||||||
|
facts: [{ name: full_name, value: display_name }],
|
||||||
|
markdown: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,12 +13,11 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
only: :command
|
only: :command
|
||||||
|
|
||||||
def command
|
def command
|
||||||
|
|
||||||
# If it's a new message (telegram also sends hooks for other reasons that we don't care about)
|
# If it's a new message (telegram also sends hooks for other reasons that we don't care about)
|
||||||
if params.key?('message')
|
if params.key?("message")
|
||||||
chat_id = params['message']['chat']['id']
|
chat_id = params["message"]["chat"]["id"]
|
||||||
|
|
||||||
message_text = process_command(params['message'])
|
message_text = process_command(params["message"])
|
||||||
|
|
||||||
if message_text.present?
|
if message_text.present?
|
||||||
message = {
|
message = {
|
||||||
|
@ -30,15 +29,15 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
|
|
||||||
DiscourseChatIntegration::Provider::TelegramProvider.sendMessage(message)
|
DiscourseChatIntegration::Provider::TelegramProvider.sendMessage(message)
|
||||||
end
|
end
|
||||||
|
elsif params.dig("channel_post", "text")&.include?("/getchatid")
|
||||||
|
chat_id = params["channel_post"]["chat"]["id"]
|
||||||
|
|
||||||
elsif params.dig('channel_post', 'text')&.include?('/getchatid')
|
message_text =
|
||||||
chat_id = params['channel_post']['chat']['id']
|
I18n.t(
|
||||||
|
"chat_integration.provider.telegram.unknown_chat",
|
||||||
message_text = I18n.t(
|
site_title: CGI.escapeHTML(SiteSetting.title),
|
||||||
"chat_integration.provider.telegram.unknown_chat",
|
chat_id: chat_id,
|
||||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
)
|
||||||
chat_id: chat_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
chat_id: chat_id,
|
chat_id: chat_id,
|
||||||
|
@ -51,43 +50,49 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
# Always give telegram a success message, otherwise we'll stop receiving webhooks
|
# Always give telegram a success message, otherwise we'll stop receiving webhooks
|
||||||
data = {
|
data = { success: true }
|
||||||
success: true
|
|
||||||
}
|
|
||||||
render json: data
|
render json: data
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_command(message)
|
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
|
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?
|
text_key =
|
||||||
"unknown_chat"
|
if channel.nil?
|
||||||
elsif !SiteSetting.chat_integration_telegram_enable_slash_commands || !message['text'].start_with?('/')
|
"unknown_chat"
|
||||||
"silent"
|
elsif !SiteSetting.chat_integration_telegram_enable_slash_commands ||
|
||||||
else
|
!message["text"].start_with?("/")
|
||||||
""
|
"silent"
|
||||||
end
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
return "" if text_key == "silent"
|
return "" if text_key == "silent"
|
||||||
|
|
||||||
if text_key.present?
|
if text_key.present?
|
||||||
return I18n.t(
|
return(
|
||||||
"chat_integration.provider.telegram.#{text_key}",
|
I18n.t(
|
||||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
"chat_integration.provider.telegram.#{text_key}",
|
||||||
chat_id: chat_id,
|
site_title: CGI.escapeHTML(SiteSetting.title),
|
||||||
|
chat_id: chat_id,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
tokens = message['text'].split(" ")
|
tokens = message["text"].split(" ")
|
||||||
|
|
||||||
tokens[0][0] = '' # Remove the slash from the first token
|
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] = tokens[0].split("@")[0] # Remove the bot name from the command (necessary for group chats)
|
||||||
|
|
||||||
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
||||||
end
|
end
|
||||||
|
@ -96,8 +101,7 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
params.require(:token)
|
params.require(:token)
|
||||||
|
|
||||||
if SiteSetting.chat_integration_telegram_secret.blank? ||
|
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
|
raise Discourse::InvalidAccess.new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -108,7 +112,5 @@ module DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
isolate_namespace DiscourseChatIntegration::Provider::TelegramProvider
|
isolate_namespace DiscourseChatIntegration::Provider::TelegramProvider
|
||||||
end
|
end
|
||||||
|
|
||||||
TelegramEngine.routes.draw do
|
TelegramEngine.routes.draw { post "command/:token" => "telegram_command#command" }
|
||||||
post "command/:token" => "telegram_command#command"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
DiscourseEvent.on(:site_setting_changed) do |setting_name, old_value, new_value|
|
DiscourseEvent.on(:site_setting_changed) do |setting_name, old_value, new_value|
|
||||||
isEnabledSetting = setting_name == :chat_integration_telegram_enabled
|
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)
|
if (isEnabledSetting || isAccessToken)
|
||||||
enabled = isEnabledSetting ? new_value == true : SiteSetting.chat_integration_telegram_enabled
|
enabled = isEnabledSetting ? new_value == true : SiteSetting.chat_integration_telegram_enabled
|
||||||
|
|
|
@ -6,30 +6,30 @@ module DiscourseChatIntegration
|
||||||
PROVIDER_NAME = "telegram".freeze
|
PROVIDER_NAME = "telegram".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+' },
|
{ key: "name", regex: '^\S+' },
|
||||||
{ key: "chat_id", regex: '^(-?[0-9]+|@\S+)$', unique: true }
|
{ key: "chat_id", regex: '^(-?[0-9]+|@\S+)$', unique: true },
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.setup_webhook
|
def self.setup_webhook
|
||||||
newSecret = SecureRandom.hex
|
newSecret = SecureRandom.hex
|
||||||
SiteSetting.chat_integration_telegram_secret = newSecret
|
SiteSetting.chat_integration_telegram_secret = newSecret
|
||||||
|
|
||||||
message = {
|
message = { url: Discourse.base_url + "/chat-integration/telegram/command/" + newSecret }
|
||||||
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
|
# If setting up webhook failed, disable provider
|
||||||
SiteSetting.chat_integration_telegram_enabled = false
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.sendMessage(message)
|
def self.sendMessage(message)
|
||||||
self.do_api_request('sendMessage', message)
|
self.do_api_request("sendMessage", message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.do_api_request(methodName, message)
|
def self.do_api_request(methodName, message)
|
||||||
|
@ -40,7 +40,7 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
uri = URI("https://api.telegram.org/bot#{access_token}/#{methodName}")
|
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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
|
@ -54,28 +54,38 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.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
|
end
|
||||||
|
|
||||||
tags = ''
|
tags = ""
|
||||||
if topic.tags.present?
|
tags = topic.tags.map(&:name).join(", ") if topic.tags.present?
|
||||||
tags = topic.tags.map(&:name).join(', ')
|
|
||||||
end
|
|
||||||
|
|
||||||
I18n.t(
|
I18n.t(
|
||||||
"chat_integration.provider.telegram.message",
|
"chat_integration.provider.telegram.message",
|
||||||
user: display_name,
|
user: display_name,
|
||||||
post_url: post.full_url,
|
post_url: post.full_url,
|
||||||
title: CGI::escapeHTML(topic.title),
|
title: CGI.escapeHTML(topic.title),
|
||||||
post_excerpt: post.excerpt(SiteSetting.chat_integration_telegram_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
post_excerpt:
|
||||||
)
|
post.excerpt(
|
||||||
|
SiteSetting.chat_integration_telegram_excerpt_length,
|
||||||
|
text_entities: true,
|
||||||
|
strip_links: true,
|
||||||
|
remap_emoji: true,
|
||||||
|
),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
chat_id = channel.data['chat_id']
|
chat_id = channel.data["chat_id"]
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
chat_id: chat_id,
|
chat_id: chat_id,
|
||||||
|
@ -86,18 +96,20 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
response = sendMessage(message)
|
response = sendMessage(message)
|
||||||
|
|
||||||
if response['ok'] != true
|
if response["ok"] != true
|
||||||
error_key = nil
|
error_key = nil
|
||||||
if response['description'].include? 'chat not found'
|
if response["description"].include? "chat not found"
|
||||||
error_key = 'chat_integration.provider.telegram.errors.channel_not_found'
|
error_key = "chat_integration.provider.telegram.errors.channel_not_found"
|
||||||
elsif response['description'].include? 'Forbidden'
|
elsif response["description"].include? "Forbidden"
|
||||||
error_key = 'chat_integration.provider.telegram.errors.forbidden'
|
error_key = "chat_integration.provider.telegram.errors.forbidden"
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,35 +5,38 @@ module DiscourseChatIntegration::Provider::WebexProvider
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_webex_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_webex_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "name", regex: '^\S+$', unique: true },
|
{ key: "name", regex: '^\S+$', unique: true },
|
||||||
{ key: "webhook_url",
|
{
|
||||||
|
key: "webhook_url",
|
||||||
regex: '^https:\/\/webexapis\.com\/v1\/webhooks\/incoming\/[A-Za-z0-9\-@\/]+\S+$',
|
regex: '^https:\/\/webexapis\.com\/v1\/webhooks\/incoming\/[A-Za-z0-9\-@\/]+\S+$',
|
||||||
unique: true,
|
unique: true,
|
||||||
hidden: true }
|
hidden: true,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
message = get_message(post)
|
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 = 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
|
req.body = message.to_json
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
|
||||||
unless response.kind_of? Net::HTTPSuccess
|
unless response.kind_of? Net::HTTPSuccess
|
||||||
if response.body.include?('Invalid webhook URL')
|
if response.body.include?("Invalid webhook URL")
|
||||||
error_key = 'chat_integration.provider.webex.errors.invalid_channel'
|
error_key = "chat_integration.provider.webex.errors.invalid_channel"
|
||||||
else
|
else
|
||||||
error_key = nil
|
error_key = nil
|
||||||
end
|
end
|
||||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key,
|
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||||
request: req.body,
|
error_key: error_key,
|
||||||
response_code: response.code,
|
request: req.body,
|
||||||
response_body: response.body }
|
response_code: response.code,
|
||||||
|
response_body: response.body,
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get_message(post)
|
def self.get_message(post)
|
||||||
|
@ -41,24 +44,31 @@ module DiscourseChatIntegration::Provider::WebexProvider
|
||||||
|
|
||||||
topic = post.topic
|
topic = post.topic
|
||||||
|
|
||||||
category = ''
|
category = ""
|
||||||
if topic.category&.uncategorized?
|
if topic.category&.uncategorized?
|
||||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||||
elsif topic.category
|
elsif topic.category
|
||||||
category = (topic.category.parent_category) ?
|
category =
|
||||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
(
|
||||||
|
if (topic.category.parent_category)
|
||||||
|
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||||
|
else
|
||||||
|
"[#{topic.category.name}]"
|
||||||
|
end
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
markdown = "**#{topic.title}**: #{category}"
|
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.full_url}) from #{display_name}:\n"
|
||||||
markdown += post.excerpt(SiteSetting.chat_integration_webex_excerpt_length,
|
markdown +=
|
||||||
text_entities: true,
|
post.excerpt(
|
||||||
strip_links: true,
|
SiteSetting.chat_integration_webex_excerpt_length,
|
||||||
remap_emoji: true
|
text_entities: true,
|
||||||
)
|
strip_links: true,
|
||||||
|
remap_emoji: true,
|
||||||
|
)
|
||||||
|
|
||||||
{ "markdown": markdown }
|
{ markdown: markdown }
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,18 +6,21 @@ module DiscourseChatIntegration
|
||||||
PROVIDER_NAME = "zulip".freeze
|
PROVIDER_NAME = "zulip".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_zulip_enabled
|
PROVIDER_ENABLED_SETTING = :chat_integration_zulip_enabled
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [
|
||||||
{ key: "stream", unique: true, regex: '^\S+' },
|
{ key: "stream", unique: true, regex: '^\S+' },
|
||||||
{ key: "subject", unique: true, regex: '^\S+' },
|
{ key: "subject", unique: true, regex: '^\S+' },
|
||||||
]
|
]
|
||||||
|
|
||||||
def self.send_message(message)
|
def self.send_message(message)
|
||||||
uri = URI("#{SiteSetting.chat_integration_zulip_server}/api/v1/messages")
|
uri = URI("#{SiteSetting.chat_integration_zulip_server}/api/v1/messages")
|
||||||
|
|
||||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
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 = 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)
|
req.set_form_data(message)
|
||||||
|
|
||||||
response = http.request(req)
|
response = http.request(req)
|
||||||
|
@ -28,23 +31,27 @@ module DiscourseChatIntegration
|
||||||
def self.generate_zulip_message(post, stream, subject)
|
def self.generate_zulip_message(post, stream, subject)
|
||||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||||
|
|
||||||
message = I18n.t('chat_integration.provider.zulip.message', user: display_name,
|
message =
|
||||||
post_url: post.full_url,
|
I18n.t(
|
||||||
title: post.topic.title,
|
"chat_integration.provider.zulip.message",
|
||||||
excerpt: post.excerpt(SiteSetting.chat_integration_zulip_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true))
|
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 = {
|
data = { type: "stream", to: stream, subject: subject, content: message }
|
||||||
type: 'stream',
|
|
||||||
to: stream,
|
|
||||||
subject: subject,
|
|
||||||
content: message
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
|
stream = channel.data["stream"]
|
||||||
stream = channel.data['stream']
|
subject = channel.data["subject"]
|
||||||
subject = channel.data['subject']
|
|
||||||
|
|
||||||
message = self.generate_zulip_message(post, stream, subject)
|
message = self.generate_zulip_message(post, stream, subject)
|
||||||
|
|
||||||
|
@ -52,12 +59,18 @@ module DiscourseChatIntegration
|
||||||
|
|
||||||
if !response.kind_of?(Net::HTTPSuccess)
|
if !response.kind_of?(Net::HTTPSuccess)
|
||||||
error_key = nil
|
error_key = nil
|
||||||
error_key = 'chat_integration.provider.zulip.errors.does_not_exist' if response.body.include?('does not exist')
|
error_key =
|
||||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_code: response.code, response_body: response.body }
|
"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
|
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)
|
Jobs.enqueue_in(time, :notify_chats, post_id: post.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
add_admin_route 'chat_integration.menu_title', 'chat-integration'
|
add_admin_route "chat_integration.menu_title", "chat-integration"
|
||||||
|
|
||||||
AdminDashboardData.add_problem_check do
|
AdminDashboardData.add_problem_check do
|
||||||
next if !SiteSetting.chat_integration_enabled
|
next if !SiteSetting.chat_integration_enabled
|
||||||
|
@ -47,7 +47,7 @@ after_initialize do
|
||||||
DiscourseChatIntegration::Provider.mount_engines
|
DiscourseChatIntegration::Provider.mount_engines
|
||||||
|
|
||||||
if defined?(DiscourseAutomation)
|
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 :message, component: :message, required: true, accepts_placeholders: true
|
||||||
field :url, component: :text, required: true
|
field :url, component: :text, required: true
|
||||||
field :channel, component: :text, required: true
|
field :channel, component: :text, required: true
|
||||||
|
@ -59,16 +59,24 @@ after_initialize do
|
||||||
script do |context, fields, automation|
|
script do |context, fields, automation|
|
||||||
sender = Discourse.system_user
|
sender = Discourse.system_user
|
||||||
|
|
||||||
content = fields.dig('message', 'value')
|
content = fields.dig("message", "value")
|
||||||
url = fields.dig('url', 'value')
|
url = fields.dig("url", "value")
|
||||||
full_content = "#{content} - #{url}"
|
full_content = "#{content} - #{url}"
|
||||||
channel_name = fields.dig('channel', 'value')
|
channel_name = fields.dig("channel", "value")
|
||||||
channel = DiscourseChatIntegration::Channel.new(provider: "slack", data: { identifier: "##{channel_name}" })
|
channel =
|
||||||
|
DiscourseChatIntegration::Channel.new(
|
||||||
|
provider: "slack",
|
||||||
|
data: {
|
||||||
|
identifier: "##{channel_name}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
icon_url =
|
icon_url =
|
||||||
if SiteSetting.chat_integration_slack_icon_url.present?
|
if SiteSetting.chat_integration_slack_icon_url.present?
|
||||||
"#{Discourse.base_url}#{SiteSetting.chat_integration_slack_icon_url}"
|
"#{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}"
|
"#{Discourse.base_url}#{url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -83,7 +91,7 @@ after_initialize do
|
||||||
channel: "##{channel_name}",
|
channel: "##{channel_name}",
|
||||||
username: slack_username,
|
username: slack_username,
|
||||||
icon_url: icon_url,
|
icon_url: icon_url,
|
||||||
attachments: []
|
attachments: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
summary = {
|
summary = {
|
||||||
|
@ -94,7 +102,7 @@ after_initialize do
|
||||||
mrkdwn_in: ["text"],
|
mrkdwn_in: ["text"],
|
||||||
title: content.truncate(100),
|
title: content.truncate(100),
|
||||||
title_link: url,
|
title_link: url,
|
||||||
thumb_url: nil
|
thumb_url: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
message[:attachments].push(summary)
|
message[:attachments].push(summary)
|
||||||
|
|
|
@ -11,9 +11,7 @@ RSpec.shared_context "with dummy provider" do
|
||||||
@@raise_exception = nil
|
@@raise_exception = nil
|
||||||
|
|
||||||
def self.trigger_notification(post, channel, rule)
|
def self.trigger_notification(post, channel, rule)
|
||||||
if @@raise_exception
|
raise @@raise_exception if @@raise_exception
|
||||||
raise @@raise_exception
|
|
||||||
end
|
|
||||||
|
|
||||||
@@sent_messages.push(post: post.id, channel: channel)
|
@@sent_messages.push(post: post.id, channel: channel)
|
||||||
end
|
end
|
||||||
|
@ -32,9 +30,7 @@ RSpec.shared_context "with dummy provider" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:each) do
|
after(:each) { ::DiscourseChatIntegration::Provider.send(:remove_const, :DummyProvider) }
|
||||||
::DiscourseChatIntegration::Provider.send(:remove_const, :DummyProvider)
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:provider) { ::DiscourseChatIntegration::Provider::DummyProvider }
|
let(:provider) { ::DiscourseChatIntegration::Provider::DummyProvider }
|
||||||
end
|
end
|
||||||
|
@ -44,9 +40,7 @@ RSpec.shared_context "with validated dummy provider" do
|
||||||
module ::DiscourseChatIntegration::Provider::Dummy2Provider
|
module ::DiscourseChatIntegration::Provider::Dummy2Provider
|
||||||
PROVIDER_NAME = "dummy2".freeze
|
PROVIDER_NAME = "dummy2".freeze
|
||||||
PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting
|
PROVIDER_ENABLED_SETTING = :chat_integration_enabled # Tie to main plugin enabled setting
|
||||||
CHANNEL_PARAMETERS = [
|
CHANNEL_PARAMETERS = [{ key: "val", regex: '^\S+$', unique: true }]
|
||||||
{ key: "val", regex: '^\S+$', unique: true }
|
|
||||||
]
|
|
||||||
|
|
||||||
@@sent_messages = []
|
@@sent_messages = []
|
||||||
|
|
||||||
|
@ -58,10 +52,7 @@ RSpec.shared_context "with validated dummy provider" do
|
||||||
@@sent_messages
|
@@sent_messages
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
after(:each) do
|
after(:each) { ::DiscourseChatIntegration::Provider.send(:remove_const, :Dummy2Provider) }
|
||||||
::DiscourseChatIntegration::Provider.send(:remove_const, :Dummy2Provider)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,229 +1,279 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
require_relative '../dummy_provider'
|
require_relative "../dummy_provider"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Manager do
|
RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
include_context "with dummy provider"
|
include_context "with dummy provider"
|
||||||
|
|
||||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||||
|
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
let(:tag1) { Fabricate(:tag) }
|
let(:tag1) { Fabricate(:tag) }
|
||||||
let(:tag2) { Fabricate(:tag) }
|
let(:tag2) { Fabricate(:tag) }
|
||||||
let(:tag3) { Fabricate(:tag) }
|
let(:tag3) { Fabricate(:tag) }
|
||||||
|
|
||||||
describe '.process_command' do
|
describe ".process_command" do
|
||||||
|
describe "add new rule" do
|
||||||
describe 'add new rule' do
|
|
||||||
# Not testing how filters are merged here, that's done in .smart_create_rule
|
# 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
|
# We just want to make sure the commands are being interpretted correctly
|
||||||
|
|
||||||
it 'should add a new rule correctly' do
|
it "should add a new rule correctly" do
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug])
|
response = DiscourseChatIntegration::Helper.process_command(chan1, ["watch", category.slug])
|
||||||
|
|
||||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should work with all four filter types' do
|
it "should work with all four filter types" do
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['thread', category.slug])
|
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
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.filter).to eq('thread')
|
expect(rule.channel).to eq(chan1)
|
||||||
|
expect(rule.filter).to eq("watch")
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', category.slug])
|
expect(rule.category_id).to eq(nil)
|
||||||
|
expect(rule.tags).to eq([tag1.name])
|
||||||
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
|
end
|
||||||
|
|
||||||
it 'errors on incorrect categories' do
|
it "should work with a category and multiple tags" do
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['watch', 'blah'])
|
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
|
end
|
||||||
|
|
||||||
context 'with tags enabled' do
|
it "errors on incorrect tags" do
|
||||||
before do
|
response =
|
||||||
SiteSetting.tagging_enabled = true
|
DiscourseChatIntegration::Helper.process_command(
|
||||||
end
|
chan1,
|
||||||
|
["watch", category.slug, "tag:blah"],
|
||||||
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.not_found.tag", name: "blah"),
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'remove rule' do
|
describe "remove rule" do
|
||||||
it 'removes the rule' do
|
it "removes the rule" do
|
||||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
rule1 =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Rule.create(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag2.name]
|
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)
|
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'help command' do
|
it "errors correctly" do
|
||||||
it 'should return the right response' do
|
response = DiscourseChatIntegration::Helper.process_command(chan1, %w[remove 1])
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ["help"])
|
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.error"))
|
||||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.help"))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'status command' do
|
describe "help command" do
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['status'])
|
response = DiscourseChatIntegration::Helper.process_command(chan1, ["help"])
|
||||||
expect(response).to eq(DiscourseChatIntegration::Helper.status_for_channel(chan1))
|
expect(response).to eq(I18n.t("chat_integration.provider.dummy.help"))
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'unknown command' do
|
describe "status command" do
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
response = DiscourseChatIntegration::Helper.process_command(chan1, ['somerandomtext'])
|
response = DiscourseChatIntegration::Helper.process_command(chan1, ["status"])
|
||||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.parse_error"))
|
expect(response).to eq(DiscourseChatIntegration::Helper.status_for_channel(chan1))
|
||||||
end
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe '.status_for_channel' do
|
describe ".status_for_channel" do
|
||||||
|
context "with no rules" do
|
||||||
context 'with no rules' do
|
it "includes the heading" do
|
||||||
it 'includes the heading' do
|
|
||||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
||||||
expect(string).to include('dummy.status.header')
|
expect(string).to include("dummy.status.header")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the no_rules string' do
|
it "includes the no_rules string" do
|
||||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with some rules' do
|
context "with some rules" do
|
||||||
let(:group) { Fabricate(:group) }
|
let(:group) { Fabricate(:group) }
|
||||||
before do
|
before do
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', category_id: category.id, tags: nil)
|
DiscourseChatIntegration::Rule.create!(
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'mute', category_id: nil, tags: nil)
|
channel: chan1,
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'follow', category_id: nil, tags: [tag1.name])
|
filter: "watch",
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch', type: 'group_message', group_id: group.id)
|
category_id: category.id,
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'watch', category_id: 1, tags: nil)
|
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
|
SiteSetting.tagging_enabled = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays the correct rules' do
|
it "displays the correct rules" do
|
||||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
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
|
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)
|
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
|
SiteSetting.tagging_enabled = true
|
||||||
string = DiscourseChatIntegration::Helper.status_for_channel(chan1)
|
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
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.delete_by_index' do
|
describe ".delete_by_index" do
|
||||||
let(:category2) { Fabricate(:category) }
|
let(:category2) { Fabricate(:category) }
|
||||||
let(:category3) { Fabricate(:category) }
|
let(:category3) { Fabricate(:category) }
|
||||||
|
|
||||||
it 'deletes the correct rule' do
|
it "deletes the correct rule" do
|
||||||
# Three identical rules, with different filters
|
# Three identical rules, with different filters
|
||||||
# Status will be sorted by precedence
|
# Status will be sorted by precedence
|
||||||
# be in this order
|
# be in this order
|
||||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
rule1 =
|
||||||
filter: 'mute',
|
DiscourseChatIntegration::Rule.create(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag2.name]
|
filter: "mute",
|
||||||
)
|
category_id: category.id,
|
||||||
rule2 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
tags: [tag1.name, tag2.name],
|
||||||
filter: 'watch',
|
)
|
||||||
category_id: category2.id,
|
rule2 =
|
||||||
tags: [tag1.name, tag2.name]
|
DiscourseChatIntegration::Rule.create(
|
||||||
)
|
channel: chan1,
|
||||||
rule3 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
filter: "watch",
|
||||||
filter: 'follow',
|
category_id: category2.id,
|
||||||
category_id: category3.id,
|
tags: [tag1.name, tag2.name],
|
||||||
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::Rule.all.size).to eq(3)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 2)).to eq(:deleted)
|
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 2)).to eq(:deleted)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(2)
|
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
|
end
|
||||||
|
|
||||||
it 'fails gracefully for out of range indexes' do
|
it "fails gracefully for out of range indexes" do
|
||||||
rule1 = DiscourseChatIntegration::Rule.create(channel: chan1,
|
rule1 =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Rule.create(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag2.name]
|
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, -1)).to eq(false)
|
||||||
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 0)).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)
|
expect(DiscourseChatIntegration::Helper.delete_by_index(chan1, 1)).to eq(:deleted)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.smart_create_rule' do
|
describe ".smart_create_rule" do
|
||||||
|
it "creates a rule when there are none" do
|
||||||
it 'creates a rule when there are none' do
|
val =
|
||||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||||
filter: 'watch',
|
channel: chan1,
|
||||||
category_id: category.id,
|
filter: "watch",
|
||||||
tags: [tag1.name]
|
category_id: category.id,
|
||||||
)
|
tags: [tag1.name],
|
||||||
|
)
|
||||||
expect(val).to eq(:created)
|
expect(val).to eq(:created)
|
||||||
|
|
||||||
record = DiscourseChatIntegration::Rule.all.first
|
record = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(record.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(record.tags).to eq([tag1.name])
|
expect(record.tags).to eq([tag1.name])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates a rule when it has the same category and tags' do
|
it "updates a rule when it has the same category and tags" do
|
||||||
existing = DiscourseChatIntegration::Rule.create!(channel: chan1,
|
existing =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Rule.create!(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag2.name, tag1.name]
|
filter: "watch",
|
||||||
)
|
category_id: category.id,
|
||||||
|
tags: [tag2.name, tag1.name],
|
||||||
|
)
|
||||||
|
|
||||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
val =
|
||||||
filter: 'mute',
|
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag2.name]
|
filter: "mute",
|
||||||
)
|
category_id: category.id,
|
||||||
|
tags: [tag1.name, tag2.name],
|
||||||
|
)
|
||||||
|
|
||||||
expect(val).to eq(:updated)
|
expect(val).to eq(:updated)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
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
|
end
|
||||||
|
|
||||||
it 'updates a rule when it has the same category and filter' do
|
it "updates a rule when it has the same category and filter" do
|
||||||
existing = DiscourseChatIntegration::Rule.create(channel: chan1,
|
existing =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Rule.create(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag2.name]
|
filter: "watch",
|
||||||
)
|
category_id: category.id,
|
||||||
|
tags: [tag1.name, tag2.name],
|
||||||
|
)
|
||||||
|
|
||||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
val =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||||
category_id: category.id,
|
channel: chan1,
|
||||||
tags: [tag1.name, tag3.name]
|
filter: "watch",
|
||||||
)
|
category_id: category.id,
|
||||||
|
tags: [tag1.name, tag3.name],
|
||||||
|
)
|
||||||
|
|
||||||
expect(val).to eq(:updated)
|
expect(val).to eq(:updated)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
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
|
end
|
||||||
|
|
||||||
it 'destroys duplicate rules on save' do
|
it "destroys duplicate rules on save" do
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch')
|
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch")
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch')
|
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: "watch")
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(2)
|
expect(DiscourseChatIntegration::Rule.all.size).to eq(2)
|
||||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1,
|
val =
|
||||||
filter: 'watch',
|
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||||
category_id: nil,
|
channel: chan1,
|
||||||
tags: nil
|
filter: "watch",
|
||||||
)
|
category_id: nil,
|
||||||
|
tags: nil,
|
||||||
|
)
|
||||||
expect(val).to eq(:updated)
|
expect(val).to eq(:updated)
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
expect(DiscourseChatIntegration::Rule.all.size).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns false on error' do
|
it "returns false on error" do
|
||||||
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1, filter: 'blah')
|
val = DiscourseChatIntegration::Helper.smart_create_rule(channel: chan1, filter: "blah")
|
||||||
|
|
||||||
expect(val).to eq(false)
|
expect(val).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.save_transcript' do
|
describe ".save_transcript" do
|
||||||
|
it "saves a transcript to redis" do
|
||||||
it 'saves a transcript to redis' do
|
|
||||||
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
||||||
|
|
||||||
expect(Discourse.redis.get("chat_integration:transcript:#{key}")).to eq("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
|
end
|
||||||
|
|
||||||
describe ".formatted_display_name" do
|
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
|
it "prioritizes correctly" do
|
||||||
SiteSetting.prioritize_username_in_ux = true
|
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
|
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
|
end
|
||||||
|
|
||||||
it "only displays one when name/username are similar" do
|
it "only displays one when name/username are similar" do
|
||||||
user.update!(username: "john_smith")
|
user.update!(username: "john_smith")
|
||||||
SiteSetting.prioritize_username_in_ux = true
|
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
|
SiteSetting.prioritize_username_in_ux = false
|
||||||
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("John Smith")
|
expect(DiscourseChatIntegration::Helper.formatted_display_name(user)).to eq("John Smith")
|
||||||
end
|
end
|
||||||
|
@ -346,10 +415,13 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
SiteSetting.enable_names = false
|
SiteSetting.enable_names = false
|
||||||
|
|
||||||
SiteSetting.prioritize_username_in_ux = true
|
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
|
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
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,42 +1,46 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
|
|
||||||
describe 'site settings' do
|
describe "site settings" do
|
||||||
before do
|
before do
|
||||||
PluginStoreRow.create!(
|
PluginStoreRow.create!(
|
||||||
plugin_name: 'discourse-slack-official',
|
plugin_name: "discourse-slack-official",
|
||||||
key: "category_#{category.id}",
|
key: "category_#{category.id}",
|
||||||
type_name: "JSON",
|
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: "t", data_type: 5, name: "slack_enabled")
|
||||||
SiteSetting.create!(value: 'token', data_type: 1, name: 'slack_access_token')
|
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: "token2", data_type: 1, name: "slack_incoming_webhook_token")
|
||||||
SiteSetting.create!(value: 300, data_type: 3, name: 'slack_discourse_excerpt_length')
|
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!(
|
||||||
SiteSetting.create!(value: "http://outbound2.com", data_type: 1, name: 'slack_icon_url')
|
value: "https://hooks.slack.com/services/something",
|
||||||
SiteSetting.create!(value: 100, data_type: 3, name: 'post_to_slack_window_secs')
|
data_type: 1,
|
||||||
SiteSetting.create!(value: User.last.username, data_type: 1, name: 'slack_discourse_username')
|
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
|
end
|
||||||
|
|
||||||
it 'should migrate the site settings correctly' do
|
it "should migrate the site settings correctly" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
expect(SiteSetting.find_by(name: 'slack_enabled').value).to eq('f')
|
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_access_token).to eq("token")
|
||||||
expect(SiteSetting.chat_integration_slack_incoming_webhook_token).to eq('token2')
|
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_excerpt_length).to eq(300)
|
||||||
|
|
||||||
expect(SiteSetting.chat_integration_slack_outbound_webhook_url)
|
expect(SiteSetting.chat_integration_slack_outbound_webhook_url).to eq(
|
||||||
.to eq("https://hooks.slack.com/services/something")
|
"https://hooks.slack.com/services/something",
|
||||||
|
)
|
||||||
|
|
||||||
expect(SiteSetting.chat_integration_slack_icon_url)
|
expect(SiteSetting.chat_integration_slack_icon_url).to eq("http://outbound2.com")
|
||||||
.to eq("http://outbound2.com")
|
|
||||||
|
|
||||||
expect(SiteSetting.chat_integration_delay_seconds).to eq(100)
|
expect(SiteSetting.chat_integration_delay_seconds).to eq(100)
|
||||||
expect(SiteSetting.chat_integration_discourse_username).to eq(User.last.username)
|
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)
|
expect(SiteSetting.chat_integration_enabled).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when slack_discourse_username is not valid' do
|
describe "when slack_discourse_username is not valid" do
|
||||||
before do
|
before { SiteSetting.find_by(name: "slack_discourse_username").update!(value: "someguy") }
|
||||||
SiteSetting.find_by(name: 'slack_discourse_username').update!(value: 'someguy')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should default to the system user' do
|
it "should default to the system user" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
expect(SiteSetting.chat_integration_discourse_username)
|
expect(SiteSetting.chat_integration_discourse_username).to eq(
|
||||||
.to eq(Discourse.system_user.username)
|
Discourse.system_user.username,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when a uncategorized filter is present' do
|
describe "when a uncategorized filter is present" do
|
||||||
before do
|
before do
|
||||||
PluginStoreRow.create!(
|
PluginStoreRow.create!(
|
||||||
plugin_name: 'discourse-slack-official',
|
plugin_name: "discourse-slack-official",
|
||||||
key: "category_*",
|
key: "category_*",
|
||||||
type_name: "JSON",
|
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
|
end
|
||||||
|
|
||||||
it 'should create the right channels and rules' do
|
it "should create the right channels and rules" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Channel.count).to eq(2)
|
expect(DiscourseChatIntegration::Channel.count).to eq(2)
|
||||||
|
@ -76,86 +80,87 @@ RSpec.describe Jobs::DiscourseChatMigrateFromSlackOfficial do
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.first
|
channel = DiscourseChatIntegration::Channel.first
|
||||||
|
|
||||||
expect(channel.value['provider']).to eq("slack")
|
expect(channel.value["provider"]).to eq("slack")
|
||||||
expect(channel.value['data']['identifier']).to eq("#channel1")
|
expect(channel.value["data"]["identifier"]).to eq("#channel1")
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.first
|
rule = DiscourseChatIntegration::Rule.first
|
||||||
|
|
||||||
expect(rule.value['channel_id']).to eq(channel.id)
|
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||||
expect(rule.value['filter']).to eq('mute')
|
expect(rule.value["filter"]).to eq("mute")
|
||||||
expect(rule.value['category_id']).to eq(nil)
|
expect(rule.value["category_id"]).to eq(nil)
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.last
|
channel = DiscourseChatIntegration::Channel.last
|
||||||
|
|
||||||
expect(channel.value['provider']).to eq("slack")
|
expect(channel.value["provider"]).to eq("slack")
|
||||||
expect(channel.value['data']['identifier']).to eq("#channel2")
|
expect(channel.value["data"]["identifier"]).to eq("#channel2")
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.last
|
rule = DiscourseChatIntegration::Rule.last
|
||||||
|
|
||||||
expect(rule.value['channel_id']).to eq(channel.id)
|
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||||
expect(rule.value['filter']).to eq('follow')
|
expect(rule.value["filter"]).to eq("follow")
|
||||||
expect(rule.value['category_id']).to eq(nil)
|
expect(rule.value["category_id"]).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when filter contains an invalid tag' do
|
describe "when filter contains an invalid tag" do
|
||||||
let(:tag) { Fabricate(:tag) }
|
let(:tag) { Fabricate(:tag) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
PluginStoreRow.create!(
|
PluginStoreRow.create!(
|
||||||
plugin_name: 'discourse-slack-official',
|
plugin_name: "discourse-slack-official",
|
||||||
key: "category_#{category.id}",
|
key: "category_#{category.id}",
|
||||||
type_name: "JSON",
|
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
|
end
|
||||||
|
|
||||||
it 'should discard invalid tags' do
|
it "should discard invalid tags" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.first
|
rule = DiscourseChatIntegration::Rule.first
|
||||||
|
|
||||||
expect(rule.value['tags']).to eq([tag.name])
|
expect(rule.value["tags"]).to eq([tag.name])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when a category filter is present' do
|
describe "when a category filter is present" do
|
||||||
before do
|
before do
|
||||||
PluginStoreRow.create!(
|
PluginStoreRow.create!(
|
||||||
plugin_name: 'discourse-slack-official',
|
plugin_name: "discourse-slack-official",
|
||||||
key: "category_#{category.id}",
|
key: "category_#{category.id}",
|
||||||
type_name: "JSON",
|
type_name: "JSON",
|
||||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]"
|
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should migrate the settings correctly' do
|
it "should migrate the settings correctly" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.first
|
channel = DiscourseChatIntegration::Channel.first
|
||||||
|
|
||||||
expect(channel.value['provider']).to eq("slack")
|
expect(channel.value["provider"]).to eq("slack")
|
||||||
expect(channel.value['data']['identifier']).to eq("#slack-channel")
|
expect(channel.value["data"]["identifier"]).to eq("#slack-channel")
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.first
|
rule = DiscourseChatIntegration::Rule.first
|
||||||
|
|
||||||
expect(rule.value['channel_id']).to eq(channel.id)
|
expect(rule.value["channel_id"]).to eq(channel.id)
|
||||||
expect(rule.value['filter']).to eq('mute')
|
expect(rule.value["filter"]).to eq("mute")
|
||||||
expect(rule.value['category_id']).to eq(category.id)
|
expect(rule.value["category_id"]).to eq(category.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when a category has been deleted' do
|
describe "when a category has been deleted" do
|
||||||
before do
|
before do
|
||||||
PluginStoreRow.create!(
|
PluginStoreRow.create!(
|
||||||
plugin_name: 'discourse-slack-official',
|
plugin_name: "discourse-slack-official",
|
||||||
key: 'category_9999',
|
key: "category_9999",
|
||||||
type_name: "JSON",
|
type_name: "JSON",
|
||||||
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]"
|
value: "[{\"channel\":\"#slack-channel\",\"filter\":\"mute\"}]",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not migrate the settings' do
|
it "should not migrate the settings" do
|
||||||
described_class.new.execute_onceoff({})
|
described_class.new.execute_onceoff({})
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Channel.count).to eq(0)
|
expect(DiscourseChatIntegration::Channel.count).to eq(0)
|
||||||
|
|
|
@ -1,48 +1,36 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe PostCreator do
|
RSpec.describe PostCreator do
|
||||||
let(:topic) { Fabricate(:post).topic }
|
let(:topic) { Fabricate(:post).topic }
|
||||||
|
|
||||||
before do
|
before { Jobs::NotifyChats.jobs.clear }
|
||||||
Jobs::NotifyChats.jobs.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'when a post is created' do
|
describe "when a post is created" do
|
||||||
describe 'when plugin is enabled' do
|
describe "when plugin is enabled" do
|
||||||
before do
|
before { SiteSetting.chat_integration_enabled = true }
|
||||||
SiteSetting.chat_integration_enabled = true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should schedule a chat notification job' do
|
it "should schedule a chat notification job" do
|
||||||
freeze_time Time.now.beginning_of_day
|
freeze_time Time.now.beginning_of_day
|
||||||
|
|
||||||
post = PostCreator.new(topic.user,
|
post = PostCreator.new(topic.user, raw: "Some post content", topic_id: topic.id).create!
|
||||||
raw: 'Some post content',
|
|
||||||
topic_id: topic.id
|
|
||||||
).create!
|
|
||||||
|
|
||||||
job = Jobs::NotifyChats.jobs.last
|
job = Jobs::NotifyChats.jobs.last
|
||||||
|
|
||||||
expect(job['at'])
|
expect(job["at"]).to eq(
|
||||||
.to eq(Time.now.to_f + SiteSetting.chat_integration_delay_seconds.seconds.to_f)
|
Time.now.to_f + SiteSetting.chat_integration_delay_seconds.seconds.to_f,
|
||||||
|
)
|
||||||
expect(job['args'].first['post_id']).to eq(post.id)
|
|
||||||
|
|
||||||
|
expect(job["args"].first["post_id"]).to eq(post.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when plugin is not enabled' do
|
describe "when plugin is not enabled" do
|
||||||
before do
|
before { SiteSetting.chat_integration_enabled = false }
|
||||||
SiteSetting.chat_integration_enabled = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should not schedule a job for chat notifications' do
|
it "should not schedule a job for chat notifications" do
|
||||||
PostCreator.new(topic.user,
|
PostCreator.new(topic.user, raw: "Some post content", topic_id: topic.id).create!
|
||||||
raw: 'Some post content',
|
|
||||||
topic_id: topic.id
|
|
||||||
).create!
|
|
||||||
|
|
||||||
expect(Jobs::NotifyChats.jobs).to eq([])
|
expect(Jobs::NotifyChats.jobs).to eq([])
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,38 +1,52 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::DiscordProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::DiscordProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_discord_enabled = true }
|
||||||
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
|
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 =
|
||||||
it 'sends a webhook request' do
|
stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").to_return(
|
||||||
stub1 = stub_request(:post, 'https://discord.com/api/webhooks/1234/abcd?wait=true').to_return(status: 200)
|
status: 200,
|
||||||
|
)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes the protocol in the avatar URL' do
|
it "includes the protocol in the avatar URL" do
|
||||||
stub1 = stub_request(:post, 'https://discord.com/api/webhooks/1234/abcd?wait=true')
|
stub1 =
|
||||||
.with(body: hash_including(embeds: [hash_including(author: hash_including(url: /^https?:\/\//))]))
|
stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").with(
|
||||||
.to_return(status: 200)
|
body:
|
||||||
|
hash_including(embeds: [hash_including(author: hash_including(url: %r{^https?://}))]),
|
||||||
|
).to_return(status: 200)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, "https://discord.com/api/webhooks/1234/abcd?wait=true").to_return(status: 400)
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,27 +1,38 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::FlowdockProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::FlowdockProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_flowdock_enabled = true }
|
||||||
SiteSetting.chat_integration_flowdock_enabled = true
|
|
||||||
|
let(:chan1) do
|
||||||
|
DiscourseChatIntegration::Channel.create!(
|
||||||
|
provider: "flowdock",
|
||||||
|
data: {
|
||||||
|
flow_token: "5d1fe04cf66e078d6a2b579ddb8a465b",
|
||||||
|
},
|
||||||
|
)
|
||||||
end
|
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)
|
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 200)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,27 +1,39 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::GitterProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::GitterProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_gitter_enabled = true }
|
||||||
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
|
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)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,36 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::GoogleProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::GoogleProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_google_enabled = true }
|
||||||
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
|
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)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,28 +1,41 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::GroupmeProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::GroupmeProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_groupme_enabled = true
|
SiteSetting.chat_integration_groupme_enabled = true
|
||||||
SiteSetting.chat_integration_groupme_bot_ids = '1a2b3c4d5e6f7g'
|
SiteSetting.chat_integration_groupme_bot_ids = "1a2b3c4d5e6f7g"
|
||||||
end
|
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)
|
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 200)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,38 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::GuildedProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::GuildedProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_guilded_enabled = true }
|
||||||
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
|
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 =
|
||||||
it 'sends a webhook request' do
|
stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 200)
|
||||||
stub1 = stub_request(:post, 'https://media.guilded.gg/webhooks/1234/abcd').to_return(status: 200)
|
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 400)
|
stub1 =
|
||||||
|
stub_request(:post, "https://media.guilded.gg/webhooks/1234/abcd").to_return(status: 400)
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +1,47 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::MatrixProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::MatrixProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_matrix_enabled = true
|
SiteSetting.chat_integration_matrix_enabled = true
|
||||||
SiteSetting.chat_integration_matrix_access_token = 'abcd'
|
SiteSetting.chat_integration_matrix_access_token = "abcd"
|
||||||
end
|
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
|
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)
|
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)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
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"}')
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,88 +1,94 @@
|
||||||
# frozen_string_literal: true
|
# 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(:category) { Fabricate(:category) }
|
||||||
let(:tag) { Fabricate(:tag) }
|
let(:tag) { Fabricate(:tag) }
|
||||||
let(:tag2) { 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
|
describe "with plugin disabled" do
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/mattermost/command.json'
|
post "/chat-integration/mattermost/command.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with plugin enabled and provider disabled' do
|
describe "with plugin enabled and provider disabled" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
SiteSetting.chat_integration_enabled = true
|
||||||
SiteSetting.chat_integration_mattermost_enabled = false
|
SiteSetting.chat_integration_mattermost_enabled = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/mattermost/command.json'
|
post "/chat-integration/mattermost/command.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'slash commands endpoint' do
|
describe "slash commands endpoint" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
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
|
SiteSetting.chat_integration_mattermost_enabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when forum is private' do
|
describe "when forum is private" do
|
||||||
it 'should not redirect to login page' do
|
it "should not redirect to login page" do
|
||||||
SiteSetting.login_required = true
|
SiteSetting.login_required = true
|
||||||
token = 'sometoken'
|
token = "sometoken"
|
||||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token = token
|
SiteSetting.chat_integration_mattermost_incoming_webhook_token = token
|
||||||
|
|
||||||
post '/chat-integration/mattermost/command.json', params: {
|
post "/chat-integration/mattermost/command.json", params: { text: "help", token: token }
|
||||||
text: 'help', token: token
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when the token is invalid' do
|
describe "when the token is invalid" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
post '/chat-integration/mattermost/command.json', params: { text: 'help' }
|
post "/chat-integration/mattermost/command.json", params: { text: "help" }
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when incoming webhook token has not been set' do
|
describe "when incoming webhook token has not been set" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
post '/chat-integration/mattermost/command.json', params: {
|
post "/chat-integration/mattermost/command.json",
|
||||||
text: 'help', token: 'some token'
|
params: {
|
||||||
}
|
text: "help",
|
||||||
|
token: "some token",
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when token is valid' do
|
describe "when token is valid" do
|
||||||
let(:token) { "Secret Sauce" }
|
let(:token) { "Secret Sauce" }
|
||||||
|
|
||||||
# No need to test every single command here, that's tested
|
# No need to test every single command here, that's tested
|
||||||
# by helper_spec upstream
|
# by helper_spec upstream
|
||||||
|
|
||||||
before do
|
before { SiteSetting.chat_integration_mattermost_incoming_webhook_token = token }
|
||||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token = token
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'add new rule' do
|
describe "add new rule" do
|
||||||
|
it "should add a new rule correctly" do
|
||||||
it 'should add a new rule correctly' do
|
post "/chat-integration/mattermost/command.json",
|
||||||
post "/chat-integration/mattermost/command.json", params: {
|
params: {
|
||||||
text: "watch #{category.slug}",
|
text: "watch #{category.slug}",
|
||||||
channel_name: 'welcome',
|
channel_name: "welcome",
|
||||||
token: token
|
token: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
json = response.parsed_body
|
||||||
|
|
||||||
|
@ -90,34 +96,40 @@ describe 'Mattermost Command Controller', type: :request do
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'from an unknown channel' do
|
describe "from an unknown channel" do
|
||||||
it 'creates the channel' do
|
it "creates the channel" do
|
||||||
post "/chat-integration/mattermost/command.json", params: {
|
post "/chat-integration/mattermost/command.json",
|
||||||
text: "watch #{category.slug}",
|
params: {
|
||||||
channel_name: 'general',
|
text: "watch #{category.slug}",
|
||||||
token: token
|
channel_name: "general",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
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
|
chan =
|
||||||
expect(chan.provider).to eq('mattermost')
|
DiscourseChatIntegration::Channel
|
||||||
|
.with_provider("mattermost")
|
||||||
|
.with_data_value("identifier", "#general")
|
||||||
|
.first
|
||||||
|
expect(chan.provider).to eq("mattermost")
|
||||||
|
|
||||||
rule = chan.rules.first
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::MattermostProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::MattermostProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
let(:upload) { Fabricate(:upload) }
|
let(:upload) { Fabricate(:upload) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -14,36 +14,47 @@ RSpec.describe DiscourseChatIntegration::Provider::MattermostProvider do
|
||||||
SiteSetting.logo_small = upload
|
SiteSetting.logo_small = upload
|
||||||
end
|
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
|
it "sends a webhook request" do
|
||||||
stub1 = stub_request(:post, 'https://mattermost.blah/hook/abcd').to_return(status: 200)
|
stub1 = stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(status: 200)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when mattermost icon is not configured' do
|
describe "when mattermost icon is not configured" do
|
||||||
it 'defaults to the right icon' do
|
it "defaults to the right icon" do
|
||||||
message = described_class.mattermost_message(post, chan1)
|
message = described_class.mattermost_message(post, chan1)
|
||||||
expect(message[:icon_url]).to eq(UrlHelper.absolute(upload.url))
|
expect(message[:icon_url]).to eq(UrlHelper.absolute(upload.url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when mattermost icon has been configured' do
|
describe "when mattermost icon has been configured" do
|
||||||
it 'should use the right icon' do
|
it "should use the right icon" do
|
||||||
SiteSetting.chat_integration_mattermost_icon_url = "https://specific_logo"
|
SiteSetting.chat_integration_mattermost_icon_url = "https://specific_logo"
|
||||||
message = described_class.mattermost_message(post, chan1)
|
message = described_class.mattermost_message(post, chan1)
|
||||||
expect(message[:icon_url]).to eq(SiteSetting.chat_integration_mattermost_icon_url)
|
expect(message[:icon_url]).to eq(SiteSetting.chat_integration_mattermost_icon_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(status: 500, body: "error")
|
stub1 =
|
||||||
|
stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(
|
||||||
|
status: 500,
|
||||||
|
body: "error",
|
||||||
|
)
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +1,38 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::RocketchatProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::RocketchatProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_rocketchat_enabled = true
|
SiteSetting.chat_integration_rocketchat_enabled = true
|
||||||
SiteSetting.chat_integration_rocketchat_webhook_url = "https://example.com/abcd"
|
SiteSetting.chat_integration_rocketchat_webhook_url = "https://example.com/abcd"
|
||||||
end
|
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
|
it "sends a webhook request" do
|
||||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(body: "{\"success\":true}")
|
stub1 = stub_request(:post, "https://example.com/abcd").to_return(body: "{\"success\":true}")
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(status: 400, body: "{}")
|
stub1 = stub_request(:post, "https://example.com/abcd").to_return(status: 400, body: "{}")
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,106 +1,99 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
describe 'Slack Command Controller', type: :request do
|
describe "Slack Command Controller", type: :request do
|
||||||
before do
|
before { Discourse.cache.clear }
|
||||||
Discourse.cache.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
let(:tag) { Fabricate(:tag) }
|
let(:tag) { Fabricate(:tag) }
|
||||||
let(:tag2) { 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
|
describe "with plugin disabled" do
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/slack/command.json'
|
post "/chat-integration/slack/command.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with plugin enabled and provider disabled' do
|
describe "with plugin enabled and provider disabled" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
SiteSetting.chat_integration_enabled = true
|
||||||
SiteSetting.chat_integration_slack_enabled = false
|
SiteSetting.chat_integration_slack_enabled = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/slack/command.json'
|
post "/chat-integration/slack/command.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'slash commands endpoint' do
|
describe "slash commands endpoint" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
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
|
SiteSetting.chat_integration_slack_enabled = true
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when forum is private' do
|
describe "when forum is private" do
|
||||||
it 'should not redirect to login page' do
|
it "should not redirect to login page" do
|
||||||
SiteSetting.login_required = true
|
SiteSetting.login_required = true
|
||||||
token = 'sometoken'
|
token = "sometoken"
|
||||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||||
|
|
||||||
post '/chat-integration/slack/command.json', params: {
|
post "/chat-integration/slack/command.json", params: { text: "help", token: token }
|
||||||
text: 'help', token: token
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when the token is invalid' do
|
describe "when the token is invalid" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
post '/chat-integration/slack/command.json', params: { text: 'help' }
|
post "/chat-integration/slack/command.json", params: { text: "help" }
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'backwards compatibility with discourse-slack-official' do
|
describe "backwards compatibility with discourse-slack-official" do
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
token = 'secret sauce'
|
token = "secret sauce"
|
||||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||||
|
|
||||||
post '/slack/command.json', params: {
|
post "/slack/command.json", params: { text: "help", token: token }
|
||||||
text: 'help', token: token
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(response.parsed_body["text"]).to be_present
|
expect(response.parsed_body["text"]).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when incoming webhook token has not been set' do
|
describe "when incoming webhook token has not been set" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
post '/chat-integration/slack/command.json', params: {
|
post "/chat-integration/slack/command.json", params: { text: "help", token: "some token" }
|
||||||
text: 'help', token: 'some token'
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when token is valid' do
|
describe "when token is valid" do
|
||||||
let(:token) { "Secret Sauce" }
|
let(:token) { "Secret Sauce" }
|
||||||
|
|
||||||
# No need to test every single command here, that's tested
|
# No need to test every single command here, that's tested
|
||||||
# by helper_spec upstream
|
# by helper_spec upstream
|
||||||
|
|
||||||
before do
|
before { SiteSetting.chat_integration_slack_incoming_webhook_token = token }
|
||||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'add new rule' do
|
describe "add new rule" do
|
||||||
|
it "should add a new rule correctly" do
|
||||||
it 'should add a new rule correctly' do
|
post "/chat-integration/slack/command.json",
|
||||||
post "/chat-integration/slack/command.json", params: {
|
params: {
|
||||||
text: "watch #{category.slug}",
|
text: "watch #{category.slug}",
|
||||||
channel_name: 'welcome',
|
channel_name: "welcome",
|
||||||
token: token
|
token: token,
|
||||||
}
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
json = response.parsed_body
|
||||||
|
|
||||||
|
@ -108,277 +101,314 @@ describe 'Slack Command Controller', type: :request do
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'from an unknown channel' do
|
describe "from an unknown channel" do
|
||||||
it 'creates the channel' do
|
it "creates the channel" do
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "watch #{category.slug}",
|
params: {
|
||||||
channel_name: 'general',
|
text: "watch #{category.slug}",
|
||||||
token: token
|
channel_name: "general",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
json = response.parsed_body
|
||||||
|
|
||||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.create.created"))
|
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
|
chan =
|
||||||
expect(chan.provider).to eq('slack')
|
DiscourseChatIntegration::Channel
|
||||||
|
.with_provider("slack")
|
||||||
|
.with_data_value("identifier", "#general")
|
||||||
|
.first
|
||||||
|
expect(chan.provider).to eq("slack")
|
||||||
|
|
||||||
rule = chan.rules.first
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'post transcript' do
|
describe "post transcript" do
|
||||||
let(:messages_fixture) {
|
let(:messages_fixture) do
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "message",
|
type: "message",
|
||||||
"user": "U6JSSESES",
|
user: "U6JSSESES",
|
||||||
"text": "Yeah, should make posting slack transcripts much easier",
|
text: "Yeah, should make posting slack transcripts much easier",
|
||||||
"ts": "1501801665.062694"
|
ts: "1501801665.062694",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "message",
|
type: "message",
|
||||||
"user": "U5Z773QLS",
|
user: "U5Z773QLS",
|
||||||
"text": "Oooh a new discourse plugin???",
|
text: "Oooh a new discourse plugin???",
|
||||||
"ts": "1501801643.056375"
|
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",
|
text: "",
|
||||||
"user": "U6E2W7R8C",
|
username: "Test Community",
|
||||||
"text": "Which one?",
|
bot_id: "B6C6JNUDN",
|
||||||
"ts": "1501801634.053761"
|
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",
|
type: "message",
|
||||||
"user": "U6JSSESES",
|
user: "U5Z773QLS",
|
||||||
"text": "So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
text: "Let’s try some *bold text*",
|
||||||
"ts": "1501801629.052212"
|
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
|
end
|
||||||
|
|
||||||
|
before { SiteSetting.chat_integration_slack_access_token = "abcde" }
|
||||||
|
|
||||||
context "with valid slack responses" do
|
context "with valid slack responses" do
|
||||||
before 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/users.list").to_return(
|
||||||
stub_request(:post, "https://slack.com/api/conversations.history").to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
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
|
end
|
||||||
|
|
||||||
it 'generates the transcript UI properly' do
|
it "generates the transcript UI properly" do
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.with(body: /attachments/)
|
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
body: /attachments/,
|
||||||
|
).to_return(body: { ok: true }.to_json)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text: "post",
|
||||||
channel_name: 'general',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
channel_id: 'C6029G78F',
|
channel_name: "general",
|
||||||
token: token
|
channel_id: "C6029G78F",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can select by url' do
|
it "can select by url" do
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.with(body: /1501801629\.052212/)
|
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
body: /1501801629\.052212/,
|
||||||
|
).to_return(body: { ok: true }.to_json)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text:
|
||||||
channel_name: 'general',
|
"post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||||
channel_id: 'C6029G78F',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
token: token
|
channel_name: "general",
|
||||||
}
|
channel_id: "C6029G78F",
|
||||||
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can select by url with thread parameter' do
|
it "can select by url with thread parameter" do
|
||||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
replies_stub =
|
||||||
.with(body: /1501801629\.052212/)
|
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
body: /1501801629\.052212/,
|
||||||
|
).to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||||
|
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.to_return(body: { ok: true }.to_json)
|
stub_request(:post, "https://slack.com/commands/1234").to_return(
|
||||||
|
body: { ok: true }.to_json,
|
||||||
|
)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501201669054212?thread_ts=1501801629.052212",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text:
|
||||||
channel_name: 'general',
|
"post https://sometestslack.slack.com/archives/C6029G78F/p1501201669054212?thread_ts=1501801629.052212",
|
||||||
channel_id: 'C6029G78F',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
token: token
|
channel_name: "general",
|
||||||
}
|
channel_id: "C6029G78F",
|
||||||
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
expect(replies_stub).to have_been_requested
|
expect(replies_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can select by thread' do
|
it "can select by thread" do
|
||||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
replies_stub =
|
||||||
.with(body: /1501801629\.052212/)
|
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
body: /1501801629\.052212/,
|
||||||
|
).to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
||||||
|
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.to_return(body: { ok: true }.to_json)
|
stub_request(:post, "https://slack.com/commands/1234").to_return(
|
||||||
|
body: { ok: true }.to_json,
|
||||||
|
)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post thread https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text:
|
||||||
channel_name: 'general',
|
"post thread https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
|
||||||
channel_id: 'C6029G78F',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
token: token
|
channel_name: "general",
|
||||||
}
|
channel_id: "C6029G78F",
|
||||||
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
expect(replies_stub).to have_been_requested
|
expect(replies_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can select by count' do
|
it "can select by count" do
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.with(body: /1501801629\.052212/)
|
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
body: /1501801629\.052212/,
|
||||||
|
).to_return(body: { ok: true }.to_json)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post 4",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text: "post 4",
|
||||||
channel_name: 'general',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
channel_id: 'C6029G78F',
|
channel_name: "general",
|
||||||
token: token
|
channel_id: "C6029G78F",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can auto select' do
|
it "can auto select" do
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.with(body: /1501615820\.949638/)
|
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
body: /1501615820\.949638/,
|
||||||
|
).to_return(body: { ok: true }.to_json)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text: "post",
|
||||||
channel_name: 'general',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
channel_id: 'C6029G78F',
|
channel_name: "general",
|
||||||
token: token
|
channel_id: "C6029G78F",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
expect(command_stub).to have_been_requested
|
expect(command_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it "supports using shortcuts to create a thread transcript" do
|
it "supports using shortcuts to create a thread transcript" do
|
||||||
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
|
replies_stub =
|
||||||
.with(body: /1501801629\.052212/)
|
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||||
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
|
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")
|
view_open_stub =
|
||||||
.with(body: /TRIGGERID/)
|
stub_request(:post, "https://slack.com/api/views.open").with(
|
||||||
.to_return(body: { ok: true, view: { id: "VIEWID" } }.to_json)
|
body: /TRIGGERID/,
|
||||||
|
).to_return(body: { ok: true, view: { id: "VIEWID" } }.to_json)
|
||||||
|
|
||||||
view_update_stub = stub_request(:post, "https://slack.com/api/views.update")
|
view_update_stub =
|
||||||
.with(body: /VIEWID/)
|
stub_request(:post, "https://slack.com/api/views.update").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
body: /VIEWID/,
|
||||||
|
).to_return(body: { ok: true }.to_json)
|
||||||
|
|
||||||
post "/chat-integration/slack/interactive.json", params: {
|
post "/chat-integration/slack/interactive.json",
|
||||||
payload: {
|
params: {
|
||||||
type: "message_action",
|
payload: {
|
||||||
channel: { name: 'general', id: 'C6029G78F' },
|
type: "message_action",
|
||||||
trigger_id: "TRIGGERID",
|
channel: {
|
||||||
message: { thread_ts: "1501801629.052212" },
|
name: "general",
|
||||||
token: token
|
id: "C6029G78F",
|
||||||
}.to_json
|
},
|
||||||
}
|
trigger_id: "TRIGGERID",
|
||||||
|
message: {
|
||||||
|
thread_ts: "1501801629.052212",
|
||||||
|
},
|
||||||
|
token: token,
|
||||||
|
}.to_json,
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
expect(view_open_stub).to have_been_requested
|
expect(view_open_stub).to have_been_requested
|
||||||
expect(view_update_stub).to have_been_requested
|
expect(view_update_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deals with failed API calls correctly' do
|
it "deals with failed API calls correctly" do
|
||||||
command_stub = stub_request(:post, "https://slack.com/commands/1234")
|
command_stub =
|
||||||
.with(body: { text: I18n.t("chat_integration.provider.slack.transcript.error_users") })
|
stub_request(:post, "https://slack.com/commands/1234").with(
|
||||||
.to_return(body: { ok: true }.to_json)
|
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)
|
stub_request(:post, "https://slack.com/api/users.list").to_return(status: 403)
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post 2",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text: "post 2",
|
||||||
channel_name: 'general',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
channel_id: 'C6029G78F',
|
channel_name: "general",
|
||||||
token: token
|
channel_id: "C6029G78F",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
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
|
expect(command_stub).to have_been_requested
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'errors correctly if there is no api key' do
|
it "errors correctly if there is no api key" do
|
||||||
SiteSetting.chat_integration_slack_access_token = ''
|
SiteSetting.chat_integration_slack_access_token = ""
|
||||||
|
|
||||||
post "/chat-integration/slack/command.json", params: {
|
post "/chat-integration/slack/command.json",
|
||||||
text: "post 2",
|
params: {
|
||||||
response_url: 'https://hooks.slack.com/commands/1234',
|
text: "post 2",
|
||||||
channel_name: 'general',
|
response_url: "https://hooks.slack.com/commands/1234",
|
||||||
channel_id: 'C6029G78F',
|
channel_name: "general",
|
||||||
token: token
|
channel_id: "C6029G78F",
|
||||||
}
|
token: token,
|
||||||
|
}
|
||||||
|
|
||||||
json = response.parsed_body
|
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
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +1,33 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackMessageFormatter do
|
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackMessageFormatter do
|
||||||
describe '.format' do
|
describe ".format" do
|
||||||
context 'with links' do
|
context "with links" do
|
||||||
it 'should return the right message' do
|
it "should return the right message" do
|
||||||
expect(described_class.format("<a href='http://somepath.com'>test</a>"))
|
expect(described_class.format("<a href='http://somepath.com'>test</a>")).to eq(
|
||||||
.to eq('<http://somepath.com|test>')
|
"<http://somepath.com|test>",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when text contains a link with an incomplete URL' do
|
describe "when text contains a link with an incomplete URL" do
|
||||||
it 'should return the right message' do
|
it "should return the right message" do
|
||||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>"))
|
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>")).to eq(
|
||||||
.to eq("test <http://localhost:3000/some/path|>")
|
"test <http://localhost:3000/some/path|>",
|
||||||
|
)
|
||||||
|
|
||||||
SiteSetting.force_https = true
|
SiteSetting.force_https = true
|
||||||
|
|
||||||
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>"))
|
expect(described_class.format("test <a href='//localhost:3000/some/path'></a>")).to eq(
|
||||||
.to eq("test <https://localhost:3000/some/path|>")
|
"test <https://localhost:3000/some/path|>",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not raise an error with unparseable urls" do
|
it "should not raise an error with unparseable urls" do
|
||||||
expect(described_class.format("<a>test</a>")).to eq("<test.localhost|test>")
|
expect(described_class.format("<a>test</a>")).to eq("<test.localhost|test>")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.excerpt' do
|
describe ".excerpt" do
|
||||||
describe 'when post contains emoijs' do
|
describe "when post contains emoijs" do
|
||||||
before do
|
before { post.update!(raw: ":slight_smile: This is a test") }
|
||||||
post.update!(raw: ':slight_smile: This is a test')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the right excerpt' do
|
it "should return the right excerpt" do
|
||||||
expect(described_class.excerpt(post)).to eq('🙂 This is a test')
|
expect(described_class.excerpt(post)).to eq("🙂 This is a test")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when post contains onebox' do
|
describe "when post contains onebox" do
|
||||||
it 'should return the right excerpt' do
|
it "should return the right excerpt" do
|
||||||
post.update!(cooked: <<~COOKED
|
post.update!(cooked: <<~COOKED)
|
||||||
<aside class=\"onebox whitelistedgeneric\">
|
<aside class=\"onebox whitelistedgeneric\">
|
||||||
<header class=\"source\">
|
<header class=\"source\">
|
||||||
<a href=\"http://somesource.com\">
|
<a href=\"http://somesource.com\">
|
||||||
|
@ -45,59 +43,97 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider do
|
||||||
<div style=\"clear: both\"></div>
|
<div style=\"clear: both\"></div>
|
||||||
</aside>
|
</aside>
|
||||||
COOKED
|
COOKED
|
||||||
)
|
|
||||||
|
|
||||||
expect(described_class.excerpt(post))
|
expect(described_class.excerpt(post)).to eq("<http://somesource.com|meta.discourse.org>")
|
||||||
.to eq('<http://somesource.com|meta.discourse.org>')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when post contains an email' do
|
describe "when post contains an email" do
|
||||||
it 'should return the right excerpt' do
|
it "should return the right excerpt" do
|
||||||
post.update!(cooked: <<~COOKED
|
post.update!(cooked: <<~COOKED)
|
||||||
The address is <a href=\"mailto:someone@domain.com\">my email</a>
|
The address is <a href=\"mailto:someone@domain.com\">my email</a>
|
||||||
COOKED
|
COOKED
|
||||||
)
|
|
||||||
|
|
||||||
expect(described_class.excerpt(post))
|
expect(described_class.excerpt(post)).to eq(
|
||||||
.to eq('The address is <mailto:someone@domain.com|my email>')
|
"The address is <mailto:someone@domain.com|my email>",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before 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
|
SiteSetting.chat_integration_slack_enabled = true
|
||||||
end
|
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
|
it "sends a webhook request" do
|
||||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
stub1 =
|
||||||
|
stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(
|
||||||
|
body: "success",
|
||||||
|
)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(status: 400, body: "error")
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with api token' do
|
describe "with api token" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_slack_access_token = "magic"
|
SiteSetting.chat_integration_slack_access_token = "magic"
|
||||||
@ts = "#{Time.now.to_i}.012345"
|
@ts = "#{Time.now.to_i}.012345"
|
||||||
@ts2 = "#{Time.now.to_i}.012346"
|
@ts2 = "#{Time.now.to_i}.012346"
|
||||||
@stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
@stub1 =
|
||||||
@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' })
|
stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(
|
||||||
@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' })
|
body: "success",
|
||||||
@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' })
|
)
|
||||||
|
@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
|
end
|
||||||
|
|
||||||
it 'sends an api request' do
|
it "sends an api request" do
|
||||||
expect(@stub2).to have_been_requested.times(0)
|
expect(@stub2).to have_been_requested.times(0)
|
||||||
expect(@thread_stub).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)
|
expect(@thread_stub).to have_been_requested.times(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'sends thread id for thread' do
|
it "sends thread id for thread" do
|
||||||
expect(@thread_stub).to have_been_requested.times(0)
|
expect(@thread_stub).to have_been_requested.times(0)
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
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
|
expect(@thread_stub).to have_been_requested.once
|
||||||
end
|
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)
|
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")
|
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
||||||
rule2 = DiscourseChatIntegration::Rule.create(channel: chan2, 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)
|
expect(described_class.get_slack_thread_ts(post.topic, "#random")).to eq(@ts2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'recognizes slack thread ts in comment' do
|
it "recognizes slack thread ts in comment" do
|
||||||
post.update!(cooked: "cooked", raw: <<~RAW
|
post.update!(cooked: "cooked", raw: <<~RAW)
|
||||||
My fingers are typing words that improve `raw_quality`
|
My fingers are typing words that improve `raw_quality`
|
||||||
<!--SLACK_CHANNEL_ID=#general;SLACK_TS=#{@ts}-->
|
<!--SLACK_CHANNEL_ID=#general;SLACK_TS=#{@ts}-->
|
||||||
RAW
|
RAW
|
||||||
)
|
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.create(channel: chan1, filter: "thread")
|
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)
|
expect(@thread_stub).to have_been_requested.times(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
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' })
|
@stub2 =
|
||||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChatIntegration::ProviderError)
|
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
|
expect(@stub2).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,77 +1,69 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscript do
|
RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscript do
|
||||||
before do
|
before { Discourse.cache.clear }
|
||||||
Discourse.cache.clear
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:messages_fixture) {
|
let(:messages_fixture) do
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"type": "message",
|
type: "message",
|
||||||
"user": "U6JSSESES",
|
user: "U6JSSESES",
|
||||||
"text": "Yeah, should make posting slack transcripts much easier",
|
text: "Yeah, should make posting slack transcripts much easier",
|
||||||
"ts": "1501801665.062694"
|
ts: "1501801665.062694",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "message",
|
type: "message",
|
||||||
"user": "U5Z773QLZ",
|
user: "U5Z773QLZ",
|
||||||
"text": "Oooh a new discourse plugin <@U5Z773QLS> ???",
|
text: "Oooh a new discourse plugin <@U5Z773QLS> ???",
|
||||||
"ts": "1501801643.056375"
|
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",
|
type: "message",
|
||||||
"user": "U6E2W7R8C",
|
user: "U820GH3LA",
|
||||||
"text": "Which one?",
|
text: "I'm interested!!",
|
||||||
"ts": "1501801635.053761"
|
ts: "1501801634.053761",
|
||||||
|
thread_ts: "1501801629.052212",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "message",
|
text: "Check this out!",
|
||||||
"user": "U6JSSESES",
|
username: "Test Community",
|
||||||
"text": "So, who's interested in the new <https://meta.discourse.org|discourse plugin>?",
|
bot_id: "B6C6JNUDN",
|
||||||
"ts": "1501801629.052212"
|
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",
|
type: "message",
|
||||||
"user": "U820GH3LA",
|
user: "U5Z773QLS",
|
||||||
"text": "I'm interested!!",
|
text: "Let’s try some *bold text* <@U5Z773QLZ> <@someotheruser>",
|
||||||
"ts": "1501801634.053761",
|
ts: "1501093331.439776",
|
||||||
"thread_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": "U5Z773QLS",
|
|
||||||
"text": "Let’s try some *bold text* <@U5Z773QLZ> <@someotheruser>",
|
|
||||||
"ts": "1501093331.439776"
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
end
|
||||||
|
|
||||||
let(:users_fixture) {
|
let(:users_fixture) do
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: "U6JSSESES",
|
id: "U6JSSESES",
|
||||||
|
@ -79,8 +71,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
profile: {
|
profile: {
|
||||||
image_24: "https://example.com/avatar",
|
image_24: "https://example.com/avatar",
|
||||||
display_name: "Threader",
|
display_name: "Threader",
|
||||||
real_name: "A. Threader"
|
real_name: "A. Threader",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "U820GH3LA",
|
id: "U820GH3LA",
|
||||||
|
@ -88,8 +80,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
profile: {
|
profile: {
|
||||||
image_24: "https://example.com/avatar",
|
image_24: "https://example.com/avatar",
|
||||||
display_name: "Responder",
|
display_name: "Responder",
|
||||||
real_name: "A. Responder"
|
real_name: "A. Responder",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "U5Z773QLS",
|
id: "U5Z773QLS",
|
||||||
|
@ -97,8 +89,8 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
profile: {
|
profile: {
|
||||||
image_24: "https://example.com/avatar",
|
image_24: "https://example.com/avatar",
|
||||||
display_name: "awesomeguy",
|
display_name: "awesomeguy",
|
||||||
real_name: "actually just a guy"
|
real_name: "actually just a guy",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "U5Z773QLZ",
|
id: "U5Z773QLZ",
|
||||||
|
@ -106,196 +98,221 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
profile: {
|
profile: {
|
||||||
image_24: "https://example.com/avatar",
|
image_24: "https://example.com/avatar",
|
||||||
display_name: "",
|
display_name: "",
|
||||||
real_name: "another guy"
|
real_name: "another guy",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
end
|
||||||
|
|
||||||
let(:transcript) { described_class.new(channel_name: "#general", channel_id: "G1234") }
|
let(:transcript) { described_class.new(channel_name: "#general", channel_id: "G1234") }
|
||||||
before do
|
before { SiteSetting.chat_integration_slack_access_token = "abcde" }
|
||||||
SiteSetting.chat_integration_slack_access_token = "abcde"
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't raise an error when there are no messages to guess" do
|
it "doesn't raise an error when there are no messages to guess" do
|
||||||
transcript.instance_variable_set(:@messages, [])
|
transcript.instance_variable_set(:@messages, [])
|
||||||
expect(transcript.guess_first_message(skip_messages: 1)).to eq(false)
|
expect(transcript.guess_first_message(skip_messages: 1)).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'loading users' do
|
describe "loading users" do
|
||||||
it 'loads users correctly' do
|
it "loads users correctly" do
|
||||||
stub_request(:post, "https://slack.com/api/users.list")
|
stub_request(:post, "https://slack.com/api/users.list").with(
|
||||||
.with(body: { token: "abcde", "cursor": nil, "limit": "200" })
|
body: {
|
||||||
.to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
|
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
|
expect(transcript.load_user_data).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles failed connection' do
|
it "handles failed connection" do
|
||||||
stub_request(:post, "https://slack.com/api/users.list")
|
stub_request(:post, "https://slack.com/api/users.list").to_return(status: 500, body: "")
|
||||||
.to_return(status: 500, body: '')
|
|
||||||
|
|
||||||
expect(transcript.load_user_data).to eq(false)
|
expect(transcript.load_user_data).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles slack failure' do
|
it "handles slack failure" do
|
||||||
stub_request(:post, "https://slack.com/api/users.list")
|
stub_request(:post, "https://slack.com/api/users.list").to_return(
|
||||||
.to_return(status: 200, body: { ok: false }.to_json)
|
status: 200,
|
||||||
|
body: { ok: false }.to_json,
|
||||||
|
)
|
||||||
|
|
||||||
expect(transcript.load_user_data).to eq(false)
|
expect(transcript.load_user_data).to eq(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with loaded users' do
|
context "with loaded users" do
|
||||||
before do
|
before do
|
||||||
stub_request(:post, "https://slack.com/api/users.list")
|
stub_request(:post, "https://slack.com/api/users.list").to_return(
|
||||||
.to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
|
status: 200,
|
||||||
|
body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json,
|
||||||
|
)
|
||||||
transcript.load_user_data
|
transcript.load_user_data
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'loading history' do
|
describe "loading history" do
|
||||||
it 'loads messages correctly' do
|
it "loads messages correctly" do
|
||||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
stub_request(:post, "https://slack.com/api/conversations.history").with(
|
||||||
.with(body: hash_including(token: "abcde", channel: 'G1234'))
|
body: hash_including(token: "abcde", channel: "G1234"),
|
||||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
).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
|
end
|
||||||
|
|
||||||
it 'handles failed connection' do
|
it "handles failed connection" do
|
||||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
stub_request(:post, "https://slack.com/api/conversations.history").to_return(
|
||||||
.to_return(status: 500, body: {}.to_json)
|
status: 500,
|
||||||
|
body: {}.to_json,
|
||||||
|
)
|
||||||
|
|
||||||
expect(transcript.load_chat_history).to be_falsey
|
expect(transcript.load_chat_history).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles slack failure' do
|
it "handles slack failure" do
|
||||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
stub_request(:post, "https://slack.com/api/conversations.history").to_return(
|
||||||
.to_return(status: 200, body: { ok: false }.to_json)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with thread_ts specified' do
|
context "with thread_ts specified" do
|
||||||
let(:thread_transcript) { described_class.new(channel_name: "#general", channel_id: "G1234", requested_thread_ts: "1501801629.052212") }
|
let(:thread_transcript) do
|
||||||
|
described_class.new(
|
||||||
|
channel_name: "#general",
|
||||||
|
channel_id: "G1234",
|
||||||
|
requested_thread_ts: "1501801629.052212",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
thread_transcript.load_user_data
|
thread_transcript.load_user_data
|
||||||
stub_request(:post, "https://slack.com/api/conversations.replies")
|
stub_request(:post, "https://slack.com/api/conversations.replies").with(
|
||||||
.with(body: hash_including(token: "abcde", channel: 'G1234', ts: "1501801629.052212"))
|
body: hash_including(token: "abcde", channel: "G1234", ts: "1501801629.052212"),
|
||||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture[3..4] }.to_json)
|
).to_return(status: 200, body: { ok: true, messages: messages_fixture[3..4] }.to_json)
|
||||||
thread_transcript.load_chat_history
|
thread_transcript.load_chat_history
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes messages in a thread' do
|
it "includes messages in a thread" do
|
||||||
expect(thread_transcript.messages.length).to eq(2)
|
expect(thread_transcript.messages.length).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'loads in chronological order' do # replies API presents messages in actual chronological order
|
it "loads in chronological order" do # replies API presents messages in actual chronological order
|
||||||
expect(thread_transcript.messages.first.ts).to eq('1501801629.052212')
|
expect(thread_transcript.messages.first.ts).to eq("1501801629.052212")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes slack thread identifiers in body' do
|
it "includes slack thread identifiers in body" do
|
||||||
text = thread_transcript.build_transcript
|
text = thread_transcript.build_transcript
|
||||||
expect(text).to include("<!--SLACK_CHANNEL_ID=#general;SLACK_TS=1501801629.052212-->")
|
expect(text).to include("<!--SLACK_CHANNEL_ID=#general;SLACK_TS=1501801629.052212-->")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with loaded messages' do
|
context "with loaded messages" do
|
||||||
before do
|
before do
|
||||||
stub_request(:post, "https://slack.com/api/conversations.history")
|
stub_request(:post, "https://slack.com/api/conversations.history").with(
|
||||||
.with(body: hash_including(token: "abcde", channel: 'G1234'))
|
body: hash_including(token: "abcde", channel: "G1234"),
|
||||||
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
).to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
|
||||||
transcript.load_chat_history
|
transcript.load_chat_history
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ignores messages in a thread' do
|
it "ignores messages in a thread" do
|
||||||
expect(transcript.messages.length).to eq(6)
|
expect(transcript.messages.length).to eq(6)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'loads in chronological order' do # API presents in reverse chronological
|
it "loads in chronological order" do # API presents in reverse chronological
|
||||||
expect(transcript.messages.first.ts).to eq('1501093331.439776')
|
expect(transcript.messages.first.ts).to eq("1501093331.439776")
|
||||||
end
|
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** ")
|
expect(transcript.messages.first.text).to start_with("Let’s try some **bold text** ")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles links' do
|
it "handles links" do
|
||||||
expect(transcript.messages[2].text).to eq("So, who's interested in the new [discourse plugin](https://meta.discourse.org)?")
|
expect(transcript.messages[2].text).to eq(
|
||||||
|
"So, who's interested in the new [discourse plugin](https://meta.discourse.org)?",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes attachments' do
|
it "includes attachments" do
|
||||||
expect(transcript.messages[1].attachments.first).to eq("Discourse can now be integrated with Mattermost! - @david")
|
expect(transcript.messages[1].attachments.first).to eq(
|
||||||
|
"Discourse can now be integrated with Mattermost! - @david",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can generate URL' do
|
it "can generate URL" do
|
||||||
expect(transcript.messages.first.url).to eq("https://slack.com/archives/G1234/p1501093331439776")
|
expect(transcript.messages.first.url).to eq(
|
||||||
|
"https://slack.com/archives/G1234/p1501093331439776",
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes attachments in raw text' do
|
it "includes attachments in raw text" do
|
||||||
transcript.set_first_message_by_ts('1501615820.949638')
|
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")
|
expect(transcript.first_message.raw_text).to eq(
|
||||||
|
"Check this out!\n - Discourse can now be integrated with Mattermost! - @david\n",
|
||||||
|
)
|
||||||
end
|
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.first_message_number).to eq(0)
|
||||||
expect(transcript.last_message_number).to eq(transcript.messages.length - 1)
|
expect(transcript.last_message_number).to eq(transcript.messages.length - 1)
|
||||||
|
|
||||||
expect(transcript.first_message.ts).to eq('1501093331.439776')
|
expect(transcript.first_message.ts).to eq("1501093331.439776")
|
||||||
expect(transcript.last_message.ts).to eq('1501801665.062694')
|
expect(transcript.last_message.ts).to eq("1501801665.062694")
|
||||||
end
|
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(999)).to be_falsey
|
||||||
expect(transcript.set_first_message_by_index(1)).to be_truthy
|
expect(transcript.set_first_message_by_index(1)).to be_truthy
|
||||||
|
|
||||||
expect(transcript.set_last_message_by_index(-2)).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.first_message.ts).to eq("1501615820.949638")
|
||||||
expect(transcript.last_message.ts).to eq('1501801643.056375')
|
expect(transcript.last_message.ts).to eq("1501801643.056375")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can change first and last messages by ts' do
|
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("blah")).to be_falsey
|
||||||
expect(transcript.set_first_message_by_ts('1501615820.949638')).to be_truthy
|
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.first_message_number).to eq(1)
|
||||||
expect(transcript.last_message_number).to eq(2)
|
expect(transcript.last_message_number).to eq(2)
|
||||||
end
|
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.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
|
end
|
||||||
|
|
||||||
it 'handles usernames correctly' do
|
it "handles usernames correctly" do
|
||||||
expect(transcript.first_message.username).to eq('awesomeguy') # Normal user
|
expect(transcript.first_message.username).to eq("awesomeguy") # Normal user
|
||||||
expect(transcript.messages[1].username).to eq('Test_Community') # Bot user
|
expect(transcript.messages[1].username).to eq("Test_Community") # Bot user
|
||||||
expect(transcript.messages[3].username).to eq(nil) # Unknown normal user
|
expect(transcript.messages[3].username).to eq(nil) # Unknown normal user
|
||||||
# Normal user, display_name not set (fall back to real_name)
|
# 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
|
end
|
||||||
|
|
||||||
it 'handles user mentions correctly' do
|
it "handles user mentions correctly" do
|
||||||
# User with display_name not set, unrecognized user
|
# User with display_name not set, unrecognized user
|
||||||
expect(transcript.first_message.text).to \
|
expect(transcript.first_message.text).to eq(
|
||||||
eq('Let’s try some **bold text** @another_guy @someotheruser')
|
"Let’s try some **bold text** @another_guy @someotheruser",
|
||||||
|
)
|
||||||
# Normal user
|
# Normal user
|
||||||
expect(transcript.messages[4].text).to \
|
expect(transcript.messages[4].text).to eq("Oooh a new discourse plugin @awesomeguy ???")
|
||||||
eq('Oooh a new discourse plugin @awesomeguy ???')
|
|
||||||
end
|
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.first_message.avatar).to eq("https://example.com/avatar") # Normal user
|
||||||
expect(transcript.messages[1].avatar).to eq(nil) # Bot user
|
expect(transcript.messages[1].avatar).to eq(nil) # Bot user
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates a transcript correctly' do
|
it "creates a transcript correctly" do
|
||||||
transcript.set_last_message_by_index(1)
|
transcript.set_last_message_by_index(1)
|
||||||
|
|
||||||
text = transcript.build_transcript
|
text = transcript.build_transcript
|
||||||
|
@ -317,7 +334,7 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
expect(text).to eq(expected)
|
expect(text).to eq(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'omits quote tags when disabled' do
|
it "omits quote tags when disabled" do
|
||||||
transcript.set_last_message_by_index(1)
|
transcript.set_last_message_by_index(1)
|
||||||
|
|
||||||
text = transcript.build_transcript
|
text = transcript.build_transcript
|
||||||
|
@ -331,7 +348,7 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
expect(text).not_to include("[/quote]")
|
expect(text).not_to include("[/quote]")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates the slack UI correctly' do
|
it "creates the slack UI correctly" do
|
||||||
transcript.set_last_message_by_index(1)
|
transcript.set_last_message_by_index(1)
|
||||||
ui = transcript.build_slack_ui
|
ui = transcript.build_slack_ui
|
||||||
|
|
||||||
|
@ -352,16 +369,17 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "message formatting" do
|
describe "message formatting" do
|
||||||
it 'handles code block newlines' do
|
it "handles code block newlines" do
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "Here is some code```my code\nwith newline```",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" => "Here is some code```my code\nwith newline```",
|
||||||
},
|
"ts" => "1501093331.439776",
|
||||||
transcript
|
},
|
||||||
)
|
transcript,
|
||||||
|
)
|
||||||
expect(message.text).to eq(<<~MD)
|
expect(message.text).to eq(<<~MD)
|
||||||
Here is some code
|
Here is some code
|
||||||
```
|
```
|
||||||
|
@ -371,16 +389,18 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
MD
|
MD
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles multiple code blocks' do
|
it "handles multiple code blocks" do
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "Here is some code```my code\nwith newline```and another```some more code```",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" =>
|
||||||
},
|
"Here is some code```my code\nwith newline```and another```some more code```",
|
||||||
transcript
|
"ts" => "1501093331.439776",
|
||||||
)
|
},
|
||||||
|
transcript,
|
||||||
|
)
|
||||||
expect(message.text).to eq(<<~MD)
|
expect(message.text).to eq(<<~MD)
|
||||||
Here is some code
|
Here is some code
|
||||||
```
|
```
|
||||||
|
@ -394,73 +414,83 @@ RSpec.describe DiscourseChatIntegration::Provider::SlackProvider::SlackTranscrip
|
||||||
MD
|
MD
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles strikethrough' do
|
it "handles strikethrough" do
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "Some ~strikethrough~",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" => "Some ~strikethrough~",
|
||||||
},
|
"ts" => "1501093331.439776",
|
||||||
transcript
|
},
|
||||||
)
|
transcript,
|
||||||
|
)
|
||||||
expect(message.text).to eq("Some ~~strikethrough~~")
|
expect(message.text).to eq("Some ~~strikethrough~~")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles slack links' do
|
it "handles slack links" do
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "A link to <https://google.com|google>, <https://autolinked.com|https://autolinked.com>, <https://notext.com>, <#channel>, <@user>",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" =>
|
||||||
},
|
"A link to <https://google.com|google>, <https://autolinked.com|https://autolinked.com>, <https://notext.com>, <#channel>, <@user>",
|
||||||
transcript
|
"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
|
end
|
||||||
|
|
||||||
it 'does not format things inside backticks' do
|
it "does not format things inside backticks" do
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" =>
|
||||||
},
|
"You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
|
||||||
transcript
|
"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
|
end
|
||||||
|
|
||||||
it 'unescapes html in backticks' do
|
it "unescapes html in backticks" do
|
||||||
# Because Slack escapes HTML entities, even in backticks
|
# Because Slack escapes HTML entities, even in backticks
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "The code is `<stuff>`",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" => "The code is `<stuff>`",
|
||||||
},
|
"ts" => "1501093331.439776",
|
||||||
transcript
|
},
|
||||||
)
|
transcript,
|
||||||
|
)
|
||||||
expect(message.text).to eq("The code is `<stuff>`")
|
expect(message.text).to eq("The code is `<stuff>`")
|
||||||
end
|
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
|
# Discourse does not allow dashes in emoji names, so this helps communities have matching custom emojis
|
||||||
message = DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
message =
|
||||||
{
|
DiscourseChatIntegration::Provider::SlackProvider::SlackMessage.new(
|
||||||
"type" => "message",
|
{
|
||||||
"user" => "U5Z773QLS",
|
"type" => "message",
|
||||||
"text" => "This is :my-emoji:",
|
"user" => "U5Z773QLS",
|
||||||
"ts" => "1501093331.439776"
|
"text" => "This is :my-emoji:",
|
||||||
},
|
"ts" => "1501093331.439776",
|
||||||
transcript
|
},
|
||||||
)
|
transcript,
|
||||||
|
)
|
||||||
expect(message.text).to eq("This is :my_emoji:")
|
expect(message.text).to eq("This is :my_emoji:")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,42 +1,47 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::TeamsProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::TeamsProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_teams_enabled = true }
|
||||||
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
|
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)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with nil user.name' do
|
describe "with nil user.name" do
|
||||||
before do
|
before { post.user.update!(name: nil) }
|
||||||
post.user.update!(name: nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'handles nil username correctly' do
|
it "handles nil username correctly" do
|
||||||
message = described_class.get_message(post)
|
message = described_class.get_message(post)
|
||||||
name = message[:sections].first[:facts].first[:name]
|
name = message[:sections].first[:facts].first[:name]
|
||||||
expect(name).to eq("")
|
expect(name).to eq("")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,32 +1,44 @@
|
||||||
# frozen_string_literal: true
|
# 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(:category) { Fabricate(:category) }
|
||||||
let!(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'telegram', data: { name: 'Amazing Channel', chat_id: '123' }) }
|
let!(:chan1) do
|
||||||
let!(:webhook_stub) { stub_request(:post, 'https://api.telegram.org/botTOKEN/setWebhook').to_return(body: "{\"ok\":true}") }
|
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
|
describe "with plugin disabled" do
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/telegram/command/abcd.json'
|
post "/chat-integration/telegram/command/abcd.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with plugin enabled and provider disabled' do
|
describe "with plugin enabled and provider disabled" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
SiteSetting.chat_integration_enabled = true
|
||||||
SiteSetting.chat_integration_telegram_enabled = false
|
SiteSetting.chat_integration_telegram_enabled = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return a 404' do
|
it "should return a 404" do
|
||||||
post '/chat-integration/telegram/command/abcd.json'
|
post "/chat-integration/telegram/command/abcd.json"
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'slash commands endpoint' do
|
describe "slash commands endpoint" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_enabled = true
|
SiteSetting.chat_integration_enabled = true
|
||||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||||
|
@ -34,85 +46,122 @@ describe 'Telegram Command Controller', type: :request do
|
||||||
SiteSetting.chat_integration_telegram_secret = "shhh"
|
SiteSetting.chat_integration_telegram_secret = "shhh"
|
||||||
end
|
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
|
describe "when forum is private" do
|
||||||
it 'should not redirect to login page' do
|
it "should not redirect to login page" do
|
||||||
SiteSetting.login_required = true
|
SiteSetting.login_required = true
|
||||||
|
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
message: { chat: { id: 123 }, text: '/help' }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/help",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when the token is invalid' do
|
describe "when the token is invalid" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
post '/chat-integration/telegram/command/blah.json', params: {
|
post "/chat-integration/telegram/command/blah.json",
|
||||||
message: { chat: { id: 123 }, text: '/help' }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/help",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when token has not been set' do
|
describe "when token has not been set" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
SiteSetting.chat_integration_telegram_access_token = ""
|
SiteSetting.chat_integration_telegram_access_token = ""
|
||||||
post '/chat-integration/telegram/command/blah.json', params: {
|
post "/chat-integration/telegram/command/blah.json",
|
||||||
message: { chat: { id: 123 }, text: '/help' }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/help",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(403)
|
expect(response.status).to eq(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'when token is valid' do
|
describe "when token is valid" do
|
||||||
let(:token) { "TOKEN" }
|
let(:token) { "TOKEN" }
|
||||||
|
|
||||||
before do
|
before { SiteSetting.chat_integration_telegram_enable_slash_commands = true }
|
||||||
SiteSetting.chat_integration_telegram_enable_slash_commands = true
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'add new rule' do
|
describe "add new rule" do
|
||||||
|
it "should add a new rule correctly" do
|
||||||
it 'should add a new rule correctly' do
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
params: {
|
||||||
message: { chat: { id: 123 }, text: "/watch #{category.slug}" }
|
message: {
|
||||||
}
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/watch #{category.slug}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(stub).to have_been_requested.once
|
expect(stub).to have_been_requested.once
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should add a new rule correctly using group chat syntax' do
|
it "should add a new rule correctly using group chat syntax" do
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
message: { chat: { id: 123 }, text: "/watch@my-awesome-bot #{category.slug}" }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/watch@my-awesome-bot #{category.slug}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(stub).to have_been_requested.once
|
expect(stub).to have_been_requested.once
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
expect(rule.channel).to eq(chan1)
|
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.category_id).to eq(category.id)
|
||||||
expect(rule.tags).to eq(nil)
|
expect(rule.tags).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'from an unknown channel' do
|
describe "from an unknown channel" do
|
||||||
it 'does nothing' do
|
it "does nothing" do
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
message: { chat: { id: 456 }, text: "/watch #{category.slug}" }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 456,
|
||||||
|
},
|
||||||
|
text: "/watch #{category.slug}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.size).to eq(0)
|
expect(DiscourseChatIntegration::Rule.all.size).to eq(0)
|
||||||
expect(DiscourseChatIntegration::Channel.all.size).to eq(1)
|
expect(DiscourseChatIntegration::Channel.all.size).to eq(1)
|
||||||
|
@ -121,16 +170,28 @@ describe 'Telegram Command Controller', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respond only to a specific command in a broadcast channel" do
|
it "should respond only to a specific command in a broadcast channel" do
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
channel_post: { chat: { id: 123 }, text: "something" }
|
params: {
|
||||||
}
|
channel_post: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "something",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(stub).to have_been_requested.times(0)
|
expect(stub).to have_been_requested.times(0)
|
||||||
|
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
channel_post: { chat: { id: 123 }, text: "/getchatid" }
|
params: {
|
||||||
}
|
channel_post: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
text: "/getchatid",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expect(stub).to have_been_requested.times(1)
|
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
|
context "when 'text' is missing" do
|
||||||
it "does not break" do
|
it "does not break" do
|
||||||
post '/chat-integration/telegram/command/shhh.json', params: {
|
post "/chat-integration/telegram/command/shhh.json",
|
||||||
message: { chat: { id: 123 } }
|
params: {
|
||||||
}
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: 123,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response).to have_http_status :ok
|
expect(response).to have_http_status :ok
|
||||||
expect(DiscourseChatIntegration::Rule.count).to eq(0)
|
expect(DiscourseChatIntegration::Rule.count).to eq(0)
|
||||||
|
|
|
@ -1,33 +1,51 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::TelegramProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::TelegramProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
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
|
before do
|
||||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||||
SiteSetting.chat_integration_telegram_enabled = true
|
SiteSetting.chat_integration_telegram_enabled = true
|
||||||
SiteSetting.chat_integration_telegram_secret = 'shhh'
|
SiteSetting.chat_integration_telegram_secret = "shhh"
|
||||||
end
|
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
|
it "sends a webhook request" do
|
||||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":true}")
|
stub1 =
|
||||||
|
stub_request(:post, "https://api.telegram.org/botTOKEN/sendMessage").to_return(
|
||||||
|
body: "{\"ok\":true}",
|
||||||
|
)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":false, \"description\":\"chat not found\"}")
|
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(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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,30 +1,37 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::WebexProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::WebexProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before { SiteSetting.chat_integration_webex_enabled = true }
|
||||||
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
|
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)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 400, body: "{}")
|
stub1 = stub_request(:post, chan1.data["webhook_url"]).to_return(status: 400, body: "{}")
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Provider::ZulipProvider do
|
RSpec.describe DiscourseChatIntegration::Provider::ZulipProvider do
|
||||||
let(:post) { Fabricate(:post) }
|
let(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
before do
|
before do
|
||||||
SiteSetting.chat_integration_zulip_enabled = true
|
SiteSetting.chat_integration_zulip_enabled = true
|
||||||
SiteSetting.chat_integration_zulip_server = "https://hello.world"
|
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"
|
SiteSetting.chat_integration_zulip_bot_api_key = "secret"
|
||||||
end
|
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
|
it "sends a webhook request" do
|
||||||
stub1 = stub_request(:post, 'https://hello.world/api/v1/messages').to_return(status: 200)
|
stub1 = stub_request(:post, "https://hello.world/api/v1/messages").to_return(status: 200)
|
||||||
described_class.trigger_notification(post, chan1, nil)
|
described_class.trigger_notification(post, chan1, nil)
|
||||||
expect(stub1).to have_been_requested.once
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles errors correctly' do
|
it "handles errors correctly" do
|
||||||
stub1 = stub_request(:post, 'https://hello.world/api/v1/messages').to_return(status: 400, body: '{}')
|
stub1 =
|
||||||
|
stub_request(:post, "https://hello.world/api/v1/messages").to_return(
|
||||||
|
status: 400,
|
||||||
|
body: "{}",
|
||||||
|
)
|
||||||
expect(stub1).to have_been_requested.times(0)
|
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
|
expect(stub1).to have_been_requested.once
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
require_relative '../dummy_provider'
|
require_relative "../dummy_provider"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Channel do
|
RSpec.describe DiscourseChatIntegration::Channel do
|
||||||
include_context "with dummy provider"
|
include_context "with dummy provider"
|
||||||
include_context "with validated 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)
|
expect(DiscourseChatIntegration::Channel.all.length).to eq(0)
|
||||||
|
|
||||||
chan = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
chan = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||||
|
@ -16,49 +16,53 @@ RSpec.describe DiscourseChatIntegration::Channel do
|
||||||
|
|
||||||
loadedChan = DiscourseChatIntegration::Channel.find(chan.id)
|
loadedChan = DiscourseChatIntegration::Channel.find(chan.id)
|
||||||
|
|
||||||
expect(loadedChan.provider).to eq('dummy')
|
expect(loadedChan.provider).to eq("dummy")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should edit successfully' do
|
it "should edit successfully" do
|
||||||
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "hello" })
|
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "hello" })
|
||||||
expect(channel.valid?).to eq(true)
|
expect(channel.valid?).to eq(true)
|
||||||
channel.save!
|
channel.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be filtered by provider' do
|
it "can be filtered by provider" do
|
||||||
channel1 = DiscourseChatIntegration::Channel.create!(provider: 'dummy')
|
channel1 = DiscourseChatIntegration::Channel.create!(provider: "dummy")
|
||||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah" })
|
channel2 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah" })
|
||||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah2" })
|
channel3 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah2" })
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Channel.all.length).to eq(3)
|
expect(DiscourseChatIntegration::Channel.all.length).to eq(3)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Channel.with_provider('dummy2').length).to eq(2)
|
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("dummy").length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be filtered by data value' do
|
it "can be filtered by data value" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "foo" })
|
channel2 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "foo" })
|
||||||
channel3 = DiscourseChatIntegration::Channel.create!(provider: 'dummy2', data: { val: "blah" })
|
channel3 = DiscourseChatIntegration::Channel.create!(provider: "dummy2", data: { val: "blah" })
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Channel.all.length).to eq(2)
|
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(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
|
end
|
||||||
|
|
||||||
it 'can find its own rules' do
|
it "can find its own rules" do
|
||||||
channel = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
channel = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||||
expect(channel.rules.size).to eq(0)
|
expect(channel.rules.size).to eq(0)
|
||||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||||
DiscourseChatIntegration::Rule.create(channel: channel)
|
DiscourseChatIntegration::Rule.create(channel: channel)
|
||||||
expect(channel.rules.size).to eq(2)
|
expect(channel.rules.size).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'destroys its rules on destroy' do
|
it "destroys its rules on destroy" do
|
||||||
channel = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
channel = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||||
expect(channel.rules.size).to eq(0)
|
expect(channel.rules.size).to eq(0)
|
||||||
rule1 = DiscourseChatIntegration::Rule.create(channel: channel)
|
rule1 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||||
rule2 = 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)
|
expect(DiscourseChatIntegration::Rule.with_channel(channel).exists?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe "validations" do
|
||||||
|
it "validates provider correctly" do
|
||||||
it 'validates provider correctly' do
|
|
||||||
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy")
|
channel = DiscourseChatIntegration::Channel.create!(provider: "dummy")
|
||||||
expect(channel.valid?).to eq(true)
|
expect(channel.valid?).to eq(true)
|
||||||
channel.provider = 'somerandomprovider'
|
channel.provider = "somerandomprovider"
|
||||||
expect(channel.valid?).to eq(false)
|
expect(channel.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'succeeds with valid data' do
|
it "succeeds with valid data" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
||||||
expect(channel2.valid?).to eq(true)
|
expect(channel2.valid?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'disallows invalid data' do
|
it "disallows invalid data" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: ' ' })
|
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: " " })
|
||||||
expect(channel2.valid?).to eq(false)
|
expect(channel2.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'disallows unknown keys' do
|
it "disallows unknown keys" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello", unknown: "world" })
|
channel2 =
|
||||||
|
DiscourseChatIntegration::Channel.new(
|
||||||
|
provider: "dummy2",
|
||||||
|
data: {
|
||||||
|
val: "hello",
|
||||||
|
unknown: "world",
|
||||||
|
},
|
||||||
|
)
|
||||||
expect(channel2.valid?).to eq(false)
|
expect(channel2.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'requires all keys' do
|
it "requires all keys" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: {})
|
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: {})
|
||||||
expect(channel2.valid?).to eq(false)
|
expect(channel2.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'disallows duplicate channels' do
|
it "disallows duplicate channels" do
|
||||||
channel1 = DiscourseChatIntegration::Channel.create(provider: "dummy2", data: { val: "hello" })
|
channel1 =
|
||||||
|
DiscourseChatIntegration::Channel.create(provider: "dummy2", data: { val: "hello" })
|
||||||
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
channel2 = DiscourseChatIntegration::Channel.new(provider: "dummy2", data: { val: "hello" })
|
||||||
expect(channel2.valid?).to eq(false)
|
expect(channel2.valid?).to eq(false)
|
||||||
channel2.data[:val] = "hello2"
|
channel2.data[:val] = "hello2"
|
||||||
expect(channel2.valid?).to eq(true)
|
expect(channel2.valid?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
require_relative '../dummy_provider'
|
require_relative "../dummy_provider"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Rule do
|
RSpec.describe DiscourseChatIntegration::Rule do
|
||||||
include_context "with dummy provider"
|
include_context "with dummy provider"
|
||||||
|
@ -9,31 +9,34 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
||||||
let(:tag1) { Fabricate(:tag) }
|
let(:tag1) { Fabricate(:tag) }
|
||||||
let(:tag2) { 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(:category) { Fabricate(:category) }
|
||||||
let(:group) { Fabricate(:group) }
|
let(:group) { Fabricate(:group) }
|
||||||
|
|
||||||
describe '.alloc_key' do
|
describe ".alloc_key" do
|
||||||
it 'should return sequential numbers' 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:1")
|
||||||
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:2")
|
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:2")
|
||||||
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:3")
|
expect(DiscourseChatIntegration::Rule.create(channel: channel).key).to eq("rule:3")
|
||||||
end
|
end
|
||||||
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)
|
rule = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||||
expect(rule.channel_id).to eq(channel.id)
|
expect(rule.channel_id).to eq(channel.id)
|
||||||
expect(rule.channel.id).to eq(channel.id)
|
expect(rule.channel.id).to eq(channel.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should save and load successfully' do
|
it "should save and load successfully" do
|
||||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(0)
|
expect(DiscourseChatIntegration::Rule.all.length).to eq(0)
|
||||||
|
|
||||||
rule = DiscourseChatIntegration::Rule.create(channel: channel,
|
rule =
|
||||||
category_id: category.id,
|
DiscourseChatIntegration::Rule.create(
|
||||||
tags: [tag1.name, tag2.name],
|
channel: channel,
|
||||||
filter: 'watch')
|
category_id: category.id,
|
||||||
|
tags: [tag1.name, tag2.name],
|
||||||
|
filter: "watch",
|
||||||
|
)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(1)
|
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.channel.id).to eq(channel.id)
|
||||||
expect(loadedRule.category_id).to eq(category.id)
|
expect(loadedRule.category_id).to eq(category.id)
|
||||||
expect(loadedRule.tags).to contain_exactly(tag1.name, tag2.name)
|
expect(loadedRule.tags).to contain_exactly(tag1.name, tag2.name)
|
||||||
expect(loadedRule.filter).to eq('watch')
|
expect(loadedRule.filter).to eq("watch")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'general operations' do
|
describe "general operations" do
|
||||||
before do
|
before do
|
||||||
rule = DiscourseChatIntegration::Rule.create(channel: channel,
|
rule =
|
||||||
category_id: category.id,
|
DiscourseChatIntegration::Rule.create(
|
||||||
tags: [tag1.name, tag2.name])
|
channel: channel,
|
||||||
|
category_id: category.id,
|
||||||
|
tags: [tag1.name, tag2.name],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be modified' do
|
it "can be modified" do
|
||||||
rule = DiscourseChatIntegration::Rule.all.first
|
rule = DiscourseChatIntegration::Rule.all.first
|
||||||
rule.tags = [tag1.name]
|
rule.tags = [tag1.name]
|
||||||
|
|
||||||
|
@ -63,7 +68,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
||||||
expect(rule.tags).to contain_exactly(tag1.name)
|
expect(rule.tags).to contain_exactly(tag1.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be deleted' do
|
it "can be deleted" do
|
||||||
DiscourseChatIntegration::Rule.new(channel: channel).save!
|
DiscourseChatIntegration::Rule.new(channel: channel).save!
|
||||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(2)
|
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)
|
expect(DiscourseChatIntegration::Rule.all.length).to eq(1)
|
||||||
end
|
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)
|
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)
|
expect(DiscourseChatIntegration::Rule.all.length).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be filtered by channel' do
|
it "can be filtered by channel" do
|
||||||
channel2 = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
channel2 = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||||
channel3 = DiscourseChatIntegration::Channel.create(provider: 'dummy')
|
channel3 = DiscourseChatIntegration::Channel.create(provider: "dummy")
|
||||||
|
|
||||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel)
|
rule2 = DiscourseChatIntegration::Rule.create(channel: channel)
|
||||||
rule3 = 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)
|
expect(DiscourseChatIntegration::Rule.with_channel(channel2).length).to eq(1)
|
||||||
end
|
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)
|
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, category_id: category.id)
|
||||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, category_id: nil)
|
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)
|
expect(DiscourseChatIntegration::Rule.with_category_id(nil).length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be filtered by group' do
|
it "can be filtered by group" do
|
||||||
group1 = Fabricate(:group)
|
group1 = Fabricate(:group)
|
||||||
group2 = Fabricate(:group)
|
group2 = Fabricate(:group)
|
||||||
rule2 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group1.id)
|
rule2 =
|
||||||
rule3 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group2.id)
|
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)
|
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)
|
expect(DiscourseChatIntegration::Rule.with_group_ids([group2.id]).length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be filtered by type' do
|
it "can be filtered by type" do
|
||||||
group1 = Fabricate(:group)
|
group1 = Fabricate(:group)
|
||||||
|
|
||||||
rule2 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_message', group_id: group1.id)
|
rule2 =
|
||||||
rule3 = DiscourseChatIntegration::Rule.create!(channel: channel, type: 'group_mention', group_id: group1.id)
|
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.all.length).to eq(3)
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.with_type('group_message').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("group_mention").length).to eq(1)
|
||||||
expect(DiscourseChatIntegration::Rule.with_type('normal').length).to eq(1)
|
expect(DiscourseChatIntegration::Rule.with_type("normal").length).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'can be sorted by precedence' do
|
it "can be sorted by precedence" do
|
||||||
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'mute')
|
rule2 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "mute")
|
||||||
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'follow')
|
rule3 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "follow")
|
||||||
rule4 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'thread')
|
rule4 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "thread")
|
||||||
rule5 = DiscourseChatIntegration::Rule.create(channel: channel, filter: 'mute')
|
rule5 = DiscourseChatIntegration::Rule.create(channel: channel, filter: "mute")
|
||||||
|
|
||||||
expect(DiscourseChatIntegration::Rule.all.length).to eq(5)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe "validations" do
|
||||||
|
|
||||||
let(:rule) do
|
let(:rule) do
|
||||||
DiscourseChatIntegration::Rule.create(filter: 'watch',
|
DiscourseChatIntegration::Rule.create(
|
||||||
channel: channel,
|
filter: "watch",
|
||||||
category_id: category.id)
|
channel: channel,
|
||||||
|
category_id: category.id,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates channel correctly' do
|
it "validates channel correctly" do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.channel_id = 'blahblahblah'
|
rule.channel_id = "blahblahblah"
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
rule.channel_id = -1
|
rule.channel_id = -1
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
|
@ -175,7 +203,7 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates group correctly' do
|
it "validates group correctly" do
|
||||||
rule.category_id = nil
|
rule.category_id = nil
|
||||||
rule.group_id = group.id
|
rule.group_id = group.id
|
||||||
rule.type = "group_message"
|
rule.type = "group_message"
|
||||||
|
@ -184,42 +212,41 @@ RSpec.describe DiscourseChatIntegration::Rule do
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates category correctly' do
|
it "validates category correctly" do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.category_id = -99
|
rule.category_id = -99
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates filter correctly' do
|
it "validates filter correctly" do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.filter = 'thread'
|
rule.filter = "thread"
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.filter = 'follow'
|
rule.filter = "follow"
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.filter = 'mute'
|
rule.filter = "mute"
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.filter = ''
|
rule.filter = ""
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
rule.filter = 'somerandomstring'
|
rule.filter = "somerandomstring"
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'validates tags correctly' do
|
it "validates tags correctly" do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.tags = []
|
rule.tags = []
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.tags = [tag1.name]
|
rule.tags = [tag1.name]
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.tags = [tag1.name, 'blah']
|
rule.tags = [tag1.name, "blah"]
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't allow save when invalid" do
|
it "doesn't allow save when invalid" do
|
||||||
expect(rule.valid?).to eq(true)
|
expect(rule.valid?).to eq(true)
|
||||||
rule.filter = 'somerandomfilter'
|
rule.filter = "somerandomfilter"
|
||||||
expect(rule.valid?).to eq(false)
|
expect(rule.valid?).to eq(false)
|
||||||
expect(rule.save).to eq(false)
|
expect(rule.save).to eq(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,34 +1,31 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
require_relative '../dummy_provider'
|
require_relative "../dummy_provider"
|
||||||
|
|
||||||
describe 'Chat Controller', type: :request do
|
|
||||||
|
|
||||||
|
describe "Chat Controller", type: :request do
|
||||||
let(:topic) { Fabricate(:post).topic }
|
let(:topic) { Fabricate(:post).topic }
|
||||||
let(:admin) { Fabricate(:admin) }
|
let(:admin) { Fabricate(:admin) }
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
let(:category2) { Fabricate(:category) }
|
let(:category2) { Fabricate(:category) }
|
||||||
let(:tag) { Fabricate(:tag) }
|
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 dummy provider"
|
||||||
include_context "with validated dummy provider"
|
include_context "with validated dummy provider"
|
||||||
|
|
||||||
before do
|
before { SiteSetting.chat_integration_enabled = true }
|
||||||
SiteSetting.chat_integration_enabled = true
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'admin constraints' do |action, route|
|
shared_examples "admin constraints" do |action, route|
|
||||||
context 'when user is not signed in' do
|
context "when user is not signed in" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
public_send(action, route)
|
public_send(action, route)
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is not an admin' do
|
context "when user is not an admin" do
|
||||||
it 'should raise the right error' do
|
it "should raise the right error" do
|
||||||
sign_in(Fabricate(:user))
|
sign_in(Fabricate(:user))
|
||||||
public_send(action, route)
|
public_send(action, route)
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
|
@ -36,154 +33,168 @@ describe 'Chat Controller', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'listing providers' do
|
describe "listing providers" do
|
||||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/providers.json'
|
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/providers.json"
|
||||||
|
|
||||||
context 'when signed in as an admin' do
|
context "when signed in as an admin" do
|
||||||
before do
|
before { sign_in(admin) }
|
||||||
sign_in(admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
get '/admin/plugins/chat-integration/providers.json'
|
get "/admin/plugins/chat-integration/providers.json"
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
json = response.parsed_body
|
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(
|
expect(json["providers"].find { |h| h["name"] == "dummy" }).to eq(
|
||||||
'name' => 'dummy',
|
"name" => "dummy",
|
||||||
'id' => 'dummy',
|
"id" => "dummy",
|
||||||
'channel_parameters' => []
|
"channel_parameters" => [],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'testing channels' do
|
describe "testing channels" do
|
||||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/test.json'
|
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/test.json"
|
||||||
|
|
||||||
context 'when signed in as an admin' do
|
context "when signed in as an admin" do
|
||||||
before do
|
before { sign_in(admin) }
|
||||||
sign_in(admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
post '/admin/plugins/chat-integration/test.json', params: {
|
post "/admin/plugins/chat-integration/test.json",
|
||||||
channel_id: channel.id, topic_id: topic.id
|
params: {
|
||||||
}
|
channel_id: channel.id,
|
||||||
|
topic_id: topic.id,
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail for invalid channel' do
|
it "should fail for invalid channel" do
|
||||||
post '/admin/plugins/chat-integration/test.json', params: {
|
post "/admin/plugins/chat-integration/test.json",
|
||||||
channel_id: 999, topic_id: topic.id
|
params: {
|
||||||
}
|
channel_id: 999,
|
||||||
|
topic_id: topic.id,
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'viewing channels' do
|
describe "viewing channels" do
|
||||||
include_examples 'admin constraints', 'get', '/admin/plugins/chat-integration/channels.json'
|
include_examples "admin constraints", "get", "/admin/plugins/chat-integration/channels.json"
|
||||||
|
|
||||||
context 'when signed in as an admin' do
|
context "when signed in as an admin" do
|
||||||
before do
|
before { sign_in(admin) }
|
||||||
sign_in(admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the right response' do
|
it "should return the right response" do
|
||||||
rule = DiscourseChatIntegration::Rule.create(
|
rule =
|
||||||
channel: channel,
|
DiscourseChatIntegration::Rule.create(
|
||||||
filter: 'follow',
|
channel: channel,
|
||||||
category_id: category.id,
|
filter: "follow",
|
||||||
tags: [tag.name]
|
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)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
channels = response.parsed_body['channels']
|
channels = response.parsed_body["channels"]
|
||||||
|
|
||||||
expect(channels.count).to eq(1)
|
expect(channels.count).to eq(1)
|
||||||
|
|
||||||
expect(channels.first).to eq(
|
expect(channels.first).to eq(
|
||||||
"id" => channel.id,
|
"id" => channel.id,
|
||||||
"provider" => 'dummy',
|
"provider" => "dummy",
|
||||||
"data" => {},
|
"data" => {
|
||||||
|
},
|
||||||
"error_key" => nil,
|
"error_key" => nil,
|
||||||
"error_info" => 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
|
end
|
||||||
|
|
||||||
it 'should fail for invalid provider' do
|
it "should fail for invalid provider" do
|
||||||
get '/admin/plugins/chat-integration/channels.json', params: { provider: 'someprovider' }
|
get "/admin/plugins/chat-integration/channels.json", params: { provider: "someprovider" }
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'adding a channel' do
|
describe "adding a channel" do
|
||||||
include_examples 'admin constraints', 'post', '/admin/plugins/chat-integration/channels.json'
|
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
|
it "should be able to add a new channel" do
|
||||||
sign_in(admin)
|
post "/admin/plugins/chat-integration/channels.json",
|
||||||
end
|
params: {
|
||||||
|
channel: {
|
||||||
it 'should be able to add a new channel' do
|
provider: "dummy",
|
||||||
post '/admin/plugins/chat-integration/channels.json', params: {
|
data: {
|
||||||
channel: {
|
},
|
||||||
provider: 'dummy',
|
},
|
||||||
data: {}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
channel = DiscourseChatIntegration::Channel.all.last
|
channel = DiscourseChatIntegration::Channel.all.last
|
||||||
|
|
||||||
expect(channel.provider).to eq('dummy')
|
expect(channel.provider).to eq("dummy")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail for invalid params' do
|
it "should fail for invalid params" do
|
||||||
post '/admin/plugins/chat-integration/channels.json', params: {
|
post "/admin/plugins/chat-integration/channels.json",
|
||||||
channel: {
|
params: {
|
||||||
provider: 'dummy2',
|
channel: {
|
||||||
data: { val: 'something with whitespace' }
|
provider: "dummy2",
|
||||||
}
|
data: {
|
||||||
}
|
val: "something with whitespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'updating a channel' do
|
describe "updating a channel" do
|
||||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy2', data: { val: "something" }) }
|
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
|
it "should be able update a channel" do
|
||||||
sign_in(admin)
|
put "/admin/plugins/chat-integration/channels/#{channel.id}.json",
|
||||||
end
|
params: {
|
||||||
|
channel: {
|
||||||
it 'should be able update a channel' do
|
data: {
|
||||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json", params: {
|
val: "something-else",
|
||||||
channel: {
|
},
|
||||||
data: { val: "something-else" }
|
},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -191,30 +202,32 @@ describe 'Chat Controller', type: :request do
|
||||||
expect(channel.data).to eq("val" => "something-else")
|
expect(channel.data).to eq("val" => "something-else")
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail for invalid params' do
|
it "should fail for invalid params" do
|
||||||
put "/admin/plugins/chat-integration/channels/#{channel.id}.json", params: {
|
put "/admin/plugins/chat-integration/channels/#{channel.id}.json",
|
||||||
channel: {
|
params: {
|
||||||
data: { val: "something with whitespace" }
|
channel: {
|
||||||
}
|
data: {
|
||||||
}
|
val: "something with whitespace",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'deleting a channel' do
|
describe "deleting a channel" do
|
||||||
let(:channel) { DiscourseChatIntegration::Channel.create(provider: 'dummy', data: {}) }
|
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
|
it "should be able delete a channel" do
|
||||||
sign_in(admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should be able delete a channel' do
|
|
||||||
delete "/admin/plugins/chat-integration/channels/#{channel.id}.json"
|
delete "/admin/plugins/chat-integration/channels/#{channel.id}.json"
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
@ -223,24 +236,22 @@ describe 'Chat Controller', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'adding a rule' do
|
describe "adding a rule" do
|
||||||
include_examples 'admin constraints', 'put', '/admin/plugins/chat-integration/rules.json'
|
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
|
it "should be able to add a new rule" do
|
||||||
sign_in(admin)
|
post "/admin/plugins/chat-integration/rules.json",
|
||||||
end
|
params: {
|
||||||
|
rule: {
|
||||||
it 'should be able to add a new rule' do
|
channel_id: channel.id,
|
||||||
post '/admin/plugins/chat-integration/rules.json', params: {
|
category_id: category.id,
|
||||||
rule: {
|
filter: "watch",
|
||||||
channel_id: channel.id,
|
tags: [tag.name],
|
||||||
category_id: category.id,
|
},
|
||||||
filter: 'watch',
|
}
|
||||||
tags: [tag.name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
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.channel_id).to eq(channel.id)
|
||||||
expect(rule.category_id).to eq(category.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])
|
expect(rule.tags).to eq([tag.name])
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail for invalid params' do
|
it "should fail for invalid params" do
|
||||||
post '/admin/plugins/chat-integration/rules.json', params: {
|
post "/admin/plugins/chat-integration/rules.json",
|
||||||
rule: {
|
params: {
|
||||||
channel_id: channel.id,
|
rule: {
|
||||||
category_id: category.id,
|
channel_id: channel.id,
|
||||||
filter: 'watch',
|
category_id: category.id,
|
||||||
tags: ['somenonexistanttag']
|
filter: "watch",
|
||||||
}
|
tags: ["somenonexistanttag"],
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'updating a rule' do
|
describe "updating a rule" do
|
||||||
let(:rule) { DiscourseChatIntegration::Rule.create(channel: channel, filter: 'follow', category_id: category.id, tags: [tag.name]) }
|
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
|
it "should be able update a rule" do
|
||||||
sign_in(admin)
|
put "/admin/plugins/chat-integration/rules/#{rule.id}.json",
|
||||||
end
|
params: {
|
||||||
|
rule: {
|
||||||
it 'should be able update a rule' do
|
channel_id: channel.id,
|
||||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json", params: {
|
category_id: category2.id,
|
||||||
rule: {
|
filter: rule.filter,
|
||||||
channel_id: channel.id,
|
tags: rule.tags,
|
||||||
category_id: category2.id,
|
},
|
||||||
filter: rule.filter,
|
}
|
||||||
tags: rule.tags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
|
@ -295,40 +311,38 @@ describe 'Chat Controller', type: :request do
|
||||||
expect(rule.category_id).to eq(category2.id)
|
expect(rule.category_id).to eq(category2.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should fail for invalid params' do
|
it "should fail for invalid params" do
|
||||||
put "/admin/plugins/chat-integration/rules/#{rule.id}.json", params: {
|
put "/admin/plugins/chat-integration/rules/#{rule.id}.json",
|
||||||
rule: {
|
params: {
|
||||||
channel_id: channel.id,
|
rule: {
|
||||||
category_id: category.id,
|
channel_id: channel.id,
|
||||||
filter: 'watch',
|
category_id: category.id,
|
||||||
tags: ['somenonexistanttag']
|
filter: "watch",
|
||||||
}
|
tags: ["somenonexistanttag"],
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'deleting a rule' do
|
describe "deleting a rule" do
|
||||||
let(:rule) do
|
let(:rule) do
|
||||||
DiscourseChatIntegration::Rule.create!(
|
DiscourseChatIntegration::Rule.create!(
|
||||||
channel_id: channel.id,
|
channel_id: channel.id,
|
||||||
filter: 'follow',
|
filter: "follow",
|
||||||
category_id: category.id,
|
category_id: category.id,
|
||||||
tags: [tag.name]
|
tags: [tag.name],
|
||||||
)
|
)
|
||||||
end
|
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
|
it "should be able delete a rule" do
|
||||||
sign_in(admin)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should be able delete a rule' do
|
|
||||||
delete "/admin/plugins/chat-integration/rules/#{rule.id}.json"
|
delete "/admin/plugins/chat-integration/rules/#{rule.id}.json"
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
@ -336,5 +350,4 @@ describe 'Chat Controller', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# 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
|
describe "loading a transcript" do
|
||||||
SiteSetting.chat_integration_enabled = true
|
it "should be able to load a transcript" do
|
||||||
end
|
|
||||||
|
|
||||||
describe 'loading a transcript' do
|
|
||||||
|
|
||||||
it 'should be able to load a transcript' do
|
|
||||||
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
key = DiscourseChatIntegration::Helper.save_transcript("Some content here")
|
||||||
|
|
||||||
get "/chat-transcript/#{key}.json"
|
get "/chat-transcript/#{key}.json"
|
||||||
|
@ -20,13 +16,11 @@ describe 'Public Controller', type: :request do
|
||||||
expect(response.body).to eq('{"content":"Some content here"}')
|
expect(response.body).to eq('{"content":"Some content here"}')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should 404 for non-existant transcript' do
|
it "should 404 for non-existant transcript" do
|
||||||
key = 'abcdefghijk'
|
key = "abcdefghijk"
|
||||||
get "/chat-transcript/#{key}.json"
|
get "/chat-transcript/#{key}.json"
|
||||||
|
|
||||||
expect(response.status).to eq(404)
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rails_helper'
|
require "rails_helper"
|
||||||
require_dependency 'post_creator'
|
require_dependency "post_creator"
|
||||||
require_relative '../dummy_provider'
|
require_relative "../dummy_provider"
|
||||||
|
|
||||||
RSpec.describe DiscourseChatIntegration::Manager do
|
RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
|
|
||||||
let(:manager) { ::DiscourseChatIntegration::Manager }
|
let(:manager) { ::DiscourseChatIntegration::Manager }
|
||||||
let(:category) { Fabricate(:category) }
|
let(:category) { Fabricate(:category) }
|
||||||
let(:group) { Fabricate(:group) }
|
let(:group) { Fabricate(:group) }
|
||||||
|
@ -14,31 +13,37 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
let(:first_post) { Fabricate(:post, topic: topic) }
|
let(:first_post) { Fabricate(:post, topic: topic) }
|
||||||
let(:second_post) { Fabricate(:post, topic: topic, post_number: 2) }
|
let(:second_post) { Fabricate(:post, topic: topic, post_number: 2) }
|
||||||
|
|
||||||
describe '.trigger_notifications' do
|
describe ".trigger_notifications" do
|
||||||
include_context "with dummy provider"
|
include_context "with dummy provider"
|
||||||
|
|
||||||
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
let(:chan1) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||||
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
let(:chan2) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||||
let(:chan3) { DiscourseChatIntegration::Channel.create!(provider: 'dummy') }
|
let(:chan3) { DiscourseChatIntegration::Channel.create!(provider: "dummy") }
|
||||||
|
|
||||||
before do
|
before { SiteSetting.chat_integration_enabled = true }
|
||||||
SiteSetting.chat_integration_enabled = true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should fail gracefully when a provider throws an exception" do
|
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
|
# 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)
|
manager.trigger_notifications(first_post.id)
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly()
|
expect(provider.sent_to_channel_ids).to contain_exactly
|
||||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq('hello')
|
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq("hello")
|
||||||
|
|
||||||
# Triggering a different error should set the error_key to a generic message
|
# Triggering a different error should set the error_key to a generic message
|
||||||
provider.set_raise_exception(StandardError.new "hello")
|
provider.set_raise_exception(StandardError.new "hello")
|
||||||
manager.trigger_notifications(first_post.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
|
||||||
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq('chat_integration.channel_exception')
|
expect(DiscourseChatIntegration::Channel.all.first.error_key).to eq(
|
||||||
|
"chat_integration.channel_exception",
|
||||||
|
)
|
||||||
|
|
||||||
provider.set_raise_exception(nil)
|
provider.set_raise_exception(nil)
|
||||||
|
|
||||||
|
@ -48,17 +53,33 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
|
|
||||||
it "should not send notifications when provider is disabled" do
|
it "should not send notifications when provider is disabled" do
|
||||||
SiteSetting.chat_integration_enabled = false
|
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)
|
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
|
end
|
||||||
|
|
||||||
it "should send a notification to watched and following channels for new topic" do
|
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!(
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'follow', category_id: category.id)
|
channel: chan1,
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan3, filter: 'mute', category_id: category.id)
|
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)
|
manager.trigger_notifications(first_post.id)
|
||||||
|
|
||||||
|
@ -66,9 +87,21 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should send a notification only to watched for reply" do
|
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!(
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan2, filter: 'follow', category_id: category.id)
|
channel: chan1,
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan3, filter: 'mute', category_id: category.id)
|
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)
|
manager.trigger_notifications(second_post.id)
|
||||||
|
|
||||||
|
@ -76,7 +109,7 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respect wildcard category settings" do
|
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)
|
manager.trigger_notifications(first_post.id)
|
||||||
|
|
||||||
|
@ -84,17 +117,25 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respect mute over watch" do
|
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: "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: "mute",
|
||||||
|
category_id: category.id,
|
||||||
|
) # Specific mute
|
||||||
|
|
||||||
manager.trigger_notifications(first_post.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
|
end
|
||||||
|
|
||||||
it "should respect watch over follow" do
|
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: "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: "watch",
|
||||||
|
category_id: category.id,
|
||||||
|
) # Specific watch
|
||||||
|
|
||||||
manager.trigger_notifications(second_post.id)
|
manager.trigger_notifications(second_post.id)
|
||||||
|
|
||||||
|
@ -102,8 +143,12 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should respect thread over watch" do
|
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: "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: "thread",
|
||||||
|
category_id: category.id,
|
||||||
|
) # Specific thread
|
||||||
|
|
||||||
manager.trigger_notifications(second_post.id)
|
manager.trigger_notifications(second_post.id)
|
||||||
|
|
||||||
|
@ -111,18 +156,23 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not notify about private messages" do
|
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)
|
private_post = Fabricate(:private_message_post)
|
||||||
|
|
||||||
manager.trigger_notifications(private_post.id)
|
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
|
end
|
||||||
|
|
||||||
it "should work for group pms" do
|
it "should work for group pms" do
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan1, filter: 'watch') # Wildcard 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
|
DiscourseChatIntegration::Rule.create!(
|
||||||
|
channel: chan2,
|
||||||
|
type: "group_message",
|
||||||
|
filter: "watch",
|
||||||
|
group_id: group.id,
|
||||||
|
) # Group watch
|
||||||
|
|
||||||
private_post = Fabricate(:private_message_post)
|
private_post = Fabricate(:private_message_post)
|
||||||
private_post.topic.invite_group(Fabricate(:user), group)
|
private_post.topic.invite_group(Fabricate(:user), group)
|
||||||
|
@ -133,8 +183,18 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should work for pms with multiple groups" do
|
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!(
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group2.id)
|
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 = Fabricate(:private_message_post)
|
||||||
private_post.topic.invite_group(Fabricate(:user), group)
|
private_post.topic.invite_group(Fabricate(:user), group)
|
||||||
|
@ -146,40 +206,77 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should work for group mentions" do
|
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: chan1, filter: "watch") # Wildcard watch
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan2, type: 'group_message', filter: 'watch', group_id: group.id)
|
DiscourseChatIntegration::Rule.create!(
|
||||||
DiscourseChatIntegration::Rule.create!(channel: chan3, type: 'group_mention', filter: 'watch', group_id: group.id)
|
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)
|
manager.trigger_notifications(third_post.id)
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id, chan3.id)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id, chan3.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should give group rule precedence over normal rules" do
|
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)
|
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)
|
manager.trigger_notifications(third_post.id)
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not notify about mentions in private messages" do
|
it "should not notify about mentions in private messages" do
|
||||||
# Group 1 watching for messages on channel 1
|
# 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
|
# 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
|
# Make a private message only accessible to group 1
|
||||||
private_message = Fabricate(:private_message_post)
|
private_message = Fabricate(:private_message_post)
|
||||||
private_message.topic.invite_group(Fabricate(:user), group)
|
private_message.topic.invite_group(Fabricate(:user), group)
|
||||||
|
|
||||||
# Mention group 2 in the message
|
# 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
|
# We expect that only group 1 receives a notification
|
||||||
manager.trigger_notifications(mention_post.id)
|
manager.trigger_notifications(mention_post.id)
|
||||||
|
@ -187,15 +284,15 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not notify about posts the chat_user cannot see" do
|
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
|
# Create a group & user
|
||||||
group = Fabricate(:group, name: "friends")
|
group = Fabricate(:group, name: "friends")
|
||||||
user = Fabricate(:user, username: 'david')
|
user = Fabricate(:user, username: "david")
|
||||||
group.add(user)
|
group.add(user)
|
||||||
|
|
||||||
# Set the chat_user to the newly created non-admin 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
|
# Create a category
|
||||||
category = Fabricate(:category, name: "Test category")
|
category = Fabricate(:category, name: "Test category")
|
||||||
|
@ -208,7 +305,7 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
|
|
||||||
# Check no notification sent
|
# Check no notification sent
|
||||||
manager.trigger_notifications(first_post.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
|
||||||
|
|
||||||
# Now expose category to new user
|
# Now expose category to new user
|
||||||
category.set_permissions(Group[:friends] => :full)
|
category.set_permissions(Group[:friends] => :full)
|
||||||
|
@ -217,20 +314,17 @@ RSpec.describe DiscourseChatIntegration::Manager do
|
||||||
# Check notification sent
|
# Check notification sent
|
||||||
manager.trigger_notifications(first_post.id)
|
manager.trigger_notifications(first_post.id)
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with tags enabled' do
|
describe "with tags enabled" do
|
||||||
let(:tag) { Fabricate(:tag, name: 'gsoc') }
|
let(:tag) { Fabricate(:tag, name: "gsoc") }
|
||||||
let(:tagged_topic) { Fabricate(:topic, category_id: category.id, tags: [tag]) }
|
let(:tagged_topic) { Fabricate(:topic, category_id: category.id, tags: [tag]) }
|
||||||
let(:tagged_first_post) { Fabricate(:post, topic: tagged_topic) }
|
let(:tagged_first_post) { Fabricate(:post, topic: tagged_topic) }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) { SiteSetting.tagging_enabled = true }
|
||||||
SiteSetting.tagging_enabled = true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should still work for rules without any tags specified' do
|
it "should still work for rules without any tags specified" 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
|
||||||
|
|
||||||
manager.trigger_notifications(first_post.id)
|
manager.trigger_notifications(first_post.id)
|
||||||
manager.trigger_notifications(tagged_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)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id, chan1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should only match tagged topics when rule has tags' do
|
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])
|
DiscourseChatIntegration::Rule.create!(
|
||||||
|
channel: chan1,
|
||||||
|
filter: "follow",
|
||||||
|
category_id: category.id,
|
||||||
|
tags: [tag.name],
|
||||||
|
)
|
||||||
|
|
||||||
manager.trigger_notifications(first_post.id)
|
manager.trigger_notifications(first_post.id)
|
||||||
manager.trigger_notifications(tagged_first_post.id)
|
manager.trigger_notifications(tagged_first_post.id)
|
||||||
|
|
||||||
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
expect(provider.sent_to_channel_ids).to contain_exactly(chan1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue