-
Notifications
You must be signed in to change notification settings - Fork 31
Agency: agency-report helper + SQLite suggestion DB + dedupe #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
MagMueller
wants to merge
1
commit into
main
Choose a base branch
from
agency-report-db
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+462
−3
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| #!/usr/bin/env python3 | ||
| """agency-report — record + post an Agency suggestion to Telegram. | ||
|
|
||
| Always: | ||
| 1. records the suggestion in /var/lib/bux/agency.db | ||
| 2. posts the body to TG with inline-keyboard buttons (default 4) | ||
| 3. wires the message_id back into the row so a button tap can record | ||
| the user's decision against the right suggestion. | ||
|
|
||
| Default buttons: ✅ Yes, do it · ❌ No · ✏️ Just do it differently · 🔄 Regenerate | ||
|
|
||
| Custom buttons: pass --button (repeatable) to override the set. | ||
|
|
||
| Dedupe: pass --source <slug> + --skip-if-exists to suppress if the same | ||
| source already has a row that is not 'pending'. Returns 0 silently when | ||
| suppressed (status of the prior row is printed to stderr). | ||
|
|
||
| Usage: | ||
| agency-report --title "Hassan HIPAA send" \\ | ||
| --description "Saved Gmail draft 19df…1d sitting unsent." \\ | ||
| --importance high \\ | ||
| --source gmail-draft-19df00477868154d \\ | ||
| --prompt "Open Gmail draft 19df…, send it, post to #wall-king-magnus." \\ | ||
| --skip-if-exists | ||
|
|
||
| agency-report --title "Pick a draft" \\ | ||
| --description "Three drafts ready for the Hassan thread." \\ | ||
| --button "Send draft A" --button "Send draft B" --button "Send draft C" | ||
| """ | ||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import json | ||
| import os | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| REPO_AGENT = Path(__file__).resolve().parent | ||
| sys.path.insert(0, str(REPO_AGENT)) | ||
|
|
||
| import agency_db # noqa: E402 | ||
|
|
||
| import urllib.request # noqa: E402 | ||
|
|
||
| DEFAULT_BUTTONS = [ | ||
| "✅ Yes, do it", | ||
| "❌ No", | ||
| "✏️ Just do it differently", | ||
| "🔄 Regenerate", | ||
| ] | ||
|
|
||
|
|
||
| def _read_kv(path: Path) -> dict: | ||
| out: dict[str, str] = {} | ||
| try: | ||
| for line in path.read_text().splitlines(): | ||
| line = line.strip() | ||
| if not line or line.startswith("#") or "=" not in line: | ||
| continue | ||
| k, v = line.split("=", 1) | ||
| out[k.strip()] = v.strip().strip('"').strip("'") | ||
| except FileNotFoundError: | ||
| pass | ||
| return out | ||
|
|
||
|
|
||
| def bot_token() -> str: | ||
| tok = os.environ.get("TG_BOT_TOKEN") | ||
| if tok: | ||
| return tok | ||
| tok = _read_kv(Path("/etc/bux/tg.env")).get("TG_BOT_TOKEN") | ||
| if not tok: | ||
| sys.exit("agency-report: TG_BOT_TOKEN missing (env or /etc/bux/tg.env)") | ||
| return tok | ||
|
|
||
|
|
||
| def chat_id() -> int: | ||
| raw = Path("/etc/bux/tg-allowed.txt").read_text().splitlines() | ||
| for line in raw: | ||
| line = line.strip() | ||
| if line: | ||
| return int(line) | ||
| sys.exit("agency-report: no bound chat (run /start in TG first)") | ||
|
|
||
|
|
||
| def send_with_buttons( | ||
| *, token: str, chat: int, thread: int, text: str, buttons: list[str] | ||
| ) -> int: | ||
| keyboard = [ | ||
| [{"text": label, "callback_data": f"agcy:{thread}:{i}"}] | ||
| for i, label in enumerate(buttons) | ||
| ] | ||
| body: dict = { | ||
| "chat_id": chat, | ||
| "text": text, | ||
| "reply_markup": {"inline_keyboard": keyboard}, | ||
| } | ||
| if thread > 0: | ||
| body["message_thread_id"] = thread | ||
| req = urllib.request.Request( | ||
| f"https://api.telegram.org/bot{token}/sendMessage", | ||
| data=json.dumps(body).encode("utf-8"), | ||
| headers={"Content-Type": "application/json"}, | ||
| method="POST", | ||
| ) | ||
| with urllib.request.urlopen(req, timeout=15) as r: | ||
| resp = json.loads(r.read()) | ||
| if not resp.get("ok"): | ||
| sys.exit(f"agency-report: sendMessage failed: {resp}") | ||
| return int(resp["result"]["message_id"]) | ||
|
|
||
|
|
||
| def main() -> int: | ||
| p = argparse.ArgumentParser(description="Record + post an Agency suggestion to Telegram.") | ||
| p.add_argument("--title", required=True, help="Short scannable headline.") | ||
| p.add_argument("--description", required=True, help="One-paragraph body.") | ||
| p.add_argument( | ||
| "--importance", | ||
| choices=("high", "med", "low"), | ||
| default="med", | ||
| help="Priority bucket for triage. Default: med.", | ||
| ) | ||
| p.add_argument( | ||
| "--source", | ||
| help="Stable slug for dedupe (e.g. slack-c-minerva, gmail-thread-19df, gh-pr-78).", | ||
| ) | ||
| p.add_argument( | ||
| "--prompt", | ||
| help="Exact action that runs if user taps yes — agent will see this in the lane.", | ||
| ) | ||
| p.add_argument( | ||
| "--button", | ||
| action="append", | ||
| default=None, | ||
| help="Custom button label. Repeatable. If omitted, uses the default 4-button set.", | ||
| ) | ||
| p.add_argument( | ||
| "--thread-id", | ||
| type=int, | ||
| default=int(os.environ.get("TG_THREAD_ID", "0")), | ||
| help="TG forum thread to post into. Defaults to $TG_THREAD_ID or 0 (general).", | ||
| ) | ||
| p.add_argument( | ||
| "--skip-if-exists", | ||
| action="store_true", | ||
| help="If a suggestion with this --source already exists and isn't pending, " | ||
| "skip posting (exit 0).", | ||
| ) | ||
| args = p.parse_args() | ||
|
|
||
| buttons = args.button or DEFAULT_BUTTONS | ||
|
|
||
| db = agency_db.conn() | ||
|
|
||
| if args.skip_if_exists and args.source: | ||
| prior = agency_db.exists(db, args.source) | ||
| if prior and prior.get("status") != "pending": | ||
| print( | ||
| f"agency-report: source={args.source!r} already exists " | ||
| f"(id={prior['id']}, status={prior['status']}). Skipping.", | ||
| file=sys.stderr, | ||
| ) | ||
| return 0 | ||
|
|
||
| sugg_id = agency_db.insert( | ||
| db, | ||
| title=args.title, | ||
| description=args.description, | ||
| importance=args.importance, | ||
| source=args.source, | ||
| prompt=args.prompt, | ||
| buttons=buttons, | ||
| chat_id=chat_id(), | ||
| thread_id=args.thread_id, | ||
| ) | ||
|
|
||
| body_lines = [f"🎫 #{sugg_id} · {args.title}", "", args.description] | ||
| if args.prompt: | ||
| body_lines += ["", "If yes I'll run:", "```", args.prompt, "```"] | ||
| body = "\n".join(body_lines) | ||
|
|
||
| msg_id = send_with_buttons( | ||
| token=bot_token(), | ||
| chat=chat_id(), | ||
| thread=args.thread_id, | ||
| text=body, | ||
| buttons=buttons, | ||
| ) | ||
| agency_db.update_message(db, sugg_id, msg_id) | ||
| print(sugg_id) | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Handle missing
/etc/bux/tg-allowed.txtinchat_id()to avoid an uncaught traceback on first-run/unbound setups.Prompt for AI agents