Software / code / 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 |
| child | 4139: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) |