Mercurial > prosody-modules
comparison mod_cloud_notify/mod_cloud_notify.lua @ 2609:6ab46ff685d0
mod_cloud_notify: Respect Daniel's business rules and remove endpoints on error
Daniel's business rules can be found here: https://mail.jabber.org/pipermail/standards/2016-February/030925.html
All implementation changes are documented in depth in the file business_rules.markdown
| author | tmolitor <thilo@eightysoft.de> |
|---|---|
| date | Sat, 11 Mar 2017 01:42:45 +0100 |
| parents | 2e641ab995b3 |
| children | 8c6562f16496 |
comparison
equal
deleted
inserted
replaced
| 2608:362ca94192ee | 2609:6ab46ff685d0 |
|---|---|
| 1 -- XEP-0357: Push (aka: My mobile OS vendor won't let me have persistent TCP connections) | 1 -- XEP-0357: Push (aka: My mobile OS vendor won't let me have persistent TCP connections) |
| 2 -- Copyright (C) 2015-2016 Kim Alvefur | 2 -- Copyright (C) 2015-2016 Kim Alvefur |
| 3 -- Copyright (C) 2017 Thilo Molitor | |
| 3 -- | 4 -- |
| 4 -- This file is MIT/X11 licensed. | 5 -- This file is MIT/X11 licensed. |
| 5 | 6 |
| 6 local st = require"util.stanza"; | 7 local st = require"util.stanza"; |
| 7 local jid = require"util.jid"; | 8 local jid = require"util.jid"; |
| 11 local xmlns_push = "urn:xmpp:push:0"; | 12 local xmlns_push = "urn:xmpp:push:0"; |
| 12 | 13 |
| 13 -- configuration | 14 -- configuration |
| 14 local include_body = module:get_option_boolean("push_notification_with_body", false); | 15 local include_body = module:get_option_boolean("push_notification_with_body", false); |
| 15 local include_sender = module:get_option_boolean("push_notification_with_sender", false); | 16 local include_sender = module:get_option_boolean("push_notification_with_sender", false); |
| 16 | 17 local max_push_errors = module:get_option_number("push_max_errors", 50); |
| 17 -- For keeping state across reloads | 18 |
| 18 local push_enabled = module:open_store(); | 19 local host_sessions = prosody.hosts[module.host].sessions; |
| 19 -- TODO map store would be better here | 20 local push_errors = {}; |
| 21 | |
| 22 -- For keeping state across reloads while caching reads | |
| 23 local push_store = (function() | |
| 24 local store = module:open_store(); | |
| 25 local push_services = {}; | |
| 26 local api = {}; | |
| 27 function api:get(user) | |
| 28 if not push_services[user] then | |
| 29 local err; | |
| 30 push_services[user], err = store:get(user); | |
| 31 if not push_services[user] and err then | |
| 32 module:log("warn", "Error reading push notification storage for user '%s': %s", user, tostring(err)); | |
| 33 push_services[user] = {}; | |
| 34 return push_services[user], false; | |
| 35 end | |
| 36 end | |
| 37 if not push_services[user] then push_services[user] = {} end | |
| 38 return push_services[user], true; | |
| 39 end | |
| 40 function api:set(user, data) | |
| 41 push_services[user] = data; | |
| 42 local ok, err = store:set(user, push_services[user]); | |
| 43 if not ok then | |
| 44 module:log("error", "Error writing push notification storage for user '%s': %s", user, tostring(err)); | |
| 45 return false; | |
| 46 end | |
| 47 return true; | |
| 48 end | |
| 49 function api:set_identifier(user, push_identifier, data) | |
| 50 local services = self:get(user); | |
| 51 services[push_identifier] = data; | |
| 52 return self:set(user, services); | |
| 53 end | |
| 54 return api; | |
| 55 end)(); | |
| 56 | |
| 57 local function handle_push_error(event) | |
| 58 local stanza = event.stanza; | |
| 59 local error_type, condition = stanza:get_error(); | |
| 60 local push_identifier = stanza.attr.id; | |
| 61 local node = jid.split(stanza.attr.to); | |
| 62 local from = stanza.attr.from; | |
| 63 local user_push_services = push_store:get(node); | |
| 64 | |
| 65 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and error_type ~= "wait" then | |
| 66 push_errors[push_identifier] = push_errors[push_identifier] + 1; | |
| 67 module:log("info", "Got error of type '%s' (%s) for identifier '%s':" | |
| 68 .."error count for this identifier is now at %s", error_type, condition, push_identifier, | |
| 69 tostring(push_errors[push_identifier])); | |
| 70 if push_errors[push_identifier] >= max_push_errors then | |
| 71 module:log("warn", "Disabling push notifications for identifier '%s'", push_identifier); | |
| 72 -- remove push settings from sessions | |
| 73 for _, session in pairs(host_sessions[node].sessions) do | |
| 74 if session.push_identifier == push_identifier then | |
| 75 session.push_identifier = nil; | |
| 76 session.push_settings = nil; | |
| 77 end | |
| 78 end | |
| 79 -- save changed global config | |
| 80 push_store:set_identifier(node, push_identifier, nil); | |
| 81 push_errors[push_identifier] = nil; | |
| 82 -- unhook iq handlers for this identifier | |
| 83 module:unhook("iq-error/bare/"..push_identifier, handle_push_error); | |
| 84 module:unhook("iq-result/bare/"..push_identifier, handle_push_success); | |
| 85 end | |
| 86 end | |
| 87 return true; | |
| 88 end | |
| 89 | |
| 90 local function handle_push_success(event) | |
| 91 local stanza = event.stanza; | |
| 92 local push_identifier = stanza.attr.id; | |
| 93 local node = jid.split(stanza.attr.to); | |
| 94 local from = stanza.attr.from; | |
| 95 local user_push_services = push_store:get(node); | |
| 96 | |
| 97 if user_push_services[push_identifier] and user_push_services[push_identifier].jid == from and push_errors[push_identifier] then | |
| 98 push_errors[push_identifier] = 0; | |
| 99 module:log("debug", "Push succeeded, error count for identifier '%s' is now at %s", push_identifier, tostring(push_errors[push_identifier])); | |
| 100 end | |
| 101 return true; | |
| 102 end | |
| 20 | 103 |
| 21 -- http://xmpp.org/extensions/xep-0357.html#disco | 104 -- http://xmpp.org/extensions/xep-0357.html#disco |
| 22 module:hook("account-disco-info", function(event) | 105 local function account_dico_info(event) |
| 23 (event.reply or event.stanza):tag("feature", {var=xmlns_push}):up(); | 106 (event.reply or event.stanza):tag("feature", {var=xmlns_push}):up(); |
| 24 end); | 107 end |
| 108 module:hook("account-disco-info", account_dico_info); | |
| 25 | 109 |
| 26 -- http://xmpp.org/extensions/xep-0357.html#enabling | 110 -- http://xmpp.org/extensions/xep-0357.html#enabling |
| 27 module:hook("iq-set/self/"..xmlns_push..":enable", function (event) | 111 local function push_enable(event) |
| 28 local origin, stanza = event.origin, event.stanza; | 112 local origin, stanza = event.origin, event.stanza; |
| 29 local enable = stanza.tags[1]; | 113 local enable = stanza.tags[1]; |
| 30 origin.log("debug", "Attempting to enable push notifications"); | 114 origin.log("debug", "Attempting to enable push notifications"); |
| 31 -- MUST contain a 'jid' attribute of the XMPP Push Service being enabled | 115 -- MUST contain a 'jid' attribute of the XMPP Push Service being enabled |
| 32 local push_jid = enable.attr.jid; | 116 local push_jid = enable.attr.jid; |
| 40 local publish_options = enable:get_child("x", "jabber:x:data"); | 124 local publish_options = enable:get_child("x", "jabber:x:data"); |
| 41 if not publish_options then | 125 if not publish_options then |
| 42 -- Could be intentional | 126 -- Could be intentional |
| 43 origin.log("debug", "No publish options in request"); | 127 origin.log("debug", "No publish options in request"); |
| 44 end | 128 end |
| 45 local user_push_services, rerr = push_enabled:get(origin.username); | 129 local push_identifier = push_jid .. "<" .. (push_node or ""); |
| 46 if not user_push_services then | 130 local push_service = { |
| 47 if rerr then | |
| 48 module:log("warn", "Error reading push notification storage: %s", rerr); | |
| 49 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | |
| 50 return true; | |
| 51 end | |
| 52 user_push_services = {}; | |
| 53 end | |
| 54 user_push_services[push_jid .. "<" .. (push_node or "")] = { | |
| 55 jid = push_jid; | 131 jid = push_jid; |
| 56 node = push_node; | 132 node = push_node; |
| 57 count = 0; | 133 count = 0; |
| 58 options = publish_options and st.preserialize(publish_options); | 134 options = publish_options and st.preserialize(publish_options); |
| 59 }; | 135 }; |
| 60 local ok, err = push_enabled:set(origin.username, user_push_services); | 136 local ok = push_store:set_identifier(origin.username, push_identifier, push_service); |
| 61 if not ok then | 137 if not ok then |
| 62 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | 138 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); |
| 63 else | 139 else |
| 64 origin.log("info", "Push notifications enabled"); | 140 origin.push_identifier = push_identifier; |
| 141 origin.push_settings = push_service; | |
| 142 origin.log("info", "Push notifications enabled (%s)", tostring(origin.push_identifier)); | |
| 65 origin.send(st.reply(stanza)); | 143 origin.send(st.reply(stanza)); |
| 66 end | 144 end |
| 67 return true; | 145 return true; |
| 68 end); | 146 end |
| 147 module:hook("iq-set/self/"..xmlns_push..":enable", push_enable); | |
| 69 | 148 |
| 70 -- http://xmpp.org/extensions/xep-0357.html#disabling | 149 -- http://xmpp.org/extensions/xep-0357.html#disabling |
| 71 module:hook("iq-set/self/"..xmlns_push..":disable", function (event) | 150 local function push_disable(event) |
| 72 local origin, stanza = event.origin, event.stanza; | 151 local origin, stanza = event.origin, event.stanza; |
| 73 local push_jid = stanza.tags[1].attr.jid; -- MUST include a 'jid' attribute | 152 local push_jid = stanza.tags[1].attr.jid; -- MUST include a 'jid' attribute |
| 74 local push_node = stanza.tags[1].attr.node; -- A 'node' attribute MAY be included | 153 local push_node = stanza.tags[1].attr.node; -- A 'node' attribute MAY be included |
| 75 if not push_jid then | 154 if not push_jid then |
| 76 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); | 155 origin.send(st.error_reply(stanza, "modify", "bad-request", "Missing jid")); |
| 77 return true; | 156 return true; |
| 78 end | 157 end |
| 79 local user_push_services = push_enabled:get(origin.username); | 158 local user_push_services = push_store:get(origin.username); |
| 80 for key, push_info in pairs(user_push_services) do | 159 for key, push_info in pairs(user_push_services) do |
| 81 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then | 160 if push_info.jid == push_jid and (not push_node or push_info.node == push_node) then |
| 161 origin.log("info", "Push notifications disabled (%s)", tostring(key)); | |
| 162 if origin.push_identifier == key then | |
| 163 origin.push_identifier = nil; | |
| 164 origin.push_settings = nil; | |
| 165 end | |
| 82 user_push_services[key] = nil; | 166 user_push_services[key] = nil; |
| 83 end | 167 push_errors[key] = nil; |
| 84 end | 168 module:unhook("iq-error/bare/"..key, handle_push_error); |
| 85 origin.send(st.reply(stanza)); | 169 module:unhook("iq-result/bare/"..key, handle_push_success); |
| 170 end | |
| 171 end | |
| 172 local ok = push_store:set(origin.username, user_push_services); | |
| 173 if not ok then | |
| 174 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | |
| 175 else | |
| 176 origin.send(st.reply(stanza)); | |
| 177 end | |
| 86 return true; | 178 return true; |
| 87 end); | 179 end |
| 180 module:hook("iq-set/self/"..xmlns_push..":disable", push_disable); | |
| 88 | 181 |
| 89 local push_form = dataform { | 182 local push_form = dataform { |
| 90 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; | 183 { name = "FORM_TYPE"; type = "hidden"; value = "urn:xmpp:push:summary"; }; |
| 91 { name = "message-count"; type = "text-single"; }; | 184 { name = "message-count"; type = "text-single"; }; |
| 92 { name = "pending-subscription-count"; type = "text-single"; }; | 185 { name = "pending-subscription-count"; type = "text-single"; }; |
| 93 { name = "last-message-sender"; type = "jid-single"; }; | 186 { name = "last-message-sender"; type = "jid-single"; }; |
| 94 { name = "last-message-body"; type = "text-single"; }; | 187 { name = "last-message-body"; type = "text-single"; }; |
| 95 }; | 188 }; |
| 96 | 189 |
| 97 -- http://xmpp.org/extensions/xep-0357.html#publishing | 190 -- http://xmpp.org/extensions/xep-0357.html#publishing |
| 98 local function handle_notify_request(origin, stanza) | 191 local function handle_notify_request(stanza, node, user_push_services) |
| 99 local to = stanza.attr.to; | 192 if not user_push_services or not #user_push_services then return end |
| 100 local node = to and jid.split(to) or origin.username; | 193 |
| 101 local user_push_services = push_enabled:get(node); | 194 if stanza and stanza._notify then |
| 102 if not user_push_services then return end | 195 module:log("debug", "Already sent push notification to %s@%s for this stanza, not doing it again", node, module.host); |
| 103 | 196 return; |
| 104 for _, push_info in pairs(user_push_services) do | 197 end |
| 198 if stanza then | |
| 199 stanza._notify = true; | |
| 200 end | |
| 201 | |
| 202 for push_identifier, push_info in pairs(user_push_services) do | |
| 203 -- increment count and save it | |
| 105 push_info.count = push_info.count + 1; | 204 push_info.count = push_info.count + 1; |
| 106 local push_jid, push_node = push_info.jid, push_info.node; | 205 push_store:set_identifier(node, push_identifier, push_info); |
| 107 local push_publish = st.iq({ to = push_jid, from = node .. "@" .. module.host, type = "set", id = "push" }) | 206 -- construct push stanza |
| 207 local push_publish = st.iq({ to = push_info.jid, from = node .. "@" .. module.host, type = "set", id = push_identifier }) | |
| 108 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) | 208 :tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" }) |
| 109 :tag("publish", { node = push_node }) | 209 :tag("publish", { node = push_info.node }) |
| 110 :tag("item") | 210 :tag("item") |
| 111 :tag("notification", { xmlns = xmlns_push }); | 211 :tag("notification", { xmlns = xmlns_push }); |
| 112 local form_data = { | 212 local form_data = { |
| 113 ["message-count"] = tostring(push_info.count); | 213 ["message-count"] = tostring(push_info.count); |
| 114 }; | 214 }; |
| 115 if include_sender then | 215 if stanza and include_sender then |
| 116 form_data["last-message-sender"] = stanza.attr.from; | 216 form_data["last-message-sender"] = stanza.attr.from; |
| 117 end | 217 end |
| 118 if include_body then | 218 if stanza and include_body then |
| 119 form_data["last-message-body"] = stanza:get_child_text("body"); | 219 form_data["last-message-body"] = stanza:get_child_text("body"); |
| 120 end | 220 end |
| 121 push_publish:add_child(push_form:form(form_data)); | 221 push_publish:add_child(push_form:form(form_data)); |
| 122 push_publish:up(); -- / notification | 222 push_publish:up(); -- / notification |
| 123 push_publish:up(); -- / publish | 223 push_publish:up(); -- / publish |
| 124 push_publish:up(); -- / pubsub | 224 push_publish:up(); -- / pubsub |
| 125 if push_info.options then | 225 if push_info.options then |
| 126 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); | 226 push_publish:tag("publish-options"):add_child(st.deserialize(push_info.options)); |
| 127 end | 227 end |
| 128 module:log("debug", "Sending push notification for %s@%s to %s", node, module.host, push_jid); | 228 -- send out push |
| 229 module:log("debug", "Sending push notification for %s@%s to %s (%s)", node, module.host, push_info.jid, tostring(push_info.node)); | |
| 230 -- handle push errors for this node | |
| 231 if push_errors[push_identifier] == nil then | |
| 232 push_errors[push_identifier] = 0; | |
| 233 module:hook("iq-error/bare/"..push_identifier, handle_push_error); | |
| 234 module:hook("iq-result/bare/"..push_identifier, handle_push_success); | |
| 235 end | |
| 129 module:send(push_publish); | 236 module:send(push_publish); |
| 130 end | 237 end |
| 131 push_enabled:set(node, user_push_services); | 238 end |
| 239 | |
| 240 -- small helper function to extract relevant push settings | |
| 241 local function get_push_settings(stanza, session) | |
| 242 local to = stanza.attr.to; | |
| 243 local node = to and jid.split(to) or session.username; | |
| 244 local user_push_services = push_store:get(node); | |
| 245 return node, user_push_services; | |
| 132 end | 246 end |
| 133 | 247 |
| 134 -- publish on offline message | 248 -- publish on offline message |
| 135 module:hook("message/offline/handle", function(event) | 249 module:hook("message/offline/handle", function(event) |
| 136 if event.stanza._notify then | 250 local node, user_push_services = get_push_settings(event.stanza, event.origin); |
| 137 event.stanza._notify = nil; | 251 return handle_notify_request(event.stanza, node, user_push_services); |
| 138 return; | |
| 139 end | |
| 140 return handle_notify_request(event.origin, event.stanza); | |
| 141 end, 1); | 252 end, 1); |
| 142 | 253 |
| 143 -- publish on unacked smacks message | 254 -- publish on unacked smacks message |
| 144 local function process_new_stanza(stanza, session) | 255 local function process_smacks_stanza(stanza, session) |
| 145 if getmetatable(stanza) ~= st.stanza_mt then | 256 if session.push_identifier then |
| 146 return stanza; -- Things we don't want to touch | 257 session.log("debug", "Invoking cloud handle_notify_request for smacks queued stanza..."); |
| 147 end | 258 local user_push_services = {[session.push_identifier] = session.push_settings}; |
| 148 if stanza.name == "message" and stanza.attr.xmlns == nil and | 259 local node = get_push_settings(stanza, session); |
| 149 ( stanza.attr.type == "chat" or ( stanza.attr.type or "normal" ) == "normal" ) and | 260 handle_notify_request(stanza, node, user_push_services); |
| 150 -- not already notified via cloud | |
| 151 not stanza._notify then | |
| 152 stanza._notify = true; | |
| 153 session.log("debug", "Invoking cloud handle_notify_request for new smacks hibernated stanza..."); | |
| 154 handle_notify_request(session, stanza) | |
| 155 end | 261 end |
| 156 return stanza; | 262 return stanza; |
| 157 end | 263 end |
| 158 | 264 |
| 159 -- smacks hibernation is started | 265 -- smacks hibernation is started |
| 160 local function hibernate_session(event) | 266 local function hibernate_session(event) |
| 161 local session = event.origin; | 267 local session = event.origin; |
| 162 local queue = event.queue; | 268 local queue = event.queue; |
| 163 -- process unacked stanzas | 269 -- process unacked stanzas |
| 164 for i=1,#queue do | 270 for i=1,#queue do |
| 165 process_new_stanza(queue[i], session); | 271 process_smacks_stanza(queue[i], session); |
| 166 end | 272 end |
| 167 -- process future unacked (hibernated) stanzas | 273 -- process future unacked (hibernated) stanzas |
| 168 filters.add_filter(session, "stanzas/out", process_new_stanza); | 274 filters.add_filter(session, "stanzas/out", process_smacks_stanza); |
| 169 end | 275 end |
| 170 | 276 |
| 171 -- smacks hibernation is ended | 277 -- smacks hibernation is ended |
| 172 local function restore_session(event) | 278 local function restore_session(event) |
| 173 local session = event.origin; | 279 local session = event.resumed; |
| 174 filters.remove_filter(session, "stanzas/out", process_new_stanza); | 280 if session then -- older smacks module versions send only the "intermediate" session in event.session and no session.resumed one |
| 281 filters.remove_filter(session, "stanzas/out", process_smacks_stanza); | |
| 282 -- this means the counter of outstanding push messages can be reset as well | |
| 283 if session.push_settings then | |
| 284 session.push_settings.count = 0; | |
| 285 push_store:set_identifier(session.username, session.push_identifier, session.push_settings); | |
| 286 end | |
| 287 end | |
| 175 end | 288 end |
| 176 | 289 |
| 177 -- smacks ack is delayed | 290 -- smacks ack is delayed |
| 178 local function ack_delayed(event) | 291 local function ack_delayed(event) |
| 179 local session = event.origin; | 292 local session = event.origin; |
| 180 local queue = event.queue; | 293 local queue = event.queue; |
| 181 -- process unacked stanzas (process_new_stanza will only send push requests for new messages) | 294 -- process unacked stanzas (handle_notify_request() will only send push requests for new stanzas) |
| 182 for i=1,#queue do | 295 for i=1,#queue do |
| 183 process_new_stanza(queue[i], session); | 296 process_smacks_stanza(queue[i], session); |
| 297 end | |
| 298 end | |
| 299 | |
| 300 -- archive message added | |
| 301 local function archive_message_added(event) | |
| 302 -- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id } | |
| 303 -- only notify for new mam messages when at least one device is only | |
| 304 if not event.for_user or not host_sessions[event.for_user] then return; end | |
| 305 local stanza = event.stanza; | |
| 306 local user_session = host_sessions[event.for_user].sessions; | |
| 307 local to = stanza.attr.to; | |
| 308 to = to and jid.split(to) or event.origin.username; | |
| 309 | |
| 310 -- only notify if the stanza destination is the mam user we store it for | |
| 311 if event.for_user == to then | |
| 312 local user_push_services = push_store:get(to); | |
| 313 if not #user_push_services then return end | |
| 314 | |
| 315 -- only notify nodes with no active sessions (smacks is counted as active and handled separate) | |
| 316 local notify_push_sevices = {}; | |
| 317 for identifier, push_info in pairs(user_push_services) do | |
| 318 local identifier_found = nil; | |
| 319 for _, session in pairs(user_session) do | |
| 320 -- module:log("debug", "searching for '%s': identifier '%s' for session %s", tostring(identifier), tostring(session.push_identifier), tostring(session.full_jid)); | |
| 321 if session.push_identifier == identifier then | |
| 322 identifier_found = session; | |
| 323 break; | |
| 324 end | |
| 325 end | |
| 326 if identifier_found then | |
| 327 identifier_found.log("debug", "Not notifying '%s' of new MAM stanza (session still alive)", identifier); | |
| 328 else | |
| 329 notify_push_sevices[identifier] = push_info; | |
| 330 end | |
| 331 end | |
| 332 | |
| 333 return handle_notify_request(event.stanza, to, notify_push_sevices); | |
| 184 end | 334 end |
| 185 end | 335 end |
| 186 | 336 |
| 187 module:hook("smacks-hibernation-start", hibernate_session); | 337 module:hook("smacks-hibernation-start", hibernate_session); |
| 188 module:hook("smacks-hibernation-end", restore_session); | 338 module:hook("smacks-hibernation-end", restore_session); |
| 189 module:hook("smacks-ack-delayed", ack_delayed); | 339 module:hook("smacks-ack-delayed", ack_delayed); |
| 190 | 340 module:hook("archive-message-added", archive_message_added); |
| 191 | 341 |
| 192 module:hook("message/offline/broadcast", function(event) | 342 local function send_ping(event) |
| 193 local origin = event.origin; | 343 local user = event.user; |
| 194 local user_push_services = push_enabled:get(origin.username); | 344 local user_push_services = push_store:get(user); |
| 195 if not user_push_services then return end | 345 local push_services = event.push_services or user_push_services; |
| 196 | 346 return handle_notify_request(nil, user, push_services); |
| 197 for _, push_info in pairs(user_push_services) do | 347 end |
| 198 if push_info then | 348 -- can be used by other modules to ping one or more (or all) push endpoints |
| 199 push_info.count = 0; | 349 module:hook("cloud-notify-ping", send_ping); |
| 200 end | 350 |
| 201 end | 351 -- TODO: this has to be done on first connect not on offline broadcast, else the counter will be incorrect |
| 202 push_enabled:set(origin.username, user_push_services); | 352 -- TODO: it seems this is already done, so this could be safely removed, couldn't it? |
| 203 end, 1); | 353 -- module:hook("message/offline/broadcast", function(event) |
| 354 -- local origin = event.origin; | |
| 355 -- local user_push_services = push_store:get(origin.username); | |
| 356 -- if not #user_push_services then return end | |
| 357 -- | |
| 358 -- for _, push_info in pairs(user_push_services) do | |
| 359 -- if push_info then | |
| 360 -- push_info.count = 0; | |
| 361 -- end | |
| 362 -- end | |
| 363 -- push_store:set(origin.username, user_push_services); | |
| 364 -- end, 1); | |
| 365 | |
| 366 function module.unload() | |
| 367 module:unhook("account-disco-info", account_dico_info); | |
| 368 module:unhook("iq-set/self/"..xmlns_push..":enable", push_enable); | |
| 369 module:unhook("iq-set/self/"..xmlns_push..":disable", push_disable); | |
| 370 | |
| 371 module:unhook("smacks-hibernation-start", hibernate_session); | |
| 372 module:unhook("smacks-hibernation-end", restore_session); | |
| 373 module:unhook("smacks-ack-delayed", ack_delayed); | |
| 374 module:unhook("archive-message-added", archive_message_added); | |
| 375 module:unhook("cloud-notify-ping", send_ping); | |
| 376 | |
| 377 for push_identifier, _ in pairs(push_errors) do | |
| 378 module:hook("iq-error/bare/"..push_identifier, handle_push_error); | |
| 379 module:hook("iq-result/bare/"..push_identifier, handle_push_success); | |
| 380 end | |
| 381 end |
