DEV: Introduce syntax_tree for ruby formatting (#149)
This commit is contained in:
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
cache: yarn
|
||||
|
||||
- name: Yarn install
|
||||
@@ -33,15 +33,15 @@ jobs:
|
||||
bundler-cache: true
|
||||
|
||||
- name: ESLint
|
||||
if: ${{ always() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets}/javascripts
|
||||
if: ${{ !cancelled() }}
|
||||
run: yarn eslint --ext .js,.js.es6 --no-error-on-unmatched-pattern {test,assets,admin/assets}/javascripts
|
||||
|
||||
- name: Prettier
|
||||
if: ${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
shell: bash
|
||||
run: |
|
||||
yarn prettier -v
|
||||
if [ 0 -lt $(find assets -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
if [ 0 -lt $(find assets admin/assets -type f \( -name "*.scss" -or -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
yarn prettier --list-different "assets/**/*.{scss,js,es6}"
|
||||
fi
|
||||
if [ 0 -lt $(find test -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
@@ -49,9 +49,18 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Ember template lint
|
||||
if: ${{ always() }}
|
||||
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts
|
||||
if: ${{ !cancelled() }}
|
||||
run: yarn ember-template-lint --no-error-on-unmatched-pattern assets/javascripts admin/assets/javascripts
|
||||
|
||||
- name: Rubocop
|
||||
if: ${{ always() }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: bundle exec rubocop .
|
||||
|
||||
- name: Syntax Tree
|
||||
if: ${{ !cancelled() }}
|
||||
run: |
|
||||
if test -f .streerc; then
|
||||
bundle exec stree check Gemfile $(git ls-files '*.rb') $(git ls-files '*.rake')
|
||||
else
|
||||
echo "Stree config not detected for this repository. Skipping."
|
||||
fi
|
||||
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
|
||||
- name: Get yarn cache directory
|
||||
id: yarn-cache-dir
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/cache@v3
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/spec -type f -name "*.rb" 2> /dev/null | wc -l) ]; then
|
||||
echo "::set-output name=files_exist::true"
|
||||
echo "files_exist=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Plugin RSpec
|
||||
@@ -142,7 +142,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
if [ 0 -lt $(find plugins/${{ github.event.repository.name }}/test/javascripts -type f \( -name "*.js" -or -name "*.es6" \) 2> /dev/null | wc -l) ]; then
|
||||
echo "::set-output name=files_exist::true"
|
||||
echo "files_exist=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Plugin QUnit
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
inherit_gem:
|
||||
rubocop-discourse: default.yml
|
||||
rubocop-discourse: stree-compat.yml
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
source "https://rubygems.org"
|
||||
|
||||
group :development do
|
||||
gem 'translations-manager', git: 'https://github.com/discourse/translations-manager.git'
|
||||
gem 'rubocop-discourse'
|
||||
gem "translations-manager", git: "https://github.com/discourse/translations-manager.git"
|
||||
gem "rubocop-discourse"
|
||||
gem "syntax_tree"
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@ GEM
|
||||
parallel (1.22.1)
|
||||
parser (3.1.2.1)
|
||||
ast (~> 2.4.1)
|
||||
prettier_print (1.2.0)
|
||||
rainbow (3.1.1)
|
||||
regexp_parser (2.6.0)
|
||||
rexml (3.2.5)
|
||||
@@ -33,6 +34,8 @@ GEM
|
||||
rubocop-rspec (2.13.2)
|
||||
rubocop (~> 1.33)
|
||||
ruby-progressbar (1.11.0)
|
||||
syntax_tree (5.1.0)
|
||||
prettier_print (>= 1.2.0)
|
||||
unicode-display_width (2.3.0)
|
||||
|
||||
PLATFORMS
|
||||
@@ -40,6 +43,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
rubocop-discourse
|
||||
syntax_tree
|
||||
translations-manager!
|
||||
|
||||
BUNDLED WITH
|
||||
|
||||
@@ -8,15 +8,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
end
|
||||
|
||||
def list_providers
|
||||
providers = ::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
||||
{
|
||||
name: x::PROVIDER_NAME,
|
||||
id: x::PROVIDER_NAME,
|
||||
channel_parameters: (defined? x::CHANNEL_PARAMETERS) ? x::CHANNEL_PARAMETERS : []
|
||||
}
|
||||
end
|
||||
providers =
|
||||
::DiscourseChatIntegration::Provider.enabled_providers.map do |x|
|
||||
{
|
||||
name: x::PROVIDER_NAME,
|
||||
id: x::PROVIDER_NAME,
|
||||
channel_parameters: (defined?(x::CHANNEL_PARAMETERS)) ? x::CHANNEL_PARAMETERS : [],
|
||||
}
|
||||
end
|
||||
|
||||
render json: providers, root: 'providers'
|
||||
render json: providers, root: "providers"
|
||||
end
|
||||
|
||||
def test
|
||||
@@ -28,9 +29,7 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
|
||||
provider = ::DiscourseChatIntegration::Provider.get_by_name(channel.provider)
|
||||
|
||||
if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
||||
raise Discourse::NotFound
|
||||
end
|
||||
raise Discourse::NotFound if !DiscourseChatIntegration::Provider.is_enabled(provider)
|
||||
|
||||
post = Topic.find(topic_id.to_i).posts.first
|
||||
|
||||
@@ -56,34 +55,36 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
raise Discourse::InvalidParameters if !providers.include?(requested_provider)
|
||||
|
||||
channels = DiscourseChatIntegration::Channel.with_provider(requested_provider)
|
||||
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: 'channels'
|
||||
render_serialized channels, DiscourseChatIntegration::ChannelSerializer, root: "channels"
|
||||
end
|
||||
|
||||
def create_channel
|
||||
begin
|
||||
providers = ::DiscourseChatIntegration::Provider.enabled_providers.map { |x| x::PROVIDER_NAME }
|
||||
providers =
|
||||
::DiscourseChatIntegration::Provider.enabled_providers.map { |x| x::PROVIDER_NAME }
|
||||
|
||||
if !defined?(params[:channel]) && defined?(params[:channel][:provider])
|
||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
||||
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||
end
|
||||
|
||||
requested_provider = params[:channel][:provider]
|
||||
|
||||
if !providers.include?(requested_provider)
|
||||
raise Discourse::InvalidParameters, 'Provider is not valid'
|
||||
raise Discourse::InvalidParameters, "Provider is not valid"
|
||||
end
|
||||
|
||||
allowed_keys = DiscourseChatIntegration::Provider.get_by_name(requested_provider)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
allowed_keys =
|
||||
DiscourseChatIntegration::Provider.get_by_name(
|
||||
requested_provider,
|
||||
)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
|
||||
hash = params.require(:channel).permit(:provider, data: allowed_keys)
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.new(hash)
|
||||
|
||||
if !channel.save
|
||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.save
|
||||
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
@@ -94,15 +95,16 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
channel = DiscourseChatIntegration::Channel.find(params[:id].to_i)
|
||||
channel.error_key = nil # Reset any error on the rule
|
||||
|
||||
allowed_keys = DiscourseChatIntegration::Provider.get_by_name(channel.provider)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
allowed_keys =
|
||||
DiscourseChatIntegration::Provider.get_by_name(
|
||||
channel.provider,
|
||||
)::CHANNEL_PARAMETERS.map { |p| p[:key].to_sym }
|
||||
|
||||
hash = params.require(:channel).permit(data: allowed_keys)
|
||||
|
||||
if !channel.update(hash)
|
||||
raise Discourse::InvalidParameters, 'Channel is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Channel is not valid" if !channel.update(hash)
|
||||
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: 'channel'
|
||||
render_serialized channel, DiscourseChatIntegration::ChannelSerializer, root: "channel"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
@@ -118,14 +120,13 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
|
||||
def create_rule
|
||||
begin
|
||||
hash = params.require(:rule).permit(:channel_id, :type, :filter, :group_id, :category_id, tags: [])
|
||||
hash =
|
||||
params.require(:rule).permit(:channel_id, :type, :filter, :group_id, :category_id, tags: [])
|
||||
rule = DiscourseChatIntegration::Rule.new(hash)
|
||||
|
||||
if !rule.save
|
||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.save
|
||||
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
@@ -136,11 +137,9 @@ class DiscourseChatIntegration::ChatController < ApplicationController
|
||||
rule = DiscourseChatIntegration::Rule.find(params[:id].to_i)
|
||||
hash = params.require(:rule).permit(:type, :filter, :group_id, :category_id, tags: [])
|
||||
|
||||
if !rule.update(hash)
|
||||
raise Discourse::InvalidParameters, 'Rule is not valid'
|
||||
end
|
||||
raise Discourse::InvalidParameters, "Rule is not valid" if !rule.update(hash)
|
||||
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: 'rule'
|
||||
render_serialized rule, DiscourseChatIntegration::RuleSerializer, root: "rule"
|
||||
rescue Discourse::InvalidParameters => e
|
||||
render json: { errors: [e.message] }, status: 422
|
||||
end
|
||||
|
||||
+37
-20
@@ -2,7 +2,6 @@
|
||||
|
||||
module DiscourseChatIntegration
|
||||
module Helper
|
||||
|
||||
def self.process_command(channel, tokens)
|
||||
guardian = DiscourseChatIntegration::Manager.guardian
|
||||
|
||||
@@ -16,13 +15,19 @@ module DiscourseChatIntegration
|
||||
when "thread", "watch", "follow", "mute"
|
||||
return error_text if tokens.empty?
|
||||
# If the first token in the command is a tag, this rule applies to all categories
|
||||
category_name = tokens[0].start_with?('tag:') ? nil : tokens.shift
|
||||
category_name = tokens[0].start_with?("tag:") ? nil : tokens.shift
|
||||
|
||||
if category_name
|
||||
category = Category.find_by(slug: category_name)
|
||||
unless category
|
||||
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(', ')
|
||||
return I18n.t("chat_integration.provider.#{provider}.not_found.category", name: category_name, list: cat_list)
|
||||
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(", ")
|
||||
return(
|
||||
I18n.t(
|
||||
"chat_integration.provider.#{provider}.not_found.category",
|
||||
name: category_name,
|
||||
list: cat_list,
|
||||
)
|
||||
)
|
||||
end
|
||||
else
|
||||
category = nil # All categories
|
||||
@@ -32,8 +37,8 @@ module DiscourseChatIntegration
|
||||
# Every remaining token must be a tag. If not, abort and send help text
|
||||
while tokens.size > 0
|
||||
token = tokens.shift
|
||||
if token.start_with?('tag:')
|
||||
tag_name = token.sub(/^tag:/, '')
|
||||
if token.start_with?("tag:")
|
||||
tag_name = token.sub(/^tag:/, "")
|
||||
else
|
||||
return error_text
|
||||
end
|
||||
@@ -47,7 +52,12 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
category_id = category.nil? ? nil : category.id
|
||||
case DiscourseChatIntegration::Helper.smart_create_rule(channel: channel, filter: cmd, category_id: category_id, tags: tags)
|
||||
case DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: channel,
|
||||
filter: cmd,
|
||||
category_id: category_id,
|
||||
tags: tags,
|
||||
)
|
||||
when :created
|
||||
I18n.t("chat_integration.provider.#{provider}.create.created")
|
||||
when :updated
|
||||
@@ -107,23 +117,25 @@ module DiscourseChatIntegration
|
||||
end
|
||||
end
|
||||
|
||||
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string",
|
||||
text << I18n.t(
|
||||
"chat_integration.provider.#{provider}.status.rule_string",
|
||||
index: i,
|
||||
filter: rule.filter,
|
||||
category: category_name
|
||||
category: category_name,
|
||||
)
|
||||
|
||||
if SiteSetting.tagging_enabled && (!rule.tags.nil?)
|
||||
text << I18n.t("chat_integration.provider.#{provider}.status.rule_string_tags_suffix", tags: rule.tags.join(', '))
|
||||
text << I18n.t(
|
||||
"chat_integration.provider.#{provider}.status.rule_string_tags_suffix",
|
||||
tags: rule.tags.join(", "),
|
||||
)
|
||||
end
|
||||
|
||||
text << "\n"
|
||||
i += 1
|
||||
end
|
||||
|
||||
if rules.size == 0
|
||||
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules")
|
||||
end
|
||||
text << I18n.t("chat_integration.provider.#{provider}.status.no_rules") if rules.size == 0
|
||||
|
||||
text
|
||||
end
|
||||
@@ -144,12 +156,15 @@ module DiscourseChatIntegration
|
||||
# :created if a new rule has been created
|
||||
# false if there was an error
|
||||
def self.smart_create_rule(channel:, filter:, category_id: nil, tags: nil)
|
||||
existing_rules = DiscourseChatIntegration::Rule.with_channel(channel).with_type('normal')
|
||||
existing_rules = DiscourseChatIntegration::Rule.with_channel(channel).with_type("normal")
|
||||
|
||||
# Select the ones that have the same category
|
||||
same_category = existing_rules.select { |rule| rule.category_id == category_id }
|
||||
|
||||
same_category_and_tags = same_category.select { |rule| (rule.tags.nil? ? [] : rule.tags.sort) == (tags.nil? ? [] : tags.sort) }
|
||||
same_category_and_tags =
|
||||
same_category.select do |rule|
|
||||
(rule.tags.nil? ? [] : rule.tags.sort) == (tags.nil? ? [] : tags.sort)
|
||||
end
|
||||
|
||||
if same_category_and_tags.size > 0
|
||||
# These rules have exactly the same criteria as what we're trying to create
|
||||
@@ -185,7 +200,9 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
# This rule is unique! Create a new one:
|
||||
return :created if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save
|
||||
if Rule.new(channel: channel, filter: filter, category_id: category_id, tags: tags).save
|
||||
return :created
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
@@ -197,13 +214,13 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
def self.formatted_display_name(user)
|
||||
if !SiteSetting.enable_names || user.name.blank?
|
||||
return "@#{user.username}"
|
||||
end
|
||||
return "@#{user.username}" if !SiteSetting.enable_names || user.name.blank?
|
||||
|
||||
full_name = user.name
|
||||
full_name_normalized = User.normalize_username(full_name.strip)
|
||||
similar = full_name_normalized.gsub(' ', '_') == user.username_lower || full_name_normalized.gsub(' ', '') == user.username_lower
|
||||
similar =
|
||||
full_name_normalized.gsub(" ", "_") == user.username_lower ||
|
||||
full_name_normalized.gsub(" ", "") == user.username_lower
|
||||
if similar && SiteSetting.prioritize_username_in_ux?
|
||||
"@#{user.username}"
|
||||
elsif similar
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
module Jobs
|
||||
class DiscourseChatAddTypeField < ::Jobs::Onceoff
|
||||
def execute_onceoff(args)
|
||||
DiscourseChatIntegration::Rule.find_each do |rule|
|
||||
rule.save(validate: false)
|
||||
end
|
||||
DiscourseChatIntegration::Rule.find_each { |rule| rule.save(validate: false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,26 +3,26 @@
|
||||
module Jobs
|
||||
class DiscourseChatMigrateFromSlackOfficial < ::Jobs::Onceoff
|
||||
def execute_onceoff(args)
|
||||
slack_installed = PluginStoreRow.where(plugin_name: 'discourse-slack-official').exists?
|
||||
slack_installed = PluginStoreRow.where(plugin_name: "discourse-slack-official").exists?
|
||||
|
||||
if slack_installed
|
||||
already_setup_rules = DiscourseChatIntegration::Channel.with_provider('slack').exists?
|
||||
already_setup_rules = DiscourseChatIntegration::Channel.with_provider("slack").exists?
|
||||
|
||||
already_setup_sitesettings =
|
||||
SiteSetting.chat_integration_slack_enabled ||
|
||||
SiteSetting.chat_integration_slack_access_token.present? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
||||
SiteSetting.chat_integration_slack_access_token.present? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token.present? ||
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url.present?
|
||||
|
||||
if !already_setup_rules && !already_setup_sitesettings
|
||||
ActiveRecord::Base.transaction do
|
||||
migrate_settings
|
||||
migrate_data
|
||||
is_slack_enabled = site_settings_value('slack_enabled')
|
||||
is_slack_enabled = site_settings_value("slack_enabled")
|
||||
|
||||
if is_slack_enabled
|
||||
slack_enabled = SiteSetting.find_by(name: 'slack_enabled')
|
||||
slack_enabled.update!(value: 'f')
|
||||
slack_enabled = SiteSetting.find_by(name: "slack_enabled")
|
||||
slack_enabled.update!(value: "f")
|
||||
|
||||
SiteSetting.chat_integration_slack_enabled = true
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
@@ -30,44 +30,55 @@ module Jobs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def migrate_data
|
||||
rows = []
|
||||
PluginStoreRow.where(plugin_name: 'discourse-slack-official')
|
||||
PluginStoreRow
|
||||
.where(plugin_name: "discourse-slack-official")
|
||||
.where("key ~* :pat", pat: "^category_.*")
|
||||
.each do |row|
|
||||
PluginStore
|
||||
.cast_value(row.type_name, row.value)
|
||||
.each do |rule|
|
||||
category_id =
|
||||
if row.key == "category_*"
|
||||
nil
|
||||
else
|
||||
row.key.gsub!("category_", "")
|
||||
row.key.to_i
|
||||
end
|
||||
|
||||
PluginStore.cast_value(row.type_name, row.value).each do |rule|
|
||||
category_id =
|
||||
if row.key == 'category_*'
|
||||
nil
|
||||
else
|
||||
row.key.gsub!('category_', '')
|
||||
row.key.to_i
|
||||
next if !category_id.nil? && !Category.exists?(id: category_id)
|
||||
|
||||
valid_tags = []
|
||||
valid_tags = Tag.where(name: rule[:tags]).pluck(:name) if rule[:tags]
|
||||
|
||||
rows << {
|
||||
category_id: category_id,
|
||||
channel: rule[:channel],
|
||||
filter: rule[:filter],
|
||||
tags: valid_tags,
|
||||
}
|
||||
end
|
||||
|
||||
next if !category_id.nil? && !Category.exists?(id: category_id)
|
||||
|
||||
valid_tags = []
|
||||
valid_tags = Tag.where(name: rule[:tags]).pluck(:name) if rule[:tags]
|
||||
|
||||
rows << {
|
||||
category_id: category_id,
|
||||
channel: rule[:channel],
|
||||
filter: rule[:filter],
|
||||
tags: valid_tags
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
rows.each do |row|
|
||||
# Load an existing channel with this identifier. If none, create it
|
||||
row[:channel] = "##{row[:channel]}" unless row[:channel].start_with?("#")
|
||||
channel = DiscourseChatIntegration::Channel.with_provider('slack').with_data_value('identifier', row[:channel]).first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider("slack")
|
||||
.with_data_value("identifier", row[:channel])
|
||||
.first
|
||||
if !channel
|
||||
channel = DiscourseChatIntegration::Channel.create(provider: 'slack', data: { identifier: row[:channel] })
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel.create(
|
||||
provider: "slack",
|
||||
data: {
|
||||
identifier: row[:channel],
|
||||
},
|
||||
)
|
||||
if !channel.id
|
||||
Rails.logger.warn("Error creating channel for #{row}")
|
||||
next
|
||||
@@ -75,50 +86,58 @@ module Jobs
|
||||
end
|
||||
|
||||
# Create the rule, with clever logic for avoiding duplicates
|
||||
success = DiscourseChatIntegration::Helper.smart_create_rule(channel: channel, filter: row[:filter], category_id: row[:category_id], tags: row[:tags])
|
||||
success =
|
||||
DiscourseChatIntegration::Helper.smart_create_rule(
|
||||
channel: channel,
|
||||
filter: row[:filter],
|
||||
category_id: row[:category_id],
|
||||
tags: row[:tags],
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def migrate_settings
|
||||
if !(slack_access_token = site_settings_value('slack_access_token')).nil?
|
||||
if !(slack_access_token = site_settings_value("slack_access_token")).nil?
|
||||
SiteSetting.chat_integration_slack_access_token = slack_access_token
|
||||
end
|
||||
|
||||
if !(slack_incoming_webhook_token = site_settings_value('slack_incoming_webhook_token')).nil?
|
||||
if !(slack_incoming_webhook_token = site_settings_value("slack_incoming_webhook_token")).nil?
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = slack_incoming_webhook_token
|
||||
end
|
||||
|
||||
if !(slack_discourse_excerpt_length = site_settings_value('slack_discourse_excerpt_length')).nil?
|
||||
if !(
|
||||
slack_discourse_excerpt_length = site_settings_value("slack_discourse_excerpt_length")
|
||||
).nil?
|
||||
SiteSetting.chat_integration_slack_excerpt_length = slack_discourse_excerpt_length
|
||||
end
|
||||
|
||||
if !(slack_outbound_webhook_url = site_settings_value('slack_outbound_webhook_url')).nil?
|
||||
if !(slack_outbound_webhook_url = site_settings_value("slack_outbound_webhook_url")).nil?
|
||||
SiteSetting.chat_integration_slack_outbound_webhook_url = slack_outbound_webhook_url
|
||||
end
|
||||
|
||||
if !(slack_icon_url = site_settings_value('slack_icon_url')).nil?
|
||||
if !(slack_icon_url = site_settings_value("slack_icon_url")).nil?
|
||||
SiteSetting.chat_integration_slack_icon_url = slack_icon_url
|
||||
end
|
||||
|
||||
if !(post_to_slack_window_secs = site_settings_value('post_to_slack_window_secs')).nil?
|
||||
if !(post_to_slack_window_secs = site_settings_value("post_to_slack_window_secs")).nil?
|
||||
SiteSetting.chat_integration_delay_seconds = post_to_slack_window_secs
|
||||
end
|
||||
|
||||
if !(slack_discourse_username = site_settings_value('slack_discourse_username')).nil?
|
||||
if !(slack_discourse_username = site_settings_value("slack_discourse_username")).nil?
|
||||
username = User.find_by(username: slack_discourse_username.downcase)&.username
|
||||
SiteSetting.chat_integration_discourse_username = (username || Discourse.system_user.username)
|
||||
SiteSetting.chat_integration_discourse_username =
|
||||
(username || Discourse.system_user.username)
|
||||
end
|
||||
end
|
||||
|
||||
def site_settings_value(name)
|
||||
value = SiteSetting.find_by(name: name)&.value
|
||||
|
||||
if value == 't'
|
||||
if value == "t"
|
||||
value = true
|
||||
elsif value == 'f'
|
||||
elsif value == "f"
|
||||
value = false
|
||||
end
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
||||
store :value, accessors: [ :provider, :error_key, :error_info, :data ], coder: JSON
|
||||
store :value, accessors: %i[provider error_key error_info data], coder: JSON
|
||||
|
||||
scope :with_provider, ->(provider) { where("value::json->>'provider'=?", provider) }
|
||||
scope :with_data_value, ->(key, value) { where("(value::json->>'data')::json->>?=?", key.to_s, value.to_s) }
|
||||
scope :with_data_value,
|
||||
->(key, value) { where("(value::json->>'data')::json->>?=?", key.to_s, value.to_s) }
|
||||
|
||||
after_initialize :init_data
|
||||
after_destroy :destroy_rules
|
||||
@@ -13,7 +14,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||
validate :provider_valid?, :data_valid?
|
||||
|
||||
def self.key_prefix
|
||||
'channel:'.freeze
|
||||
"channel:".freeze
|
||||
end
|
||||
|
||||
def rules
|
||||
@@ -52,9 +53,7 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||
|
||||
data.each do |key, value|
|
||||
regex_string = params.find { |p| p[:key] == key }[:regex]
|
||||
if !Regexp.new(regex_string).match(value)
|
||||
errors.add(:data, "data.#{key} is invalid")
|
||||
end
|
||||
errors.add(:data, "data.#{key} is invalid") if !Regexp.new(regex_string).match(value)
|
||||
|
||||
unique = params.find { |p| p[:key] == key }[:unique]
|
||||
if unique
|
||||
@@ -63,8 +62,6 @@ class DiscourseChatIntegration::Channel < DiscourseChatIntegration::PluginModel
|
||||
end
|
||||
end
|
||||
|
||||
if check_unique && matching_channels.exists?
|
||||
errors.add(:data, "matches an existing channel")
|
||||
end
|
||||
errors.add(:data, "matches an existing channel") if check_unique && matching_channels.exists?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||
PLUGIN_NAME = 'discourse-chat-integration'
|
||||
PLUGIN_NAME = "discourse-chat-integration"
|
||||
|
||||
default_scope { self.default_scope }
|
||||
|
||||
@@ -9,13 +9,14 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||
before_save :set_key
|
||||
|
||||
def self.default_scope
|
||||
where(type_name: 'JSON')
|
||||
.where(plugin_name: self::PLUGIN_NAME)
|
||||
.where("key LIKE ?", "#{self.key_prefix}%")
|
||||
where(type_name: "JSON").where(plugin_name: self::PLUGIN_NAME).where(
|
||||
"key LIKE ?",
|
||||
"#{self.key_prefix}%",
|
||||
)
|
||||
end
|
||||
|
||||
def self.key_prefix
|
||||
raise 'Not implemented'
|
||||
raise "Not implemented"
|
||||
end
|
||||
|
||||
private
|
||||
@@ -25,7 +26,7 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||
end
|
||||
|
||||
def init_plugin_model
|
||||
self.type_name ||= 'JSON'
|
||||
self.type_name ||= "JSON"
|
||||
self.plugin_name ||= PLUGIN_NAME
|
||||
end
|
||||
|
||||
@@ -37,5 +38,4 @@ class DiscourseChatIntegration::PluginModel < PluginStoreRow
|
||||
"#{self.key_prefix}#{max_id}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+40
-31
@@ -2,54 +2,65 @@
|
||||
|
||||
class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
# Setup ActiveRecord::Store to use the JSON field to read/write these values
|
||||
store :value, accessors: [ :channel_id, :type, :group_id, :category_id, :tags, :filter ], coder: JSON
|
||||
store :value, accessors: %i[channel_id type group_id category_id tags filter], coder: JSON
|
||||
|
||||
scope :with_type, ->(type) { where("value::json->>'type'=?", type.to_s) }
|
||||
scope :with_channel, ->(channel) { with_channel_id(channel.id) }
|
||||
scope :with_channel_id, ->(channel_id) { where("value::json->>'channel_id'=?", channel_id.to_s) }
|
||||
|
||||
scope :with_category_id, ->(category_id) do
|
||||
if category_id.nil?
|
||||
where("(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'")
|
||||
else
|
||||
where("value::json->>'category_id'=?", category_id.to_s)
|
||||
end
|
||||
end
|
||||
scope :with_category_id,
|
||||
->(category_id) {
|
||||
if category_id.nil?
|
||||
where(
|
||||
"(value::json->'category_id') IS NULL OR json_typeof(value::json->'category_id')='null'",
|
||||
)
|
||||
else
|
||||
where("value::json->>'category_id'=?", category_id.to_s)
|
||||
end
|
||||
}
|
||||
|
||||
scope :with_group_ids, ->(group_id) do
|
||||
where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s))
|
||||
end
|
||||
scope :with_group_ids,
|
||||
->(group_id) { where("value::json->>'group_id' IN (?)", group_id.map!(&:to_s)) }
|
||||
|
||||
scope :order_by_precedence, -> {
|
||||
order("
|
||||
scope :order_by_precedence,
|
||||
-> {
|
||||
order(
|
||||
"
|
||||
CASE
|
||||
WHEN value::json->>'type' = 'group_mention' THEN 1
|
||||
WHEN value::json->>'type' = 'group_message' THEN 2
|
||||
ELSE 3
|
||||
END
|
||||
",
|
||||
"
|
||||
"
|
||||
CASE
|
||||
WHEN value::json->>'filter' = 'mute' THEN 1
|
||||
WHEN value::json->>'filter' = 'thread' THEN 2
|
||||
WHEN value::json->>'filter' = 'watch' THEN 3
|
||||
WHEN value::json->>'filter' = 'follow' THEN 4
|
||||
END
|
||||
")
|
||||
}
|
||||
",
|
||||
)
|
||||
}
|
||||
|
||||
after_initialize :init_filter
|
||||
|
||||
validates :filter, inclusion: { in: %w(thread watch follow mute),
|
||||
message: "%{value} is not a valid filter" }
|
||||
validates :filter,
|
||||
inclusion: {
|
||||
in: %w[thread watch follow mute],
|
||||
message: "%{value} is not a valid filter",
|
||||
}
|
||||
|
||||
validates :type, inclusion: { in: %w(normal group_message group_mention),
|
||||
message: "%{value} is not a valid filter" }
|
||||
validates :type,
|
||||
inclusion: {
|
||||
in: %w[normal group_message group_mention],
|
||||
message: "%{value} is not a valid filter",
|
||||
}
|
||||
|
||||
validate :channel_valid?, :category_valid?, :group_valid?, :tags_valid?
|
||||
|
||||
def self.key_prefix
|
||||
'rule:'.freeze
|
||||
"rule:".freeze
|
||||
end
|
||||
|
||||
# We never want an empty array, set it to nil instead
|
||||
@@ -62,7 +73,7 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
end
|
||||
|
||||
# These are only allowed to be integers
|
||||
%w(channel_id category_id group_id).each do |name|
|
||||
%w[channel_id category_id group_id].each do |name|
|
||||
define_method "#{name}=" do |val|
|
||||
if val.nil? || val.blank?
|
||||
super(nil)
|
||||
@@ -91,11 +102,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
end
|
||||
|
||||
def category_valid?
|
||||
if type != 'normal' && !category_id.nil?
|
||||
if type != "normal" && !category_id.nil?
|
||||
errors.add(:category_id, "cannot be specified for that type of rule")
|
||||
end
|
||||
|
||||
return unless type == 'normal'
|
||||
return unless type == "normal"
|
||||
|
||||
if !(category_id.nil? || Category.where(id: category_id).exists?)
|
||||
errors.add(:category_id, "#{category_id} is not a valid category id")
|
||||
@@ -103,11 +114,11 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
end
|
||||
|
||||
def group_valid?
|
||||
if type == 'normal' && !group_id.nil?
|
||||
if type == "normal" && !group_id.nil?
|
||||
errors.add(:group_id, "cannot be specified for that type of rule")
|
||||
end
|
||||
|
||||
return if type == 'normal'
|
||||
return if type == "normal"
|
||||
|
||||
if !Group.where(id: group_id).exists?
|
||||
errors.add(:group_id, "#{group_id} is not a valid group id")
|
||||
@@ -118,14 +129,12 @@ class DiscourseChatIntegration::Rule < DiscourseChatIntegration::PluginModel
|
||||
return if tags.nil?
|
||||
|
||||
tags.each do |tag|
|
||||
if !Tag.where(name: tag).exists?
|
||||
errors.add(:tags, "#{tag} is not a valid tag")
|
||||
end
|
||||
errors.add(:tags, "#{tag} is not a valid tag") if !Tag.where(name: tag).exists?
|
||||
end
|
||||
end
|
||||
|
||||
def init_filter
|
||||
self.filter ||= 'watch'
|
||||
self.type ||= 'normal'
|
||||
self.filter ||= "watch"
|
||||
self.type ||= "normal"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Discourse::Application.routes.append do
|
||||
mount ::DiscourseChatIntegration::AdminEngine, at: '/admin/plugins/chat-integration', constraints: AdminConstraint.new
|
||||
mount ::DiscourseChatIntegration::PublicEngine, at: '/chat-transcript/', as: 'chat-transcript'
|
||||
mount ::DiscourseChatIntegration::Provider::HookEngine, at: '/chat-integration/'
|
||||
mount ::DiscourseChatIntegration::AdminEngine,
|
||||
at: "/admin/plugins/chat-integration",
|
||||
constraints: AdminConstraint.new
|
||||
mount ::DiscourseChatIntegration::PublicEngine, at: "/chat-transcript/", as: "chat-transcript"
|
||||
mount ::DiscourseChatIntegration::Provider::HookEngine, at: "/chat-integration/"
|
||||
|
||||
# For backwards compatibility with Slack plugin
|
||||
post "/slack/command" => "discourse_chat_integration/provider/slack_provider/slack_command#command"
|
||||
post "/slack/command" =>
|
||||
"discourse_chat_integration/provider/slack_provider/slack_command#command"
|
||||
end
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_dependency 'admin_constraint'
|
||||
require_dependency "admin_constraint"
|
||||
|
||||
module ::DiscourseChatIntegration
|
||||
AdminEngine.routes.draw do
|
||||
get "" => "chat#respond"
|
||||
get '/providers' => "chat#list_providers"
|
||||
post '/test' => "chat#test"
|
||||
get "/providers" => "chat#list_providers"
|
||||
post "/test" => "chat#test"
|
||||
|
||||
get '/channels' => "chat#list_channels"
|
||||
post '/channels' => "chat#create_channel"
|
||||
put '/channels/:id' => "chat#update_channel"
|
||||
delete '/channels/:id' => "chat#destroy_channel"
|
||||
get "/channels" => "chat#list_channels"
|
||||
post "/channels" => "chat#create_channel"
|
||||
put "/channels/:id" => "chat#update_channel"
|
||||
delete "/channels/:id" => "chat#destroy_channel"
|
||||
|
||||
post '/rules' => "chat#create_rule"
|
||||
put '/rules/:id' => "chat#update_rule"
|
||||
delete '/rules/:id' => "chat#destroy_rule"
|
||||
post "/rules" => "chat#create_rule"
|
||||
put "/rules/:id" => "chat#update_rule"
|
||||
delete "/rules/:id" => "chat#destroy_rule"
|
||||
|
||||
get "/:provider" => "chat#respond"
|
||||
end
|
||||
|
||||
PublicEngine.routes.draw do
|
||||
get '/:secret' => "public#post_transcript"
|
||||
end
|
||||
PublicEngine.routes.draw { get "/:secret" => "public#post_transcript" }
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative './rule_serializer'
|
||||
require_relative "./rule_serializer"
|
||||
|
||||
class DiscourseChatIntegration::ChannelSerializer < ApplicationSerializer
|
||||
attributes :id, :provider, :error_key, :error_info, :data, :rules
|
||||
|
||||
+28
-22
@@ -2,7 +2,6 @@
|
||||
|
||||
module DiscourseChatIntegration
|
||||
module Manager
|
||||
|
||||
def self.guardian
|
||||
Guardian.new(User.find_by(username: SiteSetting.chat_integration_discourse_username))
|
||||
end
|
||||
@@ -23,19 +22,26 @@ module DiscourseChatIntegration
|
||||
if topic.archetype == Archetype.private_message
|
||||
group_ids_with_access = topic.topic_allowed_groups.pluck(:group_id)
|
||||
return if group_ids_with_access.empty?
|
||||
matching_rules = DiscourseChatIntegration::Rule.with_type('group_message').with_group_ids(group_ids_with_access)
|
||||
matching_rules =
|
||||
DiscourseChatIntegration::Rule.with_type("group_message").with_group_ids(
|
||||
group_ids_with_access,
|
||||
)
|
||||
else
|
||||
matching_rules = DiscourseChatIntegration::Rule.with_type('normal').with_category_id(topic.category_id)
|
||||
matching_rules =
|
||||
DiscourseChatIntegration::Rule.with_type("normal").with_category_id(topic.category_id)
|
||||
if topic.category # Also load the rules for the wildcard category
|
||||
matching_rules += DiscourseChatIntegration::Rule.with_type('normal').with_category_id(nil)
|
||||
matching_rules += DiscourseChatIntegration::Rule.with_type("normal").with_category_id(nil)
|
||||
end
|
||||
|
||||
# If groups are mentioned, check for any matching rules and append them
|
||||
mentions = post.raw_mentions
|
||||
if mentions && mentions.length > 0
|
||||
groups = Group.where('LOWER(name) IN (?)', mentions)
|
||||
groups = Group.where("LOWER(name) IN (?)", mentions)
|
||||
if groups.exists?
|
||||
matching_rules += DiscourseChatIntegration::Rule.with_type('group_mention').with_group_ids(groups.map(&:id))
|
||||
matching_rules +=
|
||||
DiscourseChatIntegration::Rule.with_type("group_mention").with_group_ids(
|
||||
groups.map(&:id),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,17 +49,19 @@ module DiscourseChatIntegration
|
||||
# If tagging is enabled, thow away rules that don't apply to this topic
|
||||
if SiteSetting.tagging_enabled
|
||||
topic_tags = topic.tags.present? ? topic.tags.pluck(:name) : []
|
||||
matching_rules = matching_rules.select do |rule|
|
||||
next true if rule.tags.nil? || rule.tags.empty? # Filter has no tags specified
|
||||
any_tags_match = !((rule.tags & topic_tags).empty?)
|
||||
next any_tags_match # If any tags match, keep this filter, otherwise throw away
|
||||
end
|
||||
matching_rules =
|
||||
matching_rules.select do |rule|
|
||||
next true if rule.tags.nil? || rule.tags.empty? # Filter has no tags specified
|
||||
any_tags_match = !((rule.tags & topic_tags).empty?)
|
||||
next any_tags_match # If any tags match, keep this filter, otherwise throw away
|
||||
end
|
||||
end
|
||||
|
||||
# Sort by order of precedence
|
||||
t_prec = { 'group_message' => 0, 'group_mention' => 1, 'normal' => 2 } # Group things win
|
||||
f_prec = { 'mute' => 0, 'thread' => 1, 'watch' => 2, 'follow' => 3 } #(mute always wins; thread beats watch beats follow)
|
||||
sort_func = proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
||||
t_prec = { "group_message" => 0, "group_mention" => 1, "normal" => 2 } # Group things win
|
||||
f_prec = { "mute" => 0, "thread" => 1, "watch" => 2, "follow" => 3 } #(mute always wins; thread beats watch beats follow)
|
||||
sort_func =
|
||||
proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
||||
matching_rules = matching_rules.sort(&sort_func)
|
||||
|
||||
# Take the first rule for each channel
|
||||
@@ -81,14 +89,15 @@ module DiscourseChatIntegration
|
||||
|
||||
begin
|
||||
provider.trigger_notification(post, channel, rule)
|
||||
channel.update_attribute('error_key', nil) if channel.error_key
|
||||
channel.update_attribute("error_key", nil) if channel.error_key
|
||||
rescue => e
|
||||
if e.class == (DiscourseChatIntegration::ProviderError) && e.info.key?(:error_key) && !e.info[:error_key].nil?
|
||||
channel.update_attribute('error_key', e.info[:error_key])
|
||||
if e.class == (DiscourseChatIntegration::ProviderError) && e.info.key?(:error_key) &&
|
||||
!e.info[:error_key].nil?
|
||||
channel.update_attribute("error_key", e.info[:error_key])
|
||||
else
|
||||
channel.update_attribute('error_key', 'chat_integration.channel_exception')
|
||||
channel.update_attribute("error_key", "chat_integration.channel_exception")
|
||||
end
|
||||
channel.update_attribute('error_info', JSON.pretty_generate(e.try(:info)))
|
||||
channel.update_attribute("error_info", JSON.pretty_generate(e.try(:info)))
|
||||
|
||||
# Log the error
|
||||
# Discourse.handle_job_exception(e,
|
||||
@@ -99,10 +108,7 @@ module DiscourseChatIntegration
|
||||
# error_info: e.class == DiscourseChatIntegration::ProviderError ? e.info : nil }
|
||||
# )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
class AddUniqueIndexToSlackThreadTs < ActiveRecord::Migration[6.1]
|
||||
def up
|
||||
add_index :topic_custom_fields, [:topic_id, :name],
|
||||
unique: true,
|
||||
where: "(name LIKE 'slack_thread_id_%')",
|
||||
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
||||
add_index :topic_custom_fields,
|
||||
%i[topic_id name],
|
||||
unique: true,
|
||||
where: "(name LIKE 'slack_thread_id_%')",
|
||||
name: "index_topic_custom_fields_on_topic_id_and_slack_thread_id"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,15 +12,11 @@ module DiscourseChatIntegration
|
||||
|
||||
module Provider
|
||||
def self.providers
|
||||
constants.select do |constant|
|
||||
constant.to_s =~ /Provider$/
|
||||
end.map(&method(:const_get))
|
||||
constants.select { |constant| constant.to_s =~ /Provider$/ }.map(&method(:const_get))
|
||||
end
|
||||
|
||||
def self.enabled_providers
|
||||
self.providers.select do |provider|
|
||||
self.is_enabled(provider)
|
||||
end
|
||||
self.providers.select { |provider| self.is_enabled(provider) }
|
||||
end
|
||||
|
||||
def self.provider_names
|
||||
@@ -36,7 +32,7 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
def self.is_enabled(provider)
|
||||
if defined? provider::PROVIDER_ENABLED_SETTING
|
||||
if defined?(provider::PROVIDER_ENABLED_SETTING)
|
||||
SiteSetting.public_send(provider::PROVIDER_ENABLED_SETTING)
|
||||
else
|
||||
false
|
||||
@@ -51,9 +47,10 @@ module DiscourseChatIntegration
|
||||
class HookController < ::ApplicationController
|
||||
requires_plugin DiscourseChatIntegration::PLUGIN_NAME
|
||||
|
||||
class ProviderDisabled < StandardError; end
|
||||
class ProviderDisabled < StandardError
|
||||
end
|
||||
|
||||
rescue_from ProviderDisabled do
|
||||
rescue_from ProviderDisabled do
|
||||
rescue_discourse_actions(:not_found, 404)
|
||||
end
|
||||
|
||||
@@ -72,22 +69,20 @@ module DiscourseChatIntegration
|
||||
def self.mount_engines
|
||||
engines = []
|
||||
DiscourseChatIntegration::Provider.providers.each do |provider|
|
||||
engine = provider.constants.select do |constant|
|
||||
constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine")
|
||||
end.map(&provider.method(:const_get)).first
|
||||
engine =
|
||||
provider
|
||||
.constants
|
||||
.select { |constant| constant.to_s =~ (/Engine$/) && (constant.to_s != "HookEngine") }
|
||||
.map(&provider.method(:const_get))
|
||||
.first
|
||||
|
||||
if engine
|
||||
engines.push(engine: engine, name: provider::PROVIDER_NAME)
|
||||
end
|
||||
engines.push(engine: engine, name: provider::PROVIDER_NAME) if engine
|
||||
end
|
||||
|
||||
DiscourseChatIntegration::Provider::HookEngine.routes.draw do
|
||||
engines.each do |engine|
|
||||
mount engine[:engine], at: engine[:name]
|
||||
end
|
||||
engines.each { |engine| mount engine[:engine], at: engine[:name] }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ module DiscourseChatIntegration
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "webhook_url", regex: '^https:\/\/discord\.com\/api\/webhooks\/', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/discord\.com\/api\/webhooks\/',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
].freeze
|
||||
|
||||
def self.send_message(url, message)
|
||||
@@ -17,7 +22,7 @@ module DiscourseChatIntegration
|
||||
|
||||
uri = URI(url)
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
@@ -25,7 +30,7 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
def self.ensure_protocol(url)
|
||||
return url if !url.start_with?('//')
|
||||
return url if !url.start_with?("//")
|
||||
"http:#{url}"
|
||||
end
|
||||
|
||||
@@ -34,24 +39,40 @@ module DiscourseChatIntegration
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
message = {
|
||||
content: SiteSetting.chat_integration_discord_message_content,
|
||||
embeds: [{
|
||||
title: "#{topic.title} #{(category == '[uncategorized]') ? '' : category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
color: topic.category ? topic.category.color.to_i(16) : nil,
|
||||
description: post.excerpt(SiteSetting.chat_integration_discord_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
url: post.full_url,
|
||||
author: {
|
||||
name: display_name,
|
||||
url: Discourse.base_url + "/u/" + post.user.username,
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url)
|
||||
}
|
||||
}]
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
color: topic.category ? topic.category.color.to_i(16) : nil,
|
||||
description:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_discord_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
url: post.full_url,
|
||||
author: {
|
||||
name: display_name,
|
||||
url: Discourse.base_url + "/u/" + post.user.username,
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
message
|
||||
@@ -59,17 +80,20 @@ module DiscourseChatIntegration
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
# Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously
|
||||
webhook_url = "#{channel.data['webhook_url']}?wait=true"
|
||||
webhook_url = "#{channel.data["webhook_url"]}?wait=true"
|
||||
message = generate_discord_message(post)
|
||||
response = send_message(webhook_url, message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
||||
error_key: nil, message: message, response_body: response.body
|
||||
})
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||
info: {
|
||||
error_key: nil,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||
|
||||
PROVIDER_NAME = "flowdock".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_flowdock_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "flow_token", regex: '^\S+', unique: true, hidden: true },
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "flow_token", regex: '^\S+', unique: true, hidden: true }]
|
||||
|
||||
def self.send_message(url, message)
|
||||
uri = URI(url)
|
||||
@@ -14,7 +11,7 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
@@ -29,15 +26,21 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||
event: "discussion",
|
||||
author: {
|
||||
name: display_name,
|
||||
avatar: post.user.small_avatar_url
|
||||
avatar: post.user.small_avatar_url,
|
||||
},
|
||||
title: I18n.t("chat_integration.provider.flowdock.message_title"),
|
||||
external_thread_id: post.topic.id,
|
||||
body: post.excerpt(SiteSetting.chat_integration_flowdock_excerpt_length, text_entities: true, strip_links: false, remap_emoji: true),
|
||||
body:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_flowdock_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: false,
|
||||
remap_emoji: true,
|
||||
),
|
||||
thread: {
|
||||
title: post.topic.title,
|
||||
external_url: post.full_url
|
||||
}
|
||||
external_url: post.full_url,
|
||||
},
|
||||
}
|
||||
|
||||
message
|
||||
@@ -50,7 +53,11 @@ module DiscourseChatIntegration::Provider::FlowdockProvider
|
||||
|
||||
unless response.kind_of?(Net::HTTPSuccess)
|
||||
error_key = nil
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,19 +3,28 @@
|
||||
module DiscourseChatIntegration
|
||||
module Provider
|
||||
module GitterProvider
|
||||
PROVIDER_NAME = 'gitter'.freeze
|
||||
PROVIDER_NAME = "gitter".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_gitter_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url", regex: '^https://webhooks\.gitter\.im/e/\S+$', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https://webhooks\.gitter\.im/e/\S+$',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = gitter_message(post)
|
||||
response = Net::HTTP.post_form(URI(channel.data['webhook_url']), message: message)
|
||||
response = Net::HTTP.post_form(URI(channel.data["webhook_url"]), message: message)
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
error_key = nil
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,7 +32,14 @@ module DiscourseChatIntegration
|
||||
display_name = post.user.username
|
||||
topic = post.topic
|
||||
parent_category = topic.category.try :parent_category
|
||||
category_name = parent_category ? "[#{parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category_name =
|
||||
(
|
||||
if parent_category
|
||||
"[#{parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
|
||||
"[__#{display_name}__ - #{topic.title} - #{category_name}](#{post.full_url})"
|
||||
end
|
||||
|
||||
@@ -3,26 +3,35 @@
|
||||
module DiscourseChatIntegration
|
||||
module Provider
|
||||
module GoogleProvider
|
||||
PROVIDER_NAME = 'google'.freeze
|
||||
PROVIDER_NAME = "google".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_google_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+$', unique: true },
|
||||
{ key: "webhook_url", regex: '^https:\/\/chat.googleapis.com\/v1\/\S+$', unique: true, hidden: true }
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/chat.googleapis.com\/v1\/\S+$',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = get_message(post)
|
||||
uri = URI(channel.data['webhook_url'])
|
||||
uri = URI(channel.data["webhook_url"])
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,49 +44,67 @@ module DiscourseChatIntegration
|
||||
widgets: [
|
||||
{
|
||||
keyValue: {
|
||||
"topLabel": I18n.t("chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}", site_title: SiteSetting.title),
|
||||
"content": post.topic.title,
|
||||
"contentMultiline": "false",
|
||||
"bottomLabel": I18n.t("chat_integration.provider.google.author", username: post.user.username),
|
||||
"onClick": {
|
||||
"openLink": {
|
||||
"url": post.full_url
|
||||
}
|
||||
}
|
||||
}
|
||||
topLabel:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.new_#{post.is_first_post? ? "topic" : "post"}",
|
||||
site_title: SiteSetting.title,
|
||||
),
|
||||
content: post.topic.title,
|
||||
contentMultiline: "false",
|
||||
bottomLabel:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.author",
|
||||
username: post.user.username,
|
||||
),
|
||||
onClick: {
|
||||
openLink: {
|
||||
url: post.full_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
widgets: [
|
||||
{
|
||||
textParagraph: {
|
||||
text: post.excerpt(SiteSetting.chat_integration_google_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)
|
||||
}
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_google_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
widgets: [
|
||||
{
|
||||
buttons: [
|
||||
{
|
||||
"textButton": {
|
||||
"text": I18n.t("chat_integration.provider.google.link", site_title: SiteSetting.title),
|
||||
"onClick": {
|
||||
"openLink": {
|
||||
"url": post.full_url
|
||||
}
|
||||
}
|
||||
}
|
||||
textButton: {
|
||||
text:
|
||||
I18n.t(
|
||||
"chat_integration.provider.google.link",
|
||||
site_title: SiteSetting.title,
|
||||
),
|
||||
onClick: {
|
||||
openLink: {
|
||||
url: post.full_url,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,27 +2,32 @@
|
||||
module DiscourseChatIntegration::Provider::GroupmeProvider
|
||||
PROVIDER_NAME = "groupme".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_groupme_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "groupme_instance_name", regex: '[\s\S]*', unique: true }]
|
||||
|
||||
def self.generate_groupme_message(post)
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "#{I18n.t('uncategorized_category_name')}"
|
||||
category = "#{I18n.t("uncategorized_category_name")}"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "#{topic.category.parent_category.name}/#{topic.category.name}" : "#{topic.category.name}"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"#{topic.category.parent_category.name}/#{topic.category.name}"
|
||||
else
|
||||
"#{topic.category.name}"
|
||||
end
|
||||
)
|
||||
end
|
||||
pre_post_text = "#{display_name} Posted to #{SiteSetting.title}\n\nTopic: #{topic.title} [#{category}]"
|
||||
pre_post_text =
|
||||
"#{display_name} Posted to #{SiteSetting.title}\n\nTopic: #{topic.title} [#{category}]"
|
||||
read_more = "(Read More: #{post.full_url})"
|
||||
post_excerpt = "#{post.excerpt(SiteSetting.chat_integration_groupme_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)}"
|
||||
data = {
|
||||
text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}"
|
||||
}
|
||||
post_excerpt =
|
||||
"#{post.excerpt(SiteSetting.chat_integration_groupme_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true)}"
|
||||
data = { text: "#{pre_post_text}\n\n#{post_excerpt}\n#{read_more}" }
|
||||
data
|
||||
end
|
||||
|
||||
@@ -35,33 +40,39 @@ module DiscourseChatIntegration::Provider::GroupmeProvider
|
||||
instance_names = SiteSetting.chat_integration_groupme_instance_names.split(/\s*,\s*/)
|
||||
|
||||
unless instance_names.length() == bot_ids.length()
|
||||
instance_names = [I18n.t('chat_integration.provider.groupme.errors.instance_names_issue')] * bot_ids.length()
|
||||
instance_names =
|
||||
[I18n.t("chat_integration.provider.groupme.errors.instance_names_issue")] * bot_ids.length()
|
||||
end
|
||||
|
||||
name_to_id = Hash[instance_names.zip(bot_ids)]
|
||||
user_input_channel = channel.data['groupme_instance_name'].strip
|
||||
unless user_input_channel.eql? 'all'
|
||||
instance_names = [user_input_channel]
|
||||
end
|
||||
instance_names.each { |instance_name|
|
||||
user_input_channel = channel.data["groupme_instance_name"].strip
|
||||
instance_names = [user_input_channel] unless user_input_channel.eql? "all"
|
||||
instance_names.each do |instance_name|
|
||||
bot_id = name_to_id["#{instance_name}"]
|
||||
uri = URI("https://api.groupme.com/v3/bots/post")
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
message[:bot_id] = bot_id
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
num_errors += 1
|
||||
if response.code.to_s == '404'
|
||||
error_key = 'chat_integration.provider.groupme.errors.not_found'
|
||||
if response.code.to_s == "404"
|
||||
error_key = "chat_integration.provider.groupme.errors.not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
last_error_raised = { error_key: error_key, groupme_name: instance_name, bot_id: bot_id, request: req.body, response_code: response.code, response_body: response.body }
|
||||
last_error_raised = {
|
||||
error_key: error_key,
|
||||
groupme_name: instance_name,
|
||||
bot_id: bot_id,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
}
|
||||
end
|
||||
if last_error_raised
|
||||
successfully_sent = instance_names.length() - num_errors
|
||||
last_error_raised[:success_rate] = "#{successfully_sent}/#{instance_names.length()}"
|
||||
|
||||
@@ -7,27 +7,43 @@ module DiscourseChatIntegration
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_guilded_enabled
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "webhook_url", regex: '^https:\/\/media\.guilded\.gg\/webhooks\/', unique: true, hidden: true }
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{
|
||||
key: "webhook_url",
|
||||
regex: '^https:\/\/media\.guilded\.gg\/webhooks\/',
|
||||
unique: true,
|
||||
hidden: true,
|
||||
},
|
||||
].freeze
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
webhook_url = channel.data['webhook_url']
|
||||
webhook_url = channel.data["webhook_url"]
|
||||
message = generate_guilded_message(post)
|
||||
response = send_message(webhook_url, message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(info: {
|
||||
error_key: nil, message: message, response_body: response.body
|
||||
})
|
||||
raise ::DiscourseChatIntegration::ProviderError.new(
|
||||
info: {
|
||||
error_key: nil,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
},
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def self.generate_guilded_message(post)
|
||||
topic = post.topic
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
@@ -37,15 +53,24 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
message = {
|
||||
embeds: [{
|
||||
title: "#{topic.title} #{(category == '[uncategorized]') ? '' : category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
url: post.full_url,
|
||||
description: post.excerpt(SiteSetting.chat_integration_guilded_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
footer: {
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
text: "#{display_name} | #{post.created_at}"
|
||||
}
|
||||
}]
|
||||
embeds: [
|
||||
{
|
||||
title:
|
||||
"#{topic.title} #{(category == "[uncategorized]") ? "" : category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
url: post.full_url,
|
||||
description:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_guilded_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
footer: {
|
||||
icon_url: ensure_protocol(post.user.small_avatar_url),
|
||||
text: "#{display_name} | #{post.created_at}",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
message
|
||||
@@ -54,9 +79,9 @@ module DiscourseChatIntegration
|
||||
def self.send_message(url, message)
|
||||
uri = URI(url)
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
@@ -64,10 +89,9 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
def self.ensure_protocol(url)
|
||||
return url if !url.start_with?('//')
|
||||
return url if !url.start_with?("//")
|
||||
"http:#{url}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,25 +6,27 @@ module DiscourseChatIntegration
|
||||
PROVIDER_NAME = "matrix".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_matrix_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true }
|
||||
]
|
||||
{ key: "name", regex: '^\S+' },
|
||||
{ key: "room_id", regex: '^\!\S+:\S+$', unique: true, hidden: true },
|
||||
]
|
||||
|
||||
def self.send_message(room_id, message)
|
||||
homeserver = SiteSetting.chat_integration_matrix_homeserver
|
||||
event_type = 'm.room.message'
|
||||
event_type = "m.room.message"
|
||||
uid = Time.now.to_i
|
||||
|
||||
url_params = URI.encode_www_form(access_token: SiteSetting.chat_integration_matrix_access_token)
|
||||
url_params =
|
||||
URI.encode_www_form(access_token: SiteSetting.chat_integration_matrix_access_token)
|
||||
|
||||
url = "#{homeserver}/_matrix/client/r0/rooms/#{CGI::escape(room_id)}/send/#{event_type}/#{uid}"
|
||||
url =
|
||||
"#{homeserver}/_matrix/client/r0/rooms/#{CGI.escape(room_id)}/send/#{event_type}/#{uid}"
|
||||
|
||||
uri = URI([url, url_params].join('?'))
|
||||
uri = URI([url, url_params].join("?"))
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = true
|
||||
|
||||
req = Net::HTTP::Put.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Put.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
@@ -35,16 +37,29 @@ module DiscourseChatIntegration
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
message = {
|
||||
msgtype: SiteSetting.chat_integration_matrix_use_notice ? 'm.notice' : 'm.text',
|
||||
body: I18n.t('chat_integration.provider.matrix.text_message', user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title),
|
||||
format: 'org.matrix.custom.html',
|
||||
formatted_body: I18n.t('chat_integration.provider.matrix.formatted_message', user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt: post.excerpt(SiteSetting.chat_integration_matrix_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true))
|
||||
|
||||
msgtype: SiteSetting.chat_integration_matrix_use_notice ? "m.notice" : "m.text",
|
||||
body:
|
||||
I18n.t(
|
||||
"chat_integration.provider.matrix.text_message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
),
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body:
|
||||
I18n.t(
|
||||
"chat_integration.provider.matrix.formatted_message",
|
||||
user: display_name,
|
||||
post_url: post.full_url,
|
||||
title: post.topic.title,
|
||||
excerpt:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_matrix_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
message
|
||||
@@ -53,24 +68,26 @@ module DiscourseChatIntegration
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = generate_matrix_message(post)
|
||||
|
||||
response = send_message(channel.data['room_id'], message)
|
||||
response = send_message(channel.data["room_id"], message)
|
||||
|
||||
if !response.kind_of?(Net::HTTPSuccess)
|
||||
error_key = nil
|
||||
begin
|
||||
responseData = JSON.parse(response.body)
|
||||
if responseData['errcode'] == "M_UNKNOWN_TOKEN"
|
||||
error_key = 'chat_integration.provider.matrix.errors.unknown_token'
|
||||
elsif responseData['errcode'] == "M_UNKNOWN"
|
||||
error_key = 'chat_integration.provider.matrix.errors.unknown_room'
|
||||
if responseData["errcode"] == "M_UNKNOWN_TOKEN"
|
||||
error_key = "chat_integration.provider.matrix.errors.unknown_token"
|
||||
elsif responseData["errcode"] == "M_UNKNOWN"
|
||||
error_key = "chat_integration.provider.matrix.errors.unknown_room"
|
||||
end
|
||||
ensure
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, message: message, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
message: message,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+17
-16
@@ -15,22 +15,18 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||
def command
|
||||
text = process_command(params)
|
||||
|
||||
render json: {
|
||||
response_type: 'ephemeral',
|
||||
text: text
|
||||
}
|
||||
render json: { response_type: "ephemeral", text: text }
|
||||
end
|
||||
|
||||
def process_command(params)
|
||||
|
||||
tokens = params[:text].split(" ")
|
||||
|
||||
# channel name fix
|
||||
channel_id =
|
||||
case params[:channel_name]
|
||||
when 'directmessage'
|
||||
when "directmessage"
|
||||
"@#{params[:user_name]}"
|
||||
when 'privategroup'
|
||||
when "privategroup"
|
||||
params[:channel_id]
|
||||
else
|
||||
"##{params[:channel_name]}"
|
||||
@@ -38,21 +34,29 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||
|
||||
provider = DiscourseChatIntegration::Provider::MattermostProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.with_provider(provider).with_data_value('identifier', channel_id).first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider(provider)
|
||||
.with_data_value("identifier", channel_id)
|
||||
.first
|
||||
|
||||
# Create channel if doesn't exist
|
||||
channel ||= DiscourseChatIntegration::Channel.create!(provider: provider, data: { identifier: channel_id })
|
||||
channel ||=
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: {
|
||||
identifier: channel_id,
|
||||
},
|
||||
)
|
||||
|
||||
::DiscourseChatIntegration::Helper.process_command(channel, tokens)
|
||||
|
||||
end
|
||||
|
||||
def mattermost_token_valid?
|
||||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_mattermost_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token != params[:token]
|
||||
|
||||
SiteSetting.chat_integration_mattermost_incoming_webhook_token != params[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
@@ -63,8 +67,5 @@ module DiscourseChatIntegration::Provider::MattermostProvider
|
||||
isolate_namespace DiscourseChatIntegration::Provider::MattermostProvider
|
||||
end
|
||||
|
||||
MattermostEngine.routes.draw do
|
||||
post "command" => "mattermost_command#command"
|
||||
end
|
||||
|
||||
MattermostEngine.routes.draw { post "command" => "mattermost_command#command" }
|
||||
end
|
||||
|
||||
@@ -5,29 +5,30 @@ module DiscourseChatIntegration
|
||||
module MattermostProvider
|
||||
PROVIDER_NAME = "mattermost".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_mattermost_enabled
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||
|
||||
def self.send_via_webhook(message)
|
||||
|
||||
uri = URI(SiteSetting.chat_integration_mattermost_webhook_url)
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include? "Couldn't find the channel"
|
||||
error_key = 'chat_integration.provider.mattermost.errors.channel_not_found'
|
||||
error_key = "chat_integration.provider.mattermost.errors.channel_not_found"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.mattermost_message(post, channel)
|
||||
@@ -35,17 +36,26 @@ module DiscourseChatIntegration
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
icon_url =
|
||||
if SiteSetting.chat_integration_mattermost_icon_url.present?
|
||||
UrlHelper.absolute(SiteSetting.chat_integration_mattermost_icon_url)
|
||||
elsif (url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)).present?
|
||||
elsif (
|
||||
url = (SiteSetting.try(:site_logo_small_url) || SiteSetting.logo_small_url)
|
||||
).present?
|
||||
UrlHelper.absolute(url)
|
||||
end
|
||||
|
||||
@@ -53,7 +63,7 @@ module DiscourseChatIntegration
|
||||
channel: channel,
|
||||
username: SiteSetting.title || "Discourse",
|
||||
icon_url: icon_url,
|
||||
attachments: []
|
||||
attachments: [],
|
||||
}
|
||||
|
||||
summary = {
|
||||
@@ -61,8 +71,15 @@ module DiscourseChatIntegration
|
||||
author_name: display_name,
|
||||
author_icon: post.user.small_avatar_url,
|
||||
color: topic.category ? "##{topic.category.color}" : nil,
|
||||
text: post.excerpt(SiteSetting.chat_integration_mattermost_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_mattermost_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
title:
|
||||
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
title_link: post.full_url,
|
||||
}
|
||||
|
||||
@@ -71,12 +88,11 @@ module DiscourseChatIntegration
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
channel_id = channel.data["identifier"]
|
||||
message = mattermost_message(post, channel_id)
|
||||
|
||||
self.send_via_webhook(message)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,36 +5,45 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
||||
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_rocketchat_enabled
|
||||
|
||||
CHANNEL_PARAMETERS = [
|
||||
{ key: "identifier", regex: '^[@#]\S*$', unique: true }
|
||||
]
|
||||
CHANNEL_PARAMETERS = [{ key: "identifier", regex: '^[@#]\S*$', unique: true }]
|
||||
|
||||
def self.rocketchat_message(post, channel)
|
||||
display_name = ::DiscourseChatIntegration::Helper.formatted_display_name(post.user)
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
category = ""
|
||||
if topic.category&.uncategorized?
|
||||
category = "[#{I18n.t('uncategorized_category_name')}]"
|
||||
category = "[#{I18n.t("uncategorized_category_name")}]"
|
||||
elsif topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]" : "[#{topic.category.name}]"
|
||||
category =
|
||||
(
|
||||
if (topic.category.parent_category)
|
||||
"[#{topic.category.parent_category.name}/#{topic.category.name}]"
|
||||
else
|
||||
"[#{topic.category.name}]"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
message = {
|
||||
channel: channel,
|
||||
attachments: []
|
||||
}
|
||||
message = { channel: channel, attachments: [] }
|
||||
|
||||
summary = {
|
||||
fallback: "#{topic.title} - #{display_name}",
|
||||
author_name: display_name,
|
||||
author_icon: post.user.small_avatar_url,
|
||||
color: topic.category ? "##{topic.category.color}" : nil,
|
||||
text: post.excerpt(SiteSetting.chat_integration_rocketchat_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
text:
|
||||
post.excerpt(
|
||||
SiteSetting.chat_integration_rocketchat_excerpt_length,
|
||||
text_entities: true,
|
||||
strip_links: true,
|
||||
remap_emoji: true,
|
||||
),
|
||||
mrkdwn_in: ["text"],
|
||||
title: "#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(', ') : ''}",
|
||||
title_link: post.full_url
|
||||
title:
|
||||
"#{topic.title} #{category} #{topic.tags.present? ? topic.tags.map(&:name).join(", ") : ""}",
|
||||
title_link: post.full_url,
|
||||
}
|
||||
|
||||
message[:attachments].push(summary)
|
||||
@@ -46,25 +55,29 @@ module DiscourseChatIntegration::Provider::RocketchatProvider
|
||||
uri = URI(SiteSetting.chat_integration_rocketchat_webhook_url)
|
||||
|
||||
http = FinalDestination::HTTP.new(uri.host, uri.port)
|
||||
http.use_ssl = (uri.scheme == 'https')
|
||||
http.use_ssl = (uri.scheme == "https")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
if response.body.include?('invalid-channel')
|
||||
error_key = 'chat_integration.provider.rocketchat.errors.invalid_channel'
|
||||
if response.body.include?("invalid-channel")
|
||||
error_key = "chat_integration.provider.rocketchat.errors.invalid_channel"
|
||||
else
|
||||
error_key = nil
|
||||
end
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: { error_key: error_key, request: req.body, response_code: response.code, response_body: response.body }
|
||||
raise ::DiscourseChatIntegration::ProviderError.new info: {
|
||||
error_key: error_key,
|
||||
request: req.body,
|
||||
response_code: response.code,
|
||||
response_body: response.body,
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
channel_id = channel.data["identifier"]
|
||||
message = rocketchat_message(post, channel_id)
|
||||
|
||||
self.send_via_webhook(message)
|
||||
|
||||
@@ -11,7 +11,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
:preload_json,
|
||||
:verify_authenticity_token,
|
||||
:redirect_to_login_if_required,
|
||||
only: [:command, :interactive]
|
||||
only: %i[command interactive]
|
||||
|
||||
def command
|
||||
message = process_command(params)
|
||||
@@ -28,15 +28,14 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
private
|
||||
|
||||
def process_command(params)
|
||||
|
||||
tokens = params[:text].split(" ")
|
||||
|
||||
# channel name fix
|
||||
channel_id =
|
||||
case params[:channel_name]
|
||||
when 'directmessage'
|
||||
when "directmessage"
|
||||
"@#{params[:user_name]}"
|
||||
when 'privategroup'
|
||||
when "privategroup"
|
||||
params[:channel_id]
|
||||
else
|
||||
"##{params[:channel_name]}"
|
||||
@@ -44,17 +43,28 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
|
||||
provider = DiscourseChatIntegration::Provider::SlackProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChatIntegration::Channel.with_provider(provider)
|
||||
.with_data_value('identifier', channel_id)
|
||||
.first
|
||||
channel =
|
||||
DiscourseChatIntegration::Channel
|
||||
.with_provider(provider)
|
||||
.with_data_value("identifier", channel_id)
|
||||
.first
|
||||
|
||||
channel ||= DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: { identifier: channel_id }
|
||||
)
|
||||
channel ||=
|
||||
DiscourseChatIntegration::Channel.create!(
|
||||
provider: provider,
|
||||
data: {
|
||||
identifier: channel_id,
|
||||
},
|
||||
)
|
||||
|
||||
if tokens[0] == 'post'
|
||||
process_post_request(channel, tokens, params[:channel_id], channel_id, params[:response_url])
|
||||
if tokens[0] == "post"
|
||||
process_post_request(
|
||||
channel,
|
||||
tokens,
|
||||
params[:channel_id],
|
||||
channel_id,
|
||||
params[:response_url],
|
||||
)
|
||||
else
|
||||
{ text: ::DiscourseChatIntegration::Helper.process_command(channel, tokens) }
|
||||
end
|
||||
@@ -66,9 +76,10 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
end
|
||||
|
||||
Scheduler::Defer.later "Processing slack transcript request" do
|
||||
response = build_post_request_response(channel, tokens, slack_channel_id, channel_name, response_url)
|
||||
response =
|
||||
build_post_request_response(channel, tokens, slack_channel_id, channel_name, response_url)
|
||||
http = DiscourseChatIntegration::Provider::SlackProvider.slack_api_http
|
||||
req = Net::HTTP::Post.new(URI(response_url), 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(URI(response_url), "Content-Type" => "application/json")
|
||||
req.body = response.to_json
|
||||
http.request(req)
|
||||
end
|
||||
@@ -81,15 +92,16 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
first_message_ts = nil
|
||||
requested_thread_ts = nil
|
||||
|
||||
thread_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p[0-9]{16}\?thread_ts=([0-9]{10}.[0-9]{6})\S*$/
|
||||
thread_url_regex =
|
||||
/^https:\/\/\S+\.slack\.com\/archives\/\S+\/p[0-9]{16}\?thread_ts=([0-9]{10}.[0-9]{6})\S*$/
|
||||
slack_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p([0-9]{16})\/?$/
|
||||
|
||||
if tokens.size > 2 && tokens[1] == "thread" && match = slack_url_regex.match(tokens[2])
|
||||
requested_thread_ts = match.captures[0].insert(10, '.')
|
||||
requested_thread_ts = match.captures[0].insert(10, ".")
|
||||
elsif tokens.size > 1 && match = thread_url_regex.match(tokens[1])
|
||||
requested_thread_ts = match.captures[0]
|
||||
elsif tokens.size > 1 && match = slack_url_regex.match(tokens[1])
|
||||
first_message_ts = match.captures[0].insert(10, '.')
|
||||
first_message_ts = match.captures[0].insert(10, ".")
|
||||
elsif tokens.size > 1
|
||||
begin
|
||||
requested_messages = Integer(tokens[1], 10)
|
||||
@@ -100,12 +112,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
|
||||
error_key = "chat_integration.provider.slack.transcript.error"
|
||||
|
||||
return { text: I18n.t(error_key) } unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id, requested_thread_ts: requested_thread_ts)
|
||||
unless transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: channel_name,
|
||||
channel_id: slack_channel_id,
|
||||
requested_thread_ts: requested_thread_ts,
|
||||
)
|
||||
return { text: I18n.t(error_key) }
|
||||
end
|
||||
return { text: I18n.t("#{error_key}_users") } unless transcript.load_user_data
|
||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||
|
||||
if first_message_ts
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_first_message_by_ts(first_message_ts)
|
||||
unless transcript.set_first_message_by_ts(first_message_ts)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
elsif requested_messages
|
||||
transcript.set_first_message_by_index(-requested_messages)
|
||||
else
|
||||
@@ -123,22 +144,21 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
# Do nothing
|
||||
elsif json[:type] == "message_action" && json[:message][:thread_ts]
|
||||
# Context menu used on a threaded message
|
||||
transcript = SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: json[:message][:thread_ts]
|
||||
)
|
||||
transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: json[:message][:thread_ts],
|
||||
)
|
||||
|
||||
# Send a loading modal within 3 seconds:
|
||||
req = Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.open",
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
||||
)
|
||||
req.body = {
|
||||
"trigger_id": json[:trigger_id],
|
||||
"view": transcript.build_modal_ui
|
||||
}.to_json
|
||||
req =
|
||||
Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.open",
|
||||
"Content-Type" => "application/json",
|
||||
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||
)
|
||||
req.body = { trigger_id: json[:trigger_id], view: transcript.build_modal_ui }.to_json
|
||||
response = http.request(req)
|
||||
view_id = JSON.parse(response.body).dig("view", "id")
|
||||
|
||||
@@ -147,19 +167,17 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
error_view = generate_error_view("history") unless transcript.load_chat_history
|
||||
|
||||
# Then update the modal with the transcript link:
|
||||
req = Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.update",
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => "Bearer #{SiteSetting.chat_integration_slack_access_token}"
|
||||
)
|
||||
req.body = {
|
||||
"view_id": view_id,
|
||||
"view": error_view || transcript.build_modal_ui
|
||||
}.to_json
|
||||
req =
|
||||
Net::HTTP::Post.new(
|
||||
"https://slack.com/api/views.update",
|
||||
"Content-Type" => "application/json",
|
||||
"Authorization" => "Bearer #{SiteSetting.chat_integration_slack_access_token}",
|
||||
)
|
||||
req.body = { view_id: view_id, view: error_view || transcript.build_modal_ui }.to_json
|
||||
response = http.request(req)
|
||||
else
|
||||
# Button clicked in one of our interactive messages
|
||||
req = Net::HTTP::Post.new(URI(json[:response_url]), 'Content-Type' => 'application/json')
|
||||
req = Net::HTTP::Post.new(URI(json[:response_url]), "Content-Type" => "application/json")
|
||||
req.body = build_interactive_response(json).to_json
|
||||
response = http.request(req)
|
||||
end
|
||||
@@ -177,26 +195,33 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
constant_val = json[:callback_id]
|
||||
changed_val = json[:actions][0][:selected_options][0][:value]
|
||||
|
||||
first_message = (action_name == 'first_message') ? changed_val : constant_val
|
||||
last_message = (action_name == 'first_message') ? constant_val : changed_val
|
||||
first_message = (action_name == "first_message") ? changed_val : constant_val
|
||||
last_message = (action_name == "first_message") ? constant_val : changed_val
|
||||
end
|
||||
|
||||
error_key = "chat_integration.provider.slack.transcript.error"
|
||||
|
||||
return { text: I18n.t(error_key) } unless transcript = SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: requested_thread
|
||||
)
|
||||
unless transcript =
|
||||
SlackTranscript.new(
|
||||
channel_name: "##{json[:channel][:name]}",
|
||||
channel_id: json[:channel][:id],
|
||||
requested_thread_ts: requested_thread,
|
||||
)
|
||||
return { text: I18n.t(error_key) }
|
||||
end
|
||||
return { text: I18n.t("#{error_key}_users") } unless transcript.load_user_data
|
||||
return { text: I18n.t("#{error_key}_history") } unless transcript.load_chat_history
|
||||
|
||||
if first_message
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_first_message_by_ts(first_message)
|
||||
unless transcript.set_first_message_by_ts(first_message)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
end
|
||||
|
||||
if last_message
|
||||
return { text: I18n.t("#{error_key}_ts") } unless transcript.set_last_message_by_ts(last_message)
|
||||
unless transcript.set_last_message_by_ts(last_message)
|
||||
return { text: I18n.t("#{error_key}_ts") }
|
||||
end
|
||||
end
|
||||
|
||||
transcript.build_slack_ui
|
||||
@@ -210,17 +235,11 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
type: "modal",
|
||||
title: {
|
||||
type: "plain_text",
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title")
|
||||
text: I18n.t("chat_integration.provider.slack.transcript.modal_title"),
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
type: "section",
|
||||
text: {
|
||||
type: "mrkdwn",
|
||||
text: ":warning: *#{I18n.t(error_key)}*"
|
||||
}
|
||||
}
|
||||
]
|
||||
{ type: "section", text: { type: "mrkdwn", text: ":warning: *#{I18n.t(error_key)}*" } },
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
@@ -228,8 +247,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != params[:token]
|
||||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != params[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
@@ -240,8 +258,7 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
json = JSON.parse(params[:payload], symbolize_names: true)
|
||||
|
||||
if SiteSetting.chat_integration_slack_incoming_webhook_token.blank? ||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != json[:token]
|
||||
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token != json[:token]
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
@@ -256,5 +273,4 @@ module DiscourseChatIntegration::Provider::SlackProvider
|
||||
post "command" => "slack_command#command"
|
||||
post "interactive" => "slack_command#interactive"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user