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.3.0
6 files changed
+133−40
README.md+35−14@@ -51,26 +51,47 @@ config (`0600`, outside any repo - never commit it). adom-wiki pkg install adom/wiki-notify-gchat ``` -## Set up your own space + webhook (each team member does this once)--1. In Google Chat, create a space for yourself, e.g. **Wiki Notifications**.-2. In that space: space name > **Apps & integrations** > **Webhooks** >- **Add webhook**, name it (e.g. `wiki-notify`), copy the URL.-3. `wiki-notify-gchat setup`, then paste the URL into `"webhook"` in- `~/.config/wiki-notify-gchat/config.json`.- The webhook URL is a secret: it lives only in that private config file.- If you ever copy it anywhere else, keep it out of git (`.gitignore` it).-4. Optional: set `"timezone"` (IANA name, e.g. `America/New_York`) so the 6am- closed-item purge happens in *your* morning, and pick your `"action"`.+## Setup (each team member does this once, ~3 minutes)++**Prerequisite:** an authorized [adom-google](https://wiki.adom.inc/adom/adom-google)+(`adom-google status` must show a refresh token; if not, run `adom-google auth`).++**Step 1 - create your own Chat space + webhook** (the one manual bit, in the+Google Chat UI):++1. Create a space for yourself, e.g. **Wiki Notifications**. It must be a+ space *you* create - the tool deletes its old board card as space manager.+2. Click the space name > **Apps & integrations** > **Webhooks** >+ **Add webhook**, name it (e.g. `wiki-notify`), and copy the URL.++**Step 2 - configure from the CLI** (no file editing needed):++```bash+wiki-notify-gchat setup # writes the starter config+wiki-notify-gchat config set webhook '<paste the URL>' # YOUR secret - stays in the 0600 config+wiki-notify-gchat config set timezone America/Chicago # IANA zone; the 6am purge follows it+wiki-notify-gchat config set meName Drew # first name, for the involvement line+wiki-notify-gchat config set fullName 'Drew Owens' # wiki author name, detects pages you own+wiki-notify-gchat config set action trash # or archive | label | keep+wiki-notify-gchat config show # review everything (token masked)+```++The webhook URL is a secret: it lives only in that private config file. If+you ever copy it anywhere else, keep it out of git (`.gitignore` it).++**Step 3 - verify, seed, go live:** ```bash-wiki-notify-gchat health # verify auth + config-wiki-notify-gchat run --dry-run # preview: what would be ingested + swept+wiki-notify-gchat health # checks auth, config, webhook shape, cron+wiki-notify-gchat run --dry-run # preview: what would be ingested + swept (sends nothing) wiki-notify-gchat backfill # seed the board from the last 30 days (no sweeping)-wiki-notify-gchat run # ingest + sweep + post the board+wiki-notify-gchat run # ingest + sweep + post the board for real wiki-notify-gchat install-cron # keep it live every 2 minutes ``` +Every command prints `OK:`/`ERROR:` plus `Hint:` lines telling you (or your+agent) the exact next step, and accepts `--json`.+  ## What happens to the emails
SKILL.md+30−17@@ -14,37 +14,50 @@ drop off the board at 6am the next day. ## Fastest path (per team member - each uses their OWN space + webhook) +Prerequisite: authorized adom-google (`adom-google status` shows a refresh+token; else run `adom-google auth`).++1. Install: `adom-wiki pkg install adom/wiki-notify-gchat`+2. Have the user create their own Chat space (e.g. "Wiki Notifications" -+ must be a space THEY create; the tool deletes its old board card as space+ manager) and add an incoming webhook: space name > Apps & integrations >+ Webhooks > Add webhook, copy the URL.+3. Configure entirely from the CLI - no file editing:+ ```bash-adom-wiki pkg install adom/wiki-notify-gchat # if not installed wiki-notify-gchat setup+wiki-notify-gchat config set webhook '<url>' # personal secret; stays in the 0600 config, never in a repo+wiki-notify-gchat config set timezone America/Chicago # IANA zone; 6am closed-item purge follows it+wiki-notify-gchat config set meName Drew # first name shown in the involvement line+wiki-notify-gchat config set fullName 'Drew Owens' # wiki author name; detects pages they own+wiki-notify-gchat config set action trash # trash (default) | archive | label | keep+wiki-notify-gchat config show # review (webhook token masked) ``` -Then have the user create their own Chat space (e.g. "Wiki Notifications")-and add an incoming webhook: space name > Apps & integrations > Webhooks >-Add webhook - and paste the URL into `"webhook"` in-`~/.config/wiki-notify-gchat/config.json`. The webhook URL is a personal-secret: it lives only in that private 0600 config, never in a repo (gitignore-it anywhere else).+4. Verify and go live: ```bash-wiki-notify-gchat health # auth + config check (needs authorized adom-google)+wiki-notify-gchat health # auth + config + cron check wiki-notify-gchat run --dry-run # preview what would be ingested + swept wiki-notify-gchat backfill # seed the board from the last 30 days (no sweeping) wiki-notify-gchat run # ingest + sweep + post the board wiki-notify-gchat install-cron # keep the board live every 2 min ``` -## Key config choices (`~/.config/wiki-notify-gchat/config.json`)+## Config keys (all via `config set <key> <value>`) -- `action`: what happens to swept emails - `trash` (default, recoverable 30+- `webhook` - the user's incoming-webhook URL (secret, validated on set).+- `action` - what happens to swept emails: `trash` (default, recoverable 30 days), `archive`, `label` (uses `labelName`, auto-created), or `keep`.-- `timezone`: IANA zone (e.g. `America/New_York`) so the 6am closed-item- purge lands in the user's morning; container default is often UTC.-- `purgeHour`, `maxRows`, `pollSeconds`, `boardTitle` as needed.-- `style`: `table` (default - colored status rail + content column, one- divider-ruled row per item) or `blocks` (per-item blocks with View buttons).- Chat cards have no native table widget; avoid single-line pipe columns,- they wrap unreadably at card width.+- `timezone` - IANA zone; container default is often UTC, which makes the+ 6am purge fire overnight in the US - set it.+- `meName` / `fullName` - involvement line ("Drew owns this page"); fullName+ is matched against `adom-wiki page stats` author_name, auto-derived from+ the notification To header when unset.+- `style` - `table` (default) or `blocks`. Chat cards have no native table+ widget; avoid single-line pipe columns, they wrap unreadably at card width.+- `purgeHour`, `maxRows`, `pollSeconds`, `boardTitle`, `query`,+ `requireInBody` as needed. ## How the board updates (why you may see a delete)
bin/__pycache__/wiki-notify-gchatcpython-312.pyc⋯ 1 unchanged line ⋯
bin/wiki-notify-gchat+66−7@@ -540,11 +540,12 @@ def cmd_setup(as_json): os.chmod(CONFIG_PATH, 0o600) emit(f"OK: wrote starter config to {CONFIG_PATH}") emit("Hint: create your own Chat space (e.g. 'Wiki Notifications'), then add an incoming webhook:")- emit("Hint: space name > Apps & integrations > Webhooks > Add webhook, copy the URL.")- emit(f"Hint: paste that URL into \"webhook\" in {CONFIG_PATH} — it is YOUR secret; never commit it.")- emit("Hint: action is 'trash' (default, recoverable 30 days), 'archive', 'label' (uses labelName), or 'keep'.")- emit("Hint: set \"timezone\" (IANA, e.g. America/New_York) so the 6am closed-item purge matches your morning.")- emit("Hint: then `wiki-notify-gchat health`, `run --dry-run`, `backfill --days 30`, and `install-cron`.")+ emit("Hint: space name > Apps & integrations > Webhooks > Add webhook, copy the URL, then run:")+ emit("Hint: wiki-notify-gchat config set webhook '<url>' (YOUR secret; lives only in this 0600 config)")+ emit("Hint: wiki-notify-gchat config set timezone America/Chicago (IANA zone; 6am purge follows it)")+ emit("Hint: wiki-notify-gchat config set meName Drew ; config set fullName 'Drew Owens' (involvement line)")+ emit("Hint: wiki-notify-gchat config set action trash|archive|label|keep (what happens to swept emails)")+ emit("Hint: then `health`, `run --dry-run`, `backfill --days 30`, and `install-cron`.") def cmd_health(as_json):@@ -722,6 +723,60 @@ def cmd_uninstall_cron(as_json): emit("OK: cron entry removed.") +INT_KEYS = {"pollSeconds", "purgeHour", "maxRows"}+++def mask_webhook(url):+ return re.sub(r"(token=)[^&]+", r"\1***", url) if url else "(not set)"+++def cmd_config(as_json, args):+ if not CONFIG_PATH.exists():+ die("no config found.", ["Run `wiki-notify-gchat setup` first."], as_json)+ cfg_raw = json.loads(CONFIG_PATH.read_text())+ if not args or args[0] == "show":+ cfg = {**DEFAULT_CONFIG, **cfg_raw}+ if as_json:+ shown = dict(cfg, webhook=mask_webhook(cfg.get("webhook", "")))+ print(json.dumps({"status": "ok", "data": shown, "hints": []}))+ return+ emit(f"OK: config at {CONFIG_PATH}")+ for k in DEFAULT_CONFIG:+ v = cfg.get(k, "")+ emit(f" {k} = {mask_webhook(v) if k == 'webhook' else v!r}")+ emit("Hint: change any value with `wiki-notify-gchat config set <key> <value>`.")+ emit("Hint: after changes run `wiki-notify-gchat run --dry-run`, then `board` to re-render.")+ return+ if args[0] == "set":+ if len(args) < 3:+ die("usage: config set <key> <value>",+ [f"keys: {', '.join(DEFAULT_CONFIG)}"], as_json)+ key, value = args[1], " ".join(args[2:])+ if key not in DEFAULT_CONFIG:+ die(f"unknown key '{key}'", [f"keys: {', '.join(DEFAULT_CONFIG)}"], as_json)+ if key in INT_KEYS:+ try:+ value = int(value)+ except ValueError:+ die(f"{key} needs a number", [f"e.g. `config set {key} 120`"], as_json)+ if key == "action" and value not in ("trash", "archive", "label", "keep"):+ die(f"invalid action '{value}'", ["Use trash | archive | label | keep."], as_json)+ if key == "style" and value not in ("table", "blocks"):+ die(f"invalid style '{value}'", ["Use table | blocks."], as_json)+ if key == "webhook" and not re.match(+ r"https://chat\.googleapis\.com/v1/spaces/[^/]+/messages\?", str(value)):+ die("that does not look like a Chat incoming-webhook URL.",+ ["Copy it from your space: name > Apps & integrations > Webhooks."], as_json)+ cfg_raw[key] = value+ CONFIG_PATH.write_text(json.dumps(cfg_raw, indent=2) + "\n")+ os.chmod(CONFIG_PATH, 0o600)+ shown = mask_webhook(value) if key == "webhook" else value+ emit(f"OK: {key} = {shown}")+ emit("Hint: run `wiki-notify-gchat health` to validate, then `run --dry-run` to preview.")+ return+ die(f"unknown config subcommand '{args[0]}'", ["Use `config show` or `config set <key> <value>`."], as_json)++ def cmd_install(as_json): target = Path.home() / ".local" / "bin" / "wiki-notify-gchat" target.parent.mkdir(parents=True, exist_ok=True)@@ -738,6 +793,7 @@ USAGE = """wiki-notify-gchat — wiki notification emails become a live Google C Usage: wiki-notify-gchat <command> [--json] [--dry-run] [--quiet] [--days N] Commands: setup write a starter config (~/.config/wiki-notify-gchat/config.json)+ config show config, or `config set <key> <value>` (webhook, action, timezone, ...) health check auth, config, and cron run one poll pass: ingest + sweep emails, purge, update the board card backfill seed the board from past emails (default 30 days, incl. trash) - no sweeping@@ -756,15 +812,18 @@ def main(): dry = "--dry-run" in args quiet = "--quiet" in args days = 30+ days_val_idx = -1 if "--days" in args:+ days_val_idx = args.index("--days") + 1 try:- days = int(args[args.index("--days") + 1])+ days = int(args[days_val_idx]) except (IndexError, ValueError): die("--days needs a number", ["e.g. `wiki-notify-gchat backfill --days 14`"], as_json)- positional = [a for a in args if not a.startswith("--") and not a.isdigit()]+ positional = [a for i, a in enumerate(args) if not a.startswith("--") and i != days_val_idx] cmd = positional[0] if positional else "help" table = { "setup": lambda: cmd_setup(as_json),+ "config": lambda: cmd_config(as_json, positional[1:]), "health": lambda: cmd_health(as_json), "run": lambda: cmd_run(as_json, dry, quiet), "backfill": lambda: cmd_backfill(as_json, days, dry),
package.json+1−1@@ -2,7 +2,7 @@ "slug": "wiki-notify-gchat", "title": "Wiki Notify for Google Chat", "type": "app",- "version": "1.2.1",+ "version": "1.3.0", "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.2.1",+ "version": "1.3.0", "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",