Mercurial > prosody-modules
comparison mod_csi_battery_saver/mod_csi_battery_saver.lua @ 2606:538c54d2dab3
mod_csi_battery_saver: CSI module to save battery on mobile devices, based on mod_csi_pump
author | tmolitor <thilo@eightysoft.de> |
---|---|
date | Fri, 10 Mar 2017 01:32:53 +0100 |
parents | |
children | b5fae17e4403 |
comparison
equal
deleted
inserted
replaced
2605:8908d001faf3 | 2606:538c54d2dab3 |
---|---|
1 -- Copyright (C) 2016 Kim Alvefur | |
2 -- Copyright (C) 2017 Thilo Molitor | |
3 -- | |
4 | |
5 module:depends"csi" | |
6 module:depends"track_muc_joins" | |
7 local s_match = string.match; | |
8 local s_sub = string.sub; | |
9 local jid = require "util.jid"; | |
10 local new_queue = require "util.queue".new; | |
11 local datetime = require "util.datetime"; | |
12 | |
13 local xmlns_delay = "urn:xmpp:delay"; | |
14 | |
15 -- a log id for this module instance | |
16 local id = s_sub(require "util.hashes".sha256(datetime.datetime(), true), 1, 4); | |
17 | |
18 -- Patched version of util.stanza:find() that supports giving stanza names | |
19 -- without their namespace, allowing for every namespace. | |
20 local function find(self, path) | |
21 local pos = 1; | |
22 local len = #path + 1; | |
23 | |
24 repeat | |
25 local xmlns, name, text; | |
26 local char = s_sub(path, pos, pos); | |
27 if char == "@" then | |
28 return self.attr[s_sub(path, pos + 1)]; | |
29 elseif char == "{" then | |
30 xmlns, pos = s_match(path, "^([^}]+)}()", pos + 1); | |
31 end | |
32 name, text, pos = s_match(path, "^([^@/#]*)([/#]?)()", pos); | |
33 name = name ~= "" and name or nil; | |
34 if pos == len then | |
35 if text == "#" then | |
36 local child = xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
37 return child and child:get_text() or nil; | |
38 end | |
39 return xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
40 end | |
41 self = xmlns ~= nil and self:get_child(name, xmlns) or self:child_with_name(name); | |
42 until not self | |
43 return nil; | |
44 end | |
45 | |
46 local function new_pump(output, ...) | |
47 -- luacheck: ignore 212/self | |
48 local q = new_queue(...); | |
49 local flush = true; | |
50 function q:pause() | |
51 flush = false; | |
52 end | |
53 function q:resume() | |
54 flush = true; | |
55 return q:flush(); | |
56 end | |
57 local push = q.push; | |
58 function q:push(item) | |
59 local ok = push(self, item); | |
60 if not ok then | |
61 q:flush(); | |
62 output(item, self); | |
63 elseif flush then | |
64 return q:flush(); | |
65 end | |
66 return true; | |
67 end | |
68 function q:flush() | |
69 local item = self:pop(); | |
70 while item do | |
71 output(item, self); | |
72 item = self:pop(); | |
73 end | |
74 return true; | |
75 end | |
76 return q; | |
77 end | |
78 | |
79 local function is_stamp_needed(stanza, session) | |
80 local st_name = stanza and stanza.name or nil; | |
81 if st_name == "presence" then | |
82 return true; | |
83 elseif st_name == "message" then | |
84 if stanza:get_child("delay", xmlns_delay) then return false; end | |
85 if stanza.attr.type == "chat" or stanza.attr.type == "groupchat" then return true; end | |
86 end | |
87 return false; | |
88 end | |
89 | |
90 local function add_stamp(stanza, session) | |
91 stanza = stanza:tag("delay", { xmlns = xmlns_delay, from = session.host, stamp = datetime.datetime()}); | |
92 return stanza; | |
93 end | |
94 | |
95 local function is_important(stanza, session) | |
96 local st_name = stanza and stanza.name or nil; | |
97 if not st_name then return false; end | |
98 if st_name == "presence" then | |
99 -- TODO check for MUC status codes? | |
100 return false; | |
101 elseif st_name == "message" then | |
102 -- unpack carbon copies | |
103 local stanza_direction = "in"; | |
104 local carbon; | |
105 -- support carbon copied message stanzas having an arbitrary message-namespace or no message-namespace at all | |
106 if not carbon then carbon = find(stanza, "{urn:xmpp:carbons:2}/forwarded/message"); end | |
107 if not carbon then carbon = find(stanza, "{urn:xmpp:carbons:1}/forwarded/message"); end | |
108 stanza_direction = carbon and stanza:child_with_name("sent") and "out" or "in"; | |
109 --session.log("debug", "mod_csi_battery_saver(%s): stanza_direction = %s, carbon = %s, stanza = %s", id, stanza_direction, carbon and "true" or "false", tostring(stanza)); | |
110 if carbon then stanza = carbon; end | |
111 -- carbon copied outgoing messages aren't important (but incoming carbon copies are!) | |
112 if carbon and stanza_direction == "out" then return false; end | |
113 | |
114 local st_type = stanza.attr.type; | |
115 if st_type == "headline" then | |
116 return false; | |
117 end | |
118 local body = stanza:get_child_text("body"); | |
119 if st_type == "groupchat" then | |
120 if stanza:get_child_text("subject") then return true; end | |
121 if not body then return false; end | |
122 if body:find(session.username, 1, true) then return true; end | |
123 local rooms = session.rooms_joined; | |
124 if not rooms then return false; end | |
125 local room_nick = rooms[jid.bare(stanza_direction == "in" and stanza.attr.from or stanza.attr.to)]; | |
126 if room_nick and body:find(room_nick, 1, true) then return true; end | |
127 return false; | |
128 end | |
129 return body ~= nil and body ~= ""; | |
130 end | |
131 return true; | |
132 end | |
133 | |
134 module:hook("csi-client-inactive", function (event) | |
135 local session = event.origin; | |
136 if session.pump then | |
137 session.pump:pause(); | |
138 else | |
139 session.log("debug", "mod_csi_battery_saver(%s): Client is inactive the first time, initializing module for this session", id); | |
140 local pump = new_pump(session.send, 100); | |
141 pump:pause(); | |
142 session.pump = pump; | |
143 session._pump_orig_send = session.send; | |
144 function session.send(stanza) | |
145 session.log("debug", "mod_csi_battery_saver(%s): Got stanza: <%s>", id, tostring(stanza.name)); | |
146 local important = is_important(stanza, session); | |
147 -- add delay stamp to unimported (buffered) stanzas that can/need be stamped | |
148 if not important and is_stamp_needed(stanza, session) then stanza = add_stamp(stanza, session); end | |
149 pump:push(stanza); | |
150 if important then | |
151 session.log("debug", "mod_csi_battery_saver(%s): Encountered important stanza, flushing buffer: <%s>", id, tostring(stanza.name)); | |
152 pump:flush(); | |
153 end | |
154 return true; | |
155 end | |
156 end | |
157 session.log("debug", "mod_csi_battery_saver(%s): Client is inactive, buffering unimportant stanzas", id); | |
158 end); | |
159 | |
160 module:hook("csi-client-active", function (event) | |
161 local session = event.origin; | |
162 if session.pump then | |
163 session.log("debug", "mod_csi_battery_saver(%s): Client is active, resuming direct delivery", id); | |
164 session.pump:resume(); | |
165 end | |
166 end); | |
167 | |
168 function module.unload() | |
169 module:log("info", "%s: Unloading module, flushing all buffers", id); | |
170 local host_sessions = prosody.hosts[module.host].sessions; | |
171 for _, user in pairs(host_sessions) do | |
172 for _, session in pairs(user.sessions) do | |
173 if session.pump then | |
174 session.pump:flush(); | |
175 session.send = session._pump_orig_send; | |
176 session.pump = nil; | |
177 session._pump_orig_send = nil; | |
178 end | |
179 end | |
180 end | |
181 end | |
182 | |
183 module:log("info", "%s: Successfully loaded module", id); |