Skip to content

Commit 02dab19

Browse files
Consolidate Telegram integration
1 parent 5309f6c commit 02dab19

9 files changed

Lines changed: 494 additions & 100 deletions

File tree

assistants/podcast/.env.example

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
1-
TELEGRAM_BOT_API_KEY=
2-
TELEGRAM_CHAT_ID=
31
GROQ_API_KEY=
42
HERU_ENGINE=codex

assistants/podcast/README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DataOps Assistant: Podcast Skill
22

3-
Podcast skill module for DataOps Assistant. It collects podcast prep material
4-
from Telegram and local inputs, then generates podcast guest documents.
3+
Podcast skill module for DataOps Assistant. It processes podcast prep material
4+
routed from the shared DataOps intake and generates podcast guest documents.
55

66
This directory is the canonical in-repo implementation for the podcast skill:
77
`assistants/podcast/`. The operator-facing assistant is DataOps Assistant;
@@ -10,10 +10,12 @@ workflow templates, and dry-run tests. The old root-level `podcast-assistant/`
1010
import name is retained here only as migration history; active development uses
1111
this module path.
1212

13-
Telegram is a DataOps Assistant intake/progress channel. This module is copied
14-
from the Telegram Writing Assistant shape, but the agent execution boundary uses
15-
Heru instead of calling Claude directly. Set `HERU_ENGINE=codex` or
16-
`HERU_ENGINE=claude` to choose which coding agent processes the inbox.
13+
Telegram is a shared DataOps intake channel, not a bot owned by this module.
14+
The deployed backend owns the single Telegram webhook, bot credentials,
15+
allowlist, and command routing. `/podcast` requests are represented as podcast
16+
assistant jobs and are then processed by this module's Heru boundary. Set
17+
`HERU_ENGINE=codex` or `HERU_ENGINE=claude` to choose which coding agent
18+
processes a local assistant run.
1719

1820
## Setup
1921

@@ -23,18 +25,18 @@ From the DataOps checkout:
2325
cd assistants/podcast
2426
uv sync
2527
cp .env.example .env
26-
uv run python main.py
2728
```
2829

29-
Required `.env` values:
30+
Optional local processing `.env` values:
3031

3132
```bash
32-
TELEGRAM_BOT_API_KEY=...
33-
TELEGRAM_CHAT_ID=...
3433
GROQ_API_KEY=...
3534
HERU_ENGINE=codex
3635
```
3736

37+
Do not configure or start a separate Telegram polling bot from this directory.
38+
Shared Telegram configuration belongs to the deployed backend integration.
39+
3840
`HERU_ENGINE` can be `codex` or `claude`. The DataOps checkout expects the local
3941
Heru source at `../heru` relative to the repo root for live processing. Heru is
4042
not installed by default because unit CI must run from a clean checkout without
@@ -45,12 +47,11 @@ running `/process` or `process_request.py`:
4547
uv pip install -e ../../../heru
4648
```
4749

48-
## Commands
50+
## Shared Telegram route
4951

50-
- `/start` - show bot help
51-
- `/status` - show inbox and document counts
52-
- `/process` - process with the default `HERU_ENGINE`
53-
- `/process codex` or `/process claude` - process with a specific Heru engine
52+
The shared DataOps bot exposes `/podcast <notes>` alongside other capabilities
53+
such as `/social` and general operations intake. The podcast module does not
54+
own `/start`, `/status`, polling, webhook registration, or Telegram secrets.
5455

5556
## Layout
5657

assistants/podcast/main.py

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@
1717
from telegram import MessageEntity, Update
1818
from telegram.constants import MessageEntityType
1919
from telegram.ext import (
20-
Application,
21-
CommandHandler,
2220
ContextTypes,
23-
MessageHandler,
24-
filters,
2521
)
2622

2723
from heru_runner import DEFAULT_ENGINE
@@ -30,8 +26,10 @@
3026

3127
load_dotenv()
3228

33-
TELEGRAM_BOT_API_KEY = os.getenv("TELEGRAM_BOT_API_KEY")
34-
TELEGRAM_CHAT_ID = int(os.getenv("TELEGRAM_CHAT_ID", "0") or 0)
29+
# Kept only for compatibility with the imported handler unit tests. The
30+
# production Telegram allowlist and bot credentials live in the shared backend
31+
# webhook; this module no longer starts an independent polling bot.
32+
TELEGRAM_CHAT_ID = 0
3533
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
3634
HERU_ENGINE = os.getenv("HERU_ENGINE", DEFAULT_ENGINE)
3735
HERU_MODEL = os.getenv("HERU_MODEL")
@@ -515,24 +513,10 @@ async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE) -> N
515513

516514

