Software /
code /
verse
File
plugins/pubsub.lua @ 506:3610196c5e83 default tip
Merge with Zash.
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Sat, 08 Jul 2023 02:17:52 +0700 |
parent | 490:6b2f31da9610 |
line wrap: on
line source
local verse = require "verse"; local t_insert = table.insert; local xmlns_pubsub = "http://jabber.org/protocol/pubsub"; local xmlns_pubsub_owner = "http://jabber.org/protocol/pubsub#owner"; local xmlns_pubsub_event = "http://jabber.org/protocol/pubsub#event"; -- local xmlns_pubsub_errors = "http://jabber.org/protocol/pubsub#errors"; local pubsub = {}; local pubsub_mt = { __index = pubsub }; function verse.plugins.pubsub(stream) stream.pubsub = setmetatable({ stream = stream }, pubsub_mt); stream:hook("message", function (message) local m_from = message.attr.from; for pubsub_event in message:childtags("event", xmlns_pubsub_event) do local items = pubsub_event:get_child("items"); if items then local node = items.attr.node; for item in items:childtags("item") do stream:event("pubsub/event", { from = m_from; node = node; item = item; }); end for retract in items:childtags("retract") do stream:event("pubsub/retraction", { from = m_from; node = node; item = retract; }); end end end end); return true; end -- COMPAT function pubsub:create(server, node, callback) return self:service(server):node(node):create(nil, callback); end function pubsub:subscribe(server, node, jid, callback) return self:service(server):node(node):subscribe(jid, nil, callback); end function pubsub:publish(server, node, id, item, callback) return self:service(server):node(node):publish(id, nil, item, callback); end -------------------------------------------------------------------------- ---------------------New and improved PubSub interface-------------------- -------------------------------------------------------------------------- local pubsub_service = {}; local pubsub_service_mt = { __index = pubsub_service }; -- TODO should the property be named 'jid' instead? function pubsub:service(service) return setmetatable({ stream = self.stream, service = service }, pubsub_service_mt) end -- Helper function for iq+pubsub tags local function pubsub_iq(iq_type, to, ns, op, node, jid, item_id, op_attr_extra) local st = verse.iq{ type = iq_type or "get", to = to } :tag("pubsub", { xmlns = ns or xmlns_pubsub }) -- ns would be ..#owner local op_attr = { node = node, jid = jid }; if op_attr_extra then for k, v in pairs(op_attr_extra) do op_attr[k] = v; end end if op then st:tag(op, op_attr); end if item_id then st:tag("item", { id = item_id ~= true and item_id or nil }); end return st; end -- http://xmpp.org/extensions/xep-0060.html#entity-subscriptions function pubsub_service:subscriptions(callback) self.stream:send_iq(pubsub_iq(nil, self.service, nil, "subscriptions") , callback and function (result) if result.attr.type == "result" then local ps = result:get_child("pubsub", xmlns_pubsub); local subs = ps and ps:get_child("subscriptions"); local nodes = {}; if subs then for sub in subs:childtags("subscription") do local node = self:node(sub.attr.node) node.subscription = sub; node.subscribed_jid = sub.attr.jid; t_insert(nodes, node); -- FIXME Good enough? -- Or how about: -- nodes[node] = sub; end end callback(nodes); else callback(false, result:get_error()); end end or nil); end -- http://xmpp.org/extensions/xep-0060.html#entity-affiliations function pubsub_service:affiliations(callback) self.stream:send_iq(pubsub_iq(nil, self.service, nil, "affiliations") , callback and function (result) if result.attr.type == "result" then local ps = result:get_child("pubsub", xmlns_pubsub); local affils = ps and ps:get_child("affiliations") or {}; local nodes = {}; if affils then for affil in affils:childtags("affiliation") do local node = self:node(affil.attr.node) node.affiliation = affil; t_insert(nodes, node); -- nodes[node] = affil; end end callback(nodes); else callback(false, result:get_error()); end end or nil); end function pubsub_service:nodes(callback) self.stream:disco_items(self.service, nil, function(items, ...) if items then for i=1,#items do items[i] = self:node(items[i].node); end end callback(items, ...) end); end local pubsub_node = {}; local pubsub_node_mt = { __index = pubsub_node }; function pubsub_service:node(node) return setmetatable({ stream = self.stream, service = self.service, node = node }, pubsub_node_mt) end function pubsub_mt:__call(service, node) local s = self:service(service); return node and s:node(node) or s; end function pubsub_node:hook(callback, prio) self._hooks = self._hooks or setmetatable({}, { __mode = 'kv' }); local function hook(event) -- FIXME service == nil would mean anyone, -- publishing would be go to your bare jid. -- So if you're only interestied in your own -- events, hook your own bare jid. if (not event.service or event.from == self.service) and event.node == self.node then return callback(event) end end self._hooks[callback] = hook; self.stream:hook("pubsub/event", hook, prio); return hook; end function pubsub_node:unhook(callback) if callback then local hook = self._hooks[callback]; self.stream:unhook("pubsub/event", hook); elseif self._hooks then for hook in pairs(self._hooks) do self.stream:unhook("pubsub/event", hook); end end end function pubsub_node:create(config, callback) if config ~= nil then error("Not implemented yet."); else self.stream:send_iq(pubsub_iq("set", self.service, nil, "create", self.node), callback); end end -- <configure/> and <default/> rolled into one function pubsub_node:configure(config, callback) if config ~= nil then error("Not implemented yet."); --[[ if config == true then self.stream:send_iq(pubsub_iq("get", self.service, nil, "configure", self.node) , function(reply) local form = reply:get_child("pubsub"):get_child("configure"):get_cild("x"); local config = callback(require"prosody.util.dataforms".something(form)) self.stream:send_iq(pubsub_iq("set", config, ...)) end); end --]] -- fetch form and pass it to the callback -- which would process it and pass it back -- and then we submit it -- elseif type(config) == "table" then -- it's a form or stanza that we submit -- end -- this would be done for everything that needs a config end self.stream:send_iq(pubsub_iq("set", self.service, nil, config == nil and "default" or "configure", self.node), callback); end function pubsub_node:publish(id, options, node, callback) if options ~= nil then error("Node configuration is not implemented yet."); end self.stream:send_iq(pubsub_iq("set", self.service, nil, "publish", self.node, nil, id or true) :add_child(node) , callback); end function pubsub_node:subscribe(jid, options, callback) jid = jid or self.stream.jid; if options ~= nil then error("Subscription configuration is not implemented yet."); end self.stream:send_iq(pubsub_iq("set", self.service, nil, "subscribe", self.node, jid) , callback); end function pubsub_node:subscription(callback) error("Not implemented yet."); end function pubsub_node:affiliation(callback) error("Not implemented yet."); end function pubsub_node:unsubscribe(jid, callback) jid = jid or self.subscribed_jid or self.stream.jid; self.stream:send_iq(pubsub_iq("set", self.service, nil, "unsubscribe", self.node, jid) , callback); end function pubsub_node:configure_subscription(options, callback) error("Not implemented yet."); end function pubsub_node:items(full, callback) if full then return self:item(nil, callback); else self.stream:disco_items(self.service, self.node, callback); end end function pubsub_node:item(id, callback) self.stream:send_iq(pubsub_iq("get", self.service, nil, "items", self.node, nil, id) , callback); end function pubsub_node:retract(id, notify, callback) if type(notify) == "function" then -- COMPAT w/ older versions before 'notify' was added notify, callback = false, notify; end self.stream:send_iq( pubsub_iq( "set", self.service, nil, "retract", self.node, nil, id, { notify = notify and "1" or nil } ), callback ); end function pubsub_node:purge(notify, callback) self.stream:send_iq( pubsub_iq( "set", self.service, xmlns_pubsub_owner, "purge", self.node, nil, nil, { notify = notify and "1" or nil } ), callback ); end function pubsub_node:delete(redirect_uri, callback) assert(not redirect_uri, "Not implemented yet."); self.stream:send_iq(pubsub_iq("set", self.service, xmlns_pubsub_owner, "delete", self.node) , callback); end