Merge pull request #3 from discourse/telegram-provider

Add Telegram provider
This commit is contained in:
David Taylor 2017-07-19 21:36:40 +01:00 committed by GitHub
commit d5a4eabb85
14 changed files with 573 additions and 259 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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