Skip to content
This repository was archived by the owner on Jul 14, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ services:
before_install:
- plugin_name=${PWD##*/} && echo $plugin_name

install: true # Prevent travis doing bundle install

script:
- >
docker run
Expand Down
13 changes: 9 additions & 4 deletions lib/discourse_chat/provider/slack/slack_command_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ def process_command(params)
case params[:channel_name]
when 'directmessage'
"@#{params[:user_name]}"
when 'privategroup'
params[:channel_id]
else
"##{params[:channel_name]}"
end
Expand Down Expand Up @@ -58,9 +56,16 @@ def process_post_request(channel, tokens, slack_channel_id, channel_name, respon
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
Expand All @@ -72,7 +77,7 @@ def process_post_request(channel, tokens, slack_channel_id, channel_name, respon

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

Expand Down
22 changes: 16 additions & 6 deletions lib/discourse_chat/provider/slack/slack_transcript.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand All @@ -30,6 +31,7 @@ def set_last_message_by_index(val)

# 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]

Expand Down Expand Up @@ -121,6 +123,8 @@ def build_slack_ui
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: [
Expand Down Expand Up @@ -201,29 +205,35 @@ def load_chat_history(count: 500)
http = Net::HTTP.new("slack.com", 443)
http.use_ssl = true

req = Net::HTTP::Post.new(URI('https://slack.com/api/channels.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,
channel: @channel_id,
count: count
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
context "with valid slack responses" do
before do
stub_request(:post, "https://slack.com/api/users.list").to_return(body: '{"ok":true,"members":[{"id":"U5Z773QLS","name":"david","profile":{"icon_24":"https://example.com/avatar"}}]}')
stub_request(:post, "https://slack.com/api/channels.history").to_return(body: { ok: true, messages: messages_fixture }.to_json)
stub_request(:post, "https://slack.com/api/conversations.history").to_return(body: { ok: true, messages: messages_fixture }.to_json)
end

it 'generates the transcript UI properly' do
Expand Down Expand Up @@ -231,6 +231,46 @@
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/)
Expand Down
28 changes: 24 additions & 4 deletions spec/lib/discourse_chat/provider/slack/slack_transcript_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,31 +105,51 @@

describe 'loading history' do
it 'loads messages correctly' do
stub_request(:post, "https://slack.com/api/channels.history")
stub_request(:post, "https://slack.com/api/conversations.history")
.with(body: hash_including(token: "abcde", channel: 'G1234'))
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)

expect(transcript.load_chat_history).to be_truthy
end

it 'handles failed connection' do
stub_request(:post, "https://slack.com/api/channels.history")
stub_request(:post, "https://slack.com/api/conversations.history")
.to_return(status: 500, body: {}.to_json)

expect(transcript.load_chat_history).to be_falsey
end

it 'handles slack failure' do
stub_request(:post, "https://slack.com/api/channels.history")
stub_request(:post, "https://slack.com/api/conversations.history")
.to_return(status: 200, body: { ok: false }.to_json)

expect(transcript.load_chat_history).to be_falsey
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/channels.history")
stub_request(:post, "https://slack.com/api/conversations.history")
.with(body: hash_including(token: "abcde", channel: 'G1234'))
.to_return(status: 200, body: { ok: true, messages: messages_fixture }.to_json)
transcript.load_chat_history
Expand Down