Skip to content

Commit 4c43e47

Browse files
committed
feat: add Telegram Chat Surface Adapter with configuration options and documentation
1 parent cf00578 commit 4c43e47

3 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
title: Telegram Chat Surface Adapter (WIP)
3+
description: "Reference page for AdminForth Agent chat surface adapters, including Telegram bot setup and webhook configuration."
4+
slug: /tutorial/Adapters/chat-surface-adapter-telegram
5+
sidebar_position: 8
6+
---
7+
8+
# Telegram Chat Surface Adapter
9+
10+
Chat surface adapters connect external chat products to the [Agent plugin](/docs/tutorial/Plugins/agent/).
11+
12+
```bash
13+
pnpm i @adminforth/chat-surface-adapter-telegram
14+
```
15+
16+
Create a Telegram bot with BotFather and add the token to `.env`:
17+
18+
```env title=".env"
19+
TELEGRAM_BOT_TOKEN=your_bot_token
20+
TELEGRAM_WEBHOOK_SECRET=your_random_secret
21+
```
22+
23+
The webhook secret confirms that the request came through Telegram. Your app should still map the Telegram user id to a real AdminForth admin user before running the agent.
24+
25+
## Admin user field `telegramId`
26+
27+
To map Telegram users to AdminForth admin users, the adapter looks up an admin user record by Telegram user id.
28+
By default it expects the admin user resource to have a field named `telegramId`.
29+
30+
Add this field to your `adminuser` resource:
31+
32+
```ts
33+
{
34+
name: 'telegramId',
35+
type: AdminForthDataTypes.STRING,
36+
showIn: ['show', 'edit', 'create'],
37+
},
38+
```
39+
40+
If your field is named differently, configure `adminUserTelegramIdField` option (see below).
41+
42+
Register the adapter in the Agent plugin:
43+
44+
```ts
45+
import AdminForthAgent from '@adminforth/agent';
46+
import TelegramChatSurfaceAdapter from '@adminforth/chat-surface-adapter-telegram';
47+
48+
new AdminForthAgent({
49+
modes: [
50+
// your modes
51+
],
52+
sessionResource: {
53+
// your session resource config
54+
},
55+
turnResource: {
56+
// your turn resource config
57+
},
58+
//diff-add
59+
chatSurfaceAdapters: [
60+
//diff-add
61+
new TelegramChatSurfaceAdapter({
62+
//diff-add
63+
botToken: process.env.TELEGRAM_BOT_TOKEN as string,
64+
//diff-add
65+
webhookSecret: process.env.TELEGRAM_WEBHOOK_SECRET,
66+
//diff-add
67+
adminUserTelegramIdField: 'telegramId',
68+
//diff-add
69+
}),
70+
//diff-add
71+
],
72+
});
73+
```
74+
75+
## Adapter options
76+
77+
All options for `new TelegramChatSurfaceAdapter(options)`:
78+
79+
- `botToken` (string, required) — Telegram bot token from BotFather.
80+
- `webhookSecret` (string, optional) — secret token configured in Telegram `setWebhook`.
81+
- `streamingMode` (`draft` | `typing` | `off`, optional) — streaming behavior for Telegram responses.
82+
- Default: `draft`.
83+
- Note: Telegram drafts work only in private chats. In non-private chats the adapter automatically falls back from `draft` to `typing`.
84+
- `draftUpdateIntervalMs` (number, optional) — throttle for draft preview updates.
85+
- Default: `650`.
86+
- `adminUserTelegramIdField` (string, optional) — admin user field that stores Telegram user id.
87+
- Default: `telegramId`.
88+
- `adminUserResourceId` (string, optional) — AdminForth resource id that stores admin users.
89+
- Default: `adminuser`.
90+
91+
The plugin exposes this webhook endpoint:
92+
93+
```txt
94+
/adminapi/v1/agent/surface/telegram/webhook
95+
```
96+
97+
Set it in Telegram:
98+
99+
```bash
100+
curl -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
101+
-H "Content-Type: application/json" \
102+
-d '{
103+
"url": "https://your-domain.com/adminapi/v1/agent/surface/telegram/webhook",
104+
"secret_token": "'"${TELEGRAM_WEBHOOK_SECRET}"'"
105+
}'
106+
```
107+
108+
Telegram does not provide a user time zone in message updates, so the adapter sends `UTC` as `userTimeZone`.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import type { AdminUser } from "../Common.js";
2+
import type { IAdminForth } from "../Back.js";
3+
4+
export type ChatSurfaceRequestContext = {
5+
body: unknown;
6+
headers: Record<string, unknown>;
7+
abortSignal: AbortSignal;
8+
rawRequest?: unknown;
9+
rawResponse?: unknown;
10+
};
11+
12+
export type ChatSurfaceIncomingMessage = {
13+
surface: string;
14+
prompt: string;
15+
externalConversationId: string;
16+
externalUserId: string;
17+
userTimeZone?: string;
18+
modeName?: string | null;
19+
metadata?: Record<string, unknown>;
20+
};
21+
22+
export type ChatSurfaceEvent =
23+
| {
24+
type: "text_delta";
25+
delta: string;
26+
}
27+
| {
28+
type: "reasoning_delta";
29+
delta: string;
30+
}
31+
| {
32+
type: "tool_call";
33+
event: unknown;
34+
}
35+
| {
36+
type: "done";
37+
text: string;
38+
}
39+
| {
40+
type: "error";
41+
message: string;
42+
};
43+
44+
export interface ChatSurfaceEventSink {
45+
emit(event: ChatSurfaceEvent): void | Promise<void>;
46+
close?(): void | Promise<void>;
47+
}
48+
49+
export interface ChatSurfaceAdapter {
50+
/**
51+
* Stable surface identifier used in webhook URLs and session ids.
52+
*/
53+
name: string;
54+
55+
/**
56+
* Validates adapter configuration during plugin initialization.
57+
*/
58+
validate(): void;
59+
60+
/**
61+
* Converts an incoming surface webhook/request into an AdminForth Agent prompt.
62+
* Return null when the request is not a supported chat message.
63+
*/
64+
parseIncomingMessage(
65+
ctx: ChatSurfaceRequestContext,
66+
): ChatSurfaceIncomingMessage | null | Promise<ChatSurfaceIncomingMessage | null>;
67+
68+
/**
69+
* Creates a response sink for sending agent events back to the external surface.
70+
*/
71+
createEventSink(
72+
ctx: ChatSurfaceRequestContext,
73+
incoming: ChatSurfaceIncomingMessage,
74+
): ChatSurfaceEventSink | Promise<ChatSurfaceEventSink>;
75+
76+
/**
77+
* Maps an external surface user to an authorized AdminForth admin user.
78+
* Return null to reject the incoming chat message.
79+
*/
80+
resolveAdminUser(input: {
81+
adminforth: IAdminForth;
82+
incoming: ChatSurfaceIncomingMessage;
83+
}): AdminUser | null | Promise<AdminUser | null>;
84+
}

adminforth/types/adapters/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
export type { EmailAdapter } from './EmailAdapter.js';
2+
export type {
3+
ChatSurfaceAdapter,
4+
ChatSurfaceEvent,
5+
ChatSurfaceEventSink,
6+
ChatSurfaceIncomingMessage,
7+
ChatSurfaceRequestContext,
8+
} from './ChatSurfaceAdapter.js';
29
export type {
310
CompletionAdapter,
411
CompletionAdapterLangChainAgentPurpose,

0 commit comments

Comments
 (0)