Mercurial > prosody-modules
comparison mod_muc_inject_mentions/mod_muc_inject_mentions.lua @ 4138:e8c1b35bc25b
mod_muc_inject_mentions: Publish module to repository
author | Seve Ferrer <seve@delape.net> |
---|---|
date | Sun, 20 Sep 2020 10:31:02 +0200 |
parents | |
children | c6bb64a12f92 |
comparison
equal
deleted
inserted
replaced
4137:5f4bcaad18ee | 4138:e8c1b35bc25b |
---|---|
1 module:depends("muc"); | |
2 | |
3 local jid_split = require "util.jid".split; | |
4 | |
5 local prefixes = module:get_option("muc_inject_mentions_prefixes", nil) | |
6 local suffixes = module:get_option("muc_inject_mentions_suffixes", nil) | |
7 local enabled_rooms = module:get_option("muc_inject_mentions_enabled_rooms", nil) | |
8 local disabled_rooms = module:get_option("muc_inject_mentions_disabled_rooms", nil) | |
9 | |
10 local reference_xmlns = "urn:xmpp:reference:0" | |
11 | |
12 local function is_room_eligible(jid) | |
13 if not enabled_rooms and not disabled_rooms then | |
14 return true; | |
15 end | |
16 | |
17 if enabled_rooms and not disabled_rooms then | |
18 for _, _jid in ipairs(enabled_rooms) do | |
19 if _jid == jid then | |
20 return true | |
21 end | |
22 end | |
23 return false | |
24 end | |
25 | |
26 if disabled_rooms and not enabled_rooms then | |
27 for _, _jid in ipairs(disabled_rooms) do | |
28 if _jid == jid then | |
29 return false | |
30 end | |
31 end | |
32 return true | |
33 end | |
34 | |
35 return true | |
36 end | |
37 | |
38 local function has_nick_prefix(body, first) | |
39 -- There is no prefix | |
40 -- but mention could still be valid | |
41 if first == 1 then return true end | |
42 | |
43 -- There are no configured prefixes | |
44 if not prefixes or #prefixes < 1 then return false end | |
45 | |
46 -- Preffix must have a space before it | |
47 -- or be the first character of the body | |
48 if body:sub(first - 2, first - 2) ~= "" and | |
49 body:sub(first - 2, first - 2) ~= " " | |
50 then | |
51 return false | |
52 end | |
53 | |
54 local preffix = body:sub(first - 1, first - 1) | |
55 for i, _preffix in ipairs(prefixes) do | |
56 if preffix == _preffix then | |
57 return true | |
58 end | |
59 end | |
60 | |
61 return false | |
62 end | |
63 | |
64 local function has_nick_suffix(body, last) | |
65 -- There is no suffix | |
66 -- but mention could still be valid | |
67 if last == #body then return true end | |
68 | |
69 -- There are no configured suffixes | |
70 if not suffixes or #suffixes < 1 then return false end | |
71 | |
72 -- Suffix must have a space after it | |
73 -- or be the last character of the body | |
74 if body:sub(last + 2, last + 2) ~= "" and | |
75 body:sub(last + 2, last + 2) ~= " " | |
76 then | |
77 return false | |
78 end | |
79 | |
80 local suffix = body:sub(last+1, last+1) | |
81 for i, _suffix in ipairs(suffixes) do | |
82 if suffix == _suffix then | |
83 return true | |
84 end | |
85 end | |
86 | |
87 return false | |
88 end | |
89 | |
90 local function search_mentions(room, stanza) | |
91 local body = stanza:get_child("body"):get_text(); | |
92 local mentions = {} | |
93 | |
94 for _, occupant in pairs(room._occupants) do | |
95 local node, host, nick = jid_split(occupant.nick); | |
96 -- Check for multiple mentions to the same nickname in a message | |
97 -- Hey @nick remember to... Ah, also @nick please let me know if... | |
98 local matches = {} | |
99 local _first, _last = 0, 0 | |
100 while true do | |
101 -- Use plain search as nick could contain | |
102 -- characters used in Lua patterns | |
103 _first, _last = body:find(nick, _last + 1, true) | |
104 if _first == nil then break end | |
105 table.insert(matches, {first=_first, last=_last}) | |
106 end | |
107 | |
108 -- Filter out intentional mentions from unintentional ones | |
109 for _, match in ipairs(matches) do | |
110 local bare_jid = occupant.bare_jid | |
111 local first, last = match.first, match.last | |
112 | |
113 -- Body only contains nickname | |
114 if first == 1 and last == #body then | |
115 table.insert(mentions, {bare_jid=bare_jid, first=first, last=last}) | |
116 | |
117 -- Nickname between spaces | |
118 elseif body:sub(first - 1, first - 1) == " " and | |
119 body:sub(last + 1, last + 1) == " " | |
120 then | |
121 table.insert(mentions, {bare_jid=bare_jid, first=first, last=last}) | |
122 else | |
123 -- Check if occupant is mentioned using affixes | |
124 local has_preffix = has_nick_prefix(body, first) | |
125 local has_suffix = has_nick_suffix(body, last) | |
126 | |
127 -- @nickname: ... | |
128 if has_preffix and has_suffix then | |
129 table.insert(mentions, {bare_jid=bare_jid, first=first, last=last}) | |
130 | |
131 -- @nickname ... | |
132 elseif has_preffix and not has_suffix then | |
133 if body:sub(last + 1, last + 1) == " " then | |
134 table.insert(mentions, {bare_jid=bare_jid, first=first, last=last}) | |
135 end | |
136 | |
137 -- nickname: ... | |
138 elseif not has_preffix and has_suffix then | |
139 if body:sub(first - 1, first - 1) == " " then | |
140 table.insert(mentions, {bare_jid=bare_jid, first=first, last=last}) | |
141 end | |
142 end | |
143 end | |
144 end | |
145 end | |
146 | |
147 return mentions | |
148 end | |
149 | |
150 local function muc_inject_mentions(event) | |
151 local room, stanza = event.room, event.stanza; | |
152 -- Inject mentions only if the room is configured for them | |
153 if not is_room_eligible(room.jid) then return; end | |
154 -- Only act on messages that do not include references. | |
155 -- If references are found, it is assumed the client has mentions support | |
156 if stanza:get_child("reference", reference_xmlns) then return; end | |
157 | |
158 local mentions = search_mentions(room, stanza) | |
159 for _, mention in ipairs(mentions) do | |
160 -- https://xmpp.org/extensions/xep-0372.html#usecase_mention | |
161 stanza:tag( | |
162 "reference", { | |
163 xmlns=reference_xmlns, | |
164 begin=tostring(mention.first - 1), -- count starts at 0 | |
165 ["end"]=tostring(mention.last - 1), | |
166 type="mention", | |
167 uri="xmpp:" .. mention.bare_jid, | |
168 } | |
169 ):up() | |
170 end | |
171 end | |
172 | |
173 module:hook("muc-occupant-groupchat", muc_inject_mentions) |