feat(telegram): add Quick Start Guide, notification toggle, and UX improvements
keyboardstaff committed
Mar 22, 2026 at 08:40 UTC
5dbc1770699bff54cf89232ea5f09ff04f579e49
4 files changed
+149
-27
plugins/_telegram_integration/default_config.yaml
+1
@@ -2,6 +2,7 @@ bots: []
2
# Example bot:
3
# - name: my_bot
4
# enabled: true
5
+# notify_messages: false # Show a WebUI notification for each incoming Telegram message.
6
# token: ""
7
# mode: polling # polling or webhook
8
# webhook_url: "" # required if mode=webhook, your A0 base URL e.g. https://yourdomain.com (webhook path appended automatically)
plugins/_telegram_integration/helpers/handler.py
+21
-19
@@ -175,15 +175,16 @@ async def handle_clear(message: TgMessage, bot_name: str, bot_cfg: dict):
175
)
176
177
# Send notification
178
- username_str = f"@{user.username}" if user.username else str(user.id)
179
- NotificationManager.send_notification(
180
- type=NotificationType.INFO,
181
- priority=NotificationPriority.NORMAL,
182
- title="Telegram: chat cleared",
183
- message=f"{username_str} cleared their chat via /clear",
184
- display_time=5,
185
- group="telegram",
186
- )
178
+ if bot_cfg.get("notify_messages", False):
179
+ username_str = f"@{user.username}" if user.username else str(user.id)
180
+ NotificationManager.send_notification(
181
+ type=NotificationType.INFO,
182
+ priority=NotificationPriority.NORMAL,
183
+ title="Telegram: chat cleared",
184
+ message=f"{username_str} cleared their chat via /clear",
185
+ display_time=5,
186
+ group="telegram",
187
+ )
188
189
190
async def handle_message(message: TgMessage, bot_name: str, bot_cfg: dict):
@@ -250,16 +251,17 @@ async def handle_message(message: TgMessage, bot_name: str, bot_cfg: dict):
251
save_tmp_chat(context)
252
253
# Send notification
253
- username_str = f"@{user.username}" if user.username else str(user.id)
254
- preview = (text[:80] + "...") if len(text) > 80 else text
255
- NotificationManager.send_notification(
256
- type=NotificationType.INFO,
257
- priority=NotificationPriority.HIGH,
258
- title="Telegram: new message",
259
- message=f"From {username_str}: {preview}",
260
- display_time=10,
261
- group="telegram",
262
- )
254
+ if bot_cfg.get("notify_messages", False):
255
+ username_str = f"@{user.username}" if user.username else str(user.id)
256
+ preview = (text[:80] + "...") if len(text) > 80 else text
257
+ NotificationManager.send_notification(
258
+ type=NotificationType.INFO,
259
+ priority=NotificationPriority.HIGH,
260
+ title="Telegram: new message",
261
+ message=f"From {username_str}: {preview}",
262
+ display_time=10,
263
+ group="telegram",
264
+ )
265
266
267
async def handle_callback_query(query: CallbackQuery, bot_name: str, bot_cfg: dict):
plugins/_telegram_integration/webui/config.html
+125
-7
@@ -13,9 +13,48 @@
13
<div class="section-title">Telegram Integration</div>
14
<div class="section-description">
15
Configure Telegram bots to communicate with Agent Zero via Telegram.
16
- Each bot connects to a Telegram Bot API token and handles messages from allowed users.
16
</div>
17
18
+ <!-- Quick Start Guide (collapsible) -->
19
+ <details class="tg-guide">
20
+ <summary class="tg-guide-toggle">Quick Start Guide</summary>
21
+ <div class="tg-guide-body">
22
+ <div class="tg-guide-section tg-guide-section-first">
23
+ <div class="tg-guide-row">
24
+ <div class="tg-guide-steps">
25
+ <div class="tg-guide-section-title">Get Bot Token</div>
26
+ <ol>
27
+ <li>Click <a href="https://t.me/BotFather" target="_blank" rel="noopener">@BotFather</a> or scan the QR code on your mobile device, or search <strong>@BotFather</strong> in Telegram</li>
28
+ <li>Send <strong>/newbot</strong> and follow the prompts to create a bot</li>
29
+ <li>Copy the API token provided by BotFather</li>
30
+ <li>Click <strong>Add Bot</strong> below, paste the token, and save</li>
31
+ </ol>
32
+ </div>
33
+ <img class="tg-qr" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0AQAAAAB84SuKAAAA50lEQVR4nMWVQWoFMQxDnz+zl2/w73+suYF8Aheni98ux1BqglEgQjOx7ETzM+r1awt/v4+ILCAHLPjqJivLAx7zLwjh7Dxg+T/pKj84/4nr5FHPgxb6bRLjAY/50QE6sED3Uz79HZrr7/ZzPiM/X2B7w/cw263uFZ+2sOb6rMf8iyZNNiqt6lfFG1fembHxzxszKQ1a9a8SO26STf9RU8MUqUX/0DLSmULSpn4T6Kx+zn+d+cMdJrWYP4zvxjy91SeSt6mKjf51cinGgOznsVP2rn7dksaEU8uF/yPAGvsu/B///H59AYlVhAI4J5PTAAAAAElFTkSuQmCC"
34
+ alt="Scan to open @BotFather" title="@BotFather" />
35
+ </div>
36
+ </div>
37
+ <div class="tg-guide-section">
38
+ <div class="tg-guide-row">
39
+ <div class="tg-guide-steps">
40
+ <div class="tg-guide-section-title">Set Allowed Users</div>
41
+ <ol>
42
+ <li>Enter your Telegram handle (e.g. <strong>@yourname</strong>) in the <strong>Allowed Users</strong> field below.</li>
43
+ <li>If you don't know your handle, click <a href="https://t.me/userinfobot" target="_blank" rel="noopener">@userinfobot</a> or scan the QR code, then send <strong>/start</strong> to get your numeric ID</li>
44
+ </ol>
45
+ </div>
46
+ <img class="tg-qr" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0AQAAAAB84SuKAAAA5klEQVR4nMWVUWrEQAxDn5f+yzfY+x8rN7BPoOLZhW0/Yyg1A6OBOJbHihLmZ/Tj1xH+/hwRGUEesMiXjSs4YMMnkqaGQG77UVJ14/lPfL331Gtf1HfRVdSA2/nhubkDG3Td5+/XW3TAQj/RmYWahPvzDydUUmEC366P36tkrFroTzkKpHtEuJjfVdEJfmqhX6a0LSGrXKt8kCnDqn9kkSZKG/6qo9waGW74T0zmsLif/zi2czmfJN76D32NhbDqH830zhC84H9CTak6l/5TgccJtPSfLOdYwEq/oDM/1eL7i3/+f30Di2x7PjI9e0cAAAAASUVORK5CYII="
47
+ alt="Scan to open @userinfobot" title="@userinfobot" />
48
+ </div>
49
+ </div>
50
+ <div class="tg-guide-note">
51
+ <strong>Group chats:</strong> To let the bot respond to all group messages (not just @mentions),
52
+ go to @BotFather → <em>/mybots</em> → select your bot → <em>Bot Settings</em> →
53
+ <em>Group Privacy</em> → <em>Turn off</em>.
54
+ </div>
55
+ </div>
56
+ </details>
57
+
58
<!-- Bot cards -->
59
<template x-for="(bot, idx) in (config.bots || [])" :key="idx">
60
<div class="bot-card">
@@ -47,6 +86,19 @@
86
</div>
87
</div>
88
89
+ <div class="field">
90
+ <div class="field-label">
91
+ <div class="field-title">Message Notifications</div>
92
+ <div class="field-description">Show a WebUI notification for each incoming Telegram message</div>
93
+ </div>
94
+ <div class="field-control">
95
+ <label class="toggle">
96
+ <input type="checkbox" x-model="bot.notify_messages" />
97
+ <span class="toggler"></span>
98
+ </label>
99
+ </div>
100
+ </div>
101
+
102
<div class="field">
103
<div class="field-label">
104
<div class="field-title">Bot Name</div>
@@ -70,7 +122,7 @@
122
<div class="field">
123
<div class="field-label">
124
<div class="field-title">Mode</div>
73
- <div class="field-description">Polling: no public IP needed. Webhook: requires HTTPS endpoint</div>
125
+ <div class="field-description">Polling: no public IP required. Webhook: needs HTTPS URL</div>
126
</div>
127
<div class="field-control">
128
<select x-model="bot.mode">
@@ -83,7 +135,7 @@
135
<div class="field" x-show="bot.mode === 'webhook'">
136
<div class="field-label">
137
<div class="field-title">Webhook URL</div>
86
- <div class="field-description">Your Agent Zero base URL (e.g. https://yourdomain.com). The webhook path is appended automatically</div>
138
+ <div class="field-description">Your Agent Zero base URL, e.g. https://yourdomain.com</div>
139
</div>
140
<div class="field-control">
141
<input type="text" x-model="bot.webhook_url" placeholder="https://yourdomain.com" />
@@ -103,7 +155,7 @@
155
<div class="field">
156
<div class="field-label">
157
<div class="field-title">Allowed Users</div>
106
- <div class="field-description">Comma-separated Telegram user IDs or @usernames. Empty = allow all</div>
158
+ <div class="field-description">Comma-separated user IDs or @usernames. Empty = anyone can use</div>
159
</div>
160
<div class="field-control">
161
<input type="text"
@@ -129,8 +181,8 @@
181
182
<div class="field">
183
<div class="field-label">
132
- <div class="field-title">User → Project Mapping</div>
133
- <div class="field-description">Map user IDs to projects. Format: user_id=project_name, comma-separated</div>
184
+ <div class="field-title">User Project Mapping</div>
185
+ <div class="field-description">Map user IDs to projects: user_id=project, separate by comma</div>
186
</div>
187
<div class="field-control">
188
<input type="text"
@@ -159,7 +211,7 @@
211
<div class="field">
212
<div class="field-label">
213
<div class="field-title">Attachment Max Age</div>
162
- <div class="field-description">Auto-delete downloaded attachments older than this many hours. 0 = keep forever</div>
214
+ <div class="field-description">Hours before attachments auto-delete. 0 = keep forever</div>
215
</div>
216
<div class="field-control">
217
<input type="number" x-model.number="bot.attachment_max_age_hours" min="0" step="1" placeholder="0" />
@@ -217,6 +269,71 @@
269
flex-direction: column;
270
gap: 8px;
271
}
272
+ .tg-guide {
273
+ border: 1px solid var(--color-border);
274
+ border-radius: 6px;
275
+ font-size: 0.85rem;
276
+ }
277
+ .tg-guide-toggle {
278
+ padding: 8px 10px;
279
+ cursor: pointer;
280
+ opacity: 0.8;
281
+ }
282
+ .tg-guide-toggle:hover {
283
+ opacity: 1;
284
+ }
285
+ .tg-guide-body {
286
+ padding: 4px 12px 12px;
287
+ border-top: 1px solid var(--color-border);
288
+ }
289
+ .tg-guide-section {
290
+ margin-top: 10px;
291
+ }
292
+ .tg-guide-section-first {
293
+ margin-top: 15px;
294
+ }
295
+ .tg-guide-steps {
296
+ flex: 1;
297
+ }
298
+ .tg-guide-section-title {
299
+ font-weight: 600;
300
+ font-size: 0.85rem;
301
+ margin-bottom: 4px;
302
+ opacity: 0.9;
303
+ }
304
+ .tg-guide-body ol {
305
+ margin: 4px 0;
306
+ padding-left: 20px;
307
+ }
308
+ .tg-guide-body li {
309
+ margin-bottom: 4px;
310
+ }
311
+ .tg-guide-body a {
312
+ color: var(--color-highlight);
313
+ }
314
+ .tg-guide-note {
315
+ margin-top: 15px;
316
+ padding: 6px 10px;
317
+ background: var(--color-background-hover, rgba(255,255,255,0.04));
318
+ border-radius: 4px;
319
+ font-size: 0.8rem;
320
+ opacity: 0.85;
321
+ }
322
+ .tg-guide-row {
323
+ display: flex;
324
+ align-items: flex-start;
325
+ gap: 12px;
326
+ }
327
+ .tg-guide-row ol {
328
+ flex: 1;
329
+ }
330
+ .tg-qr {
331
+ width: 88px;
332
+ height: 88px;
333
+ border-radius: 4px;
334
+ flex-shrink: 0;
335
+ image-rendering: pixelated;
336
+ }
337
.bot-card {
338
border: 1px solid var(--color-border);
339
border-radius: 6px;
@@ -270,6 +387,7 @@
387
}
388
.bot-add-btn {
389
margin-top: 4px;
390
+ width: fit-content;
391
}
392
.bot-test-row {
393
margin-top: 12px;
plugins/_telegram_integration/webui/telegram-config-store.js
+2
-1
@@ -24,7 +24,8 @@ export const store = createStore("telegramConfig", {
24
defaultBot() {
25
return {
26
name: "",
27
- enabled: false,
27
+ enabled: true,
28
+ notify_messages: false,
29
token: "",
30
mode: "polling",
31
webhook_url: "",