openkbs 0.0.67 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +1 -0
  2. package/elastic/README.md +1 -1
  3. package/elastic/functions.md +5 -5
  4. package/elastic/pulse.md +2 -2
  5. package/package.json +2 -2
  6. package/scripts/deploy.js +68 -0
  7. package/src/actions.js +7 -0
  8. package/templates/.claude/skills/openkbs/SKILL.md +37 -8
  9. package/templates/.claude/skills/openkbs/examples/monitoring-bot/README.md +55 -0
  10. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/instructions.txt +40 -0
  11. package/templates/.claude/skills/openkbs/examples/monitoring-bot/app/settings.json +41 -0
  12. package/templates/.claude/skills/openkbs/examples/monitoring-bot/openkbs.json +3 -0
  13. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/actions.js +141 -0
  14. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/handler.js +32 -0
  15. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/memoryHelpers.js +91 -0
  16. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onCronjob.js +105 -0
  17. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onPublicAPIRequest.js +165 -0
  18. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onRequest.js +2 -0
  19. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Events/onResponse.js +2 -0
  20. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.js +74 -0
  21. package/templates/.claude/skills/openkbs/examples/monitoring-bot/src/Frontend/contentRender.json +3 -0
  22. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/index.mjs +228 -0
  23. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/auth/package.json +7 -0
  24. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/index.mjs +287 -0
  25. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/posts/package.json +10 -0
  26. package/templates/.claude/skills/openkbs/examples/nodejs-demo/functions/settings.json +4 -0
  27. package/templates/.claude/skills/openkbs/examples/nodejs-demo/openkbs.json +16 -0
  28. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/index.html +658 -0
  29. package/templates/.claude/skills/openkbs/examples/nodejs-demo/site/settings.json +4 -0
  30. package/templates/.claude/skills/openkbs/patterns/cronjob-batch-processing.md +278 -0
  31. package/templates/.claude/skills/openkbs/patterns/cronjob-monitoring.md +341 -0
  32. package/templates/.claude/skills/openkbs/patterns/file-upload.md +205 -0
  33. package/templates/.claude/skills/openkbs/patterns/image-generation.md +139 -0
  34. package/templates/.claude/skills/openkbs/patterns/memory-system.md +264 -0
  35. package/templates/.claude/skills/openkbs/patterns/public-api-item-proxy.md +254 -0
  36. package/templates/.claude/skills/openkbs/patterns/scheduled-tasks.md +157 -0
  37. package/templates/.claude/skills/openkbs/patterns/telegram-webhook.md +424 -0
  38. package/templates/.claude/skills/openkbs/patterns/telegram.md +222 -0
  39. package/templates/.claude/skills/openkbs/patterns/vectordb-archive.md +231 -0
  40. package/templates/.claude/skills/openkbs/patterns/video-generation.md +145 -0
  41. package/templates/.claude/skills/openkbs/patterns/web-publishing.md +257 -0
  42. package/templates/.claude/skills/openkbs/reference/backend-sdk.md +13 -2
  43. package/templates/.claude/skills/openkbs/reference/elastic-services.md +61 -29
  44. package/templates/platform/README.md +15 -50
  45. package/templates/platform/agents/assistant/app/icon.png +0 -0
  46. package/templates/platform/agents/assistant/app/instructions.txt +9 -21
  47. package/templates/platform/agents/assistant/app/settings.json +11 -15
  48. package/templates/platform/agents/assistant/src/Events/actions.js +31 -62
  49. package/templates/platform/agents/assistant/src/Events/handler.js +54 -0
  50. package/templates/platform/agents/assistant/src/Events/onRequest.js +3 -0
  51. package/templates/platform/agents/assistant/src/Events/onRequest.json +5 -0
  52. package/templates/platform/agents/assistant/src/Events/onResponse.js +2 -40
  53. package/templates/platform/agents/assistant/src/Events/onResponse.json +4 -2
  54. package/templates/platform/agents/assistant/src/Frontend/contentRender.js +17 -16
  55. package/templates/platform/agents/assistant/src/Frontend/contentRender.json +1 -1
  56. package/templates/platform/functions/api/index.mjs +17 -23
  57. package/templates/platform/functions/api/package.json +4 -0
  58. package/templates/platform/openkbs.json +7 -2
  59. package/templates/platform/site/index.html +18 -19
  60. package/version.json +3 -3
  61. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/run_job.js +0 -26
  62. package/templates/.claude/skills/openkbs/examples/ai-copywriter-agent/scripts/utils/agent_client.js +0 -265
