foliko 1.0.4 → 1.0.5
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/package.json +1 -1
- package/plugins/telegram-plugin.js +127 -4
package/package.json
CHANGED
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
const { Plugin } = require('../src/core/plugin-base')
|
|
13
13
|
const { z } = require('zod')
|
|
14
14
|
|
|
15
|
+
// 转义 MarkdownV2 特殊字符
|
|
16
|
+
function escapeMarkdown(text) {
|
|
17
|
+
if (!text) return ''
|
|
18
|
+
// MarkdownV2 需要转义的字符
|
|
19
|
+
return text.replace(/([\_*\[\]()~`>#+\-=|{}.!])/g, '\\$1')
|
|
20
|
+
}
|
|
21
|
+
|
|
15
22
|
module.exports = function(Plugin) {
|
|
16
23
|
return class TelegramPlugin extends Plugin {
|
|
17
24
|
constructor(config = {}) {
|
|
@@ -155,9 +162,24 @@ module.exports = function(Plugin) {
|
|
|
155
162
|
* 处理消息
|
|
156
163
|
*/
|
|
157
164
|
async _handleMessage(msg) {
|
|
158
|
-
if (!msg || !msg.chat
|
|
165
|
+
if (!msg || !msg.chat) return
|
|
159
166
|
|
|
160
167
|
const chatId = msg.chat.id.toString()
|
|
168
|
+
|
|
169
|
+
// 处理图片消息
|
|
170
|
+
if (msg.photo) {
|
|
171
|
+
await this._handlePhoto(msg)
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 处理文档消息
|
|
176
|
+
if (msg.document) {
|
|
177
|
+
await this._handleDocument(msg)
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!msg.text) return
|
|
182
|
+
|
|
161
183
|
const text = msg.text.trim()
|
|
162
184
|
|
|
163
185
|
// 命令处理
|
|
@@ -279,11 +301,13 @@ module.exports = function(Plugin) {
|
|
|
279
301
|
// 保存助手回复到历史
|
|
280
302
|
session.history.push({ role: 'assistant', content: fullResponse })
|
|
281
303
|
|
|
282
|
-
//
|
|
283
|
-
|
|
304
|
+
// 发送最终回复(转义 Markdown 特殊字符)
|
|
305
|
+
const safeResponse = fullResponse || '抱歉,我没有收到有效的回复。'
|
|
306
|
+
await this._bot.editMessageText(safeResponse, {
|
|
284
307
|
chat_id: chatId,
|
|
285
308
|
message_id: thinkingMsg.message_id,
|
|
286
|
-
|
|
309
|
+
parse_mode: 'MarkdownV2'
|
|
310
|
+
})
|
|
287
311
|
|
|
288
312
|
} catch (err) {
|
|
289
313
|
console.error('[Telegram] Chat error:', err)
|
|
@@ -318,6 +342,105 @@ module.exports = function(Plugin) {
|
|
|
318
342
|
return this.config.allowedChats.includes(chatId)
|
|
319
343
|
}
|
|
320
344
|
|
|
345
|
+
/**
|
|
346
|
+
* 处理图片消息
|
|
347
|
+
*/
|
|
348
|
+
async _handlePhoto(msg) {
|
|
349
|
+
const chatId = msg.chat.id.toString()
|
|
350
|
+
const caption = msg.caption?.trim() || ''
|
|
351
|
+
|
|
352
|
+
// 权限检查
|
|
353
|
+
if (!this._checkPermission(chatId)) {
|
|
354
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
355
|
+
return
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// 获取最大尺寸的图片
|
|
359
|
+
const photo = msg.photo[msg.photo.length - 1]
|
|
360
|
+
console.log(`[Telegram] Photo received, file_id: ${photo.file_id}`)
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
// 下载图片
|
|
364
|
+
const filePath = await this._downloadFile(photo.file_id, '.jpg', 'telegram_images')
|
|
365
|
+
console.log(`[Telegram] Photo saved: ${filePath}`)
|
|
366
|
+
|
|
367
|
+
const savedMsg = `收到图片${caption ? ': ' + caption : ''}\n已保存至: ${filePath}`
|
|
368
|
+
await this._sendMessage(chatId, savedMsg, msg.message_id)
|
|
369
|
+
|
|
370
|
+
// TODO: 调用 AI 视觉能力分析图片
|
|
371
|
+
// await this._analyzeImage(chatId, filePath, caption)
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.error('[Telegram] Failed to save photo:', err.message)
|
|
374
|
+
await this._sendMessage(chatId, '图片保存失败: ' + err.message, msg.message_id)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 处理文档消息
|
|
380
|
+
*/
|
|
381
|
+
async _handleDocument(msg) {
|
|
382
|
+
const chatId = msg.chat.id.toString()
|
|
383
|
+
const fileName = msg.document.file_name || '未命名文件'
|
|
384
|
+
const caption = msg.caption?.trim() || ''
|
|
385
|
+
|
|
386
|
+
// 权限检查
|
|
387
|
+
if (!this._checkPermission(chatId)) {
|
|
388
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
console.log(`[Telegram] Document received: ${fileName}, file_id: ${msg.document.file_id}`)
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
// 根据文件扩展名确定保存目录和扩展名
|
|
396
|
+
const ext = fileName.includes('.') ? fileName.split('.').pop() : 'bin'
|
|
397
|
+
const filePath = await this._downloadFile(msg.document.file_id, ext, 'telegram_documents')
|
|
398
|
+
console.log(`[Telegram] Document saved: ${filePath}`)
|
|
399
|
+
|
|
400
|
+
const savedMsg = `收到文件: ${fileName}\n已保存至: ${filePath}${caption ? '\n\n说明: ' + caption : ''}`
|
|
401
|
+
await this._sendMessage(chatId, savedMsg, msg.message_id)
|
|
402
|
+
} catch (err) {
|
|
403
|
+
console.error('[Telegram] Failed to save document:', err.message)
|
|
404
|
+
await this._sendMessage(chatId, '文件保存失败: ' + err.message, msg.message_id)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* 下载文件
|
|
410
|
+
* @param {string} fileId - Telegram 文件 ID
|
|
411
|
+
* @param {string} ext - 文件扩展名
|
|
412
|
+
* @param {string} subDir - 子目录名
|
|
413
|
+
* @returns {Promise<string>} 保存的文件路径
|
|
414
|
+
*/
|
|
415
|
+
async _downloadFile(fileId, ext, subDir) {
|
|
416
|
+
return new Promise((resolve, reject) => {
|
|
417
|
+
const path = require('path')
|
|
418
|
+
const fs = require('fs')
|
|
419
|
+
|
|
420
|
+
// 创建保存目录
|
|
421
|
+
const saveDir = path.join(process.cwd(), '.agent', 'data', subDir)
|
|
422
|
+
if (!fs.existsSync(saveDir)) {
|
|
423
|
+
fs.mkdirSync(saveDir, { recursive: true })
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// 生成文件名
|
|
427
|
+
const fileName = `${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`
|
|
428
|
+
const filePath = path.join(saveDir, fileName)
|
|
429
|
+
|
|
430
|
+
// 下载文件
|
|
431
|
+
this._bot.downloadFile(fileId, saveDir)
|
|
432
|
+
.then((savedPath) => {
|
|
433
|
+
// 重命名为期望的文件名
|
|
434
|
+
const finalPath = path.join(saveDir, fileName)
|
|
435
|
+
if (savedPath !== finalPath) {
|
|
436
|
+
fs.renameSync(savedPath, finalPath)
|
|
437
|
+
}
|
|
438
|
+
resolve(finalPath)
|
|
439
|
+
})
|
|
440
|
+
.catch(reject)
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
|
|
321
444
|
/**
|
|
322
445
|
* 清除会话
|
|
323
446
|
*/
|