From 8ca4797797c21510b9c645fd2c6b2c7bc3e35d2f Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 24 Jul 2017 16:43:37 +0100 Subject: [PATCH] Add transcript posting support to slack --- config/locales/server.en.yml | 6 ++ .../slack/slack_command_controller.rb | 80 +++++++++++++++++++ spec/helpers/helper_spec.rb | 2 + .../slack/slack_command_controller_spec.rb | 50 ++++++++++++ 4 files changed, 138 insertions(+) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 59b3a09..357593f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -73,7 +73,13 @@ en: *List rules:* `/discourse status` + *Post transcript:* `/discourse post [n]` + Create a draft topic on discourse containing the last `n` posts in this channel + *Help:* `/discourse help` + transcript_error: "Something went wrong when building the transcript, sorry!" + post_to_discourse: "Click here to post on Discourse" + api_required: "Sorry, this integration isn't setup to support posting transcripts." ####################################### ########## TELEGRAM STRINGS ########### diff --git a/lib/discourse_chat/provider/slack/slack_command_controller.rb b/lib/discourse_chat/provider/slack/slack_command_controller.rb index 7410250..abdc0e5 100644 --- a/lib/discourse_chat/provider/slack/slack_command_controller.rb +++ b/lib/discourse_chat/provider/slack/slack_command_controller.rb @@ -38,10 +38,90 @@ module DiscourseChat::Provider::SlackProvider # Create channel if doesn't exist channel ||= DiscourseChat::Channel.create!(provider:provider, data:{identifier: channel_id}) + if tokens[0] == 'post' + return process_post_request(channel, tokens, params[:channel_id]) + end + return ::DiscourseChat::Helper.process_command(channel, tokens) end + def process_post_request(channel, tokens, slack_channel_id) + if SiteSetting.chat_integration_slack_access_token.empty? + return I18n.t("chat_integration.provider.slack.api_required") + end + + http = Net::HTTP.new("slack.com", 443) + http.use_ssl = true + + messages_to_load = 10 + + if tokens.size > 1 + begin + messages_to_load = Integer(tokens[1], 10) + rescue ArgumentError + return I18n.t("chat_integration.provider.slack.parse_error") + end + end + + error_text = I18n.t("chat_integration.provider.slack.transcript_error") + + # Load the user data (we need this to change user IDs into usernames) + req = Net::HTTP::Post.new(URI('https://slack.com/api/users.list')) + req.set_form_data({token: SiteSetting.chat_integration_slack_access_token}) + response = http.request(req) + return error_text unless response.kind_of? Net::HTTPSuccess + json = JSON.parse(response.body) + return error_text unless json['ok'] + users = json["members"] + + # Now load the chat message history + req = Net::HTTP::Post.new(URI('https://slack.com/api/channels.history')) + + data = { + token: SiteSetting.chat_integration_slack_access_token, + channel: slack_channel_id, + count: messages_to_load + } + + req.set_form_data(data) + response = http.request(req) + return error_text unless response.kind_of? Net::HTTPSuccess + json = JSON.parse(response.body) + return error_text unless json['ok'] + post_content = "" + + json["messages"].reverse.each do |message| + next unless message["type"] == "message" + + username = "" + if message["user"] + username = users.find{|u| u["id"] == message["user"]}["name"] + elsif message.key?("username") + username = message["username"] + end + + post_content << "[quote='@#{username}']\n" + post_content << message["text"] + + if message.key?("attachments") + message["attachments"].each do |attachment| + next unless attachment.key?("fallback") + post_content << "\n[quote]\n#{attachment["fallback"]}\n[/quote]" + end + end + + post_content << "\n[/quote]\n\n" + end + + secret = DiscourseChat::Helper.save_transcript(post_content) + + link = "#{Discourse.base_url}/chat-transcript/#{secret}" + + return "<#{link}|#{I18n.t("chat_integration.provider.slack.post_to_discourse")}>" + + end + def slack_token_valid? params.require(:token) diff --git a/spec/helpers/helper_spec.rb b/spec/helpers/helper_spec.rb index d43f7de..49f0fc0 100644 --- a/spec/helpers/helper_spec.rb +++ b/spec/helpers/helper_spec.rb @@ -304,6 +304,8 @@ RSpec.describe DiscourseChat::Manager do describe '.save_transcript' do it 'saves a transcript to redis' do + freeze_time + key = DiscourseChat::Helper.save_transcript("Some content here") expect($redis.get("chat_integration:transcript:#{key}")).to eq("Some content here") diff --git a/spec/lib/discourse_chat/provider/slack/slack_command_controller_spec.rb b/spec/lib/discourse_chat/provider/slack/slack_command_controller_spec.rb index 33ed12d..5b245df 100644 --- a/spec/lib/discourse_chat/provider/slack/slack_command_controller_spec.rb +++ b/spec/lib/discourse_chat/provider/slack/slack_command_controller_spec.rb @@ -109,6 +109,56 @@ describe 'Slack Command Controller', type: :request do end end end + + describe 'post transcript' do + before do + SiteSetting.chat_integration_slack_access_token = 'abcde' + end + + it 'generates a transcript properly' do + stub1 = stub_request(:post, "https://slack.com/api/users.list").to_return(body: '{"ok":true,"members":[{"id":"U5Z773QLS","name":"david"}]}') + stub2 = stub_request(:post, "https://slack.com/api/channels.history").to_return(body: '{"ok":true,"messages":[{"type":"message","user":"U5Z773QLS","text":"And this is a slack message with an attachment: ","attachments":[{"title":"Discourse Meta","title_link":"https:\/\/meta.discourse.org","text":"Discussion about the next-generation open source Discourse forum software","fallback":"Discourse Meta","thumb_url":"https:\/\/discourse-meta.s3-us-west-1.amazonaws.com\/original\/3X\/c\/b\/cb4bec8901221d4a646e45e1fa03db3a65e17f59.png","from_url":"https:\/\/meta.discourse.org","thumb_width":350,"thumb_height":349,"service_icon":"https:\/\/discourse-meta.s3-us-west-1.amazonaws.com\/original\/3X\/c\/b\/cb4bec8901221d4a646e45e1fa03db3a65e17f59.png","service_name":"meta.discourse.org","id":1}],"ts":"1500910064.045243"},{"type":"message","user":"U5Z773QLS","text":"Hello world, this is a slack message","ts":"1500910051.036792"}],"has_more":true}') + + post "/chat-integration/slack/command.json", + text: "post 2", + channel_name: 'general', + channel_id:'C6029G78F', + token: token + + json = JSON.parse(response.body) + + expect(json["text"]).to include(I18n.t("chat_integration.provider.slack.post_to_discourse")) + end + + it 'deals with failed API calls correctly' do + stub1 = stub_request(:post, "https://slack.com/api/users.list").to_return(status: 403) + + post "/chat-integration/slack/command.json", + text: "post 2", + channel_name: 'general', + channel_id:'C6029G78F', + token: token + + json = JSON.parse(response.body) + + expect(json["text"]).to include(I18n.t("chat_integration.provider.slack.transcript_error")) + end + + it 'errors correctly if there is no api key' do + SiteSetting.chat_integration_slack_access_token = '' + + post "/chat-integration/slack/command.json", + text: "post 2", + channel_name: 'general', + channel_id:'C6029G78F', + token: token + + json = JSON.parse(response.body) + + expect(json["text"]).to include(I18n.t("chat_integration.provider.slack.api_required")) + end + end + end end end