Files
null-bot/AGENTS.md

2.6 KiB

AGENTS.md

Run

python main.py

Uses runpy to exec bot.py. No tests, no lint, no CI.

Architecture

  • bot.py — Telegram bot (python-telegram-bot), single-worker asyncio.Queue, all crawl/parse serialized
  • agent.py — pydantic-ai with local Ollama (granite4.1:8b), three agents (opportunity/event/workshop), output_type=str
  • database.py — PocketBase client, collections events / opportunities / workshops
  • scraper.py — crawl4ai AsyncWebCrawler URL → markdown
  • prompts.py — system prompts for both agent types
  • schemas.pyunused at runtime, per file comment: "File doesn't do anything, its just an outline"

Windows event-loop quirk

DO NOT remove the dual-event-loop pattern in bot.py:

  1. PTB runs on WindowsSelectorEventLoopPolicy (line 357)
  2. Scraper runs in a separate thread with WindowsProactorEventLoopPolicy (line 25) — required for Playwright subprocesses on Windows

The get_clean_content() wrapper in bot.py runs _run_scraper_in_thread via ThreadPoolExecutor. Scraper functions must not be called directly.

Agent output format

Agents use output_type=str (not structured output). JSON is parsed manually from agent.run() result:

  • LLM returns JSON wrapped in json ... markdown backticks
  • agent.py:56 strips the backtick wrappers before json.loads()
  • The markdown wrapper is specified in prompts.py — don't change prompt format without updating the strip logic

Date formats

  • Prompts instruct LLM: DD-MM-YYYY (or DD-MM-YYYY (HH:MM) for events)
  • PocketBase expects: YYYY-MM-DD HH:MM:SS
  • database.py field mapping: event date_time → PocketBase datetime, end_dateend_datetime; workshop same; opportunity deadline stays deadline

PocketBase collections

  • events — fields: title, org, datetime, end_datetime, summary, location, url
  • opportunities — fields: title, org, type, deadline, summary, location, url
  • workshops — fields: title, org, datetime, end_datetime, summary, location, url
  • Auth uses admin credentials from .env

Env vars

.env required: TG_TOKEN, OLLAMA_BASE_URL, ALLOWED_USERS, POCKETBASE_URL, POCKETBASE_ADMIN_EMAIL, POCKETBASE_ADMIN_PASSWORD

Auth

ALLOWED_USERS — comma-separated Telegram user IDs (no brackets). Checked on every command/message handler.

Save flow

When source is pasted text (not URL), the bot asks for a source URL before saving. context.user_data keys: last_extracted, last_source_value, last_source_kind, last_entry_type, awaiting_save_url, pending_save_url, pending_url_to_process.