517515
def main() -> None:
518-
ensure_directories()
519-
if not TELEGRAM_BOT_API_KEY:
520-
raise RuntimeError("TELEGRAM_BOT_API_KEY is required")
521-
if not TELEGRAM_CHAT_ID:
522-
raise RuntimeError("TELEGRAM_CHAT_ID is required")
523-
524-
application = Application.builder().token(TELEGRAM_BOT_API_KEY).build()
525-
application.add_handler(CommandHandler("start", start_command))
526-
application.add_handler(CommandHandler("status", status_command))
527-
application.add_handler(CommandHandler("process", process_command))
528-
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_text_message))
529-
application.add_handler(MessageHandler(filters.VOICE, handle_voice_message))
530-
application.add_handler(MessageHandler(filters.AUDIO, handle_audio_message))
531-
application.add_handler(MessageHandler(filters.PHOTO, handle_photo_message))
532-
application.add_handler(MessageHandler(filters.VIDEO | filters.ANIMATION, handle_video_message))
533-
application.add_handler(MessageHandler(filters.Document.ALL, handle_document_message))
534-
application.add_error_handler(error_handler)
535-
application.run_polling(allowed_updates=Update.ALL_TYPES)
516+
raise RuntimeError(
517+
"The podcast module no longer starts a Telegram bot. "
518+
"Use the shared DataOps /api/webhook/telegram integration and /podcast route."
519+
)
536520

537521

538522
if __name__ == "__main__":

backend/README.md

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,38 @@ npm run seed
5252
From the repository root, `npm run seed:backend` runs both default user and
5353
template seeders.
5454

55-
### Social draft assistant configuration
55+
### Shared Telegram and assistant configuration
5656

57-
The first social drafting slice is covered by local tests with mocked external
58-
services. A real local route call uses configured z.ai and Typefully credentials
59-
when the target account is unambiguous:
57+
DataOps has one Telegram bot and one webhook:
58+
59+
```text
60+
POST /api/webhook/telegram
61+
```
62+
63+
The webhook routes ordinary messages and attachments to intake, `/podcast` to
64+
the podcast assistant job flow, and `/social` to social drafting. Assistant
65+
modules do not own separate Telegram tokens, polling processes, or webhooks.
66+
67+
Local development can use `TELEGRAM_BOT_TOKEN`,
68+
`TELEGRAM_WEBHOOK_SECRET`, and comma-separated
69+
`TELEGRAM_ALLOWED_CHAT_IDS`. Production uses one out-of-band AWS Secrets
70+
Manager JSON secret named by `TELEGRAM_INTEGRATION_SECRET_NAME`:
71+
72+
```json
73+
{
74+
"botToken": "...",
75+
"webhookSecret": "...",
76+
"allowedChatIds": ["..."]
77+
}
78+
```
79+
80+
Do not commit real values. The webhook fails closed when its configuration is
81+
missing, rejects a wrong Telegram secret header, and rejects chats outside the
82+
allowlist.
83+
84+
The social drafting path is covered by local tests with mocked external
85+
services. A direct local route call can still exercise the assistant without
86+
Telegram:
6087

6188
```bash
6289
curl -X POST http://localhost:3000/api/assistant-social-drafts/mock-telegram \
@@ -75,8 +102,10 @@ Production-style external calls require managed credentials and account config:
75102
| `TYPEFULLY_API_KEY` | Typefully API key for saved draft creation |
76103
| `TYPEFULLY_SOCIAL_SET_ALEXEY` | Typefully social set id for Alexey / `Al_Grigor` |
77104
| `TYPEFULLY_SOCIAL_SET_DATATALKSCLUB` | Typefully social set id for DataTalksClub |
78-
| `TELEGRAM_WEBHOOK_SECRET` | Telegram webhook secret token for real webhook delivery |
79-
| `TELEGRAM_BOT_TOKEN` | Telegram bot token for optional replies |
105+
| `TELEGRAM_INTEGRATION_SECRET_NAME` | Production shared Telegram JSON secret name |
106+
| `TELEGRAM_WEBHOOK_SECRET` | Local-only webhook secret fallback |
107+
| `TELEGRAM_BOT_TOKEN` | Local-only bot token fallback for replies |
108+
| `TELEGRAM_ALLOWED_CHAT_IDS` | Local-only comma-separated chat allowlist fallback |
80109

81110
The assistant route creates Typefully saved drafts only. It does not schedule or
82111
publish posts. Automated tests use mocked z.ai and Typefully clients; real z.ai,

backend/src/assistant/socialDraftAssistant.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,15 @@ async function runSocialDraftAssistant(
383383
requestedBy: actorId,
384384
inputRefs: [{
385385
type: 'source-message',
386-
title: 'Mock Telegram social drafting request',
386+
title: 'Telegram social drafting request',
387387
id: source.messageId,
388-
metadata: {
388+
metadata: Object.fromEntries(Object.entries({
389389
source: 'telegram',
390390
chatId: source.chatId,
391391
chatTitle: source.chatTitle,
392392
senderHandle: source.senderHandle,
393393
text: source.text,
394-
},
394+
}).filter(([, value]) => value !== undefined)),
395395
}],
396396
approvalRequired: true,
397397
approval: { status: 'pending' },

backend/src/router.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const AUTH_EXEMPT_PATHS = new Set([
5757
function isAuthExempt(method: string, path: string): boolean {
5858
if (AUTH_EXEMPT_PATHS.has(path)) return true;
5959
if (method === 'POST' && path === '/api/v1/intake/email-documents') return true;
60+
if (method === 'POST' && path === '/api/webhook/telegram') return true;
6061
// Static assets
6162
if (method === 'GET' && path.startsWith('/public/')) return true;
6263
return false;

0 commit comments

Comments
 (0)