A config-driven AI monitoring & briefing bot. Point it at a topic and a set of keywords, run it on a schedule, and it researches, curates, and delivers a compact briefing to a channel (currently Telegram) — with no repeat items across runs.
BriefBot follows a simple three-stage pattern:
- Gather sources — Claude's
web_searchtool searches each of your configured keywords for recent items, and optionally considers a list of curated source URLs you provide. - LLM curates & summarizes — the model writes a compact briefing (at
most
max_itemsitems) restricted to the lastlookback_daysdays, then a second structured-output call extracts a normalized headline per item so future runs can avoid repeating it. - Deliver to a channel — the briefing text is sent to the configured channel (e.g. a Telegram chat). Delivered headlines are recorded to a local history file so the next run skips anything still "fresh".
De-duplication is best-effort, not a hard guarantee: the model is only
instructed (at the prompt level) not to repeat recently-briefed items, and
History governs what gets persisted for that instruction on the next
run — nothing enforces that the delivered text actually excludes
previously-seen items.
A scheduler (cron, systemd timer, Kubernetes CronJob, etc.) simply invokes the CLI on whatever cadence you want — BriefBot itself does not manage scheduling.
# from freelance/briefbot/
pip install -e .
cp config.example.yaml config.yaml
# edit config.yaml: name, topic, keywords, sources, channel, etc.
cp .env.example .env
# edit .env with real values, then export them into your shell/cron env
# (BriefBot does not load .env itself — see "Secrets via env only" below)
export ANTHROPIC_API_KEY=...
export TELEGRAM_BOT_TOKEN=...
export TELEGRAM_CHAT_ID=...
python -m briefbot run --config config.yamlEach invocation of run performs one research → curate → deliver cycle and
exits. Run it again later (e.g. via cron) for the next briefing.
Config is a YAML file loaded by briefbot.config.load_config. See
config.example.yaml for a working example. Fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | — | Display name for the bot, used in the research prompt (e.g. "AI Daily Briefing"). |
model |
string | yes | — | Anthropic model id to use for both research and headline extraction (e.g. claude-sonnet-5). |
topic |
string | yes | — | One-line description of what this bot briefs on. |
keywords |
list[string] | yes | — | Search angles; each is searched separately via web_search. |
channel |
dict | yes | — | Delivery channel config, e.g. {type: telegram}. Secrets for the channel come from env vars, never from this file. |
sources |
list[string] | no | [] |
Curated source URLs considered in addition to web search results. |
lookback_days |
int | no | 2 |
Only include items published within this many days. |
max_items |
int | no | 8 |
Maximum number of items in a single briefing. |
history_path |
string | no | "history.json" |
Path to the local JSON file tracking recently-briefed headlines (used to avoid repeats; entries expire after 30 days). |
prompt_extra |
string | no | "" |
Freeform text appended to the end of the research prompt for extra instructions. |
BriefBot has no built-in scheduler. Run the CLI on whatever cadence you want using your platform's scheduler:
# crontab entry — run every morning at 08:00 in the machine's local time
0 8 * * * cd /path/to/briefbot && /usr/bin/env python -m briefbot run --config config.yamlOr as a Kubernetes CronJob, systemd timer, GitHub Actions scheduled
workflow, etc. — anything that can run a shell command on a schedule and has
the required environment variables available to it works.
model in the config controls both the research call and the headline
extraction call.
claude-sonnet-5(default) — good balance of quality, latency, and cost for high-volume, frequent summarization.claude-opus-4-8— use this when you want maximum research and writing quality (e.g. for a low-frequency, high-stakes briefing) and are willing to trade off latency/cost for it.
BriefBot never reads secrets from the config file. All credentials come from environment variables, resolved at runtime:
| Variable | Used for |
|---|---|
ANTHROPIC_API_KEY |
Authenticates the Anthropic client (read implicitly by the anthropic SDK — no explicit config field for it). |
TELEGRAM_BOT_TOKEN |
Required when channel.type is telegram. |
TELEGRAM_CHAT_ID |
Required when channel.type is telegram. |
.env.example documents the expected variable names with placeholder
values only. Copy it to .env, fill in real values, and export them into
the environment the scheduler runs BriefBot in — BriefBot does not parse
.env files itself. Never commit .env, config.yaml (if it contains
anything sensitive), or history.json; all three are excluded via
.gitignore.
pip install -e ".[dev]"
python -m pytest -q