Add basic support for thread transcripts

This commit is contained in:
David Taylor 2018-04-08 03:16:36 +01:00
parent 234203dc2b
commit 0ed6343874
4 changed files with 84 additions and 7 deletions

View File

@ -56,9 +56,16 @@ module DiscourseChat::Provider::SlackProvider
Scheduler::Defer.later "Processing slack transcript request" do
requested_messages = nil
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*$/
slack_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p([0-9]{16})\/?$/
if tokens.size > 1 && match = slack_url_regex.match(tokens[1])
if tokens.size > 2 && tokens[1] == "thread" && match = slack_url_regex.match(tokens[2])
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, '.')
elsif tokens.size > 1
begin
@ -70,7 +77,7 @@ module DiscourseChat::Provider::SlackProvider
error_message = { text: I18n.t("chat_integration.provider.slack.transcript.error") }
return error_message unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id)
return error_message unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id, requested_thread_ts: requested_thread_ts)
return error_message unless transcript.load_user_data
return error_message unless transcript.load_chat_history

View File

@ -2,9 +2,10 @@ module DiscourseChat::Provider::SlackProvider
class SlackTranscript
attr_reader :users, :channel_id, :messages
def initialize(channel_name:, channel_id:)
def initialize(channel_name:, channel_id:, requested_thread_ts: nil)
@channel_name = channel_name
@channel_id = channel_id
@requested_thread_ts = requested_thread_ts
@first_message_index = 0
@last_message_index = -1 # We can use negative array indicies to select the last message - fancy!
@ -30,6 +31,7 @@ module DiscourseChat::Provider::SlackProvider
# Apply a heuristic to decide which is the first message in the current conversation
def guess_first_message(skip_messages: 5) # Can skip the last n messages
return true if @requested_thread_ts # Always start thread on first message
possible_first_messages = @messages[0..-skip_messages]
@ -121,6 +123,8 @@ module DiscourseChat::Provider::SlackProvider
secret = DiscourseChat::Helper.save_transcript(post_content)
link = "#{Discourse.base_url}/chat-transcript/#{secret}"
return { text: "<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>" } if @requested_thread_ts
{
text: "<#{link}|#{I18n.t("chat_integration.provider.slack.transcript.post_to_discourse")}>",
attachments: [
@ -201,7 +205,9 @@ module DiscourseChat::Provider::SlackProvider
http = Net::HTTP.new("slack.com", 443)
http.use_ssl = true
req = Net::HTTP::Post.new(URI('https://slack.com/api/conversations.history'))
endpoint = @requested_thread_ts ? "replies" : "history"
req = Net::HTTP::Post.new(URI("https://slack.com/api/conversations.#{endpoint}"))
data = {
token: SiteSetting.chat_integration_slack_access_token,
@ -209,21 +215,25 @@ module DiscourseChat::Provider::SlackProvider
limit: count
}
data[:ts] = @requested_thread_ts if @requested_thread_ts
req.set_form_data(data)
response = http.request(req)
return false unless response.kind_of? Net::HTTPSuccess
json = JSON.parse(response.body)
return false unless json['ok']
raw_messages = json['messages'].reverse
raw_messages = json['messages']
raw_messages = raw_messages.reverse unless @requested_thread_ts
# Build some message objects
@messages = []
raw_messages.each_with_index do |message, index|
# Only load messages
next unless message["type"] == "message"
# Don't load responses to threads (if ts==thread_ts then it's the thread parent)
next if message["thread_ts"] && message["thread_ts"] != message["ts"]
# Don't load responses to threads unless specifically requested (if ts==thread_ts then it's the thread parent)
next if !@requested_thread_ts && message["thread_ts"] && message["thread_ts"] != message["ts"]
this_message = SlackMessage.new(message, self)
@messages << this_message

View File

@ -231,6 +231,46 @@ describe 'Slack Command Controller', type: :request do
expect(command_stub).to have_been_requested
end
it 'can select by url with thread parameter' do
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
.with(body: /1501801629\.052212/)
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", params: {
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501201669054212?thread_ts=1501801629.052212",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general',
channel_id: 'C6029G78F',
token: token
}
expect(command_stub).to have_been_requested
expect(replies_stub).to have_been_requested
end
it 'can select by thread' do
replies_stub = stub_request(:post, "https://slack.com/api/conversations.replies")
.with(body: /1501801629\.052212/)
.to_return(body: { ok: true, messages: messages_fixture }.to_json)
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", params: {
text: "post thread https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general',
channel_id: 'C6029G78F',
token: token
}
expect(command_stub).to have_been_requested
expect(replies_stub).to have_been_requested
end
it 'can select by count' do
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.with(body: /1501801629\.052212/)

View File

@ -127,6 +127,26 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
end
end
context 'with thread_ts specified' do
let(:thread_transcript) { described_class.new(channel_name: "#general", channel_id: "G1234", requested_thread_ts: "1501801629.052212") }
before do
stub_request(:post, "https://slack.com/api/conversations.replies")
.with(body: hash_including(token: "abcde", channel: 'G1234', ts: "1501801629.052212"))
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
thread_transcript.load_chat_history
end
it 'includes messages in a thread' do
expect(thread_transcript.messages.length).to eq(7)
end
it 'loads in chronological order' do # replies API presents messages in actual chronological order
expect(thread_transcript.messages.first.ts).to eq('1501801665.062694')
end
end
context 'with loaded messages' do
before do
stub_request(:post, "https://slack.com/api/conversations.history")