foliko 1.0.3 → 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/.claude/settings.local.json +3 -1
- package/package.json +2 -1
- package/plugins/ai-plugin.js +2 -1
- package/plugins/default-plugins.js +33 -0
- package/plugins/telegram-plugin.js +510 -0
- package/test-tg-bot.js +42 -0
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"Bash(node -e \":*)",
|
|
27
27
|
"Bash(node -e \"\nconst { loadAgentConfig } = require\\('./plugins/default-plugins'\\);\nconst config = loadAgentConfig\\('D:/Date/20260321/app/.agent'\\);\nconsole.log\\('skillsDirs:', config.skillsDirs\\);\n\")",
|
|
28
28
|
"Bash(cd D:/Code/vb-agent && pnpm install --shamefully-hoist 2>&1 | head -20)",
|
|
29
|
-
"Bash(cd D:/Code/vb-agent && rm -rf node_modules && pnpm install)"
|
|
29
|
+
"Bash(cd D:/Code/vb-agent && rm -rf node_modules && pnpm install)",
|
|
30
|
+
"Bash(cd D:/Code/vb-agent && timeout 8 node test-tg.js 2>&1 || true)",
|
|
31
|
+
"Bash(cd D:/Code/vb-agent && timeout 10 node test-tg.js 2>&1 || true)"
|
|
30
32
|
]
|
|
31
33
|
}
|
|
32
34
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "foliko",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "简约的插件化 Agent 框架",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"marked": "^11.2.0",
|
|
32
32
|
"marked-terminal": "6",
|
|
33
33
|
"node-cron": "^4.2.1",
|
|
34
|
+
"node-telegram-bot-api": "^0.67.0",
|
|
34
35
|
"zod": "^3.24.0"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/plugins/ai-plugin.js
CHANGED
|
@@ -212,6 +212,25 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
212
212
|
console.log('[Bootstrap] AI Plugin loaded')
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
// 1.5 创建主 Agent(供 Telegram 等需要绑定 Agent 的插件使用)
|
|
216
|
+
if (!framework._mainAgent && aiConfig.provider) {
|
|
217
|
+
const { Agent } = require('../src/core/agent')
|
|
218
|
+
const aiPlugin = framework.pluginManager.get('ai')
|
|
219
|
+
const aiClient = aiPlugin ? aiPlugin.getAIClient() : null
|
|
220
|
+
console.log('[Bootstrap] Creating Main Agent - aiClient:', !!aiClient)
|
|
221
|
+
|
|
222
|
+
framework._mainAgent = new Agent(framework, {
|
|
223
|
+
name: 'MainAgent',
|
|
224
|
+
systemPrompt: '你是一个有帮助的AI助手。',
|
|
225
|
+
model: aiConfig.model,
|
|
226
|
+
provider: aiConfig.provider,
|
|
227
|
+
apiKey: aiConfig.apiKey,
|
|
228
|
+
baseURL: aiConfig.baseURL
|
|
229
|
+
})
|
|
230
|
+
framework._agents.push(framework._mainAgent)
|
|
231
|
+
console.log('[Bootstrap] Main Agent created, has _chatHandler:', !!framework._mainAgent._chatHandler)
|
|
232
|
+
}
|
|
233
|
+
|
|
215
234
|
// 2. Storage 存储插件
|
|
216
235
|
if (!framework.pluginManager.has('storage')) {
|
|
217
236
|
const { StoragePlugin } = require('./storage-plugin')
|
|
@@ -351,6 +370,20 @@ async function bootstrapDefaults(framework, config = {}) {
|
|
|
351
370
|
console.log('[Bootstrap] Python Plugin Loader already loaded, skipping')
|
|
352
371
|
}
|
|
353
372
|
|
|
373
|
+
// 12.6 Telegram 插件(如果配置了Token)
|
|
374
|
+
if (!framework.pluginManager.has('telegram')) {
|
|
375
|
+
const telegramToken = process.env.TELEGRAM_BOT_TOKEN || agentConfig.telegram?.botToken
|
|
376
|
+
if (telegramToken) {
|
|
377
|
+
const { Plugin } = require('../src/core/plugin-base')
|
|
378
|
+
const createTelegramPlugin = require('./telegram-plugin')
|
|
379
|
+
const TelegramPlugin = createTelegramPlugin(Plugin)
|
|
380
|
+
await framework.loadPlugin(new TelegramPlugin({ botToken: telegramToken }))
|
|
381
|
+
console.log('[Bootstrap] Telegram Plugin loaded')
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
console.log('[Bootstrap] Telegram Plugin already loaded, skipping')
|
|
385
|
+
}
|
|
386
|
+
|
|
354
387
|
// 13. 加载自定义插件
|
|
355
388
|
await loadCustomPlugins(framework, agentConfig)
|
|
356
389
|
|
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram 插件 v2
|
|
3
|
+
* 支持绑定主Agent进行持续对话
|
|
4
|
+
*
|
|
5
|
+
* 配置:
|
|
6
|
+
* - botToken: Telegram Bot Token (必需)
|
|
7
|
+
* - allowedChats: 允许的聊天ID数组
|
|
8
|
+
* - groupMode: 是否启用群组模式
|
|
9
|
+
* - prefix: 命令前缀
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { Plugin } = require('../src/core/plugin-base')
|
|
13
|
+
const { z } = require('zod')
|
|
14
|
+
|
|
15
|
+
// 转义 MarkdownV2 特殊字符
|
|
16
|
+
function escapeMarkdown(text) {
|
|
17
|
+
if (!text) return ''
|
|
18
|
+
// MarkdownV2 需要转义的字符
|
|
19
|
+
return text.replace(/([\_*\[\]()~`>#+\-=|{}.!])/g, '\\$1')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = function(Plugin) {
|
|
23
|
+
return class TelegramPlugin extends Plugin {
|
|
24
|
+
constructor(config = {}) {
|
|
25
|
+
super()
|
|
26
|
+
this.name = 'telegram'
|
|
27
|
+
this.version = '2.0.0'
|
|
28
|
+
this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
|
|
29
|
+
this.priority = 80
|
|
30
|
+
|
|
31
|
+
this.config = {
|
|
32
|
+
botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
|
|
33
|
+
allowedChats: config.allowedChats || [],
|
|
34
|
+
groupMode: config.groupMode || false,
|
|
35
|
+
prefix: config.prefix || '/',
|
|
36
|
+
systemPrompt: config.systemPrompt || '你是一个有帮助的AI助手。'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this._framework = null
|
|
40
|
+
this._bot = null
|
|
41
|
+
this._sessions = new Map() // chatId -> { agent, history, lastActive }
|
|
42
|
+
this._streamingMessages = new Map() // chatId -> messageId
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
install(framework) {
|
|
46
|
+
this._framework = framework
|
|
47
|
+
return this
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
start(framework) {
|
|
51
|
+
if (!this.config.botToken) {
|
|
52
|
+
console.warn('[Telegram] No bot token. Set TELEGRAM_BOT_TOKEN env var.')
|
|
53
|
+
return this
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this._initBot()
|
|
57
|
+
return this
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
_initBot() {
|
|
61
|
+
try {
|
|
62
|
+
// 添加 .agent/node_modules 到搜索路径
|
|
63
|
+
// const path = require('path')
|
|
64
|
+
// const agentNodeModules = path.join(process.cwd(), '.agent', 'node_modules')
|
|
65
|
+
// if (!module.paths.includes(agentNodeModules)) {
|
|
66
|
+
// module.paths.unshift(agentNodeModules)
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
const TelegramBot = require('node-telegram-bot-api')
|
|
70
|
+
this._bot = new TelegramBot(this.config.botToken, { polling: true })
|
|
71
|
+
|
|
72
|
+
console.log('[Telegram] Bot started successfully')
|
|
73
|
+
|
|
74
|
+
// 注册菜单命令
|
|
75
|
+
this._bot.setMyCommands([
|
|
76
|
+
{ command: 'start', description: '显示帮助信息' },
|
|
77
|
+
{ command: 'clear', description: '清除对话历史' },
|
|
78
|
+
{ command: 'history', description: '查看对话状态' }
|
|
79
|
+
]).then(() => {
|
|
80
|
+
console.log('[Telegram] Commands registered')
|
|
81
|
+
}).catch((err) => {
|
|
82
|
+
console.error('[Telegram] Failed to register commands:', err.message)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// 监听消息
|
|
86
|
+
this._bot.on('message', (msg) => this._handleMessage(msg))
|
|
87
|
+
|
|
88
|
+
// 监听编辑消息
|
|
89
|
+
this._bot.on('edit_message', (msg) => this._handleEdit(msg))
|
|
90
|
+
|
|
91
|
+
// 监听回调
|
|
92
|
+
this._bot.on('callback_query', (query) => this._handleCallback(query))
|
|
93
|
+
|
|
94
|
+
// 监听错误
|
|
95
|
+
this._bot.on('polling_error', (err) => {
|
|
96
|
+
console.error('[Telegram] Polling error:', err.message)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
this._bot.on('error', (err) => {
|
|
100
|
+
console.error('[Telegram] Bot error:', err.message)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// 监听 Agent 创建事件,确保在 Agent 创建后能正确获取
|
|
104
|
+
if (this._framework) {
|
|
105
|
+
this._framework.on('agent:created', (agent) => {
|
|
106
|
+
console.log('[Telegram] New agent created:', agent.name)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error('[Telegram] Failed to initialize bot:', err.message)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 获取主Agent
|
|
117
|
+
*/
|
|
118
|
+
_getMainAgent() {
|
|
119
|
+
console.log('[Telegram] _getMainAgent called')
|
|
120
|
+
console.log('[Telegram] _framework:', !!this._framework)
|
|
121
|
+
console.log('[Telegram] _framework._mainAgent:', !!this._framework?._mainAgent)
|
|
122
|
+
console.log('[Telegram] _framework._agents:', this._framework?._agents?.length || 0)
|
|
123
|
+
|
|
124
|
+
if (this._framework._mainAgent) {
|
|
125
|
+
console.log('[Telegram] returning _mainAgent')
|
|
126
|
+
return this._framework._mainAgent
|
|
127
|
+
}
|
|
128
|
+
const agents = this._framework._agents || []
|
|
129
|
+
console.log('[Telegram] returning last agent:', agents.length > 0 ? agents[agents.length - 1].name : 'none')
|
|
130
|
+
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* 获取或创建会话Agent
|
|
135
|
+
*/
|
|
136
|
+
_getSessionAgent(chatId) {
|
|
137
|
+
console.log('[Telegram] _getSessionAgent called for chatId:', chatId)
|
|
138
|
+
let session = this._sessions.get(chatId)
|
|
139
|
+
|
|
140
|
+
if (!session) {
|
|
141
|
+
const mainAgent = this._getMainAgent()
|
|
142
|
+
console.log('[Telegram] mainAgent:', !!mainAgent, mainAgent?.name)
|
|
143
|
+
if (!mainAgent) {
|
|
144
|
+
console.log('[Telegram] ERROR: mainAgent is null!')
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 创建会话Agent(复用主Agent的配置)
|
|
149
|
+
session = {
|
|
150
|
+
agent: mainAgent,
|
|
151
|
+
history: [], // 对话历史
|
|
152
|
+
lastActive: new Date()
|
|
153
|
+
}
|
|
154
|
+
this._sessions.set(chatId, session)
|
|
155
|
+
console.log(`[Telegram] New session created for: ${chatId}`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return session
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* 处理消息
|
|
163
|
+
*/
|
|
164
|
+
async _handleMessage(msg) {
|
|
165
|
+
if (!msg || !msg.chat) return
|
|
166
|
+
|
|
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
|
+
|
|
183
|
+
const text = msg.text.trim()
|
|
184
|
+
|
|
185
|
+
// 命令处理
|
|
186
|
+
if (text.startsWith(this.config.prefix)) {
|
|
187
|
+
await this._handleCommand(msg)
|
|
188
|
+
return
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 群组模式检查
|
|
192
|
+
if (msg.chat.type === 'group' || msg.chat.type === 'supergroup') {
|
|
193
|
+
if (!this.config.groupMode) return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 权限检查
|
|
197
|
+
if (!this._checkPermission(chatId)) {
|
|
198
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 处理对话
|
|
203
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 处理命令
|
|
208
|
+
*/
|
|
209
|
+
async _handleCommand(msg) {
|
|
210
|
+
const chatId = msg.chat.id.toString()
|
|
211
|
+
const text = msg.text.trim()
|
|
212
|
+
const parts = text.split(' ')
|
|
213
|
+
const command = parts[0].substring(1)
|
|
214
|
+
const args = parts.slice(1).join(' ')
|
|
215
|
+
|
|
216
|
+
switch (command.toLowerCase()) {
|
|
217
|
+
case 'start':
|
|
218
|
+
await this._sendMessage(chatId,
|
|
219
|
+
'👋 欢迎使用 AI 助手!\n\n' +
|
|
220
|
+
'直接发送消息即可与我对话,我会尽力帮助您。\n\n' +
|
|
221
|
+
'可用命令:\n' +
|
|
222
|
+
'/start - 显示帮助\n' +
|
|
223
|
+
'/clear - 清除对话历史\n' +
|
|
224
|
+
'/history - 查看历史消息数',
|
|
225
|
+
msg.message_id)
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
case 'clear':
|
|
229
|
+
this._clearSession(chatId)
|
|
230
|
+
await this._sendMessage(chatId, '✅ 对话历史已清除', msg.message_id)
|
|
231
|
+
break
|
|
232
|
+
|
|
233
|
+
case 'history':
|
|
234
|
+
const session = this._sessions.get(chatId)
|
|
235
|
+
await this._sendMessage(chatId,
|
|
236
|
+
`📊 当前状态\n\n会话数:${this._sessions.size}\n` +
|
|
237
|
+
`历史消息:${session?.history.length || 0}\n` +
|
|
238
|
+
`最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
|
|
239
|
+
msg.message_id)
|
|
240
|
+
break
|
|
241
|
+
|
|
242
|
+
default:
|
|
243
|
+
// 未识别命令,作为普通消息处理
|
|
244
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 处理对话
|
|
250
|
+
*/
|
|
251
|
+
async _processChat(chatId, text, replyToMessageId) {
|
|
252
|
+
console.log('[Telegram] _processChat called, chatId:', chatId, 'text:', text.substring(0, 50))
|
|
253
|
+
const session = this._getSessionAgent(chatId)
|
|
254
|
+
console.log('[Telegram] session:', !!session)
|
|
255
|
+
if (!session) {
|
|
256
|
+
await this._sendMessage(chatId, '❌ AI 服务未初始化', replyToMessageId)
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// 添加用户消息到历史
|
|
261
|
+
session.history.push({ role: 'user', content: text })
|
|
262
|
+
session.lastActive = new Date()
|
|
263
|
+
|
|
264
|
+
// 发送思考中消息
|
|
265
|
+
let thinkingMsg
|
|
266
|
+
try {
|
|
267
|
+
thinkingMsg = await this._bot.sendMessage(chatId, '🤔 思考中...', {
|
|
268
|
+
reply_to_message_id: replyToMessageId
|
|
269
|
+
})
|
|
270
|
+
} catch (err) {
|
|
271
|
+
console.error('[Telegram] Send thinking error:', err.message)
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
let fullResponse = ''
|
|
277
|
+
const chatIdStr = `telegram_${chatId}`
|
|
278
|
+
|
|
279
|
+
// 使用流式响应
|
|
280
|
+
for await (const chunk of session.agent.chatStream(text, {
|
|
281
|
+
sessionId: chatIdStr
|
|
282
|
+
})) {
|
|
283
|
+
// chatStream 返回 { type: 'text', text } 或 { type: 'tool-call', toolName, args }
|
|
284
|
+
if (chunk.type === 'text' && chunk.text) {
|
|
285
|
+
fullResponse += chunk.text
|
|
286
|
+
|
|
287
|
+
// 定期更新消息
|
|
288
|
+
if (fullResponse.length % 100 === 0) {
|
|
289
|
+
try {
|
|
290
|
+
await this._bot.editMessageText(`📝 ${fullResponse}▌`, {
|
|
291
|
+
chat_id: chatId,
|
|
292
|
+
message_id: thinkingMsg.message_id
|
|
293
|
+
})
|
|
294
|
+
} catch (e) {
|
|
295
|
+
// 忽略编辑错误
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 保存助手回复到历史
|
|
302
|
+
session.history.push({ role: 'assistant', content: fullResponse })
|
|
303
|
+
|
|
304
|
+
// 发送最终回复(转义 Markdown 特殊字符)
|
|
305
|
+
const safeResponse = fullResponse || '抱歉,我没有收到有效的回复。'
|
|
306
|
+
await this._bot.editMessageText(safeResponse, {
|
|
307
|
+
chat_id: chatId,
|
|
308
|
+
message_id: thinkingMsg.message_id,
|
|
309
|
+
parse_mode: 'MarkdownV2'
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
} catch (err) {
|
|
313
|
+
console.error('[Telegram] Chat error:', err)
|
|
314
|
+
await this._bot.editMessageText(`❌ 发生错误:${err.message}`, {
|
|
315
|
+
chat_id: chatId,
|
|
316
|
+
message_id: thinkingMsg.message_id
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* 处理编辑消息
|
|
323
|
+
*/
|
|
324
|
+
async _handleEdit(msg) {
|
|
325
|
+
// 支持编辑后重新处理
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* 处理回调查询
|
|
330
|
+
*/
|
|
331
|
+
async _handleCallback(query) {
|
|
332
|
+
await this._bot.answerCallbackQuery(query.id, {
|
|
333
|
+
text: '处理中...'
|
|
334
|
+
})
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* 权限检查
|
|
339
|
+
*/
|
|
340
|
+
_checkPermission(chatId) {
|
|
341
|
+
if (this.config.allowedChats.length === 0) return true
|
|
342
|
+
return this.config.allowedChats.includes(chatId)
|
|
343
|
+
}
|
|
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
|
+
|
|
444
|
+
/**
|
|
445
|
+
* 清除会话
|
|
446
|
+
*/
|
|
447
|
+
_clearSession(chatId) {
|
|
448
|
+
const session = this._sessions.get(chatId)
|
|
449
|
+
if (session && session.agent) {
|
|
450
|
+
session.agent.clearHistory?.()
|
|
451
|
+
}
|
|
452
|
+
this._sessions.delete(chatId)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* 发送消息
|
|
457
|
+
*/
|
|
458
|
+
async _sendMessage(chatId, text, replyToMessageId = null) {
|
|
459
|
+
if (!this._bot) return
|
|
460
|
+
return this._bot.sendMessage(chatId, text, {
|
|
461
|
+
reply_to_message_id: replyToMessageId,
|
|
462
|
+
})
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* 获取插件状态
|
|
467
|
+
*/
|
|
468
|
+
getStatus() {
|
|
469
|
+
return {
|
|
470
|
+
connected: !!this._bot,
|
|
471
|
+
sessionCount: this._sessions.size,
|
|
472
|
+
sessions: Array.from(this._sessions.entries()).map(([chatId, session]) => ({
|
|
473
|
+
chatId,
|
|
474
|
+
historyLength: session.history.length,
|
|
475
|
+
lastActive: session.lastActive
|
|
476
|
+
})),
|
|
477
|
+
config: {
|
|
478
|
+
groupMode: this.config.groupMode,
|
|
479
|
+
prefix: this.config.prefix,
|
|
480
|
+
allowedChatsCount: this.config.allowedChats.length
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* 停止 Bot
|
|
487
|
+
*/
|
|
488
|
+
stopBot() {
|
|
489
|
+
if (this._bot) {
|
|
490
|
+
this._bot.stopPolling()
|
|
491
|
+
this._bot = null
|
|
492
|
+
console.log('[Telegram] Bot stopped')
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
reload(framework) {
|
|
497
|
+
this._framework = framework
|
|
498
|
+
if (this.config.botToken) {
|
|
499
|
+
this.stopBot()
|
|
500
|
+
this._initBot()
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
uninstall(framework) {
|
|
505
|
+
this.stopBot()
|
|
506
|
+
this._sessions.clear()
|
|
507
|
+
this._framework = null
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
package/test-tg-bot.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require('dotenv').config();
|
|
2
|
+
const { Framework } = require('./src');
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
console.log('Starting test...\n');
|
|
6
|
+
|
|
7
|
+
const framework = new Framework({ debug: false });
|
|
8
|
+
|
|
9
|
+
await framework.bootstrap({
|
|
10
|
+
agentDir: process.cwd() + '/.agent',
|
|
11
|
+
aiConfig: {
|
|
12
|
+
provider: process.env.FOLIKO_PROVIDER || 'minimax',
|
|
13
|
+
model: process.env.FOLIKO_MODEL || 'MiniMax-M2.7',
|
|
14
|
+
baseURL: process.env.FOLIKO_BASE_URL || 'https://api.minimaxi.com/v1',
|
|
15
|
+
apiKey: process.env.MINIMAX_API_KEY || process.env.DEEPSEEK_API_KEY
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 检查主 Agent
|
|
20
|
+
const mainAgent = framework._mainAgent;
|
|
21
|
+
console.log('Main Agent:', mainAgent ? mainAgent.name : 'NOT FOUND');
|
|
22
|
+
console.log('Agent apiKey:', mainAgent?.apiKey ? 'set' : 'NOT SET');
|
|
23
|
+
console.log('Agent _chatHandler:', mainAgent?._chatHandler ? 'exists' : 'NOT EXISTS');
|
|
24
|
+
console.log('Agent _chatHandler._aiClient:', mainAgent?._chatHandler?._aiClient ? 'exists' : 'NOT EXISTS');
|
|
25
|
+
|
|
26
|
+
// 检查 Telegram 插件
|
|
27
|
+
const tgPlugin = framework.pluginManager.get('telegram');
|
|
28
|
+
console.log('\nTelegram Plugin:', tgPlugin ? 'loaded' : 'NOT LOADED');
|
|
29
|
+
|
|
30
|
+
if (tgPlugin) {
|
|
31
|
+
console.log('Telegram _bot:', tgPlugin._bot ? 'exists' : 'NOT EXISTS');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log('\n========================================');
|
|
35
|
+
console.log('Bot is running. Send a message to test!');
|
|
36
|
+
console.log('========================================\n');
|
|
37
|
+
|
|
38
|
+
// 保持运行
|
|
39
|
+
await new Promise(() => {});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
main().catch(console.error);
|