FEATURE: Improve formatting for Slack transcript messages (#70)

- Fix multi-line code blocks
- Add strikethrough support
- Fix HTML entities inside code blocks
- Do not process formatting inside code blocks
- Ensure links are never created with no URL
- Replace - with _ in emoji names
This commit is contained in:
David Taylor 2021-04-22 18:49:21 +01:00 committed by GitHub
parent 23d65684f1
commit 08e6718722
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 152 additions and 3 deletions

View File

@ -29,11 +29,27 @@ module DiscourseChat::Provider::SlackProvider
def text
text = @raw['text'].nil? ? "" : @raw['text']
pre = {}
# Extract code blocks and replace with placeholder
text = text.gsub(/```(.*?)```/m) do |match|
key = "pre:" + SecureRandom.alphanumeric(50)
pre[key] = HTMLEntities.new.decode $1
"\n```\n#{key}\n```\n"
end
# # Extract inline code and replace with placeholder
text = text.gsub(/(?<!`)`([^`]+?)`(?!`)/) do |match|
key = "pre:" + SecureRandom.alphanumeric(50)
pre[key] = HTMLEntities.new.decode $1
"`#{key}`"
end
# Format links (don't worry about special cases @ # !)
text = text.gsub(/<(.*?)>/) do |match|
group = $1
parts = group.split('|')
link = parts[0].start_with?('@', '#', '!') ? '' : parts[0]
link = parts[0].start_with?('@', '#', '!') ? nil : parts[0]
text = parts.length > 1 ? parts[1] : parts[0]
if parts[0].start_with?('@')
@ -46,14 +62,35 @@ module DiscourseChat::Provider::SlackProvider
next "@#{user_name}"
end
"[#{text}](#{link})"
if link.nil?
text
elsif link == text
"<#{link}>"
else
"[#{text}](#{link})"
end
end
# Add an extra * to each side for bold
text = text.gsub(/\*(.*?)\*/) do |match|
text = text.gsub(/\*.*?\*/) do |match|
"*#{match}*"
end
# Add an extra ~ to each side for strikethrough
text = text.gsub(/~.*?~/) do |match|
"~#{match}~"
end
# Replace emoji - with _
text = text.gsub(/:[a-z0-9_-]+:/) do |match|
match.gsub("-") { "_" }
end
# Restore pre-formatted code block content
pre.each do |key, value|
text = text.gsub(key) { value }
end
text
end

View File

@ -333,5 +333,117 @@ RSpec.describe DiscourseChat::Provider::SlackProvider::SlackTranscript do
expect(first_ui[:text]).to eq(transcript.first_message.raw_text)
end
end
describe "message formatting" do
it 'handles code block newlines' do
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "Here is some code```my code\nwith newline```",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq(<<~MD)
Here is some code
```
my code
with newline
```
MD
end
it 'handles multiple code blocks' do
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "Here is some code```my code\nwith newline```and another```some more code```",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq(<<~MD)
Here is some code
```
my code
with newline
```
and another
```
some more code
```
MD
end
it 'handles strikethrough' do
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "Some ~strikethrough~",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq("Some ~~strikethrough~~")
end
it 'handles slack links' do
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "A link to <https://google.com|google>, <https://autolinked.com|https://autolinked.com>, <https://notext.com>, <#channel>, <@user>",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq("A link to [google](https://google.com), <https://autolinked.com>, <https://notext.com>, #channel, @user")
end
it 'does not format things inside backticks' do
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq("You can strikethrough like `~this~`, bold like `*this*` and link like `[https://example.com](https://example.com)`")
end
it 'unescapes html in backticks' do
# Because Slack escapes HTML entities, even in backticks
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "The code is `&lt;stuff&gt;`",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq("The code is `<stuff>`")
end
it 'updates emoji dashes to underscores' do
# Discourse does not allow dashes in emoji names, so this helps communities have matching custom emojis
message = DiscourseChat::Provider::SlackProvider::SlackMessage.new(
{
"type" => "message",
"user" => "U5Z773QLS",
"text" => "This is :my-emoji:",
"ts" => "1501093331.439776"
},
transcript
)
expect(message.text).to eq("This is :my_emoji:")
end
end
end
end