@@ -0,0 +1,424 @@
1
+ # Telegram Webhook Pattern
2
+
3
+ Complete working code for receiving and processing Telegram messages via webhook.
4
+
5
+ ## Concept
6
+
7
+ Full Telegram bot integration with:
8
+ - Webhook setup/removal via query params
9
+ - Secret token validation
10
+ - Channel posts and direct messages
11
+ - Photo upload to OpenKBS storage
12
+ - Message editing and deletion
13
+ - Duplicate detection
14
+ - Autonomous chat processing
15
+
16
+ ## onPublicAPIRequest.js
17
+
18
+ ```javascript
19
+ import {
20
+ setAgentSetting,
21
+ getAgentSetting,
22
+ storeTelegramMessage,
23
+ getTelegramMessage,
24
+ deleteTelegramMessage
25
+ } from './memoryHelpers.js';
26
+
27
+ const AUTONOMOUS_CHAT_RULES = `**Autonomous Chat Rules:**
28
+ This chat has NO USER listening. You MUST:
29
+ 1. ONLY output commands - no explanatory text
30
+ 2. To communicate with user: Use sendToTelegramChannel command
31
+ 3. Plain text "END" to finish the chat`;
32
+
33
+ export const handler = async ({ payload, queryStringParameters, headers }) => {
34
+ try {
35
+ const BOT_TOKEN = '{{secrets.telegramBotToken}}';
36
+
37
+ // Get channel ID from secrets or saved setting
38
+ let CHANNEL_ID = '{{secrets.telegramChannelID}}';
39
+ if (!CHANNEL_ID || CHANNEL_ID.includes('{{')) {
40
+ CHANNEL_ID = await getAgentSetting('agent_telegramChannelID');
41
+ }
42
+
43
+ // === WEBHOOK SETUP ===
44
+ if (queryStringParameters?.setupTelegramWebhook === 'true') {
45
+ return await setupWebhook(BOT_TOKEN, CHANNEL_ID);
46
+ }
47
+
48
+ // === WEBHOOK REMOVAL ===
49
+ if (queryStringParameters?.removeTelegramWebhook === 'true') {
50
+ return await removeWebhook(BOT_TOKEN);
51
+ }
52
+
53
+ // === VALIDATE SECRET TOKEN ===
54
+ const expectedToken = crypto.createHash('sha256')
55
+ .update(BOT_TOKEN).digest('hex').substring(0, 32);
56
+
57
+ const receivedToken = headers?.[
58
+ Object.keys(headers || {}).find(key =>
59
+ key.toLowerCase() === 'x-telegram-bot-api-secret-token'
60
+ )
61
+ ];
62
+
63
+ if (receivedToken && receivedToken !== expectedToken) {
64
+ return { ok: false, error: 'Invalid secret token' };
65
+ }
66
+
67
+ // === PROCESS UPDATES ===
68
+ const { message, channel_post, edited_message, edited_channel_post, callback_query } = payload;
69
+
70
+ // Handle channel posts
71
+ if (channel_post) {
72
+ return await handleChannelPost(channel_post, BOT_TOKEN, CHANNEL_ID);
73
+ }
74
+
75
+ // Handle direct messages
76
+ if (message) {
77
+ return await handleDirectMessage(message, BOT_TOKEN);
78
+ }
79
+
80
+ // Handle edited messages
81
+ if (edited_channel_post) {
82
+ return await handleEditedMessage(edited_channel_post);
83
+ }
84
+
85
+ if (edited_message) {
86
+ return await handleEditedMessage(edited_message);
87
+ }
88
+
89
+ // Handle button callbacks
90
+ if (callback_query) {
91
+ return { ok: true, processed: 'callback', data: callback_query.data };
92
+ }
93
+
94
+ return { ok: true, message: 'Update type not handled' };
95
+
96
+ } catch (error) {
97
+ // Always return ok:true to prevent Telegram retries
98
+ return { ok: true, error: error.message };
99
+ }
100
+ };
101
+ ```
102
+
103
+ ## Webhook Setup/Removal
104
+
105
+ ```javascript
106
+ async function setupWebhook(BOT_TOKEN, CHANNEL_ID) {
107
+ // Check if already configured
108
+ const existing = await getAgentSetting('agent_telegramWebhookSetup');
109
+ if (existing) {
110
+ return { ok: false, error: 'Webhook already configured', setupDate: existing };
111
+ }
112
+
113
+ try {
114
+ // Generate secret token
115
+ const SECRET_TOKEN = crypto.createHash('sha256')
116
+ .update(BOT_TOKEN).digest('hex').substring(0, 32);
117
+
118
+ const WEBHOOK_URL = `https://chat.openkbs.com/publicAPIRequest?kbId=${openkbs.kbId}&source=telegram`;
119
+
120
+ // Remove any existing webhook first
121
+ await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/deleteWebhook`);
122
+
123
+ // Set new webhook
124
+ const response = await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/setWebhook`, {
125
+ url: WEBHOOK_URL,
126
+ allowed_updates: ['message', 'edited_message', 'channel_post', 'edited_channel_post', 'callback_query'],
127
+ drop_pending_updates: true,
128
+ secret_token: SECRET_TOKEN
129
+ });
130
+
131
+ if (!response.data.ok) {
132
+ throw new Error(response.data.description);
133
+ }
134
+
135
+ // Save setup flag
136
+ const setupDate = new Date().toISOString();
137
+ await setAgentSetting('agent_telegramWebhookSetup', setupDate);
138
+
139
+ // Send test message
140
+ if (CHANNEL_ID) {
141
+ await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
142
+ chat_id: CHANNEL_ID,
143
+ text: '✅ *Telegram Integration Active*\n\nWebhook configured successfully!',
144
+ parse_mode: 'Markdown'
145
+ });
146
+ }
147
+
148
+ return {
149
+ ok: true,
150
+ message: 'Webhook configured',
151
+ webhookUrl: WEBHOOK_URL,
152
+ setupDate
153
+ };
154
+
155
+ } catch (error) {
156
+ return { ok: false, error: error.message };
157
+ }
158
+ }
159
+
160
+ async function removeWebhook(BOT_TOKEN) {
161
+ const webhookSetup = await getAgentSetting('agent_telegramWebhookSetup');
162
+ if (webhookSetup) {
163
+ return { ok: false, error: 'Cannot remove active webhook. Remove internally first.' };
164
+ }
165
+
166
+ await axios.post(`https://api.telegram.org/bot${BOT_TOKEN}/deleteWebhook`, {
167
+ drop_pending_updates: true
168
+ });
169
+
170
+ return { ok: true, message: 'Webhook removed' };
171
+ }
172
+ ```
173
+
174
+ ## Message Handlers
175
+
176
+ ```javascript
177
+ async function handleChannelPost(post, BOT_TOKEN, CHANNEL_ID) {
178
+ const text = post.text || post.caption || '';
179
+ const photo = post.photo;
180
+ const messageId = post.message_id;
181
+ const chatId = post.chat.id;
182
+ const date = post.date;
183
+
184
+ // Auto-save channel ID on first message
185
+ if (!CHANNEL_ID) {
186
+ await setAgentSetting('agent_telegramChannelID', chatId.toString());
187
+ }
188
+
189
+ // Get sender info
190
+ let senderName = post.from?.username ||
191
+ post.from?.first_name ||
192
+ post.author_signature ||
193
+ post.sender_chat?.title ||
194
+ 'Unknown';
195
+
196
+ // Check for duplicate
197
+ const existing = await getTelegramMessage(messageId);
198
+ if (existing) {
199
+ return { ok: true, duplicate: true, messageId };
200
+ }
201
+
202
+ // Upload photo if present
203
+ let uploadedImageUrl = null;
204
+ if (photo?.length > 0) {
205
+ uploadedImageUrl = await uploadTelegramPhoto(photo, BOT_TOKEN);
206
+ }
207
+
208
+ // Store message
209
+ await storeTelegramMessage(messageId, {
210
+ text: text.substring(0, 100000),
211
+ date,
212
+ type: 'channel',
213
+ from: senderName,
214
+ chatId,
215
+ ...(uploadedImageUrl && { imageUrl: uploadedImageUrl })
216
+ });
217
+
218
+ // Build LLM message
219
+ let chatMessage;
220
+ if (uploadedImageUrl) {
221
+ chatMessage = JSON.stringify([
222
+ { type: "text", text: `PROCESS_TELEGRAM_MESSAGE from ${senderName}\n\n${text || '[image]'}\n\n${AUTONOMOUS_CHAT_RULES}` },
223
+ { type: "image_url", image_url: { url: uploadedImageUrl } }
224
+ ]);
225
+ } else {
226
+ chatMessage = `PROCESS_TELEGRAM_MESSAGE from ${senderName}\n\n${senderName} wrote:\n"""\n${text}\n"""\n\n${AUTONOMOUS_CHAT_RULES}`;
227
+ }
228
+
229
+ // Create chat for processing
230
+ const timeStr = new Date(date * 1000).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
231
+ await openkbs.chats({
232
+ chatTitle: `TG: ${senderName} - ${timeStr}`,
233
+ message: chatMessage
234
+ });
235
+
236
+ return { ok: true, processed: 'channel_post', messageId };
237
+ }
238
+
239
+ async function handleDirectMessage(message, BOT_TOKEN) {
240
+ const text = message.text || message.caption || '';
241
+ const photo = message.photo;
242
+ const messageId = message.message_id;
243
+ const userName = message.from.username || message.from.first_name;
244
+
245
+ // Check duplicate
246
+ const existing = await getTelegramMessage(messageId);
247
+ if (existing) {
248
+ return { ok: true, duplicate: true };
249
+ }
250
+
251
+ // Upload photo
252
+ let uploadedImageUrl = null;
253
+ if (photo?.length > 0) {
254
+ uploadedImageUrl = await uploadTelegramPhoto(photo, BOT_TOKEN);
255
+ }
256
+
257
+ // Store message
258
+ await storeTelegramMessage(messageId, {
259
+ text: text.substring(0, 20000),
260
+ date: message.date,
261
+ type: 'direct',
262
+ from: userName,
263
+ chatId: message.chat.id,
264
+ userId: message.from.id,
265
+ ...(uploadedImageUrl && { imageUrl: uploadedImageUrl })
266
+ });
267
+
268
+ // Create chat
269
+ const content = [
270
+ { type: "text", text: `TELEGRAM DM from ${userName}\n\n"${text}"\n\n${AUTONOMOUS_CHAT_RULES}` }
271
+ ];
272
+ if (uploadedImageUrl) {
273
+ content.push({ type: "image_url", image_url: { url: uploadedImageUrl } });
274
+ }
275
+
276
+ await openkbs.chats({
277
+ chatTitle: `TG DM: ${userName}`,
278
+ message: JSON.stringify(content)
279
+ });
280
+
281
+ return { ok: true, processed: 'message' };
282
+ }
283
+ ```
284
+
285
+ ## Photo Upload Helper
286
+
287
+ ```javascript
288
+ async function uploadTelegramPhoto(photo, BOT_TOKEN) {
289
+ try {
290
+ // Get largest photo
291
+ const largestPhoto = photo[photo.length - 1];
292
+
293
+ // Get file path from Telegram
294
+ const fileInfo = await axios.get(
295
+ `https://api.telegram.org/bot${BOT_TOKEN}/getFile?file_id=${largestPhoto.file_id}`
296
+ );
297
+ const filePath = fileInfo.data.result.file_path;
298
+
299
+ // Download file
300
+ const fileUrl = `https://api.telegram.org/file/bot${BOT_TOKEN}/${filePath}`;
301
+ const fileResponse = await axios.get(fileUrl, { responseType: 'arraybuffer' });
302
+
303
+ // Generate filename
304
+ const timestamp = Date.now();
305
+ const ext = filePath.split('.').pop() || 'jpg';
306
+ const filename = `telegram_photo_${timestamp}.${ext}`;
307
+
308
+ // Get presigned URL and upload
309
+ const presignedUrl = await openkbs.kb({
310
+ action: 'createPresignedURL',
311
+ namespace: 'files',
312
+ fileName: filename,
313
+ fileType: 'image/jpeg',
314
+ presignedOperation: 'putObject'
315
+ });
316
+
317
+ await axios.put(presignedUrl, fileResponse.data, {
318
+ headers: { 'Content-Type': 'image/jpeg', 'Content-Length': fileResponse.data.length }
319
+ });
320
+
321
+ return `https://yourdomain.com/media/${filename}`;
322
+
323
+ } catch (error) {
324
+ console.error('Photo upload failed:', error);
325
+ return null;
326
+ }
327
+ }
328
+ ```
329
+
330
+ ## Edited Message Handler
331
+
332
+ ```javascript
333
+ async function handleEditedMessage(edited) {
334
+ const messageId = edited.message_id;
335
+ const newText = edited.text;
336
+ const editDate = edited.edit_date;
337
+
338
+ const existing = await getTelegramMessage(messageId);
339
+ if (!existing) {
340
+ return { ok: true, note: 'Message not found' };
341
+ }
342
+
343
+ const itemId = `telegram_${messageId.toString().padStart(12, '0')}`;
344
+
345
+ // Special: "_delete" prefix removes message from history
346
+ if (newText?.trim().toLowerCase().startsWith('_delete')) {
347
+ await deleteTelegramMessage(itemId);
348
+ return { ok: true, action: 'deleted' };
349
+ }
350
+
351
+ // Update message
352
+ await storeTelegramMessage(messageId, {
353
+ ...existing,
354
+ text: newText,
355
+ edited: true,
356
+ editedAt: editDate,
357
+ originalText: existing.originalText || existing.text
358
+ });
359
+
360
+ return { ok: true, action: 'edited' };
361
+ }
362
+ ```
363
+
364
+ ## memoryHelpers.js
365
+
366
+ ```javascript
367
+ export async function storeTelegramMessage(messageId, data) {
368
+ // Zero-pad for proper DynamoDB sorting
369
+ const itemId = `telegram_${messageId.toString().padStart(12, '0')}`;
370
+ const body = { ...data, storedAt: new Date().toISOString() };
371
+
372
+ try {
373
+ await openkbs.updateItem({ itemType: 'telegram', itemId, body });
374
+ } catch (e) {
375
+ await openkbs.createItem({ itemType: 'telegram', itemId, body });
376
+ }
377
+ }
378
+
379
+ export async function getTelegramMessage(messageId) {
380
+ const itemId = `telegram_${messageId.toString().padStart(12, '0')}`;
381
+ try {
382
+ const result = await openkbs.getItem(itemId);
383
+ return result?.item?.body;
384
+ } catch (e) {
385
+ return null;
386
+ }
387
+ }
388
+
389
+ export async function deleteTelegramMessage(itemId) {
390
+ try {
391
+ await openkbs.deleteItem(itemId);
392
+ } catch (e) {}
393
+ }
394
+ ```
395
+
396
+ ## settings.json - Priority Items
397
+
398
+ ```json
399
+ {
400
+ "options": {
401
+ "priorityItems": [
402
+ { "limit": 100, "prefix": "memory" },
403
+ { "limit": 100, "prefix": "telegram" }
404
+ ]
405
+ }
406
+ }
407
+ ```
408
+
409
+ ## Setup URL
410
+
411
+ ```
412
+ https://chat.openkbs.com/publicAPIRequest?kbId=YOUR_KB_ID&setupTelegramWebhook=true
413
+ ```
414
+
415
+ ## Key Points
416
+
417
+ 1. **Webhook setup via query param** - `?setupTelegramWebhook=true`
418
+ 2. **Secret token validation** - SHA256 hash of bot token
419
+ 3. **Always return `ok: true`** - Prevents Telegram retries
420
+ 4. **Photo upload** - Download from Telegram, upload to OpenKBS storage
421
+ 5. **Duplicate detection** - Check `getTelegramMessage` before processing
422
+ 6. **Zero-padded messageId** - For proper DynamoDB string sorting
423
+ 7. **Autonomous chat rules** - Tell LLM how to behave without user
424
+ 8. **Edit as delete** - `_delete` prefix removes from history
@@ -0,0 +1,222 @@
1
+ # Telegram Integration Pattern
2
+
3
+ Complete working code for Telegram bot messaging and webhooks.
4
+
5
+ ## Setup
6
+
7
+ 1. Create bot with @BotFather
8
+ 2. Get bot token
9
+ 3. Add to secrets: `{{secrets.telegramBotToken}}`
10
+ 4. Optional: Set channel ID in secrets or detect automatically
11
+
12
+ ## actions.js
13
+
14
+ ```javascript
15
+ const TELEGRAM_BOT_TOKEN = '{{secrets.telegramBotToken}}';
16
+
17
+ // Helper to get channel ID from secrets or saved setting
18
+ async function getTelegramChannelId() {
19
+ const secretsChannelId = '{{secrets.telegramChannelID}}';
20
+ if (secretsChannelId && !secretsChannelId.includes('{{')) {
21
+ return secretsChannelId;
22
+ }
23
+ // Fallback to saved setting
24
+ return await getAgentSetting('agent_telegramChannelID');
25
+ }
26
+
27
+ // Send text message to Telegram channel
28
+ async function sendToTelegramChannel(message, options = {}) {
29
+ try {
30
+ const channelId = await getTelegramChannelId();
31
+ if (!channelId) {
32
+ throw new Error('Telegram channel ID not configured');
33
+ }
34
+
35
+ const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
36
+ const response = await axios.post(url, {
37
+ chat_id: channelId,
38
+ text: message,
39
+ parse_mode: options.parse_mode || 'Markdown',
40
+ disable_notification: options.silent || false
41
+ });
42
+
43
+ if (response.data.ok) {
44
+ return { success: true, messageId: response.data.result.message_id };
45
+ }
46
+ throw new Error(response.data.description || 'Telegram error');
47
+ } catch (error) {
48
+ return { success: false, error: error.message };
49
+ }
50
+ }
51
+
52
+ // Send photo to Telegram channel
53
+ async function sendPhotoToTelegramChannel(photoUrl, caption = '') {
54
+ try {
55
+ const channelId = await getTelegramChannelId();
56
+ if (!channelId) {
57
+ throw new Error('Telegram channel ID not configured');
58
+ }
59
+
60
+ const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendPhoto`;
61
+ const response = await axios.post(url, {
62
+ chat_id: channelId,
63
+ photo: photoUrl,
64
+ caption: caption,
65
+ parse_mode: 'Markdown'
66
+ });
67
+
68
+ if (response.data.ok) {
69
+ return { success: true, messageId: response.data.result.message_id };
70
+ }
71
+ throw new Error(response.data.description || 'Telegram error');
72
+ } catch (error) {
73
+ return { success: false, error: error.message };
74
+ }
75
+ }
76
+
77
+ // Command: Send message to Telegram
78
+ [/<sendToTelegramChannel>([\s\S]*?)<\/sendToTelegramChannel>/s, async (match) => {
79
+ try {
80
+ const data = JSON.parse(match[1].trim());
81
+ const result = await sendToTelegramChannel(data.message, {
82
+ parse_mode: data.parse_mode || 'Markdown',
83
+ silent: data.silent || false
84
+ });
85
+
86
+ return {
87
+ type: "TELEGRAM_MESSAGE_SENT",
88
+ success: result.success,
89
+ messageId: result.messageId,
90
+ error: result.error,
91
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
92
+ };
93
+ } catch (e) {
94
+ return {
95
+ type: "TELEGRAM_ERROR",
96
+ error: e.message,
97
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
98
+ };
99
+ }
100
+ }],
101
+
102
+ // Command: Send photo to Telegram
103
+ [/<sendPhotoToTelegramChannel>([\s\S]*?)<\/sendPhotoToTelegramChannel>/s, async (match) => {
104
+ try {
105
+ const data = JSON.parse(match[1].trim());
106
+ const result = await sendPhotoToTelegramChannel(data.photoUrl, data.caption || '');
107
+
108
+ return {
109
+ type: "TELEGRAM_PHOTO_SENT",
110
+ success: result.success,
111
+ messageId: result.messageId,
112
+ error: result.error,
113
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
114
+ };
115
+ } catch (e) {
116
+ return {
117
+ type: "TELEGRAM_ERROR",
118
+ error: e.message,
119
+ _meta_actions: ["REQUEST_CHAT_MODEL"]
120
+ };
121
+ }
122
+ }]
123
+ ```
124
+
125
+ ## Webhook Handler (onPublicAPIRequest.js)
126
+
127
+ Receive messages from Telegram:
128
+
129
+ ```javascript
130
+ export const handler = async (event) => {
131
+ const body = JSON.parse(event.body || '{}');
132
+
133
+ // Telegram webhook payload
134
+ if (body.message || body.channel_post) {
135
+ const msg = body.message || body.channel_post;
136
+
137
+ // Auto-detect and save channel ID
138
+ if (msg.chat?.id && msg.chat?.type === 'channel') {
139
+ await setAgentSetting('agent_telegramChannelID', String(msg.chat.id));
140
+ }
141
+
142
+ // Store message for context
143
+ await storeTelegramMessage(msg.message_id, {
144
+ text: msg.text || '',
145
+ date: msg.date,
146
+ from: msg.from?.username || msg.chat?.title || 'Unknown',
147
+ chatId: msg.chat?.id,
148
+ type: msg.photo ? 'photo' : 'text'
149
+ });
150
+
151
+ // Create chat with message for agent to process
152
+ await openkbs.chats({
153
+ chatTitle: `Telegram: ${msg.from?.username || 'Channel'}`,
154
+ message: JSON.stringify([{
155
+ type: "text",
156
+ text: `[TELEGRAM] ${msg.from?.username || 'User'}: ${msg.text || '[media]'}`
157
+ }])
158
+ });
159
+ }
160
+
161
+ return {
162
+ statusCode: 200,
163
+ body: JSON.stringify({ ok: true })
164
+ };
165
+ };
166
+ ```
167
+
168
+ ## memoryHelpers.js - Store Telegram Messages
169
+
170
+ ```javascript
171
+ export async function storeTelegramMessage(messageId, data) {
172
+ const itemId = `telegram_${messageId}`;
173
+ const body = {
174
+ ...data,
175
+ storedAt: new Date().toISOString()
176
+ };
177
+
178
+ try {
179
+ await openkbs.updateItem({ itemType: 'telegram', itemId, body });
180
+ } catch (e) {
181
+ await openkbs.createItem({ itemType: 'telegram', itemId, body });
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## settings.json - Priority Items for Telegram
187
+
188
+ ```json
189
+ {
190
+ "options": {
191
+ "priorityItems": [
192
+ { "limit": 100, "prefix": "memory" },
193
+ { "limit": 50, "prefix": "telegram" }
194
+ ]
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## instructions.txt (LLM prompt)
200
+
201
+ ```
202
+ Telegram Commands:
203
+
204
+ Send message:
205
+ <sendToTelegramChannel>{"message": "*Bold* message with _italic_"}</sendToTelegramChannel>
206
+
207
+ Send photo:
208
+ <sendPhotoToTelegramChannel>{"photoUrl": "https://...", "caption": "Photo caption"}</sendPhotoToTelegramChannel>
209
+
210
+ Markdown supported: *bold*, _italic_, `code`, [link](url)
211
+
212
+ Incoming Telegram messages appear with [TELEGRAM] prefix.
213
+ ```
214
+
215
+ ## Key Points
216
+
217
+ 1. **Bot token in secrets**: `{{secrets.telegramBotToken}}`
218
+ 2. **Channel ID**: Auto-detected from first message or set in secrets
219
+ 3. **Webhook setup**: Set webhook URL to your KB's public API endpoint
220
+ 4. **Message storage**: Store in `telegram_` prefix for context injection
221
+ 5. **Markdown mode**: Default parse_mode is Markdown
222
+ 6. **Photo URL**: Must be publicly accessible URL