Redact whole review threads on decision (replies too)

The bot was granted a redact/mod power level in the review room, so it can now
clear a resolved thread entirely, not just the card: redact_thread redacts the
card root then scans recent history for its m.thread replies (the human's yes/no
+ any bot messages) and redacts those too, so decided threads drop out of the
threads view, not only the main timeline. Drops the in-thread confirmation on a
successful decision (the thread clearing is the ack; a confirmation would keep
the thread alive). redact_resolved.py extended to also clear replies of already-
resolved threads for the one-time backfill. Bot-only; no s9pk change.
This commit is contained in:
Keysat
2026-06-18 12:32:06 -05:00
parent 9044641b08
commit b2690c4342
3 changed files with 59 additions and 25 deletions
+17 -12
View File
@@ -39,7 +39,8 @@ async def main(apply):
sync = await client.sync(timeout=10000, full_state=False)
token = sync.next_batch
cards = {} # event_id -> snippet (dedup across pages)
cards = {} # root event_id -> snippet (still-identifiable card bodies)
replies = {} # reply event_id -> (thread_root, snippet)
for _ in range(MAX_PAGES):
resp = await client.room_messages(review_room, start=token,
direction=MessageDirection.back, limit=100)
@@ -47,24 +48,28 @@ async def main(apply):
if not chunk:
break
for ev in chunk:
if getattr(ev, "sender", None) != mx["user_id"]:
continue
body = getattr(ev, "body", "") or ""
if CARD_MARKER in body:
cards[ev.event_id] = body.replace("\n", " ")[:70]
body = (getattr(ev, "body", "") or "").replace("\n", " ")
rel = ((getattr(ev, "source", None) or {}).get("content", {}) or {}).get("m.relates_to") or {}
if rel.get("rel_type") == "m.thread" and rel.get("event_id"):
replies[ev.event_id] = (rel["event_id"], body[:50]) # a threaded reply (card already redacted)
elif getattr(ev, "sender", None) == mx["user_id"] and CARD_MARKER in body:
cards[ev.event_id] = body[:70] # an un-redacted card root
token = getattr(resp, "end", None)
if not token:
break
to_redact = [(eid, snip) for eid, snip in cards.items() if eid not in open_ids]
print(f"bot cards found: {len(cards)}; resolved (to redact): {len(to_redact)}")
for eid, snip in to_redact:
print(("APPLY redact " if apply else "WOULD redact ") + eid + " :: " + snip)
# Redact card roots that aren't still pending, AND any reply whose thread isn't still pending.
targets = [(eid, "card :: " + snip) for eid, snip in cards.items() if eid not in open_ids]
targets += [(eid, "reply :: " + snip) for eid, (root, snip) in replies.items() if root not in open_ids]
print(f"resolved cards: {sum(1 for e,_ in cards.items() if e not in open_ids)}; "
f"thread replies to clear: {sum(1 for _,(r,_) in replies.items() if r not in open_ids)}")
for eid, label in targets:
print(("APPLY redact " if apply else "WOULD redact ") + eid + " :: " + label)
if apply:
r = await client.room_redact(review_room, eid, reason="retroactive cleanup of resolved cards")
r = await client.room_redact(review_room, eid, reason="retroactive cleanup of resolved review threads")
if not hasattr(r, "event_id"):
print(f" ! redact failed: {r}")
print(("done — redacted " if apply else "dry run — would redact ") + f"{len(to_redact)} card(s).")
print(("done — redacted " if apply else "dry run — would redact ") + f"{len(targets)} event(s).")
finally:
await client.close()