clix/moderate.lua

changeset 160
6c1953fbe0fa
parent 159
68e09745d928
child 162
28acd1ca0ffd
equal deleted inserted replaced
159:68e09745d928 160:6c1953fbe0fa
1 local jid_split = require"util.jid".split;
2 local datetime = require "util.datetime";
3 local st = require "util.stanza";
4 local uuid = require"util.uuid".generate;
5
6 return function(opts, arg)
7 if opts.help then
8 print("clix moderate --to=room@muc.example.com")
9 print("\t--start=timestamp")
10 print("\t--end=timestamp")
11 print("\t--from=nickname")
12 print("\t--body-contains=\"some spam\"")
13 print("\t--dry-run")
14 return 0;
15 elseif opts.short_help or arg[1] or not opts.room then
16 print("Remove messages from a MUC");
17 return;
18 end
19
20 local function parse_datetime(s)
21 if s:match("^%d%d:") then
22 s = datetime.date().."T"..s;
23 end
24 if #s < 20 then
25 s = s .. ("0000-01-01T00:00:00Z"):sub(#s+1)
26 end
27 return datetime.parse(s)
28 end
29
30 if opts.start then
31 opts.start = parse_datetime(opts.start);
32 end
33 if opts["end"] then
34 opts["end"] = parse_datetime(opts["end"]);
35 end
36
37 local function matches(message)
38 local nick = select(3, jid_split(message.attr.from));
39 if opts.from and opts.from ~= nick then return end
40 local body = message:get_child_text("body");
41 if opts.body_contains and not string.find(body or "", opts.body_contains, 1, true) then return end
42 if opts.body_match and not string.find(body or "", opts.body_match) then return end
43 return true;
44 end
45
46 local function on_connect(conn)
47 local waiting = {}; -- to keep track of outstanding queries
48
49 local function done(with)
50 waiting[with] = nil;
51 if next(waiting) == nil then
52 conn:close();
53 end
54 end
55
56 local function moderate(id)
57 waiting[id] = true;
58 -- TODO maybe queue and send the next request when a response comes in?
59 local mod_iq = st.iq({ id = uuid(); type = "set"; to = opts.room })
60 :tag("apply-to", { xmlns = "urn:xmpp:fasten:0"; id = id })
61 :tag("moderate", { xmlns = "urn:xmpp:message-moderate:0" })
62 :tag("retract", { xmlns = "urn:xmpp:message-retract:0" }):up()
63 if opts.reason then
64 mod_iq:tag("reason", { xmlns = "urn:xmpp:message-moderate:0" }):text(opts.reason):up()
65 end
66 mod_iq:reset();
67
68 if opts.dry_run then
69 done(id);
70 conn:debug("Would send: %s", mod_iq);
71 return;
72 end
73
74 return conn:send_iq(mod_iq, function (ret)
75 if ret.attr.type == "error" then
76 local t, cond, msg = ret:get_error();
77 conn:error("Retracting message with id %s failed: %s(%s, %s)", msg or "", t, cond);
78 end
79 done(id);
80 end);
81 end
82
83 local function handle_results(result, err)
84 if not result then
85 conn:error("Archive query failed: %s", err);
86 return done(true);
87 end
88 for _, item in ipairs(result) do
89 if matches(item.message) then
90 conn:info("Moderate %s", item.message:top_tag())
91 moderate(item.id);
92 else
93 conn:debug("Skip %s", item.message:top_tag())
94 end
95 end
96
97 if result.complete == nil then -- COMPAT verse
98 result.complete = opts.after == nil or result[1] == nil;
99 end
100
101 if not result.complete then
102 -- Proceed to the next page
103 opts.after = result.last;
104 return conn:query_archive(opts.room, opts, handle_results);
105 else
106 -- All done, just wait for any outstanding moderation queries to complete
107 done(true);
108 end
109 end
110
111 waiting[true] = true; -- for the archive query
112 conn:query_archive(opts.room, opts, handle_results);
113 end
114
115 clix_connect(opts, on_connect, { "archive" });
116 end

mercurial