mirror of
https://github.com/discourse/discourse-chat-integration.git
synced 2025-02-16 16:34:50 +00:00
Merge pull request #3 from discourse/telegram-provider
Add Telegram provider
This commit is contained in:
commit
d5a4eabb85
@ -36,6 +36,7 @@ class DiscourseChat::ChatController < ApplicationController
|
||||
rescue Discourse::InvalidParameters, ActiveRecord::RecordNotFound => e
|
||||
render json: {errors: [e.message]}, status: 422
|
||||
rescue DiscourseChat::ProviderError => e
|
||||
Rails.logger.error("Test provider failed #{e.info}")
|
||||
if e.info.key?(:error_key) and !e.info[:error_key].nil?
|
||||
render json: {error_key: e.info[:error_key]}, status: 422
|
||||
else
|
||||
|
@ -1,6 +1,77 @@
|
||||
module DiscourseChat
|
||||
module Helper
|
||||
|
||||
def self.process_command(channel, tokens)
|
||||
guardian = DiscourseChat::Manager.guardian
|
||||
|
||||
provider = channel.provider
|
||||
|
||||
cmd = tokens.shift if tokens.size >= 1
|
||||
|
||||
error_text = I18n.t("chat_integration.provider.#{provider}.parse_error")
|
||||
|
||||
case cmd
|
||||
when "watch", "follow", "mute"
|
||||
return error_text if tokens.empty?
|
||||
# If the first token in the command is a tag, this rule applies to all categories
|
||||
category_name = tokens[0].start_with?('tag:') ? nil : tokens.shift
|
||||
|
||||
if category_name
|
||||
category = Category.find_by(slug: category_name)
|
||||
unless category
|
||||
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(', ')
|
||||
return I18n.t("chat_integration.provider.#{provider}.not_found.category", name: category_name, list:cat_list)
|
||||
end
|
||||
else
|
||||
category = nil # All categories
|
||||
end
|
||||
|
||||
tags = []
|
||||
# Every remaining token must be a tag. If not, abort and send help text
|
||||
while tokens.size > 0
|
||||
token = tokens.shift
|
||||
if token.start_with?('tag:')
|
||||
tag_name = token.sub(/^tag:/, '')
|
||||
else
|
||||
return error_text # Abort and send help text
|
||||
end
|
||||
|
||||
tag = Tag.find_by(name: tag_name)
|
||||
unless tag # If tag doesn't exist, abort
|
||||
return I18n.t("chat_integration.provider.#{provider}.not_found.tag", name: tag_name)
|
||||
end
|
||||
tags.push(tag.name)
|
||||
end
|
||||
|
||||
category_id = category.nil? ? nil : category.id
|
||||
case DiscourseChat::Helper.smart_create_rule(channel:channel, filter:cmd, category_id: category_id, tags:tags)
|
||||
when :created
|
||||
return I18n.t("chat_integration.provider.#{provider}.create.created")
|
||||
when :updated
|
||||
return I18n.t("chat_integration.provider.#{provider}.create.updated")
|
||||
else
|
||||
return I18n.t("chat_integration.provider.#{provider}.create.error")
|
||||
end
|
||||
when "remove"
|
||||
return error_text unless tokens.size == 1
|
||||
|
||||
rule_number = tokens[0].to_i
|
||||
return error_text unless rule_number.to_s == tokens[0] # Check we were given a number
|
||||
|
||||
if DiscourseChat::Helper.delete_by_index(channel, rule_number)
|
||||
return I18n.t("chat_integration.provider.#{provider}.delete.success")
|
||||
else
|
||||
return I18n.t("chat_integration.provider.#{provider}.delete.error")
|
||||
end
|
||||
when "status"
|
||||
return DiscourseChat::Helper.status_for_channel(channel)
|
||||
when "help"
|
||||
return I18n.t("chat_integration.provider.#{provider}.help")
|
||||
else
|
||||
return error_text
|
||||
end
|
||||
end
|
||||
|
||||
# Produce a string with a list of all rules associated with a channel
|
||||
def self.status_for_channel(channel)
|
||||
rules = channel.rules.order_by_precedence
|
||||
|
@ -50,6 +50,6 @@ class DiscourseChat::Channel < DiscourseChat::PluginModel
|
||||
|
||||
scope :with_provider, ->(provider) { where("value::json->>'provider'=?", provider)}
|
||||
|
||||
scope :with_data_value, ->(key, value) { where("(value::json->>'data')::json->>?=?", key, value)}
|
||||
scope :with_data_value, ->(key, value) { where("(value::json->>'data')::json->>?=?", key.to_s, value.to_s)}
|
||||
|
||||
end
|
@ -63,4 +63,13 @@ en:
|
||||
#######################################
|
||||
telegram:
|
||||
title: "Telegram"
|
||||
channel_instructions: "Enter a chat_id. You will need to add the bot to the chat first."
|
||||
param:
|
||||
name:
|
||||
title: "Name"
|
||||
help: "A name to describe the channel. It is not used for the connection for telegram."
|
||||
chat_id:
|
||||
title: Chat ID
|
||||
help: A number given to you by the bot, or a broadcast channel identifier in the form @channelname
|
||||
errors:
|
||||
channel_not_found: "The specified channel does not exist on Telegram"
|
||||
forbidden: "The bot does not have permission to post to this channel"
|
@ -10,7 +10,7 @@ en:
|
||||
#######################################
|
||||
chat_integration_slack_enabled: 'Enable the slack chat-integration provider'
|
||||
chat_integration_slack_outbound_webhook_url: 'URL for outbound slack requests'
|
||||
chat_integration_slack_excerpt_length: 'Post excerpt length'
|
||||
chat_integration_slack_excerpt_length: 'Slack post excerpt length'
|
||||
chat_integration_slack_icon_url: 'Icon to post to slack with (defaults to forum logo)'
|
||||
chat_integration_slack_access_token: 'Token if you are using the Web API instead of webhooks'
|
||||
|
||||
@ -20,7 +20,10 @@ en:
|
||||
#######################################
|
||||
######### TELEGRAM SETTINGS ###########
|
||||
#######################################
|
||||
chat_integration_telegram_enabled: "Enable the telegram chat-integration provider"
|
||||
chat_integration_telegram_enabled: "Enable the Telegram chat-integration provider"
|
||||
chat_integration_telegram_access_token: "Your bot's access token from the Telegram botfather"
|
||||
chat_integration_telegram_excerpt_length: "Telegram post excerpt length"
|
||||
chat_integration_telegram_enable_slash_commands: "Allow telegram subscriptions to be managed using 'slash commands'"
|
||||
|
||||
chat_integration:
|
||||
|
||||
@ -47,7 +50,6 @@ en:
|
||||
not_found:
|
||||
tag: "The *%{name}* tag cannot be found."
|
||||
category: "The *%{name}* category cannot be found. Available categories: *%{list}*"
|
||||
|
||||
help: |
|
||||
*New rule:* `/discourse [watch|follow|mute] [category] [tag:name]`
|
||||
(you must specify a rule type and at least one category or tag)
|
||||
@ -60,4 +62,44 @@ en:
|
||||
|
||||
*List rules:* `/discourse status`
|
||||
|
||||
*Help:* `/discourse help`
|
||||
*Help:* `/discourse help`
|
||||
|
||||
telegram:
|
||||
unknown_chat: "This chat isn't setup on %{site_title}. Ask an administrator to add a channel with 'Chat ID' %{chat_id}."
|
||||
known_chat: "This chat is setup on %{site_title}. Configure it in the admin panel. (Chat ID: %{chat_id})"
|
||||
message: |-
|
||||
<b>%{user}</b> posted in <a href="%{post_url}">%{title}</a>
|
||||
|
||||
<pre>%{post_excerpt}</pre>
|
||||
|
||||
status:
|
||||
header: |
|
||||
<b>Rules for this channel</b>
|
||||
(if multiple rules match a post, the topmost rule is executed)
|
||||
no_rules: "There are no rules set up for this channel. Run <code>/help</code> for instructions."
|
||||
rule_string: "<b>%{index})</b> <b>%{filter}</b> posts in <b>%{category}</b>"
|
||||
rule_string_tags_suffix: " with tags: <b>%{tags}</b>"
|
||||
parse_error: "Sorry, I didn't understand that. Run <code>/help</code> for instructions."
|
||||
create:
|
||||
created: "Rule created successfully"
|
||||
updated: "Rule updated successfully"
|
||||
error: "Sorry, an error occured while creating that rule."
|
||||
delete:
|
||||
success: "Rule deleted successfully"
|
||||
error: "Sorry, an error occured while deleting that rule. Run <code>/status</code> for a list of rules."
|
||||
not_found:
|
||||
tag: "The <b>%{name}</b> tag cannot be found."
|
||||
category: "The <b>%{name}</b> category cannot be found. Available categories: <b>%{list}</b>"
|
||||
help: |
|
||||
<b>New rule:</b> <code>/[watch|follow|mute] [category] [tag:name]</code>
|
||||
(you must specify a rule type and at least one category or tag)
|
||||
- <b>watch</b> – notify this channel for new topics and new replies
|
||||
- <b>follow</b> – notify this channel for new topics
|
||||
- <b>mute</b> – block notifications to this channel
|
||||
|
||||
<b>Remove rule:</b> <code>/remove [rule number]</code>
|
||||
(<code>[rule number]</code> can be found by running <code>/status</code>)
|
||||
|
||||
<b>List rules:</b> <code>/status</code>
|
||||
|
||||
<b>Help:</b> <code>/help</code>
|
@ -30,3 +30,11 @@ plugins:
|
||||
#######################################
|
||||
chat_integration_telegram_enabled:
|
||||
default: false
|
||||
chat_integration_telegram_access_token:
|
||||
default: ''
|
||||
chat_integration_telegram_excerpt_length:
|
||||
default: 400
|
||||
chat_integration_telegram_enable_slash_commands:
|
||||
default: true
|
||||
chat_integration_telegram_secret:
|
||||
hidden: true
|
@ -17,7 +17,6 @@ module DiscourseChat::Provider::SlackProvider
|
||||
end
|
||||
|
||||
def process_command(params)
|
||||
guardian = DiscourseChat::Manager.guardian
|
||||
|
||||
tokens = params[:text].split(" ")
|
||||
|
||||
@ -39,70 +38,7 @@ module DiscourseChat::Provider::SlackProvider
|
||||
# Create channel if doesn't exist
|
||||
channel ||= DiscourseChat::Channel.create!(provider:provider, data:{identifier: channel_id})
|
||||
|
||||
cmd = tokens.shift if tokens.size >= 1
|
||||
|
||||
error_text = I18n.t("chat_integration.provider.slack.parse_error")
|
||||
|
||||
case cmd
|
||||
when "watch", "follow", "mute"
|
||||
return error_text if tokens.empty?
|
||||
# If the first token in the command is a tag, this rule applies to all categories
|
||||
category_name = tokens[0].start_with?('tag:') ? nil : tokens.shift
|
||||
|
||||
if category_name
|
||||
category = Category.find_by(slug: category_name)
|
||||
unless category
|
||||
cat_list = (CategoryList.new(guardian).categories.map(&:slug)).join(', ')
|
||||
return I18n.t("chat_integration.provider.slack.not_found.category", name: category_name, list:cat_list)
|
||||
end
|
||||
else
|
||||
category = nil # All categories
|
||||
end
|
||||
|
||||
tags = []
|
||||
# Every remaining token must be a tag. If not, abort and send help text
|
||||
while tokens.size > 0
|
||||
token = tokens.shift
|
||||
if token.start_with?('tag:')
|
||||
tag_name = token.sub(/^tag:/, '')
|
||||
else
|
||||
return error_text # Abort and send help text
|
||||
end
|
||||
|
||||
tag = Tag.find_by(name: tag_name)
|
||||
unless tag # If tag doesn't exist, abort
|
||||
return I18n.t("chat_integration.provider.slack.not_found.tag", name: tag_name)
|
||||
end
|
||||
tags.push(tag.name)
|
||||
end
|
||||
|
||||
category_id = category.nil? ? nil : category.id
|
||||
case DiscourseChat::Helper.smart_create_rule(channel:channel, filter:cmd, category_id: category_id, tags:tags)
|
||||
when :created
|
||||
return I18n.t("chat_integration.provider.slack.create.created")
|
||||
when :updated
|
||||
return I18n.t("chat_integration.provider.slack.create.updated")
|
||||
else
|
||||
return I18n.t("chat_integration.provider.slack.create.error")
|
||||
end
|
||||
when "remove"
|
||||
return error_text unless tokens.size == 1
|
||||
|
||||
rule_number = tokens[0].to_i
|
||||
return error_text unless rule_number.to_s == tokens[0] # Check we were given a number
|
||||
|
||||
if DiscourseChat::Helper.delete_by_index(channel, rule_number)
|
||||
return I18n.t("chat_integration.provider.slack.delete.success")
|
||||
else
|
||||
return I18n.t("chat_integration.provider.slack.delete.error")
|
||||
end
|
||||
when "status"
|
||||
return DiscourseChat::Helper.status_for_channel(channel)
|
||||
when "help"
|
||||
return I18n.t("chat_integration.provider.slack.help")
|
||||
else
|
||||
return error_text
|
||||
end
|
||||
return ::DiscourseChat::Helper.process_command(channel, tokens)
|
||||
|
||||
end
|
||||
|
||||
|
@ -2,9 +2,79 @@ module DiscourseChat::Provider::TelegramProvider
|
||||
class TelegramCommandController < DiscourseChat::Provider::HookController
|
||||
requires_provider ::DiscourseChat::Provider::TelegramProvider::PROVIDER_NAME
|
||||
|
||||
def say_hello
|
||||
before_filter :telegram_token_valid?, only: :command
|
||||
|
||||
render json: {hello: "from telegram"}
|
||||
skip_before_filter :check_xhr,
|
||||
:preload_json,
|
||||
:verify_authenticity_token,
|
||||
:redirect_to_login_if_required,
|
||||
only: :command
|
||||
|
||||
def command
|
||||
|
||||
# If it's a new message (telegram also sends hooks for other reasons that we don't care about)
|
||||
if params.key?('message')
|
||||
chat_id = params['message']['chat']['id']
|
||||
|
||||
message_text = process_command(params['message'])
|
||||
|
||||
message = {
|
||||
chat_id: chat_id,
|
||||
text: message_text,
|
||||
parse_mode: "html",
|
||||
disable_web_page_preview: true,
|
||||
}
|
||||
|
||||
DiscourseChat::Provider::TelegramProvider.sendMessage(message)
|
||||
|
||||
end
|
||||
|
||||
# Always give telegram a success message, otherwise we'll stop receiving webhooks
|
||||
data = {
|
||||
success: true
|
||||
}
|
||||
render json: data
|
||||
end
|
||||
|
||||
def process_command(message)
|
||||
chat_id = params['message']['chat']['id']
|
||||
|
||||
provider = DiscourseChat::Provider::TelegramProvider::PROVIDER_NAME
|
||||
|
||||
channel = DiscourseChat::Channel.with_provider(provider).with_data_value('chat_id',chat_id).first
|
||||
|
||||
if channel.nil?
|
||||
return I18n.t(
|
||||
"chat_integration.provider.telegram.unknown_chat",
|
||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
)
|
||||
end
|
||||
|
||||
# If slash commands disabled, send a generic message
|
||||
if !SiteSetting.chat_integration_telegram_enable_slash_commands
|
||||
return I18n.t(
|
||||
"chat_integration.provider.telegram.known_chat",
|
||||
site_title: CGI::escapeHTML(SiteSetting.title),
|
||||
chat_id: chat_id,
|
||||
)
|
||||
end
|
||||
|
||||
tokens = message['text'].split(" ")
|
||||
|
||||
tokens[0][0] = '' # Remove the slash from the first token
|
||||
|
||||
return ::DiscourseChat::Helper.process_command(channel, tokens)
|
||||
end
|
||||
|
||||
def telegram_token_valid?
|
||||
params.require(:token)
|
||||
|
||||
if SiteSetting.chat_integration_telegram_secret.blank? ||
|
||||
SiteSetting.chat_integration_telegram_secret != params[:token]
|
||||
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -14,6 +84,6 @@ module DiscourseChat::Provider::TelegramProvider
|
||||
end
|
||||
|
||||
TelegramEngine.routes.draw do
|
||||
get "command" => "telegram_command#say_hello"
|
||||
post "command/:token" => "telegram_command#command"
|
||||
end
|
||||
end
|
13
lib/discourse_chat/provider/telegram/telegram_initializer.rb
Normal file
13
lib/discourse_chat/provider/telegram/telegram_initializer.rb
Normal file
@ -0,0 +1,13 @@
|
||||
DiscourseEvent.on(:site_setting_saved) do |sitesetting|
|
||||
isEnabledSetting = sitesetting.name == 'chat_integration_telegram_enabled'
|
||||
isAccessToken = sitesetting.name == 'chat_integration_telegram_access_token'
|
||||
|
||||
if (isEnabledSetting or isAccessToken)
|
||||
enabled = isEnabledSetting ? sitesetting.value == 't' : SiteSetting.chat_integration_telegram_enabled
|
||||
if enabled
|
||||
Scheduler::Defer.later("Setup Telegram Webhook") do
|
||||
DiscourseChat::Provider::TelegramProvider.setup_webhook()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -3,10 +3,107 @@ module DiscourseChat
|
||||
module TelegramProvider
|
||||
PROVIDER_NAME = "telegram".freeze
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_telegram_enabled
|
||||
CHANNEL_PARAMETERS = []
|
||||
CHANNEL_PARAMETERS = [
|
||||
{key: "name", regex: '^\S+'},
|
||||
{key: "chat_id", regex: '^(-?[0-9]+|@\S+)$'}
|
||||
]
|
||||
|
||||
def self.setup_webhook
|
||||
newSecret = SecureRandom.hex
|
||||
SiteSetting.chat_integration_telegram_secret = newSecret
|
||||
|
||||
message = {
|
||||
url: Discourse.base_url+'/chat-integration/telegram/command/'+newSecret,
|
||||
}
|
||||
|
||||
response = self.do_api_request('setWebhook', message)
|
||||
|
||||
if not response['ok'] == true
|
||||
# If setting up webhook failed, disable provider
|
||||
SiteSetting.chat_integration_telegram_enabled = false
|
||||
Rails.logger.error("Failed to setup telegram webhook. Message data= "+message.to_json+ " response="+response.to_json)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.sendMessage(message)
|
||||
return self.do_api_request('sendMessage', message)
|
||||
end
|
||||
|
||||
def self.do_api_request(methodName, message)
|
||||
http = Net::HTTP.new("api.telegram.org", 443)
|
||||
http.use_ssl = true
|
||||
|
||||
access_token = SiteSetting.chat_integration_telegram_access_token
|
||||
|
||||
uri = URI("https://api.telegram.org/bot#{access_token}/#{methodName}")
|
||||
|
||||
req = Net::HTTP::Post.new(uri, 'Content-Type' =>'application/json')
|
||||
req.body = message.to_json
|
||||
response = http.request(req)
|
||||
|
||||
responseData = JSON.parse(response.body)
|
||||
|
||||
return responseData
|
||||
end
|
||||
|
||||
def self.message_text(post)
|
||||
display_name = "@#{post.user.username}"
|
||||
full_name = post.user.name || ""
|
||||
|
||||
if !(full_name.strip.empty?) && (full_name.strip.gsub(' ', '_').casecmp(post.user.username) != 0) && (full_name.strip.gsub(' ', '').casecmp(post.user.username) != 0)
|
||||
display_name = "#{full_name} @#{post.user.username}"
|
||||
end
|
||||
|
||||
topic = post.topic
|
||||
|
||||
category = ''
|
||||
if topic.category
|
||||
category = (topic.category.parent_category) ? "[#{topic.category.parent_category.name}/#{topic.category.name}]": "[#{topic.category.name}]"
|
||||
end
|
||||
|
||||
tags = ''
|
||||
if topic.tags.present?
|
||||
tags = topic.tags.map(&:name).join(', ')
|
||||
end
|
||||
|
||||
return I18n.t(
|
||||
"chat_integration.provider.telegram.message",
|
||||
user: display_name,
|
||||
post_url: "https://meta.discourse.org",
|
||||
title: CGI::escapeHTML(topic.title),
|
||||
post_excerpt: post.excerpt(SiteSetting.chat_integration_telegram_excerpt_length, text_entities: true, strip_links: true, remap_emoji: true),
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
chat_id = channel.data['chat_id']
|
||||
|
||||
message = {
|
||||
chat_id: chat_id,
|
||||
text: message_text(post),
|
||||
parse_mode: "html",
|
||||
disable_web_page_preview: true,
|
||||
}
|
||||
|
||||
response = sendMessage(message)
|
||||
|
||||
if not response['ok'] == true
|
||||
error_key = nil
|
||||
if response['description'].include? 'chat not found'
|
||||
error_key = 'chat_integration.provider.telegram.channel_not_found'
|
||||
elsif response['description'].include? 'Forbidden'
|
||||
error_key = 'chat_integration.provider.telegram.forbidden'
|
||||
end
|
||||
raise ::DiscourseChat::ProviderError.new info: {error_key: error_key, message: message, response_body:response}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "telegram_command_controller.rb"
|
||||
require_relative "telegram_command_controller.rb"
|
||||
require_relative "telegram_initializer.rb"
|
@ -14,6 +14,129 @@ RSpec.describe DiscourseChat::Manager do
|
||||
let(:tag2){Fabricate(:tag)}
|
||||
let(:tag3){Fabricate(:tag)}
|
||||
|
||||
describe '.process_command' do
|
||||
|
||||
describe 'add new rule' do
|
||||
# Not testing how filters are merged here, that's done in .smart_create_rule
|
||||
# We just want to make sure the commands are being interpretted correctly
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['watch',category.slug])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChat::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 eq(nil)
|
||||
end
|
||||
|
||||
it 'should work with all three filter types' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['watch',category.slug])
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('watch')
|
||||
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['follow',category.slug])
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('follow')
|
||||
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['mute',category.slug])
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('mute')
|
||||
end
|
||||
|
||||
it 'errors on incorrect categories' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['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 do
|
||||
SiteSetting.tagging_enabled = true
|
||||
end
|
||||
|
||||
it 'should add a new tag rule correctly' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['watch',"tag:#{tag1.name}"])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.create.created"))
|
||||
|
||||
rule = DiscourseChat::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 = DiscourseChat::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 = DiscourseChat::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 = DiscourseChat::Helper.process_command(chan1, ['watch',category.slug, "tag:blah"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.not_found.tag", name:"blah"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remove rule' do
|
||||
it 'removes the rule' do
|
||||
rule1 = DiscourseChat::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag1.name, tag2.name]
|
||||
)
|
||||
|
||||
expect(DiscourseChat::Rule.all.size).to eq(1)
|
||||
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['remove','1'])
|
||||
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.success"))
|
||||
|
||||
expect(DiscourseChat::Rule.all.size).to eq(0)
|
||||
end
|
||||
|
||||
it 'errors correctly' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['remove','1'])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.delete.error"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'help command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ["help"])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.help"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['status'])
|
||||
expect(response).to eq(DiscourseChat::Helper.status_for_channel(chan1))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'unknown command' do
|
||||
it 'should return the right response' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['somerandomtext'])
|
||||
expect(response).to eq(I18n.t("chat_integration.provider.dummy.parse_error"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.status_for_channel' do
|
||||
|
||||
context 'with no rules' do
|
||||
|
@ -62,14 +62,15 @@ describe 'Slack Command Controller', type: :request do
|
||||
describe 'when token is valid' do
|
||||
let(:token) { "Secret Sauce" }
|
||||
|
||||
# No need to test every single command here, that's tested
|
||||
# by helper_spec upstream
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_incoming_webhook_token = token
|
||||
end
|
||||
|
||||
describe 'add new rule' do
|
||||
# Not testing how filters are merged here, that's done upstream in helper_spec
|
||||
# We just want to make sure the slash commands are being interpretted correctly
|
||||
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch #{category.slug}",
|
||||
@ -87,100 +88,6 @@ describe 'Slack Command Controller', type: :request do
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
it 'should work with all three filter types' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch #{category.slug}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('watch')
|
||||
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "follow #{category.slug}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('follow')
|
||||
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "mute #{category.slug}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('mute')
|
||||
end
|
||||
|
||||
it 'errors on incorrect categories' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch blah",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
json = JSON.parse(response.body)
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.not_found.category", name:'blah', list:'uncategorized'))
|
||||
end
|
||||
|
||||
context 'with tags enabled' do
|
||||
before do
|
||||
SiteSetting.tagging_enabled = true
|
||||
end
|
||||
|
||||
it 'should add a new tag rule correctly' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch tag:#{tag.name}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.create.created"))
|
||||
|
||||
rule = DiscourseChat::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([tag.name])
|
||||
end
|
||||
|
||||
it 'should work with a category and multiple tags' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch #{category.slug} tag:#{tag.name} tag:#{tag2.name}",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.create.created"))
|
||||
|
||||
rule = DiscourseChat::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(tag.name, tag2.name)
|
||||
end
|
||||
|
||||
it 'errors on incorrect tags' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "watch tag:blah",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.not_found.tag", name:"blah"))
|
||||
end
|
||||
end
|
||||
|
||||
context 'from an unknown channel' do
|
||||
it 'creates the channel' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
@ -202,92 +109,6 @@ describe 'Slack Command Controller', type: :request do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'remove rule' do
|
||||
it 'removes the rule' do
|
||||
rule1 = DiscourseChat::Rule.create(channel: chan1,
|
||||
filter: 'watch',
|
||||
category_id: category.id,
|
||||
tags: [tag.name, tag2.name]
|
||||
)
|
||||
|
||||
expect(DiscourseChat::Rule.all.size).to eq(1)
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "remove 1",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.delete.success"))
|
||||
|
||||
expect(DiscourseChat::Rule.all.size).to eq(0)
|
||||
end
|
||||
|
||||
it 'errors correctly' do
|
||||
post "/chat-integration/slack/command.json",
|
||||
text: "remove 1",
|
||||
channel_name: 'welcome',
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.delete.error"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'help command' do
|
||||
it 'should return the right response' do
|
||||
post '/chat-integration/slack/command.json', text: "help", channel_name: "welcome", token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.help"))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status command' do
|
||||
# No need to test this with different combinations of rules
|
||||
# That's done upstream in helper_spec
|
||||
|
||||
it 'should return the right response' do
|
||||
post '/chat-integration/slack/command.json',
|
||||
text: "status",
|
||||
channel_name: "welcome",
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(DiscourseChat::Helper.status_for_channel(chan1))
|
||||
end
|
||||
end
|
||||
|
||||
describe 'unknown command' do
|
||||
# No need to test this with different combinations of rules
|
||||
# That's done upstream in helper_spec
|
||||
|
||||
it 'should return the right response' do
|
||||
post '/chat-integration/slack/command.json',
|
||||
text: "somerandomtext",
|
||||
channel_name: "welcome",
|
||||
token: token
|
||||
|
||||
expect(response).to be_success
|
||||
|
||||
json = JSON.parse(response.body)
|
||||
|
||||
expect(json["text"]).to eq(I18n.t("chat_integration.provider.slack.parse_error"))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,93 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'Telegram Command Controller', type: :request do
|
||||
let(:category) { Fabricate(:category) }
|
||||
let!(:chan1){DiscourseChat::Channel.create!(provider:'telegram', data:{name: 'Amazing Channel', chat_id:'123'})}
|
||||
|
||||
describe 'with plugin disabled' do
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/telegram/command/abcd.json'
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with plugin enabled and provider disabled' do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_telegram_enabled = false
|
||||
end
|
||||
|
||||
it 'should return a 404' do
|
||||
post '/chat-integration/telegram/command/abcd.json'
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'slash commands endpoint' do
|
||||
before do
|
||||
SiteSetting.chat_integration_enabled = true
|
||||
SiteSetting.chat_integration_telegram_secret = "shhh"
|
||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||
SiteSetting.chat_integration_telegram_enabled = true
|
||||
end
|
||||
|
||||
let!(:stub){stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":true}")}
|
||||
|
||||
describe 'when forum is private' do
|
||||
it 'should not redirect to login page' do
|
||||
SiteSetting.login_required = true
|
||||
post '/chat-integration/telegram/command/shhh.json', message: {chat: {id:123}, text: '/help' }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the token is invalid' do
|
||||
it 'should raise the right error' do
|
||||
post '/chat-integration/telegram/command/blah.json', message: {chat: {id:123}, text: '/help' }
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token has not been set' do
|
||||
it 'should raise the right error' do
|
||||
SiteSetting.chat_integration_telegram_access_token = ""
|
||||
post '/chat-integration/telegram/command/blah.json', message: {chat: {id:123}, text: '/help' }
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when token is valid' do
|
||||
let(:token) { "TOKEN" }
|
||||
|
||||
before do
|
||||
SiteSetting.chat_integration_telegram_enable_slash_commands = true
|
||||
end
|
||||
|
||||
describe 'add new rule' do
|
||||
|
||||
it 'should add a new rule correctly' do
|
||||
post '/chat-integration/telegram/command/shhh.json', message: {chat: {id:123}, text: "/watch #{category.slug}" }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(stub).to have_been_requested.once
|
||||
|
||||
rule = DiscourseChat::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 eq(nil)
|
||||
end
|
||||
|
||||
context 'from an unknown channel' do
|
||||
it 'does nothing' do
|
||||
post '/chat-integration/telegram/command/shhh.json', message: {chat: {id:456}, text: "/watch #{category.slug}" }
|
||||
expect(DiscourseChat::Rule.all.size).to eq(0)
|
||||
expect(DiscourseChat::Channel.all.size).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,30 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe DiscourseChat::Provider::TelegramProvider do
|
||||
let(:post) { Fabricate(:post) }
|
||||
|
||||
describe '.trigger_notifications' do
|
||||
before do
|
||||
SiteSetting.chat_integration_telegram_access_token = "TOKEN"
|
||||
SiteSetting.chat_integration_telegram_secret = 'shhh'
|
||||
SiteSetting.chat_integration_telegram_enabled = true
|
||||
end
|
||||
|
||||
let(:chan1){DiscourseChat::Channel.create!(provider:'telegram', data:{name: "Awesome Channel", chat_id: '123'})}
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":true}")
|
||||
described_class.trigger_notification(post, chan1)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, 'https://api.telegram.org/botTOKEN/sendMessage').to_return(body: "{\"ok\":false, \"description\":\"chat not found\"}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect{described_class.trigger_notification(post, chan1)}.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user