discourse/plugins/chat/lib/chat_message_mentions.rb
Andrei Prigorshnev 75b81b6854
DEV: extract the logic for extracting and expanding mentions from ChatNotifier (#20290)
Initially, the ChatMention model / db table was introduced to better support notifications (see discourse/discourse-chat@0801d10). That means that currently, we create a new chat_mention record only if a user will be notified about the mention.

Now we plan to start using the ChatMention model in other scenarios (for example for implementing user status on mentions) so we need to always create a new record in the chat_mention table. This PR does the first step into that direction by decoupling the logic for extracting and expanding mentions from the code related to notifications.

This doesn't change any behavior, only extracts code from ChatNotifier.
2023-02-16 19:55:18 +04:00

89 lines
2.4 KiB
Ruby

# frozen_string_literal: true
class Chat::ChatMessageMentions
def initialize(message)
@message = message
mentions = parse_mentions(message)
group_mentions = parse_group_mentions(message)
@has_global_mention = mentions.include?("@all")
@has_here_mention = mentions.include?("@here")
@parsed_direct_mentions = normalize(mentions)
@parsed_group_mentions = normalize(group_mentions)
end
attr_accessor :has_global_mention,
:has_here_mention,
:parsed_direct_mentions,
:parsed_group_mentions
def global_mentions
return User.none unless @has_global_mention
channel_members.where.not(username_lower: @parsed_direct_mentions)
end
def direct_mentions
chat_users.where(username_lower: @parsed_direct_mentions)
end
def group_mentions
chat_users.includes(:groups).joins(:groups).where(groups: mentionable_groups)
end
def here_mentions
return User.none unless @has_here_mention
channel_members
.where("last_seen_at > ?", 5.minutes.ago)
.where.not(username_lower: @parsed_direct_mentions)
end
def mentionable_groups
@mentionable_groups ||=
Group.mentionable(@message.user, include_public: false).where(id: visible_groups.map(&:id))
end
def visible_groups
@visible_groups ||=
Group.where("LOWER(name) IN (?)", @parsed_group_mentions).visible_groups(@message.user)
end
private
def channel_members
chat_users.where(
user_chat_channel_memberships: {
following: true,
chat_channel_id: @message.chat_channel.id,
},
)
end
def chat_users
User
.includes(:user_chat_channel_memberships, :group_users)
.distinct
.joins("LEFT OUTER JOIN user_chat_channel_memberships uccm ON uccm.user_id = users.id")
.joins(:user_option)
.real
.not_suspended
.where(user_options: { chat_enabled: true })
.where.not(username_lower: @message.user.username.downcase)
end
def parse_mentions(message)
Nokogiri::HTML5.fragment(message.cooked).css(".mention").map(&:text)
end
def parse_group_mentions(message)
Nokogiri::HTML5.fragment(message.cooked).css(".mention-group").map(&:text)
end
def normalize(mentions)
mentions.reduce([]) do |memo, mention|
%w[@here @all].include?(mention.downcase) ? memo : (memo << mention[1..-1].downcase)
end
end
end