Use Slack display names instead of the "name" field (#29)

* Use Slack display names instead of the "name" field

The "name" field is the left-hand side of the email address in many
cases.  This information is not otherwise available so we shouldn't
expose it in forum posts.

* Fall back to real_name, add comments

* Store users as a hash

This should avoid a lot of O(n) lookups

* Format user mentions with the correct name also

* Fix the tests (first try)

* Fix the tests (second try); add a test for user mentions

* Fix the tests (third try)

* Empty commit to trigger Travis

* Fix the tests (fourth try)

* Fix the tests (fifth try)

* Change spaces to underscores

* Updates per feedback
This commit is contained in:
James Nylen 2019-07-04 18:50:07 +00:00 committed by Robin Ward
parent fc61b92183
commit 5ca3652ba7
4 changed files with 66 additions and 18 deletions

View File

@ -9,9 +9,10 @@ module DiscourseChat::Provider::SlackProvider
def username def username
if user if user
user['name'] user["_transcript_username"]
elsif @raw.key?("username") elsif @raw.key?("username")
@raw["username"] # This is for bot messages
@raw["username"].gsub(' ', '_')
end end
end end
@ -36,8 +37,13 @@ module DiscourseChat::Provider::SlackProvider
text = parts.length > 1 ? parts[1] : parts[0] text = parts.length > 1 ? parts[1] : parts[0]
if parts[0].start_with?('@') if parts[0].start_with?('@')
user = @transcript.users.find { |u| u["id"] == parts[0].gsub('@', '') } user_id = parts[0][1..-1]
next "@#{user['name']}" if user = @transcript.users[user_id]
user_name = user['_transcript_username']
else
user_name = user_id
end
next "@#{user_name}"
end end
"[#{text}](#{link})" "[#{text}](#{link})"
@ -91,7 +97,7 @@ module DiscourseChat::Provider::SlackProvider
def user def user
return nil unless user_id = @raw["user"] return nil unless user_id = @raw["user"]
@transcript.users.find { |u| u["id"] == user_id } @transcript.users[user_id]
end end
end end
end end

View File

@ -197,7 +197,7 @@ module DiscourseChat::Provider::SlackProvider
cursor = nil cursor = nil
req = Net::HTTP::Post.new(URI('https://slack.com/api/users.list')) req = Net::HTTP::Post.new(URI('https://slack.com/api/users.list'))
@users = [] @users = {}
loop do loop do
break if cursor == "" break if cursor == ""
req.set_form_data(token: SiteSetting.chat_integration_slack_access_token, limit: 200, cursor: cursor) req.set_form_data(token: SiteSetting.chat_integration_slack_access_token, limit: 200, cursor: cursor)
@ -206,9 +206,18 @@ module DiscourseChat::Provider::SlackProvider
json = JSON.parse(response.body) json = JSON.parse(response.body)
return false unless json['ok'] return false unless json['ok']
cursor = json['response_metadata']['next_cursor'] cursor = json['response_metadata']['next_cursor']
@users << json['members'] json['members'].each do |user|
# Slack uses display_name and falls back to real_name if it is not set
if user['profile']['display_name'].blank?
user['_transcript_username'] = user['profile']['real_name']
else
user['_transcript_username'] = user['profile']['display_name']
end
user['_transcript_username'] = user['_transcript_username'].gsub(' ', '_')
@users[user['id']] = user
end
end end
@users&.flatten! true
end end
def load_chat_history(count: 500) def load_chat_history(count: 500)

View File

@ -197,7 +197,7 @@ describe 'Slack Command Controller', type: :request do
context "with valid slack responses" do context "with valid slack responses" do
before 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"}}],"response_metadata":{"next_cursor":""}}') stub_request(:post, "https://slack.com/api/users.list").to_return(body: '{"ok":true,"members":[{"id":"U5Z773QLS","profile":{"display_name":"david","real_name":"david","icon_24":"https://example.com/avatar"}}],"response_metadata":{"next_cursor":""}}')
stub_request(:post, "https://slack.com/api/conversations.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 end

View File

@ -14,8 +14,8 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
}, },
{ {
"type": "message", "type": "message",
"user": "U5Z773QLS", "user": "U5Z773QLZ",
"text": "Oooh a new discourse plugin???", "text": "Oooh a new discourse plugin <@U5Z773QLS> ???",
"ts": "1501801643.056375" "ts": "1501801643.056375"
}, },
{ {
@ -62,10 +62,32 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
{ {
"type": "message", "type": "message",
"user": "U5Z773QLS", "user": "U5Z773QLS",
"text": "Lets try some *bold text*", "text": "Lets try some *bold text* <@U5Z773QLZ> <@someotheruser>",
"ts": "1501093331.439776" "ts": "1501093331.439776"
}, },
]
}
let(:users_fixture) {
[
{
id: "U5Z773QLS",
name: "awesomeguyemail",
profile: {
image_24: "https://example.com/avatar",
display_name: "awesomeguy",
real_name: "actually just a guy"
}
},
{
id: "U5Z773QLZ",
name: "otherguyemail",
profile: {
image_24: "https://example.com/avatar",
display_name: "",
real_name: "another guy"
}
}
] ]
} }
@ -78,7 +100,7 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
it 'loads users correctly' do it 'loads users correctly' do
stub_request(:post, "https://slack.com/api/users.list") stub_request(:post, "https://slack.com/api/users.list")
.with(body: { token: "abcde", "cursor": nil, "limit": "200" }) .with(body: { token: "abcde", "cursor": nil, "limit": "200" })
.to_return(status: 200, body: { ok: true, members: [{ id: "U5Z773QLS", name: "awesomeguy", profile: { image_24: "https://example.com/avatar" } }], response_metadata: { next_cursor: "" } }.to_json) .to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
expect(transcript.load_user_data).to be_truthy expect(transcript.load_user_data).to be_truthy
end end
@ -101,7 +123,7 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
context 'with loaded users' do context 'with loaded users' do
before do before do
stub_request(:post, "https://slack.com/api/users.list") stub_request(:post, "https://slack.com/api/users.list")
.to_return(status: 200, body: { ok: true, members: [{ id: "U5Z773QLS", name: "awesomeguy", profile: { image_24: "https://example.com/avatar" } }], response_metadata: { next_cursor: "" } }.to_json) .to_return(status: 200, body: { ok: true, members: users_fixture, response_metadata: { next_cursor: "" } }.to_json)
transcript.load_user_data transcript.load_user_data
end end
@ -166,7 +188,7 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
end end
it 'handles bold text' do it 'handles bold text' do
expect(transcript.messages.first.text).to eq("Lets try some **bold text**") expect(transcript.messages.first.text).to start_with("Lets try some **bold text** ")
end end
it 'handles links' do it 'handles links' do
@ -221,8 +243,19 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
it 'handles usernames correctly' do it 'handles usernames correctly' do
expect(transcript.first_message.username).to eq('awesomeguy') # Normal user expect(transcript.first_message.username).to eq('awesomeguy') # Normal user
expect(transcript.messages[1].username).to eq('Test_Community') # Bot user
expect(transcript.messages[2].username).to eq(nil) # Unknown normal user expect(transcript.messages[2].username).to eq(nil) # Unknown normal user
expect(transcript.messages[1].username).to eq('Test Community') # Bot user # Normal user, display_name not set (fall back to real_name)
expect(transcript.messages[4].username).to eq('another_guy')
end
it 'handles user mentions correctly' do
# User with display_name not set, unrecognized user
expect(transcript.first_message.text).to \
eq('Lets try some **bold text** @another_guy @someotheruser')
# Normal user
expect(transcript.messages[4].text).to \
eq('Oooh a new discourse plugin @awesomeguy ???')
end end
it 'handles avatars correctly' do it 'handles avatars correctly' do
@ -241,9 +274,9 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
[quote] [quote]
[**View in #general on Slack**](https://slack.com/archives/G1234/p1501093331439776) [**View in #general on Slack**](https://slack.com/archives/G1234/p1501093331439776)
![awesomeguy] **@awesomeguy:** Lets try some **bold text** ![awesomeguy] **@awesomeguy:** Lets try some **bold text** @another_guy @someotheruser
**@Test Community:** **@Test_Community:**
> Discourse can now be integrated with Mattermost! - @david > Discourse can now be integrated with Mattermost! - @david
[/quote] [/quote]