@codebam/cf-workers-telegram-bot 12.6.4 → 12.6.6
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.
- package/README.md +17 -0
- package/dist/ai.d.ts +23 -0
- package/dist/ai.js +381 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +1 -0
- package/dist/telegram_execution_context.js +2 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -119,8 +119,25 @@ npx wrangler deploy
|
|
|
119
119
|
|
|
120
120
|
To automate deployments, use the [Wrangler Action](https://github.com/cloudflare/wrangler-action) or Cloudflare's built-in [GitHub integration](https://developers.cloudflare.com/workers/ci-cd/github-actions/).
|
|
121
121
|
|
|
122
|
+
## Structure
|
|
123
|
+
|
|
124
|
+
This is a monorepo containing:
|
|
125
|
+
- Root: Core library `@codebam/cf-workers-telegram-bot`
|
|
126
|
+
- `ai-workflow`: A Cloudflare Workflow for handling long-running AI tasks
|
|
127
|
+
- `webapp`: A Svelte 5 web application for interacting with the bot
|
|
128
|
+
- `consumer`: A minimal consumer of the library
|
|
129
|
+
|
|
122
130
|
## Development
|
|
123
131
|
|
|
132
|
+
You can use the root `Makefile` to run common tasks across all projects:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
make build # Build all projects
|
|
136
|
+
make test # Run tests for all projects
|
|
137
|
+
make lint # Lint all projects
|
|
138
|
+
make format # Format all projects
|
|
139
|
+
```
|
|
140
|
+
|
|
124
141
|
### Setup
|
|
125
142
|
|
|
126
143
|
1. **Install dependencies**:
|
package/dist/ai.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import TelegramExecutionContext from './telegram_execution_context';
|
|
2
|
+
/**
|
|
3
|
+
* Robustly extract text from various AI response formats.
|
|
4
|
+
* Handles OpenAI, Cloudflare, and Google Gemini structures.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractText(obj: any): string;
|
|
7
|
+
/**
|
|
8
|
+
* Custom runner that supports tool calls across different AI models.
|
|
9
|
+
*/
|
|
10
|
+
export declare function customRunWithTools(ai: any, model: string, input: {
|
|
11
|
+
messages: any[];
|
|
12
|
+
tools?: any[];
|
|
13
|
+
}, config: {
|
|
14
|
+
streamFinalResponse: boolean;
|
|
15
|
+
}): Promise<any>;
|
|
16
|
+
/**
|
|
17
|
+
* Stream AI response to Telegram, with periodic updates to avoid rate limits.
|
|
18
|
+
*/
|
|
19
|
+
export declare function streamAiResponseToTelegram(bot: TelegramExecutionContext, ai: any, modelId: string, messages: any[], task: any, tools?: any[]): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a mock TelegramExecutionContext for use in environments where the full context isn't available (e.g., Workflows).
|
|
22
|
+
*/
|
|
23
|
+
export declare function createMockTelegramExecutionContext(task: Record<string, unknown>): TelegramExecutionContext;
|
package/dist/ai.js
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import TelegramApi from './telegram_api';
|
|
2
|
+
import { markdownToHtml } from './utils';
|
|
3
|
+
/**
|
|
4
|
+
* Robustly extract text from various AI response formats.
|
|
5
|
+
* Handles OpenAI, Cloudflare, and Google Gemini structures.
|
|
6
|
+
*/
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
export function extractText(obj) {
|
|
9
|
+
if (typeof obj === 'string') {
|
|
10
|
+
return obj;
|
|
11
|
+
}
|
|
12
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
// Direct fields
|
|
16
|
+
if (typeof obj.response === 'string') {
|
|
17
|
+
return obj.response;
|
|
18
|
+
}
|
|
19
|
+
if (typeof obj.text === 'string') {
|
|
20
|
+
return obj.text;
|
|
21
|
+
}
|
|
22
|
+
if (typeof obj.content === 'string') {
|
|
23
|
+
return obj.content;
|
|
24
|
+
}
|
|
25
|
+
if (typeof obj.delta === 'string') {
|
|
26
|
+
return obj.delta;
|
|
27
|
+
}
|
|
28
|
+
// Nested fields
|
|
29
|
+
if (obj.choices && Array.isArray(obj.choices) && obj.choices.length > 0) {
|
|
30
|
+
return extractText(obj.choices[0]);
|
|
31
|
+
}
|
|
32
|
+
if (obj.message) {
|
|
33
|
+
return extractText(obj.message);
|
|
34
|
+
}
|
|
35
|
+
if (obj.delta) {
|
|
36
|
+
return extractText(obj.delta);
|
|
37
|
+
}
|
|
38
|
+
if (obj.candidates && Array.isArray(obj.candidates) && obj.candidates.length > 0) {
|
|
39
|
+
return extractText(obj.candidates[0]);
|
|
40
|
+
}
|
|
41
|
+
if (obj.content) {
|
|
42
|
+
return extractText(obj.content);
|
|
43
|
+
}
|
|
44
|
+
if (obj.parts && Array.isArray(obj.parts) && obj.parts.length > 0) {
|
|
45
|
+
return extractText(obj.parts[0]);
|
|
46
|
+
}
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Custom runner that supports tool calls across different AI models.
|
|
51
|
+
*/
|
|
52
|
+
export async function customRunWithTools(
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
ai, model,
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
input, config) {
|
|
57
|
+
const messages = [...input.messages];
|
|
58
|
+
const tools = input.tools || [];
|
|
59
|
+
const isGemini = model.includes('google/gemini');
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
const cfTools = tools.map((t) => ({
|
|
62
|
+
type: 'function',
|
|
63
|
+
function: {
|
|
64
|
+
name: t.name,
|
|
65
|
+
description: t.description,
|
|
66
|
+
parameters: t.parameters,
|
|
67
|
+
},
|
|
68
|
+
}));
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
const runModel = async (msgs, stream) => {
|
|
71
|
+
if (isGemini) {
|
|
72
|
+
const systemMessage = msgs.find((m) => m.role === 'system');
|
|
73
|
+
const otherMessages = msgs.filter((m) => m.role !== 'system');
|
|
74
|
+
const geminiInput = {
|
|
75
|
+
contents: otherMessages.map((m) => ({
|
|
76
|
+
role: m.role === 'assistant' ? 'model' : 'user',
|
|
77
|
+
parts: [{ text: m.content }],
|
|
78
|
+
})),
|
|
79
|
+
stream,
|
|
80
|
+
};
|
|
81
|
+
if (systemMessage) {
|
|
82
|
+
geminiInput.system_instruction = {
|
|
83
|
+
parts: [{ text: systemMessage.content }],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return await ai.run(model, geminiInput);
|
|
87
|
+
}
|
|
88
|
+
return await ai.run(model, { messages: msgs, tools: cfTools.length > 0 ? cfTools : undefined, stream });
|
|
89
|
+
};
|
|
90
|
+
if (cfTools.length === 0 || isGemini) {
|
|
91
|
+
return await runModel(messages, config.streamFinalResponse);
|
|
92
|
+
}
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
94
|
+
const response = (await runModel(messages, false));
|
|
95
|
+
// FIX: Robustly extract from BOTH Cloudflare formats (Standard and OpenAI-compatible)
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
97
|
+
let toolCalls = [];
|
|
98
|
+
if (response?.tool_calls) {
|
|
99
|
+
toolCalls = [...response.tool_calls];
|
|
100
|
+
}
|
|
101
|
+
else if (response?.choices?.[0]?.message?.tool_calls) {
|
|
102
|
+
toolCalls = [...response.choices[0].message.tool_calls];
|
|
103
|
+
}
|
|
104
|
+
let responseText = response?.response || response?.choices?.[0]?.message?.content || '';
|
|
105
|
+
// GEMMA/LLAMA FALLBACK: Catch raw tokens if native interception fails
|
|
106
|
+
if (toolCalls.length === 0) {
|
|
107
|
+
const gemmaRegex = /<\|tool_call>\s*call:\s*([a-zA-Z0-9_]+)([\s\S]*?)<tool_call\|>/g;
|
|
108
|
+
const standardRegex = /<tool_call>\s*([\s\S]*?)\s*<\/tool_call>/g;
|
|
109
|
+
let match;
|
|
110
|
+
while ((match = gemmaRegex.exec(responseText)) !== null) {
|
|
111
|
+
let name = match[1].trim();
|
|
112
|
+
if (name === 'http_fetch' || name === 'api_fetch') {
|
|
113
|
+
name = 'fetch';
|
|
114
|
+
}
|
|
115
|
+
let argsString = match[2].trim();
|
|
116
|
+
// Sanitize malformed JSON syntax
|
|
117
|
+
argsString = argsString.replace(/([{,]\s*)([a-zA-Z0-9_]+)\s*:/g, '$1"$2":').replace(/:\s*'([^']*)'/g, ': "$1"');
|
|
118
|
+
toolCalls.push({
|
|
119
|
+
id: `call_${Math.random().toString(36).substring(2, 9)}`,
|
|
120
|
+
type: 'function',
|
|
121
|
+
function: { name, arguments: argsString },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
while ((match = standardRegex.exec(responseText)) !== null) {
|
|
125
|
+
const content = match[1].trim();
|
|
126
|
+
try {
|
|
127
|
+
// Handle both raw JSON and name/args format
|
|
128
|
+
const parsed = JSON.parse(content.replace(/'/g, '"'));
|
|
129
|
+
const name = parsed.name || 'fetch';
|
|
130
|
+
const args = parsed.arguments || parsed;
|
|
131
|
+
toolCalls.push({
|
|
132
|
+
id: `call_${Math.random().toString(36).substring(2, 9)}`,
|
|
133
|
+
type: 'function',
|
|
134
|
+
function: { name, arguments: typeof args === 'string' ? args : JSON.stringify(args) },
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console.error('Failed to parse tool call:', content, e);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Strip the raw tokens from the visible response
|
|
142
|
+
responseText = responseText
|
|
143
|
+
.replace(/<\|tool_call>[\s\S]*?<tool_call\|>/g, '')
|
|
144
|
+
.replace(/<tool_call>[\s\S]*?<\/tool_call>/g, '')
|
|
145
|
+
.trim();
|
|
146
|
+
}
|
|
147
|
+
if (toolCalls.length > 0) {
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
149
|
+
const normalizedToolCalls = toolCalls.map((call, index) => {
|
|
150
|
+
const name = call.name || (call.function && call.function.name);
|
|
151
|
+
let args = call.arguments || (call.function && call.function.arguments);
|
|
152
|
+
if (typeof args !== 'string') {
|
|
153
|
+
try {
|
|
154
|
+
args = JSON.stringify(args);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
args = '{}';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
id: call.id || `call_${Math.random().toString(36).substring(2, 9)}_${index}`,
|
|
162
|
+
type: 'function',
|
|
163
|
+
function: { name, arguments: args },
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
messages.push({
|
|
167
|
+
role: 'assistant',
|
|
168
|
+
content: responseText,
|
|
169
|
+
tool_calls: normalizedToolCalls,
|
|
170
|
+
});
|
|
171
|
+
for (const call of normalizedToolCalls) {
|
|
172
|
+
const toolName = call.function.name;
|
|
173
|
+
const toolId = call.id;
|
|
174
|
+
const toolArgsString = call.function.arguments;
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
176
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
177
|
+
if (tool && tool.function) {
|
|
178
|
+
try {
|
|
179
|
+
let parsedArgs;
|
|
180
|
+
try {
|
|
181
|
+
parsedArgs = JSON.parse(toolArgsString);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
parsedArgs = toolArgsString;
|
|
185
|
+
}
|
|
186
|
+
const result = await tool.function(parsedArgs);
|
|
187
|
+
messages.push({ role: 'tool', tool_call_id: toolId, name: toolName, content: String(result) });
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
messages.push({ role: 'tool', tool_call_id: toolId, name: toolName, content: String(e) });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
messages.push({ role: 'tool', tool_call_id: toolId, name: toolName, content: 'Tool not found' });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return await runModel(messages, true);
|
|
198
|
+
}
|
|
199
|
+
return response;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Stream AI response to Telegram, with periodic updates to avoid rate limits.
|
|
203
|
+
*/
|
|
204
|
+
export async function streamAiResponseToTelegram(bot,
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
206
|
+
ai, modelId,
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
|
+
messages,
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
210
|
+
task,
|
|
211
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
|
+
tools = []) {
|
|
213
|
+
const botApi = new TelegramApi();
|
|
214
|
+
// Use updateId as a stable draftId if available, otherwise generate one
|
|
215
|
+
const draftId = task.updateId || Date.now();
|
|
216
|
+
// Skip Thinking message for guest messages and business messages as they only support one response or don't support drafts
|
|
217
|
+
if (task.updateType !== 'guest_message' && task.updateType !== 'business_message') {
|
|
218
|
+
await botApi.sendMessageDraft(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
219
|
+
chat_id: task.chatId,
|
|
220
|
+
text: 'Thinking...',
|
|
221
|
+
parse_mode: 'HTML',
|
|
222
|
+
message_thread_id: task.threadId,
|
|
223
|
+
business_connection_id: task.businessConnectionId,
|
|
224
|
+
draft_id: draftId,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
let streamContent = '';
|
|
228
|
+
let lastUpdate = Date.now();
|
|
229
|
+
try {
|
|
230
|
+
const aiResponse = await customRunWithTools(ai, modelId, {
|
|
231
|
+
messages,
|
|
232
|
+
tools,
|
|
233
|
+
}, { streamFinalResponse: true });
|
|
234
|
+
if (typeof aiResponse === 'object' && aiResponse !== null && 'getReader' in aiResponse) {
|
|
235
|
+
const stream = aiResponse;
|
|
236
|
+
const reader = stream.getReader();
|
|
237
|
+
const decoder = new TextDecoder();
|
|
238
|
+
while (true) {
|
|
239
|
+
const { done, value } = await reader.read();
|
|
240
|
+
if (done) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
244
|
+
const lines = chunk.split('\n');
|
|
245
|
+
for (const line of lines) {
|
|
246
|
+
if (line.startsWith('data: ')) {
|
|
247
|
+
const data = line.slice(6);
|
|
248
|
+
if (data === '[DONE]') {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const parsed = JSON.parse(data);
|
|
253
|
+
const text = extractText(parsed);
|
|
254
|
+
streamContent += text;
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Ignore malformed JSON chunks
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Update Telegram every 2 seconds to avoid rate limits
|
|
262
|
+
if (task.updateType !== 'guest_message' &&
|
|
263
|
+
task.updateType !== 'business_message' &&
|
|
264
|
+
Date.now() - lastUpdate > 2000 &&
|
|
265
|
+
streamContent.trim()) {
|
|
266
|
+
const currentContent = streamContent;
|
|
267
|
+
bot
|
|
268
|
+
.streamReply(await markdownToHtml(currentContent + '...'), draftId, 'HTML')
|
|
269
|
+
.catch((e) => console.error('Streaming error:', e));
|
|
270
|
+
lastUpdate = Date.now();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// Handle static response
|
|
276
|
+
streamContent = extractText(aiResponse);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
console.error('Error reading AI stream:', e);
|
|
281
|
+
}
|
|
282
|
+
// Send final response (blocking)
|
|
283
|
+
if (streamContent.trim()) {
|
|
284
|
+
await bot.streamReply(await markdownToHtml(streamContent), draftId, 'HTML', {}, true);
|
|
285
|
+
}
|
|
286
|
+
return streamContent;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Creates a mock TelegramExecutionContext for use in environments where the full context isn't available (e.g., Workflows).
|
|
290
|
+
*/
|
|
291
|
+
export function createMockTelegramExecutionContext(task) {
|
|
292
|
+
return {
|
|
293
|
+
chat: { id: task.chatId },
|
|
294
|
+
from: { id: task.userId },
|
|
295
|
+
update_type: task.updateType,
|
|
296
|
+
reply: async (text, options = {}) => {
|
|
297
|
+
const api = new TelegramApi();
|
|
298
|
+
if (task.updateType === 'guest_message' && task.guestQueryId) {
|
|
299
|
+
return await api.answerGuestQuery(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
300
|
+
guest_query_id: task.guestQueryId,
|
|
301
|
+
result: {
|
|
302
|
+
type: 'article',
|
|
303
|
+
id: crypto.randomUUID(),
|
|
304
|
+
title: 'Response',
|
|
305
|
+
input_message_content: { message_text: text, parse_mode: (options.parse_mode || 'HTML') },
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return await api.sendMessage(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
310
|
+
chat_id: task.chatId,
|
|
311
|
+
text,
|
|
312
|
+
parse_mode: (options.parse_mode || 'HTML'),
|
|
313
|
+
reply_markup: options.reply_markup,
|
|
314
|
+
message_thread_id: task.threadId,
|
|
315
|
+
business_connection_id: task.businessConnectionId,
|
|
316
|
+
reply_to_message_id: task.messageId,
|
|
317
|
+
});
|
|
318
|
+
},
|
|
319
|
+
streamReply: async (text, draft_id, parse_mode = '', options = {}, finish = false) => {
|
|
320
|
+
const api = new TelegramApi();
|
|
321
|
+
if (task.updateType === 'guest_message' || task.updateType === 'business_message') {
|
|
322
|
+
if (finish) {
|
|
323
|
+
if (task.updateType === 'guest_message' && task.guestQueryId) {
|
|
324
|
+
return await api.answerGuestQuery(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
325
|
+
guest_query_id: task.guestQueryId,
|
|
326
|
+
result: {
|
|
327
|
+
type: 'article',
|
|
328
|
+
id: crypto.randomUUID(),
|
|
329
|
+
title: 'Response',
|
|
330
|
+
input_message_content: { message_text: text, parse_mode: (parse_mode || 'HTML') },
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
return await api.sendMessage(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
335
|
+
chat_id: task.chatId,
|
|
336
|
+
text,
|
|
337
|
+
parse_mode: (parse_mode || 'HTML'),
|
|
338
|
+
reply_markup: options.reply_markup,
|
|
339
|
+
message_thread_id: task.threadId,
|
|
340
|
+
business_connection_id: task.businessConnectionId,
|
|
341
|
+
reply_to_message_id: task.messageId,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
if (finish) {
|
|
347
|
+
// Send a final message draft to signal the end of animation
|
|
348
|
+
await api.sendMessageDraft(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
349
|
+
chat_id: task.chatId,
|
|
350
|
+
text,
|
|
351
|
+
parse_mode: (parse_mode || 'HTML'),
|
|
352
|
+
draft_id,
|
|
353
|
+
message_thread_id: task.threadId,
|
|
354
|
+
business_connection_id: task.businessConnectionId,
|
|
355
|
+
finish: true,
|
|
356
|
+
...options,
|
|
357
|
+
});
|
|
358
|
+
// Then send the actual final message as a reply
|
|
359
|
+
return await api.sendMessage(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
360
|
+
chat_id: task.chatId,
|
|
361
|
+
text,
|
|
362
|
+
parse_mode: (parse_mode || 'HTML'),
|
|
363
|
+
reply_markup: options.reply_markup,
|
|
364
|
+
message_thread_id: task.threadId,
|
|
365
|
+
business_connection_id: task.businessConnectionId,
|
|
366
|
+
reply_to_message_id: task.messageId,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return await api.sendMessageDraft(`https://api.telegram.org/bot${task.telegramToken || task.token}`, {
|
|
370
|
+
chat_id: task.chatId,
|
|
371
|
+
text,
|
|
372
|
+
parse_mode: (parse_mode || 'HTML'),
|
|
373
|
+
draft_id,
|
|
374
|
+
message_thread_id: task.threadId,
|
|
375
|
+
business_connection_id: task.businessConnectionId,
|
|
376
|
+
finish: false,
|
|
377
|
+
...options,
|
|
378
|
+
});
|
|
379
|
+
},
|
|
380
|
+
};
|
|
381
|
+
}
|
package/dist/main.d.ts
CHANGED
|
@@ -9,5 +9,6 @@ import PartialTelegramUpdate from './types/PartialTelegramUpdate.js';
|
|
|
9
9
|
import TelegramInlineQueryType from './types/TelegramInlineQueryType.js';
|
|
10
10
|
import { markdownToHtml, fetchTool } from './utils.js';
|
|
11
11
|
import { HistoryManager, getBalance } from './history_manager.js';
|
|
12
|
+
export { extractText, customRunWithTools, streamAiResponseToTelegram, createMockTelegramExecutionContext } from './ai.js';
|
|
12
13
|
export default TelegramBot;
|
|
13
14
|
export { TelegramBot, TelegramExecutionContext, Webhook, TelegramApi, TelegramApiBaseParams, SendMessageParams, SendMessageDraftParams, SendPhotoParams, SendVideoParams, SendVoiceParams, SendChatActionParams, AnswerCallbackParams, AnswerInlineParams, AnswerGuestParams, SendInvoiceParams, AnswerPreCheckoutParams, TelegramApiParams, TelegramCommand, TelegramFrom, TelegramChat, TelegramUser, TelegramMessageEntity, TelegramPhotoSize, TelegramMessage, TelegramVoice, TelegramGuestMessage, TelegramInputMessageContent, TelegramInlineQuery, TelegramUpdate, PartialTelegramUpdate, TelegramInlineQueryType, TelegramInlineQueryResult, TelegramInlineQueryResultPhoto, TelegramInlineQueryResultArticle, TelegramInlineQueryResultVideo, TelegramInlineQueryResultVoice, ChatPermissions, TelegramBusinessMessage, TelegramCallbackQuery, TelegramPreCheckoutQuery, TelegramDocument, TelegramSuccessfulPayment, markdownToHtml, fetchTool, HistoryManager, getBalance, };
|
package/dist/main.js
CHANGED
|
@@ -4,5 +4,6 @@ import Webhook from './webhook.js';
|
|
|
4
4
|
import TelegramApi from './telegram_api.js';
|
|
5
5
|
import { markdownToHtml, fetchTool } from './utils.js';
|
|
6
6
|
import { HistoryManager, getBalance } from './history_manager.js';
|
|
7
|
+
export { extractText, customRunWithTools, streamAiResponseToTelegram, createMockTelegramExecutionContext } from './ai.js';
|
|
7
8
|
export default TelegramBot;
|
|
8
9
|
export { TelegramBot, TelegramExecutionContext, Webhook, TelegramApi, markdownToHtml, fetchTool, HistoryManager, getBalance, };
|
|
@@ -477,9 +477,9 @@ export default class TelegramExecutionContext {
|
|
|
477
477
|
* @returns Promise with the API response
|
|
478
478
|
*/
|
|
479
479
|
async streamReply(message, draft_id, parse_mode = '', options = {}, finish = false) {
|
|
480
|
-
if (this.update_type === 'guest_message') {
|
|
480
|
+
if (this.update_type === 'guest_message' || this.update_type === 'business_message') {
|
|
481
481
|
if (finish) {
|
|
482
|
-
return await this.
|
|
482
|
+
return await this.reply(message, parse_mode, true, options);
|
|
483
483
|
}
|
|
484
484
|
return null;
|
|
485
485
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codebam/cf-workers-telegram-bot",
|
|
3
|
-
"version": "12.6.
|
|
3
|
+
"version": "12.6.6",
|
|
4
4
|
"description": "serverless telegram bot on cf workers",
|
|
5
5
|
"main": "./dist/main.js",
|
|
6
6
|
"module": "./dist/main.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"test:all": "npm test && npm test --prefix ai-workflow && npm test --prefix consumer && npm test --prefix webapp",
|
|
30
30
|
"format": "prettier --write src test *.json *.js *.mjs",
|
|
31
31
|
"format:check": "prettier --check src test *.json *.js *.mjs",
|
|
32
|
-
"docs": "typedoc --options typedoc.json",
|
|
32
|
+
"docs": "typedoc --options typedoc.json && node fix-docs.js",
|
|
33
33
|
"deploy:docs": "npm run docs && wrangler pages deploy docs",
|
|
34
34
|
"ncu": "npx npm-check-updates -u --root",
|
|
35
35
|
"ncu:interactive": "npx npm-check-updates -i --root",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@cloudflare/vitest-pool-workers": "^0.16.6",
|
|
46
|
+
"@eslint/compat": "^2.1.0",
|
|
46
47
|
"@eslint/js": "^10.0.1",
|
|
47
48
|
"@types/node": "^25.8.0",
|
|
48
49
|
"@typescript-eslint/eslint-plugin": "^8.59.3",
|