mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 07:53:49 +08:00
DEV: Move discourse-chat
to the core repo. (#18776)
As part of this move, we are also renaming `discourse-chat` to `chat`.
This commit is contained in:
177
plugins/chat/lib/chat_transcript_service.rb
Normal file
177
plugins/chat/lib/chat_transcript_service.rb
Normal file
@ -0,0 +1,177 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
##
|
||||
# Used to generate BBCode [chat] tags for the message IDs provided.
|
||||
#
|
||||
# If there is > 1 message then the channel name will be shown at
|
||||
# the top of the first message, and subsequent messages will have
|
||||
# the chained attribute, which will affect how they are displayed
|
||||
# in the UI.
|
||||
#
|
||||
# Subsequent messages from the same user will be put into the same
|
||||
# tag. Each new user in the chain of messages will have a new [chat]
|
||||
# tag created.
|
||||
#
|
||||
# A single message will have the channel name displayed to the right
|
||||
# of the username and datetime of the message.
|
||||
class ChatTranscriptService
|
||||
CHAINED_ATTR = "chained=\"true\""
|
||||
MULTIQUOTE_ATTR = "multiQuote=\"true\""
|
||||
NO_LINK_ATTR = "noLink=\"true\""
|
||||
|
||||
class ChatTranscriptBBCode
|
||||
attr_reader :channel, :multiquote, :chained, :no_link, :include_reactions
|
||||
|
||||
def initialize(
|
||||
channel: nil,
|
||||
acting_user: nil,
|
||||
multiquote: false,
|
||||
chained: false,
|
||||
no_link: false,
|
||||
include_reactions: false
|
||||
)
|
||||
@channel = channel
|
||||
@acting_user = acting_user
|
||||
@multiquote = multiquote
|
||||
@chained = chained
|
||||
@no_link = no_link
|
||||
@include_reactions = include_reactions
|
||||
@message_data = []
|
||||
end
|
||||
|
||||
def add(message:, reactions: nil)
|
||||
@message_data << { message: message, reactions: reactions }
|
||||
end
|
||||
|
||||
def render
|
||||
attrs = [quote_attr(@message_data.first[:message])]
|
||||
|
||||
if channel
|
||||
attrs << channel_attr
|
||||
attrs << channel_id_attr
|
||||
end
|
||||
|
||||
attrs << MULTIQUOTE_ATTR if multiquote
|
||||
attrs << CHAINED_ATTR if chained
|
||||
attrs << NO_LINK_ATTR if no_link
|
||||
attrs << reactions_attr if include_reactions
|
||||
|
||||
<<~MARKDOWN
|
||||
[chat #{attrs.compact.join(" ")}]
|
||||
#{@message_data.map { |msg| msg[:message].to_markdown }.join("\n\n")}
|
||||
[/chat]
|
||||
MARKDOWN
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def reactions_attr
|
||||
reaction_data =
|
||||
@message_data.reduce([]) do |array, msg_data|
|
||||
if msg_data[:reactions].any?
|
||||
array << msg_data[:reactions].map { |react| "#{react.emoji}:#{react.usernames}" }
|
||||
end
|
||||
array
|
||||
end
|
||||
return if reaction_data.empty?
|
||||
"reactions=\"#{reaction_data.join(";")}\""
|
||||
end
|
||||
|
||||
def quote_attr(message)
|
||||
"quote=\"#{message.user.username};#{message.id};#{message.created_at.iso8601}\""
|
||||
end
|
||||
|
||||
def channel_attr
|
||||
"channel=\"#{channel.title(@acting_user)}\""
|
||||
end
|
||||
|
||||
def channel_id_attr
|
||||
"channelId=\"#{channel.id}\""
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(channel, acting_user, messages_or_ids: [], opts: {})
|
||||
@channel = channel
|
||||
@acting_user = acting_user
|
||||
|
||||
if messages_or_ids.all? { |m| m.is_a?(Numeric) }
|
||||
@message_ids = messages_or_ids
|
||||
else
|
||||
@messages = messages_or_ids
|
||||
end
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def generate_markdown
|
||||
previous_message = nil
|
||||
rendered_markdown = []
|
||||
all_messages_same_user = messages.count(:user_id) == 1
|
||||
open_bbcode_tag =
|
||||
ChatTranscriptBBCode.new(
|
||||
channel: @channel,
|
||||
acting_user: @acting_user,
|
||||
multiquote: messages.length > 1,
|
||||
chained: !all_messages_same_user,
|
||||
no_link: @opts[:no_link],
|
||||
include_reactions: @opts[:include_reactions],
|
||||
)
|
||||
|
||||
messages.each.with_index do |message, idx|
|
||||
if previous_message.present? && previous_message.user_id != message.user_id
|
||||
rendered_markdown << open_bbcode_tag.render
|
||||
|
||||
open_bbcode_tag =
|
||||
ChatTranscriptBBCode.new(
|
||||
acting_user: @acting_user,
|
||||
chained: !all_messages_same_user,
|
||||
no_link: @opts[:no_link],
|
||||
include_reactions: @opts[:include_reactions],
|
||||
)
|
||||
end
|
||||
|
||||
if @opts[:include_reactions]
|
||||
open_bbcode_tag.add(message: message, reactions: reactions_for_message(message))
|
||||
else
|
||||
open_bbcode_tag.add(message: message)
|
||||
end
|
||||
previous_message = message
|
||||
end
|
||||
|
||||
# tie off the last open bbcode + render
|
||||
rendered_markdown << open_bbcode_tag.render
|
||||
rendered_markdown.join("\n")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def messages
|
||||
@messages ||=
|
||||
ChatMessage
|
||||
.includes(:user, chat_uploads: :upload)
|
||||
.where(id: @message_ids, chat_channel_id: @channel.id)
|
||||
.order(:created_at)
|
||||
end
|
||||
|
||||
##
|
||||
# Queries reactions and returns them in this format
|
||||
#
|
||||
# emoji | usernames | chat_message_id
|
||||
# ----------------------------------------
|
||||
# +1 | foo,bar,baz | 102
|
||||
# heart | foo | 102
|
||||
# sob | bar,baz | 103
|
||||
def reactions
|
||||
@reactions ||= DB.query(<<~SQL, @messages.map(&:id))
|
||||
SELECT emoji, STRING_AGG(DISTINCT users.username, ',') AS usernames, chat_message_id
|
||||
FROM chat_message_reactions
|
||||
INNER JOIN users on users.id = chat_message_reactions.user_id
|
||||
WHERE chat_message_id IN (?)
|
||||
GROUP BY emoji, chat_message_id
|
||||
ORDER BY chat_message_id, emoji
|
||||
SQL
|
||||
end
|
||||
|
||||
def reactions_for_message(message)
|
||||
reactions.select { |react| react.chat_message_id == message.id }
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user