app
Wiki Notify for Google Chat
Public Made by Adomby adom
Turn wiki.adom.inc notification emails into a live Google Chat board. One card, one row per issue - newest first, OPEN/CLOSED status, origination date, actor, and a View link - continuously updated in your own Chat space via your own webhook. Emails are swept out of your inbox (trash, archive, or label). Closed items linger until 6am the next day, then drop off. Delegates all auth to adom-google; the only secret is your webhook URL, kept in your private config.
← Commit history
Publish 1.6.1
5 files changed
+71−10
bin/__pycache__/wiki-notify-gchatcpython-312.pyc⋯ 1 unchanged line ⋯
bin/wiki-notify-gchat+54−8@@ -217,6 +217,45 @@ def page_info(page, state): return info +def enrich_issue_meta(state, me_full):+ """Authoritative creator + creation time from `adom-wiki issue list`.+ Email inference is unreliable here: your own issue-creation sends you no+ email (so the earliest email is often someone's comment), and footers can+ disagree with the wiki DB. One lookup per page, once per item."""+ items = state.get("items", {})+ need = {}+ for it in items.values():+ if it.get("num") and not it.get("metaFetched"):+ need.setdefault(it["page"], []).append(it)+ for page, its in need.items():+ code, out, _ = run_cmd(["adom-wiki", "issue", "list", page, "--json"])+ if code != 0:+ continue+ try:+ j = json.loads(out)+ discs = {str(dd.get("number")): dd+ for dd in (j.get("data", j) or {}).get("discussions", [])}+ except Exception:+ continue+ for it in its:+ dd = discs.get(str(it["num"]))+ if not dd:+ continue+ if dd.get("author_name"):+ it["creator"] = dd["author_name"]+ ca = dd.get("created_at")+ if ca:+ try:+ dt = datetime.strptime(ca, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)+ it["openedAt"] = int(dt.timestamp() * 1000)+ except ValueError:+ pass+ if (it.get("invLabel") == "opened this" and me_full+ and it.get("creator") and it["creator"] != me_full):+ it["invLabel"], it["invRank"] = "", 0 # footer contradicted the wiki DB+ it["metaFetched"] = True++ def annotate_page_ownership(state, me_full): """Mark items with page authorship (drives 'owns this page') + hero URL.""" state.pop("pageAuthors", None) # superseded cache key@@ -316,9 +355,10 @@ def apply_event(items, ev): rank, label = involvement_of(ev) if rank > it.get("invRank", 0): it["invRank"], it["invLabel"] = rank, label- if ev["ts"] <= it["openedAt"] and ev["actor"]:- it["creator"] = ev["actor"] # actor of the earliest event we have- it["openedAt"] = min(it["openedAt"], ev["ts"])+ if not it.get("metaFetched"): # wiki issue metadata, once fetched, is authoritative+ if ev["ts"] <= it["openedAt"] and ev["actor"]:+ it["creator"] = ev["actor"] # actor of the earliest event we have+ it["openedAt"] = min(it["openedAt"], ev["ts"]) if ev["ts"] >= it["lastAt"]: it["lastAt"] = ev["ts"] it["lastEvent"] = ev["event"]@@ -433,8 +473,8 @@ def render_table_widgets(shown, tz, me="You"): title = html_mod.escape(title, quote=False) dim = lambda s: f'<font color="#8a94a6">{s}</font>' creator = it.get("creator") or it.get("actor") or "-"- if it.get("invLabel") == "opened this":- creator = me # the mail footer ('you opened the issue') outranks our earliest-email guess+ if it.get("invLabel") == "opened this" and not it.get("metaFetched"):+ creator = me # footer fallback only until wiki issue metadata arrives actor = it.get("actor") or "-" event = EVENT_CAP.get(it["lastEvent"], it["lastEvent"] or "Activity") linked_item = f'<a href="{it["url"]}">{item}</a>' if it["url"] else f"<b>{item}</b>"@@ -690,7 +730,9 @@ def cmd_run(as_json, dry_run=False, quiet=False): board_changed = False if not dry_run: try:- annotate_page_ownership(state, cfg.get("fullName") or state.get("meFullName", ""))+ me_full = cfg.get("fullName") or state.get("meFullName", "")+ enrich_issue_meta(state, me_full)+ annotate_page_ownership(state, me_full) board_changed = push_board(cfg, state) except Exception as e: errors.append(f"board: {e}")@@ -724,7 +766,9 @@ def cmd_backfill(as_json, days=30, dry_run=False): board_changed = False if not dry_run: try:- annotate_page_ownership(state, cfg.get("fullName") or state.get("meFullName", ""))+ me_full = cfg.get("fullName") or state.get("meFullName", "")+ enrich_issue_meta(state, me_full)+ annotate_page_ownership(state, me_full) board_changed = push_board(cfg, state) except Exception as e: errors.append(f"board: {e}")@@ -750,7 +794,9 @@ def cmd_board(as_json, dry_run=False): card = render_card(state["items"], cfg) print(json.dumps(card, indent=2)[:6000]) return- annotate_page_ownership(state, cfg.get("fullName") or state.get("meFullName", ""))+ me_full = cfg.get("fullName") or state.get("meFullName", "")+ enrich_issue_meta(state, me_full)+ annotate_page_ownership(state, me_full) changed = push_board(cfg, state, force=True) emit(f"OK: board {'reposted' if changed else 'unchanged'} ({len(state['items'])} item(s)).")
docs/DESIGN.md+15@@ -81,6 +81,21 @@ roughly 55-60 characters per line at desktop card width. labeling (and `messages.insert`, which is how the e2e test injects a synthetic notification). No full-mode auth needed. +## Email inference has a ceiling - the wiki DB is the authority++Two field bugs proved you cannot derive issue facts from notification emails+alone. (1) **Your own actions send you no email**, so for an issue you+opened, the earliest email you hold is someone else's comment - the board+showed "Created by Drew on Jul 20 11:01" for an issue actually created+Jul 16, stamping the creation with a commenter's timestamp. (2) **Footers+can contradict the wiki's database**: an email footer claimed "you opened+the issue" for an issue the DB attributes to someone else. The fix:+`adom-wiki issue list <page> --json` is fetched once per item+(`metaFetched`), and its `author_name` + `created_at` override every+email-derived guess; a footer-derived "opened this" involvement is dropped+when the DB disagrees. Email remains the *event stream*; the wiki is the+*record*.+ ## The involvement line (and why it needed two data sources) The email footer ("You're receiving this because ...") is **issue-scoped**:
package.json+1−1@@ -2,7 +2,7 @@ "slug": "wiki-notify-gchat", "title": "Wiki Notify for Google Chat", "type": "app",- "version": "1.6.0",+ "version": "1.6.1", "description": "Turn wiki.adom.inc notification emails into a live Google Chat board. One card, one row per issue - newest first, OPEN/CLOSED status, origination date, actor, and a View link - continuously updated in your own Chat space via your own webhook. Emails are swept out of your inbox (trash, archive, or label). Closed items linger until 6am the next day, then drop off. Delegates all auth to adom-google; the only secret is your webhook URL, kept in your private config.", "tags": [ "cli",
page.json+1−1@@ -2,7 +2,7 @@ "slug": "wiki-notify-gchat", "title": "Wiki Notify for Google Chat", "type": "app",- "version": "1.6.0",+ "version": "1.6.1", "description": "Turn wiki.adom.inc notification emails into a live Google Chat board. One card, one row per issue - newest first, OPEN/CLOSED status, origination date, actor, and a View link - continuously updated in your own Chat space via your own webhook. Emails are swept out of your inbox (trash, archive, or label). Closed items linger until 6am the next day, then drop off. Delegates all auth to adom-google; the only secret is your webhook URL, kept in your private config.", "tags": [ "cli",