Module: Collavre::MentionParser

Defined in:
app/services/collavre/mention_parser.rb

Overview

Centralized mention parsing and resolution. All @mention logic should go through this module so changes to the mention format only need to be made in one place.

Constant Summary collapse

MENTION_PREFIX_CHARS =

Characters allowed before @ in mentions (besides start-of-text)

/[\s:.,;\n\r]/
MENTION_PATTERN =

Canonical mention: @name: (with colon separator) Matches at start of text or after whitespace/punctuation/newline

/(?:\A|(?<=#{MENTION_PREFIX_CHARS}))@([^:]+?):\s*/
MENTION_LOOSE_PATTERN =

Mention without colon: @name followed by whitespace (start-of-text only to avoid false positives like email addresses)

/\A@(\S+)\s+/
MENTION_SCAN_PATTERN =

Scan pattern: finds all @name: mentions anywhere in text

/(?:^|(?<=#{MENTION_PREFIX_CHARS}))@([^:]+?):/

Class Method Summary collapse

Class Method Details

.extract_all_names(text) ⇒ Object

Extract all mentioned names from text



29
30
31
32
33
# File 'app/services/collavre/mention_parser.rb', line 29

def self.extract_all_names(text)
  return [] if text.blank?

  text.scan(MENTION_SCAN_PATTERN).flatten.map(&:strip).uniq
end

.extract_name(text) ⇒ Object

Extract the first mentioned name from text (returns nil if no mention found)



21
22
23
24
25
26
# File 'app/services/collavre/mention_parser.rb', line 21

def self.extract_name(text)
  return nil if text.blank?

  match = text.match(MENTION_PATTERN) || text.match(MENTION_LOOSE_PATTERN)
  match ? match[1].strip : nil
end

.find_user_by_name(name) ⇒ Object

Find a User by case-insensitive name match



36
37
38
39
40
# File 'app/services/collavre/mention_parser.rb', line 36

def self.find_user_by_name(name)
  return nil if name.blank?

  User.where("LOWER(name) = ?", name.strip.downcase).first
end

.resolve_all_users(text) ⇒ Object

Resolve all mentioned users from text (finds mentions anywhere)



49
50
51
# File 'app/services/collavre/mention_parser.rb', line 49

def self.resolve_all_users(text)
  extract_all_names(text).filter_map { |name| find_user_by_name(name) }.uniq
end

.resolve_user(text) ⇒ Object

Extract mention and resolve to a User in one step



43
44
45
46
# File 'app/services/collavre/mention_parser.rb', line 43

def self.resolve_user(text)
  name = extract_name(text)
  name ? find_user_by_name(name) : nil
end

.strip_self_mention(text, agent_name) ⇒ Object

Strip self-mention prefix from text (both @name: and @name formats)



54
55
56
57
58
59
60
61
# File 'app/services/collavre/mention_parser.rb', line 54

def self.strip_self_mention(text, agent_name)
  return text if text.blank? || agent_name.blank?

  escaped = Regexp.escape(agent_name)
  text
    .sub(/\A@#{escaped}:\s*/i, "")
    .sub(/\A@#{escaped}\s+/i, "")
end