Mercurial > prosody-modules
comparison mod_rtbl/mod_rtbl.lua @ 6301:77b5dd33a8f3
mod_rtbl: New generic API module for subscribing to RTBLs via XEP-0060
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Wed, 30 Jul 2025 14:13:44 +0100 |
| parents | |
| children | 13f674a9fb22 |
comparison
equal
deleted
inserted
replaced
| 6300:8689f49d21ef | 6301:77b5dd33a8f3 |
|---|---|
| 1 local array = require "util.array"; | |
| 2 local id = require "util.id"; | |
| 3 local it = require "util.iterators"; | |
| 4 local set = require "util.set"; | |
| 5 local st = require "util.stanza"; | |
| 6 | |
| 7 module:depends("pubsub_subscription"); | |
| 8 | |
| 9 local function new_rtbl_subscription(rtbl_service_jid, rtbl_node, handlers) | |
| 10 local rtbl = {}; | |
| 11 local items = {}; | |
| 12 rtbl.items = items; | |
| 13 | |
| 14 local function notify(event_type, hash) | |
| 15 local handler = handlers[event_type]; | |
| 16 if not handler then return; end | |
| 17 handler(hash); | |
| 18 end | |
| 19 | |
| 20 module:add_item("pubsub-subscription", { | |
| 21 service = rtbl_service_jid; | |
| 22 node = rtbl_node; | |
| 23 | |
| 24 -- Callbacks: | |
| 25 on_subscribed = function() | |
| 26 module:log("info", "RTBL active: %s:%s", rtbl_service_jid, rtbl_node); | |
| 27 rtbl.subscribed = true; | |
| 28 end; | |
| 29 | |
| 30 on_error = function(err) | |
| 31 module:log( | |
| 32 "error", | |
| 33 "Failed to subscribe to RTBL: %s:%s %s::%s: %s", | |
| 34 rtbl_service_jid, | |
| 35 rtbl_node, | |
| 36 err.type, | |
| 37 err.condition, | |
| 38 err.text | |
| 39 ); | |
| 40 rtbl.subscribed = false; | |
| 41 rtbl.error = err; | |
| 42 end; | |
| 43 | |
| 44 on_item = function(event) | |
| 45 local hash = event.item.attr.id; | |
| 46 if not hash then return; end | |
| 47 module:log("debug", "Received new hash from %s:%s: %s", rtbl_service_jid, rtbl_node, hash); | |
| 48 items[hash] = true; | |
| 49 notify("added", hash); | |
| 50 end; | |
| 51 | |
| 52 on_retract = function (event) | |
| 53 local hash = event.item.attr.id; | |
| 54 if not hash then return; end | |
| 55 module:log("debug", "Retracted hash from %s:%s: %s", rtbl_service_jid, rtbl_node, hash); | |
| 56 items[hash] = nil; | |
| 57 notify("removed", hash); | |
| 58 end; | |
| 59 | |
| 60 purge = function() | |
| 61 module:log("debug", "Purge all hashes from %s:%s", rtbl_service_jid, rtbl_node); | |
| 62 for hash in pairs(items) do | |
| 63 items[hash] = nil; | |
| 64 notify("removed", hash); | |
| 65 end | |
| 66 end; | |
| 67 }); | |
| 68 | |
| 69 local request_id = "rtbl-request-"..id.short(); | |
| 70 | |
| 71 local function request_list() | |
| 72 local items_request = st.iq({ to = rtbl_service_jid, from = module.host, type = "get", id = request_id }) | |
| 73 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | |
| 74 :tag("items", { node = rtbl_node }):up() | |
| 75 :up(); | |
| 76 module:send(items_request); | |
| 77 end | |
| 78 | |
| 79 local function update_list(event) | |
| 80 local from_jid = event.stanza.attr.from; | |
| 81 if from_jid ~= rtbl_service_jid then | |
| 82 module:log("debug", "Ignoring RTBL response from unknown sender: %s", from_jid); | |
| 83 return; | |
| 84 end | |
| 85 local items_el = event.stanza:find("{http://jabber.org/protocol/pubsub}pubsub/items"); | |
| 86 if not items_el then | |
| 87 module:log("warn", "Invalid items response from RTBL service %s:%s", rtbl_service_jid, rtbl_node); | |
| 88 return; | |
| 89 end | |
| 90 | |
| 91 local old_entries = set.new(array.collect(it.keys(items))); | |
| 92 | |
| 93 local n_added, n_removed, n_total = 0, 0, 0; | |
| 94 for item in items_el:childtags("item") do | |
| 95 local hash = item.attr.id; | |
| 96 if hash then | |
| 97 n_total = n_total + 1; | |
| 98 if not old_entries:contains(hash) then | |
| 99 -- New entry | |
| 100 n_added = n_added + 1; | |
| 101 items[hash] = true; | |
| 102 notify("added", hash); | |
| 103 else | |
| 104 -- Entry already existed | |
| 105 old_entries:remove(hash); | |
| 106 end | |
| 107 end | |
| 108 end | |
| 109 | |
| 110 -- Remove old entries that weren't in the received list | |
| 111 for hash in old_entries do | |
| 112 n_removed = n_removed + 1; | |
| 113 items[hash] = nil; | |
| 114 notify("removed", hash); | |
| 115 end | |
| 116 | |
| 117 module:log("info", "%d RTBL entries received from %s:%s (%d added, %d removed)", n_total, from_jid, rtbl_node, n_added, n_removed); | |
| 118 return true; | |
| 119 end | |
| 120 | |
| 121 module:hook("iq-result/host/"..request_id, update_list); | |
| 122 module:add_timer(0, request_list); | |
| 123 | |
| 124 return rtbl; | |
| 125 end | |
| 126 | |
| 127 return { | |
| 128 new_rtbl_subscription = new_rtbl_subscription; | |
| 129 }; |
