Software / code / prosody-modules
Comparison
mod_cloud_notify/mod_cloud_notify.lua @ 3055:6abee021d9db
mod_cloud_notify: Limit number of devices to 5 and change some default settings
| author | tmolitor <thilo@eightysoft.de> |
|---|---|
| date | Mon, 28 May 2018 05:28:07 +0200 |
| parent | 3010:7ee59f417c16 |
| child | 3078:6b860de18a53 |
comparison
equal
deleted
inserted
replaced
| 3054:5e94061c1aa7 | 3055:6abee021d9db |
|---|---|
| 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 -- Copyright (C) 2017-2018 Thilo Molitor |
| 4 -- | 4 -- |
| 5 -- This file is MIT/X11 licensed. | 5 -- This file is MIT/X11 licensed. |
| 6 | 6 |
| 7 local t_insert = table.insert; | 7 local t_insert = table.insert; |
| 8 local s_match = string.match; | 8 local s_match = string.match; |
| 9 local s_sub = string.sub; | 9 local s_sub = string.sub; |
| 10 local os_time = os.time; | |
| 10 local st = require"util.stanza"; | 11 local st = require"util.stanza"; |
| 11 local jid = require"util.jid"; | 12 local jid = require"util.jid"; |
| 12 local dataform = require"util.dataforms".new; | 13 local dataform = require"util.dataforms".new; |
| 13 local filters = require"util.filters"; | 14 local filters = require"util.filters"; |
| 14 local hashes = require"util.hashes"; | 15 local hashes = require"util.hashes"; |
| 16 local xmlns_push = "urn:xmpp:push:0"; | 17 local xmlns_push = "urn:xmpp:push:0"; |
| 17 | 18 |
| 18 -- configuration | 19 -- configuration |
| 19 local include_body = module:get_option_boolean("push_notification_with_body", false); | 20 local include_body = module:get_option_boolean("push_notification_with_body", false); |
| 20 local include_sender = module:get_option_boolean("push_notification_with_sender", false); | 21 local include_sender = module:get_option_boolean("push_notification_with_sender", false); |
| 21 local max_push_errors = module:get_option_number("push_max_errors", 50); | 22 local max_push_errors = module:get_option_number("push_max_errors", 16); |
| 22 local dummy_body = module:get_option_string("push_notification_important_body", ""); | 23 local max_push_devices = module:get_option_number("push_max_devices", 5); |
| 24 local dummy_body = module:get_option_string("push_notification_important_body", "New Message!"); | |
| 23 | 25 |
| 24 local host_sessions = prosody.hosts[module.host].sessions; | 26 local host_sessions = prosody.hosts[module.host].sessions; |
| 25 local push_errors = {}; | 27 local push_errors = {}; |
| 26 local id2node = {}; | 28 local id2node = {}; |
| 29 | |
| 30 -- ordered table iterator, allow to iterate on the natural order of the keys of a table, | |
| 31 -- see http://lua-users.org/wiki/SortedIteration | |
| 32 local function __genOrderedIndex( t ) | |
| 33 local orderedIndex = {} | |
| 34 for key in pairs(t) do | |
| 35 table.insert( orderedIndex, key ) | |
| 36 end | |
| 37 -- sort in reverse order (newest one first) | |
| 38 table.sort( orderedIndex, function(a, b) | |
| 39 if a == nil or t[a] == nil or b == nil or t[b] == nil then return false end | |
| 40 -- only one timestamp given, this is the newer one | |
| 41 if t[a].timestamp ~= nil and t[b].timestamp == nil then return true end | |
| 42 if t[a].timestamp == nil and t[b].timestamp ~= nil then return false end | |
| 43 -- both timestamps given, sort normally | |
| 44 if t[a].timestamp ~= nil and t[b].timestamp ~= nil then return t[a].timestamp > t[b].timestamp end | |
| 45 return false -- normally not reached | |
| 46 end) | |
| 47 return orderedIndex | |
| 48 end | |
| 49 local function orderedNext(t, state) | |
| 50 -- Equivalent of the next function, but returns the keys in timestamp | |
| 51 -- order. We use a temporary ordered key table that is stored in the | |
| 52 -- table being iterated. | |
| 53 | |
| 54 local key = nil | |
| 55 --print("orderedNext: state = "..tostring(state) ) | |
| 56 if state == nil then | |
| 57 -- the first time, generate the index | |
| 58 t.__orderedIndex = __genOrderedIndex( t ) | |
| 59 key = t.__orderedIndex[1] | |
| 60 else | |
| 61 -- fetch the next value | |
| 62 for i = 1,table.getn(t.__orderedIndex) do | |
| 63 if t.__orderedIndex[i] == state then | |
| 64 key = t.__orderedIndex[i+1] | |
| 65 end | |
| 66 end | |
| 67 end | |
| 68 | |
| 69 if key then | |
| 70 return key, t[key] | |
| 71 end | |
| 72 | |
| 73 -- no more value to return, cleanup | |
| 74 t.__orderedIndex = nil | |
| 75 return | |
| 76 end | |
| 77 local function orderedPairs(t) | |
| 78 -- Equivalent of the pairs() function on tables. Allows to iterate | |
| 79 -- in order | |
| 80 return orderedNext, t, nil | |
| 81 end | |
| 82 | |
| 83 -- small helper function to return new table with only "maximum" elements containing only the newest entries | |
| 84 local function reduce_table(table, maximum) | |
| 85 local count = 0; | |
| 86 local result = {}; | |
| 87 for key, value in orderedPairs(table) do | |
| 88 count = count + 1; | |
| 89 if count > maximum then break end | |
| 90 result[key] = value; | |
| 91 end | |
| 92 return result; | |
| 93 end | |
| 27 | 94 |
| 28 -- For keeping state across reloads while caching reads | 95 -- For keeping state across reloads while caching reads |
| 29 local push_store = (function() | 96 local push_store = (function() |
| 30 local store = module:open_store(); | 97 local store = module:open_store(); |
| 31 local push_services = {}; | 98 local push_services = {}; |
| 42 end | 109 end |
| 43 if not push_services[user] then push_services[user] = {} end | 110 if not push_services[user] then push_services[user] = {} end |
| 44 return push_services[user], true; | 111 return push_services[user], true; |
| 45 end | 112 end |
| 46 function api:set(user, data) | 113 function api:set(user, data) |
| 47 push_services[user] = data; | 114 push_services[user] = reduce_table(data, max_push_devices); |
| 48 local ok, err = store:set(user, push_services[user]); | 115 local ok, err = store:set(user, push_services[user]); |
| 49 if not ok then | 116 if not ok then |
| 50 module:log("error", "Error writing push notification storage for user '%s': %s", user, tostring(err)); | 117 module:log("error", "Error writing push notification storage for user '%s': %s", user, tostring(err)); |
| 51 return false; | 118 return false; |
| 52 end | 119 end |
| 158 local push_service = { | 225 local push_service = { |
| 159 jid = push_jid; | 226 jid = push_jid; |
| 160 node = push_node; | 227 node = push_node; |
| 161 include_payload = include_payload; | 228 include_payload = include_payload; |
| 162 options = publish_options and st.preserialize(publish_options); | 229 options = publish_options and st.preserialize(publish_options); |
| 230 timestamp = os_time(); | |
| 163 }; | 231 }; |
| 164 local ok = push_store:set_identifier(origin.username, push_identifier, push_service); | 232 local ok = push_store:set_identifier(origin.username, push_identifier, push_service); |
| 165 if not ok then | 233 if not ok then |
| 166 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); | 234 origin.send(st.error_reply(stanza, "wait", "internal-server-error")); |
| 167 else | 235 else |
| 416 end | 484 end |
| 417 | 485 |
| 418 -- archive message added | 486 -- archive message added |
| 419 local function archive_message_added(event) | 487 local function archive_message_added(event) |
| 420 -- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id } | 488 -- event is: { origin = origin, stanza = stanza, for_user = store_user, id = id } |
| 421 -- only notify for new mam messages when at least one device is only | 489 -- only notify for new mam messages when at least one device is online |
| 422 if not event.for_user or not host_sessions[event.for_user] then return; end | 490 if not event.for_user or not host_sessions[event.for_user] then return; end |
| 423 local stanza = event.stanza; | 491 local stanza = event.stanza; |
| 424 local user_session = host_sessions[event.for_user].sessions; | 492 local user_session = host_sessions[event.for_user].sessions; |
| 425 local to = stanza.attr.to; | 493 local to = stanza.attr.to; |
| 426 to = to and jid.split(to) or event.origin.username; | 494 to = to and jid.split(to) or event.origin.username; |