Sat, 06 Nov 2021 22:11:19 +0100
clix.archive: Fix MAM callback argument order
local jid_split = require"util.jid".split; local datetime = require "util.datetime"; local st = require "util.stanza"; local uuid = require"util.uuid".generate; return function(opts, arg) if opts.help then print("clix moderate --to=room@muc.example.com") print("\t--start=timestamp") print("\t--end=timestamp") print("\t--from=nickname") print("\t--body-contains=\"some spam\"") print("\t--dry-run") return 0; elseif opts.short_help or arg[1] or not opts.room then print("Remove messages from a MUC"); return; end local function parse_datetime(s) if s:match("^%d%d:") then s = datetime.date().."T"..s; end if #s < 20 then s = s .. ("0000-01-01T00:00:00Z"):sub(#s+1) end return datetime.parse(s) end if opts.start then opts.start = parse_datetime(opts.start); end if opts["end"] then opts["end"] = parse_datetime(opts["end"]); end local function matches(message) local nick = select(3, jid_split(message.attr.from)); if opts.from and opts.from ~= nick then return end local body = message:get_child_text("body"); if opts.body_contains and not string.find(body or "", opts.body_contains, 1, true) then return end if opts.body_match and not string.find(body or "", opts.body_match) then return end return true; end local function on_connect(conn) local waiting = {}; -- to keep track of outstanding queries local function done(with) waiting[with] = nil; if next(waiting) == nil then conn:close(); end end local function moderate(id) waiting[id] = true; -- TODO maybe queue and send the next request when a response comes in? local mod_iq = st.iq({ id = uuid(); type = "set"; to = opts.room }) :tag("apply-to", { xmlns = "urn:xmpp:fasten:0"; id = id }) :tag("moderate", { xmlns = "urn:xmpp:message-moderate:0" }) :tag("retract", { xmlns = "urn:xmpp:message-retract:0" }):up() if opts.reason then mod_iq:tag("reason", { xmlns = "urn:xmpp:message-moderate:0" }):text(opts.reason):up() end mod_iq:reset(); if opts.dry_run then done(id); conn:debug("Would send: %s", mod_iq); return; end return conn:send_iq(mod_iq, function (ret) if ret.attr.type == "error" then local t, cond, msg = ret:get_error(); conn:error("Retracting message with id %s failed: %s(%s, %s)", msg or "", t, cond); end done(id); end); end local function handle_results(result, err) if not result then conn:error("Archive query failed: %s", err); return done(true); end for _, item in ipairs(result) do if matches(item.message) then conn:info("Moderate %s", item.message:top_tag()) moderate(item.id); else conn:debug("Skip %s", item.message:top_tag()) end end if result.complete == nil then -- COMPAT verse result.complete = opts.after == nil or result[1] == nil; end if not result.complete then -- Proceed to the next page opts.after = result.last; return conn:query_archive(opts.room, opts, handle_results); else -- All done, just wait for any outstanding moderation queries to complete done(true); end end waiting[true] = true; -- for the archive query conn:query_archive(opts.room, opts, handle_results); end clix_connect(opts, on_connect, { "archive" }); end