Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9895809
feat(web): add notion write-back smoke tooling
thxforall May 7, 2026
d44d531
feat(web): add telegram digest smoke tooling
thxforall May 7, 2026
a813da6
fix(web): clarify telegram smoke chat access failures
thxforall May 7, 2026
3d89e3d
feat(web): group telegram digests by topic
thxforall May 7, 2026
4f1da53
feat(web): add telegram polling raw ingest
thxforall May 7, 2026
db078cc
feat(web): add telegram raw ingest replay
thxforall May 7, 2026
2cc3263
Label Telegram topic digest titles
thxforall May 7, 2026
2cb781f
Add Telegram digest JSON preview
thxforall May 7, 2026
67c7969
Add Telegram digest Notion write pilot
thxforall May 7, 2026
5b8e9bf
Populate Notion write pages with body blocks
thxforall May 7, 2026
f1b891e
Guard Notion digest writes by trace id
thxforall May 7, 2026
f6e022a
Add Telegram webhook front door
thxforall May 7, 2026
d8b1787
Stabilize Telegram digest trace ids
thxforall May 7, 2026
e2b1076
Store Telegram webhook raw events
thxforall May 7, 2026
f66dded
Validate Telegram webhook payloads
thxforall May 7, 2026
38075a7
Constrain Telegram webhook update types
thxforall May 7, 2026
8bc8612
Add Telegram raw events table
thxforall May 7, 2026
e72c8e8
Document Telegram digest Notion smoke
thxforall May 7, 2026
19b5fde
docs: define telegram digest notion schema
thxforall May 7, 2026
4f5ed85
feat: split telegram digest notion write-back
thxforall May 7, 2026
d4252c0
docs: design telegram digest phase 2a
thxforall May 7, 2026
00c2731
feat: extract telegram digest candidates
thxforall May 7, 2026
9961318
Merge branch 'dev' into feature/notion-smoke-474
thxforall May 28, 2026
71e06dd
docs(agent): add frontmatter to telegram-ingest.md
thxforall May 28, 2026
4601528
chore(docs): fix dev-accumulated wiki:lint violations
thxforall May 28, 2026
a5414db
Merge branch 'dev' into feature/notion-smoke-474
thxforall May 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,9 @@
| OMC | Claude + Gemini 듀얼 | 대규모 작업 보조 |
| GSD quick | 원자적 단발 패치 | 유지보수 |

<!-- Last Updated: 2026-05-21 -->
## Commit discipline

- After completing implementation or verification work, create a git commit for the completed scope unless the user explicitly says not to commit.
- Keep commits scoped to the task and do not include unrelated worktree changes.

<!-- Last Updated: 2026-05-28 -->
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

99 changes: 99 additions & 0 deletions docs/agent/telegram-ingest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Telegram ingest
owner: human
status: approved
updated: 2026-05-28
tags: [agent, telegram, ops]
---

# Telegram ingest

`packages/web` has a local Telegram ingest lane for `getUpdates` polling. It can replay stored raw updates into Telegram Digest previews and can run a guarded Notion write pilot for exactly one selected digest.

## Commands

Run from `packages/web`:

```bash
bun run telegram:ingest
bun run telegram:replay
bun run telegram:replay --json
bun run telegram:digest:write --json --limit 1
```

## Environment

- `TELEGRAM_BOT_TOKEN`: Bot API token.
- `TELEGRAM_BOT_TOKEN_SECRET_REF`: Optional env var name that contains the token. Defaults to `TELEGRAM_BOT_TOKEN`.
- `TELEGRAM_WEBHOOK_SECRET`: Secret token expected in Telegram's `X-Telegram-Bot-Api-Secret-Token` webhook header.
- `TELEGRAM_WEBHOOK_SECRET_REF`: Optional env var name that contains the webhook secret. Defaults to `TELEGRAM_WEBHOOK_SECRET`.
- `TELEGRAM_CHAT_ID`: Optional chat filter for printed digest candidates.
- `TELEGRAM_INGEST_DIR`: Optional local store directory. Defaults to `.logs/telegram`.
- `TELEGRAM_RAW_STORE_PATH`: Optional replay input path. Defaults to `${TELEGRAM_INGEST_DIR}/raw-updates.jsonl`.
- `TELEGRAM_GETUPDATES_LIMIT`: Optional poll limit. Defaults to `100`.
- `TELEGRAM_GETUPDATES_TIMEOUT`: Optional Bot API long-poll timeout. Defaults to `0`.
- `NOTION_TELEGRAM_DIGEST_DATA_SOURCE_ID`: Required for `telegram:digest:write --commit`. Points to the dedicated Telegram Digest database, not the Meeting Summary database.
- `TELEGRAM_DIGEST_WRITE_CONFIRM`: Must be `1` before `telegram:digest:write --commit` creates Notion rows.

## Local store contract

Raw updates are appended as JSONL envelopes:

```json
{ "fetchedAt": "2026-05-07T12:00:00.000Z", "update": { "update_id": 10 } }
```

Offset state is stored separately:

```json
{
"nextOffset": 12
}
```

The next offset is `max(update_id) + 1`. Empty polls do not rewrite the offset.

## Output contract

The ingest flow returns:

- raw update count
- normalized text messages
- topic digest candidates grouped by chat and `message_thread_id`

Digest candidates use the dedicated Telegram Digest payload shape. `telegram:replay --json` prints the exact candidate payloads without writing.

`telegram:digest:write` defaults to dry-run mode. It only writes when all of these are true:

- `--commit` is present.
- `TELEGRAM_DIGEST_WRITE_CONFIRM=1` is set.
- candidate filters select exactly one digest.

The Notion write path checks the Telegram Digest data source for an existing `Trace ID` before creating a row, so repeated runs with the same trace do not create duplicates. Phase 1 writes only a Telegram Digest row; it does not create Meeting Summary or Action Item rows. Candidate task text is stored on the digest row as `Action Item Candidate`.

## Webhook front door

`POST /api/telegram/webhook` validates Telegram's secret token header before reading the payload. Malformed update payloads return `400` and are not stored. Supported `message` and `channel_post` updates return `202` with a stable event id shaped as `telegram:<chat_id>:<message_id>`.

Unsupported update types are acknowledged with `200` and `accepted: false` so Telegram does not keep retrying non-actionable membership or service updates.

When registering the webhook with Telegram, restrict `allowed_updates` to:

```json
["message", "channel_post"]
```

The app-side payload builder uses `drop_pending_updates: false` by default so registration does not silently discard unprocessed updates.

Accepted updates are upserted into `telegram_raw_events` using `event_id` as the conflict key before the route returns `202`. The expected row contract is:

- `event_id`
- `source`
- `update_id`
- `update_kind`
- `trace_id`
- `received_at`
- `payload`
- `status`

Creating or migrating this table remains an infra task and should be handled separately with a human checkpoint.
Loading
Loading