foliko 1.0.40 → 1.0.41
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 +35 -1
- package/cli/src/ui/chat-ui.js +7 -2
- package/docs/features.md +120 -0
- package/examples/basic.js +110 -110
- package/examples/bootstrap.js +19 -0
- package/examples/mcp-example.js +53 -53
- package/examples/skill-example.js +49 -49
- package/examples/test-chat.js +6 -0
- package/examples/test-mcp.js +79 -79
- package/examples/test-reload.js +61 -61
- package/examples/test-web-plugin.js +98 -0
- package/package.json +4 -3
- package/plugins/default-plugins.js +11 -10
- package/plugins/feishu-plugin.js +291 -537
- package/plugins/scheduler-plugin.js +1 -1
- package/plugins/subagent-plugin.js +4 -4
- package/plugins/telegram-plugin.js +307 -522
- package/plugins/think-plugin.js +2 -2
- package/plugins/web-plugin.js +542 -0
- package/plugins/weixin-plugin.js +320 -274
- package/src/core/agent-chat.js +24 -27
- package/src/core/agent.js +77 -0
- package/src/core/framework.js +35 -0
- package/src/executors/executor-base.js +58 -58
- package/test-server.js +25 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram 插件
|
|
2
|
+
* Telegram 插件
|
|
3
3
|
* 支持绑定主Agent进行持续对话
|
|
4
4
|
*
|
|
5
5
|
* 配置:
|
|
@@ -15,615 +15,400 @@ const { z } = require('zod')
|
|
|
15
15
|
// 转义 MarkdownV2 特殊字符
|
|
16
16
|
function escapeMarkdown(text) {
|
|
17
17
|
if (!text) return ''
|
|
18
|
-
// MarkdownV2 需要转义的所有字符
|
|
19
18
|
return text.replace(/([\_*\[\]()~`>#+\-=|{}.!])/g, '\\$1')
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
groupMode: config.groupMode || false,
|
|
38
|
-
prefix: config.prefix || '/'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
this._framework = null
|
|
42
|
-
this._bot = null
|
|
43
|
-
this._sessionPlugin = null
|
|
44
|
-
this._sessionDeleteHandler = null
|
|
45
|
-
this._sessionAgents = new Map() // chatId -> Agent
|
|
21
|
+
class TelegramPlugin extends Plugin {
|
|
22
|
+
constructor(config = {}) {
|
|
23
|
+
super()
|
|
24
|
+
this.name = 'telegram'
|
|
25
|
+
this.version = '2.1.0'
|
|
26
|
+
this.description = 'Telegram 对话插件,绑定主Agent进行持续对话'
|
|
27
|
+
this.priority = 80
|
|
28
|
+
this.enabled = false
|
|
29
|
+
this.systemPrompt = '你是一个有帮助的AI助手。回复内容不要使用markdown格式文本。'
|
|
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 || '/'
|
|
46
36
|
}
|
|
47
37
|
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
this._framework = null
|
|
39
|
+
this._bot = null
|
|
40
|
+
this._sessionPlugin = null
|
|
41
|
+
this._sessionDeleteHandler = null
|
|
42
|
+
this._sessionAgents = new Map()
|
|
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.')
|
|
50
53
|
return this
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
console.warn('[Telegram] No bot token. Set TELEGRAM_BOT_TOKEN env var.')
|
|
56
|
-
return this
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// 获取 SessionPlugin 引用
|
|
60
|
-
this._sessionPlugin = framework.pluginManager.get('session')
|
|
56
|
+
this._framework = framework
|
|
57
|
+
this._sessionPlugin = framework.pluginManager.get('session')
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (sessionId.startsWith('telegram_')) {
|
|
67
|
-
console.log(`[Telegram] Session deleted: ${sessionId}`)
|
|
68
|
-
}
|
|
59
|
+
if (this._sessionPlugin) {
|
|
60
|
+
this._sessionDeleteHandler = (sessionId) => {
|
|
61
|
+
if (sessionId.startsWith('telegram_')) {
|
|
62
|
+
console.log(`[Telegram] Session deleted: ${sessionId}`)
|
|
69
63
|
}
|
|
70
|
-
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
71
64
|
}
|
|
72
|
-
|
|
73
|
-
this._initBot()
|
|
74
|
-
return this
|
|
65
|
+
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
75
66
|
}
|
|
76
67
|
|
|
77
|
-
_initBot()
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// const path = require('path')
|
|
81
|
-
// const agentNodeModules = path.join(process.cwd(), '.agent', 'node_modules')
|
|
82
|
-
// if (!module.paths.includes(agentNodeModules)) {
|
|
83
|
-
// module.paths.unshift(agentNodeModules)
|
|
84
|
-
// }
|
|
85
|
-
|
|
86
|
-
const TelegramBot = require('node-telegram-bot-api')
|
|
87
|
-
this._bot = new TelegramBot(this.config.botToken, { polling: true })
|
|
88
|
-
|
|
89
|
-
console.log('[Telegram] Bot started successfully')
|
|
90
|
-
|
|
91
|
-
// 注册菜单命令
|
|
92
|
-
this._bot.setMyCommands([
|
|
93
|
-
{ command: 'start', description: '显示帮助信息' },
|
|
94
|
-
{ command: 'clear', description: '清除对话历史' },
|
|
95
|
-
{ command: 'history', description: '查看对话状态' }
|
|
96
|
-
]).then(() => {
|
|
97
|
-
console.log('[Telegram] Commands registered')
|
|
98
|
-
}).catch((err) => {
|
|
99
|
-
console.error('[Telegram] Failed to register commands:', err.message)
|
|
100
|
-
})
|
|
68
|
+
this._initBot()
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
101
71
|
|
|
102
|
-
|
|
103
|
-
|
|
72
|
+
_initBot() {
|
|
73
|
+
try {
|
|
74
|
+
const TelegramBot = require('node-telegram-bot-api')
|
|
75
|
+
this._bot = new TelegramBot(this.config.botToken, { polling: true })
|
|
104
76
|
|
|
105
|
-
|
|
106
|
-
this._bot.on('edit_message', (msg) => this._handleEdit(msg))
|
|
77
|
+
console.log('[Telegram] Bot started successfully')
|
|
107
78
|
|
|
108
|
-
|
|
109
|
-
|
|
79
|
+
this._bot.setMyCommands([
|
|
80
|
+
{ command: 'start', description: '显示帮助信息' },
|
|
81
|
+
{ command: 'clear', description: '清除对话历史' },
|
|
82
|
+
{ command: 'history', description: '查看对话状态' }
|
|
83
|
+
]).catch((err) => {
|
|
84
|
+
console.error('[Telegram] Failed to register commands:', err.message)
|
|
85
|
+
})
|
|
110
86
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
87
|
+
this._bot.on('message', (msg) => this._handleMessage(msg))
|
|
88
|
+
this._bot.on('edit_message', (msg) => this._handleEdit(msg))
|
|
89
|
+
this._bot.on('callback_query', (query) => this._handleCallback(query))
|
|
90
|
+
this._bot.on('polling_error', (err) => console.error('[Telegram] Polling error:', err.message))
|
|
91
|
+
this._bot.on('error', (err) => console.error('[Telegram] Bot error:', err.message))
|
|
115
92
|
|
|
116
|
-
|
|
117
|
-
|
|
93
|
+
if (this._framework) {
|
|
94
|
+
this._framework.on('agent:created', (agent) => {
|
|
95
|
+
console.log('[Telegram] New agent created:', agent.name)
|
|
96
|
+
})
|
|
97
|
+
this._framework.on('scheduler:reminder', async (data) => {
|
|
98
|
+
await this._handleScheduledReminder(data)
|
|
118
99
|
})
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error('[Telegram] Failed to initialize bot:', err.message)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
119
105
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.log('[Telegram] New agent created:', agent.name)
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
// 监听定时提醒事件
|
|
127
|
-
this._framework.on('scheduler:reminder', async (data) => {
|
|
128
|
-
console.log('[Telegram] Received scheduler reminder:', data)
|
|
129
|
-
await this._handleScheduledReminder(data)
|
|
130
|
-
})
|
|
131
|
-
}
|
|
106
|
+
async _handleScheduledReminder(data) {
|
|
107
|
+
const { taskName, message, sessionId } = data
|
|
108
|
+
const reminderText = `🔔 [${taskName}]\n\n${message}`
|
|
132
109
|
|
|
110
|
+
if (sessionId && sessionId.startsWith('telegram_')) {
|
|
111
|
+
const chatId = sessionId.replace('telegram_', '')
|
|
112
|
+
try {
|
|
113
|
+
await this._bot.sendMessage(chatId, reminderText)
|
|
114
|
+
console.log(`[Telegram] Reminder sent to chat ${chatId}`)
|
|
133
115
|
} catch (err) {
|
|
134
|
-
console.error(
|
|
116
|
+
console.error(`[Telegram] Failed to send reminder:`, err.message)
|
|
135
117
|
}
|
|
118
|
+
return
|
|
136
119
|
}
|
|
137
120
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const { taskName, message, sessionId } = data
|
|
143
|
-
|
|
144
|
-
// 构建提醒消息
|
|
145
|
-
const reminderText = `🔔 [${taskName}]\n\n${message}`
|
|
121
|
+
if (this._sessionPlugin) {
|
|
122
|
+
const sessions = this._sessionPlugin.listSessions()
|
|
123
|
+
.filter(s => s.id.startsWith('telegram_'))
|
|
124
|
+
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
146
125
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const chatId = sessionId.replace('telegram_', '')
|
|
126
|
+
if (sessions.length > 0) {
|
|
127
|
+
const chatId = sessions[0].id.replace('telegram_', '')
|
|
150
128
|
try {
|
|
151
129
|
await this._bot.sendMessage(chatId, reminderText)
|
|
152
|
-
console.log(`[Telegram] Reminder sent to chat ${chatId}`)
|
|
130
|
+
console.log(`[Telegram] Reminder sent to recent chat ${chatId}`)
|
|
153
131
|
} catch (err) {
|
|
154
132
|
console.error(`[Telegram] Failed to send reminder:`, err.message)
|
|
155
133
|
}
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// 其他情况(包括 null 或其他 sessionId),发送到最近的 Telegram 会话
|
|
160
|
-
if (this._sessionPlugin) {
|
|
161
|
-
const allSessions = this._sessionPlugin.listSessions()
|
|
162
|
-
const telegramSessions = allSessions
|
|
163
|
-
.filter(s => s.id.startsWith('telegram_'))
|
|
164
|
-
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
165
|
-
|
|
166
|
-
if (telegramSessions.length > 0) {
|
|
167
|
-
const chatId = telegramSessions[0].id.replace('telegram_', '')
|
|
168
|
-
try {
|
|
169
|
-
await this._bot.sendMessage(chatId, reminderText)
|
|
170
|
-
console.log(`[Telegram] Reminder sent to chat ${chatId}`)
|
|
171
|
-
} catch (err) {
|
|
172
|
-
console.error(`[Telegram] Failed to send reminder to ${chatId}:`, err.message)
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
console.log('[Telegram] No active Telegram sessions to send reminder')
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
console.log('[Telegram] No active Telegram sessions to send reminder')
|
|
179
134
|
}
|
|
180
135
|
}
|
|
136
|
+
}
|
|
181
137
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (this._framework._mainAgent) {
|
|
192
|
-
console.log('[Telegram] returning _mainAgent')
|
|
193
|
-
return this._framework._mainAgent
|
|
194
|
-
}
|
|
195
|
-
const agents = this._framework._agents || []
|
|
196
|
-
console.log('[Telegram] returning last agent:', agents.length > 0 ? agents[agents.length - 1].name : 'none')
|
|
197
|
-
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
138
|
+
_getMainAgent() {
|
|
139
|
+
if (this._framework._mainAgent) return this._framework._mainAgent
|
|
140
|
+
const agents = this._framework._agents || []
|
|
141
|
+
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
_getSessionAgent(chatId) {
|
|
145
|
+
if (this._sessionAgents.has(chatId)) {
|
|
146
|
+
return { agent: this._sessionAgents.get(chatId), sessionId: `telegram_${chatId}` }
|
|
198
147
|
}
|
|
199
148
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
_getSessionAgent(chatId) {
|
|
205
|
-
console.log('[Telegram] _getSessionAgent called for chatId:', chatId)
|
|
206
|
-
|
|
207
|
-
// 检查缓存
|
|
208
|
-
if (this._sessionAgents.has(chatId)) {
|
|
209
|
-
const agent = this._sessionAgents.get(chatId)
|
|
210
|
-
const sessionId = `telegram_${chatId}`
|
|
211
|
-
console.log('[Telegram] Reusing cached session agent for chatId:', chatId)
|
|
212
|
-
return { agent, sessionId }
|
|
213
|
-
}
|
|
149
|
+
const agent = this._framework.createSessionAgent(`telegram_${chatId}`, {
|
|
150
|
+
systemPrompt: this.systemPrompt
|
|
151
|
+
})
|
|
152
|
+
this._sessionAgents.set(chatId, agent)
|
|
214
153
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
154
|
+
if (this._sessionPlugin) {
|
|
155
|
+
this._sessionPlugin.getOrCreateSession(`telegram_${chatId}`, {
|
|
156
|
+
metadata: { platform: 'telegram', chatId }
|
|
218
157
|
})
|
|
219
|
-
this._sessionAgents.set(chatId, agent)
|
|
220
|
-
console.log('[Telegram] Created new session agent for chatId:', chatId)
|
|
221
|
-
|
|
222
|
-
// 使用 SessionPlugin 管理会话历史
|
|
223
|
-
if (this._sessionPlugin) {
|
|
224
|
-
const sessionId = `telegram_${chatId}`
|
|
225
|
-
this._sessionPlugin.getOrCreateSession(sessionId, {
|
|
226
|
-
metadata: { platform: 'telegram', chatId }
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
console.log(`[Telegram] Session ready for chatId: ${chatId}`)
|
|
231
|
-
return { agent, sessionId: `telegram_${chatId}` }
|
|
232
158
|
}
|
|
233
159
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
*/
|
|
237
|
-
async _handleMessage(msg) {
|
|
238
|
-
if (!msg || !msg.chat) return
|
|
239
|
-
|
|
240
|
-
const chatId = msg.chat.id.toString()
|
|
241
|
-
|
|
242
|
-
// 处理图片消息
|
|
243
|
-
if (msg.photo) {
|
|
244
|
-
await this._handlePhoto(msg)
|
|
245
|
-
return
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// 处理文档消息
|
|
249
|
-
if (msg.document) {
|
|
250
|
-
await this._handleDocument(msg)
|
|
251
|
-
return
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (!msg.text) return
|
|
160
|
+
return { agent, sessionId: `telegram_${chatId}` }
|
|
161
|
+
}
|
|
255
162
|
|
|
256
|
-
|
|
163
|
+
async _handleMessage(msg) {
|
|
164
|
+
if (!msg || !msg.chat) return
|
|
165
|
+
const chatId = msg.chat.id.toString()
|
|
257
166
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return
|
|
262
|
-
}
|
|
167
|
+
if (msg.photo) { await this._handlePhoto(msg); return }
|
|
168
|
+
if (msg.document) { await this._handleDocument(msg); return }
|
|
169
|
+
if (!msg.text) return
|
|
263
170
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
171
|
+
const text = msg.text.trim()
|
|
172
|
+
if (text.startsWith(this.config.prefix)) {
|
|
173
|
+
await this._handleCommand(msg)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
268
176
|
|
|
269
|
-
|
|
270
|
-
if (!this.
|
|
271
|
-
|
|
272
|
-
return
|
|
273
|
-
}
|
|
177
|
+
if (msg.chat.type === 'group' || msg.chat.type === 'supergroup') {
|
|
178
|
+
if (!this.config.groupMode) return
|
|
179
|
+
}
|
|
274
180
|
|
|
275
|
-
|
|
276
|
-
await this.
|
|
181
|
+
if (!this._checkPermission(chatId)) {
|
|
182
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
183
|
+
return
|
|
277
184
|
}
|
|
278
185
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
186
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async _handleCommand(msg) {
|
|
190
|
+
const chatId = msg.chat.id.toString()
|
|
191
|
+
const text = msg.text.trim()
|
|
192
|
+
const parts = text.split(' ')
|
|
193
|
+
const command = parts[0].substring(1)
|
|
194
|
+
const args = parts.slice(1).join(' ')
|
|
195
|
+
|
|
196
|
+
switch (command.toLowerCase()) {
|
|
197
|
+
case 'start':
|
|
198
|
+
await this._sendMessage(chatId,
|
|
199
|
+
'👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/start - 显示帮助\n/clear - 清除对话历史\n/history - 查看历史消息数',
|
|
200
|
+
msg.message_id)
|
|
201
|
+
break
|
|
202
|
+
case 'clear':
|
|
203
|
+
this._clearSession(chatId)
|
|
204
|
+
await this._sendMessage(chatId, '✅ 对话历史已清除', msg.message_id)
|
|
205
|
+
break
|
|
206
|
+
case 'history':
|
|
207
|
+
if (this._sessionPlugin) {
|
|
208
|
+
const session = this._sessionPlugin.getSession(`telegram_${chatId}`)
|
|
209
|
+
const sessions = this._sessionPlugin.listSessions().filter(s => s.id.startsWith('telegram_'))
|
|
291
210
|
await this._sendMessage(chatId,
|
|
292
|
-
|
|
293
|
-
'直接发送消息即可与我对话,我会尽力帮助您。\n\n' +
|
|
294
|
-
'可用命令:\n' +
|
|
295
|
-
'/start - 显示帮助\n' +
|
|
296
|
-
'/clear - 清除对话历史\n' +
|
|
297
|
-
'/history - 查看历史消息数',
|
|
211
|
+
`📊 当前状态\n\n会话数:${sessions.length}\n历史消息:${session?.messages?.length || 0}\n最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
|
|
298
212
|
msg.message_id)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
case 'history':
|
|
307
|
-
if (this._sessionPlugin) {
|
|
308
|
-
const sessionId = `telegram_${chatId}`
|
|
309
|
-
const session = this._sessionPlugin.getSession(sessionId)
|
|
310
|
-
const allSessions = this._sessionPlugin.listSessions()
|
|
311
|
-
const telegramSessionCount = allSessions.filter(s => s.id.startsWith('telegram_')).length
|
|
312
|
-
await this._sendMessage(chatId,
|
|
313
|
-
`📊 当前状态\n\n会话数:${telegramSessionCount}\n` +
|
|
314
|
-
`历史消息:${session?.messages.length || 0}\n` +
|
|
315
|
-
`最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
|
|
316
|
-
msg.message_id)
|
|
317
|
-
} else {
|
|
318
|
-
await this._sendMessage(chatId, '❌ 会话服务不可用', msg.message_id)
|
|
319
|
-
}
|
|
320
|
-
break
|
|
321
|
-
|
|
322
|
-
default:
|
|
323
|
-
// 未识别命令,作为普通消息处理
|
|
324
|
-
await this._processChat(chatId, text, msg.message_id)
|
|
325
|
-
}
|
|
213
|
+
} else {
|
|
214
|
+
await this._sendMessage(chatId, '❌ 会话服务不可用', msg.message_id)
|
|
215
|
+
}
|
|
216
|
+
break
|
|
217
|
+
default:
|
|
218
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
326
219
|
}
|
|
220
|
+
}
|
|
327
221
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
console.log('[Telegram] session:', !!sessionInfo)
|
|
335
|
-
if (!sessionInfo) {
|
|
336
|
-
await this._sendMessage(chatId, '❌ AI 服务未初始化', replyToMessageId)
|
|
337
|
-
return
|
|
338
|
-
}
|
|
222
|
+
async _processChat(chatId, text, replyToMessageId) {
|
|
223
|
+
const sessionInfo = this._getSessionAgent(chatId)
|
|
224
|
+
if (!sessionInfo) {
|
|
225
|
+
await this._sendMessage(chatId, '❌ AI 服务未初始化', replyToMessageId)
|
|
226
|
+
return
|
|
227
|
+
}
|
|
339
228
|
|
|
340
|
-
|
|
229
|
+
const { agent, sessionId } = sessionInfo
|
|
341
230
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
231
|
+
if (this._sessionPlugin) {
|
|
232
|
+
this._sessionPlugin.addMessage(sessionId, { role: 'user', content: text })
|
|
233
|
+
}
|
|
346
234
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
235
|
+
let thinkingMsg
|
|
236
|
+
try {
|
|
237
|
+
thinkingMsg = await this._bot.sendMessage(chatId, '🤔 思考中...', {
|
|
238
|
+
reply_to_message_id: replyToMessageId
|
|
239
|
+
})
|
|
240
|
+
} catch (err) {
|
|
241
|
+
console.error('[Telegram] Send thinking error:', err.message)
|
|
242
|
+
return
|
|
243
|
+
}
|
|
357
244
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (fullResponse.length % 100 === 0) {
|
|
371
|
-
try {
|
|
372
|
-
await this._bot.editMessageText(`📝 ${escapeMarkdown(fullResponse)}▌`, {
|
|
373
|
-
chat_id: chatId,
|
|
374
|
-
message_id: thinkingMsg.message_id
|
|
375
|
-
})
|
|
376
|
-
} catch (e) {
|
|
377
|
-
// 忽略编辑错误
|
|
378
|
-
}
|
|
379
|
-
}
|
|
245
|
+
try {
|
|
246
|
+
let fullResponse = ''
|
|
247
|
+
for await (const chunk of agent.chatStream(text, { sessionId })) {
|
|
248
|
+
if (chunk.type === 'text' && chunk.text) {
|
|
249
|
+
fullResponse += chunk.text
|
|
250
|
+
if (fullResponse.length % 100 === 0) {
|
|
251
|
+
try {
|
|
252
|
+
await this._bot.editMessageText(`📝 ${escapeMarkdown(fullResponse)}▌`, {
|
|
253
|
+
chat_id: chatId,
|
|
254
|
+
message_id: thinkingMsg.message_id
|
|
255
|
+
})
|
|
256
|
+
} catch (e) { /* ignore */ }
|
|
380
257
|
}
|
|
381
258
|
}
|
|
382
|
-
|
|
383
|
-
// 保存助手回复到历史(使用 SessionPlugin)
|
|
384
|
-
if (this._sessionPlugin) {
|
|
385
|
-
this._sessionPlugin.addMessage(sessionId, { role: 'assistant', content: fullResponse })
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// 发送最终回复(转义 Markdown 特殊字符)
|
|
389
|
-
const safeResponse = escapeMarkdown(fullResponse) || '抱歉,我没有收到有效的回复。'
|
|
390
|
-
await this._bot.editMessageText(safeResponse, {
|
|
391
|
-
chat_id: chatId,
|
|
392
|
-
message_id: thinkingMsg.message_id,
|
|
393
|
-
parse_mode: 'MarkdownV2'
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
} catch (err) {
|
|
397
|
-
console.error('[Telegram] Chat error:', err)
|
|
398
|
-
await this._bot.editMessageText(escapeMarkdown(`❌ 发生错误:${err.message}`), {
|
|
399
|
-
chat_id: chatId,
|
|
400
|
-
message_id: thinkingMsg.message_id
|
|
401
|
-
})
|
|
402
259
|
}
|
|
403
|
-
}
|
|
404
260
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
async _handleEdit(msg) {
|
|
409
|
-
// 支持编辑后重新处理
|
|
410
|
-
}
|
|
261
|
+
if (this._sessionPlugin) {
|
|
262
|
+
this._sessionPlugin.addMessage(sessionId, { role: 'assistant', content: fullResponse })
|
|
263
|
+
}
|
|
411
264
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
265
|
+
const safeResponse = escapeMarkdown(fullResponse) || '抱歉,我没有收到有效的回复。'
|
|
266
|
+
await this._bot.editMessageText(safeResponse, {
|
|
267
|
+
chat_id: chatId,
|
|
268
|
+
message_id: thinkingMsg.message_id,
|
|
269
|
+
parse_mode: 'MarkdownV2'
|
|
270
|
+
})
|
|
271
|
+
} catch (err) {
|
|
272
|
+
console.error('[Telegram] Chat error:', err)
|
|
273
|
+
await this._bot.editMessageText(escapeMarkdown(`❌ 发生错误:${err.message}`), {
|
|
274
|
+
chat_id: chatId,
|
|
275
|
+
message_id: thinkingMsg.message_id
|
|
418
276
|
})
|
|
419
277
|
}
|
|
278
|
+
}
|
|
420
279
|
|
|
421
|
-
|
|
422
|
-
* 权限检查
|
|
423
|
-
*/
|
|
424
|
-
_checkPermission(chatId) {
|
|
425
|
-
if (this.config.allowedChats.length === 0) return true
|
|
426
|
-
return this.config.allowedChats.includes(chatId)
|
|
427
|
-
}
|
|
280
|
+
async _handleEdit(msg) { /* 支持编辑后重新处理 */ }
|
|
428
281
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
async _handlePhoto(msg) {
|
|
433
|
-
const chatId = msg.chat.id.toString()
|
|
434
|
-
const caption = msg.caption?.trim() || ''
|
|
435
|
-
|
|
436
|
-
// 权限检查
|
|
437
|
-
if (!this._checkPermission(chatId)) {
|
|
438
|
-
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
439
|
-
return
|
|
440
|
-
}
|
|
282
|
+
async _handleCallback(query) {
|
|
283
|
+
await this._bot.answerCallbackQuery(query.id, { text: '处理中...' })
|
|
284
|
+
}
|
|
441
285
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
286
|
+
_checkPermission(chatId) {
|
|
287
|
+
return this.config.allowedChats.length === 0 || this.config.allowedChats.includes(chatId)
|
|
288
|
+
}
|
|
445
289
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const savedMsg = `收到图片${caption ? ': ' + caption : ''}\n已保存至: ${filePath}`
|
|
452
|
-
await this._sendMessage(chatId, savedMsg, msg.message_id)
|
|
453
|
-
if(caption){
|
|
454
|
-
const message=`图片:${filePath}, ${caption}`
|
|
455
|
-
await this._processChat(chatId, message, msg.message_id)
|
|
456
|
-
}
|
|
457
|
-
// TODO: 调用 AI 视觉能力分析图片
|
|
458
|
-
// await this._analyzeImage(chatId, filePath, capti on)
|
|
459
|
-
} catch (err) {
|
|
460
|
-
console.error('[Telegram] Failed to save photo:', err.message)
|
|
461
|
-
await this._sendMessage(chatId, '图片保存失败: ' + err.message, msg.message_id)
|
|
462
|
-
}
|
|
290
|
+
async _handlePhoto(msg) {
|
|
291
|
+
const chatId = msg.chat.id.toString()
|
|
292
|
+
if (!this._checkPermission(chatId)) {
|
|
293
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
294
|
+
return
|
|
463
295
|
}
|
|
464
296
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
*/
|
|
468
|
-
async _handleDocument(msg) {
|
|
469
|
-
const chatId = msg.chat.id.toString()
|
|
470
|
-
const fileName = msg.document.file_name || '未命名文件'
|
|
471
|
-
const caption = msg.caption?.trim() || ''
|
|
472
|
-
|
|
473
|
-
// 权限检查
|
|
474
|
-
if (!this._checkPermission(chatId)) {
|
|
475
|
-
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
476
|
-
return
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
console.log(`[Telegram] Document received: ${fileName}, file_id: ${msg.document.file_id}`)
|
|
297
|
+
const caption = msg.caption?.trim() || ''
|
|
298
|
+
const photo = msg.photo[msg.photo.length - 1]
|
|
480
299
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const savedMsg = `收到文件: ${fileName}\n已保存至: ${filePath}${caption ? '\n\n说明: ' + caption : ''}`
|
|
488
|
-
await this._sendMessage(chatId, savedMsg, msg.message_id)
|
|
489
|
-
if(caption){
|
|
490
|
-
const message=`文件:${filePath}, ${caption}`
|
|
491
|
-
await this._processChat(chatId, message, msg.message_id)
|
|
492
|
-
}
|
|
493
|
-
} catch (err) {
|
|
494
|
-
console.error('[Telegram] Failed to save document:', err.message)
|
|
495
|
-
await this._sendMessage(chatId, '文件保存失败: ' + err.message, msg.message_id)
|
|
300
|
+
try {
|
|
301
|
+
const filePath = await this._downloadFile(photo.file_id, '.jpg', 'telegram_images')
|
|
302
|
+
await this._sendMessage(chatId, `收到图片${caption ? ': ' + caption : ''}\n已保存至: ${filePath}`, msg.message_id)
|
|
303
|
+
if (caption) {
|
|
304
|
+
await this._processChat(chatId, `图片:${filePath}, ${caption}`, msg.message_id)
|
|
496
305
|
}
|
|
306
|
+
} catch (err) {
|
|
307
|
+
console.error('[Telegram] Failed to save photo:', err.message)
|
|
308
|
+
await this._sendMessage(chatId, '图片保存失败: ' + err.message, msg.message_id)
|
|
497
309
|
}
|
|
310
|
+
}
|
|
498
311
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
* @returns {Promise<string>} 保存的文件路径
|
|
505
|
-
*/
|
|
506
|
-
async _downloadFile(fileId, ext, subDir) {
|
|
507
|
-
return new Promise((resolve, reject) => {
|
|
508
|
-
const path = require('path')
|
|
509
|
-
const fs = require('fs')
|
|
510
|
-
|
|
511
|
-
// 创建保存目录
|
|
512
|
-
const saveDir = path.join(process.cwd(), '.agent', 'data', subDir)
|
|
513
|
-
if (!fs.existsSync(saveDir)) {
|
|
514
|
-
fs.mkdirSync(saveDir, { recursive: true })
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// 生成文件名
|
|
518
|
-
const fileName = `${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`
|
|
519
|
-
const filePath = path.join(saveDir, fileName)
|
|
520
|
-
|
|
521
|
-
// 下载文件
|
|
522
|
-
this._bot.downloadFile(fileId, saveDir)
|
|
523
|
-
.then((savedPath) => {
|
|
524
|
-
// 重命名为期望的文件名
|
|
525
|
-
const finalPath = path.join(saveDir, fileName)
|
|
526
|
-
if (savedPath !== finalPath) {
|
|
527
|
-
fs.renameSync(savedPath, finalPath)
|
|
528
|
-
}
|
|
529
|
-
resolve(filePath)
|
|
530
|
-
})
|
|
531
|
-
.catch(reject)
|
|
532
|
-
})
|
|
312
|
+
async _handleDocument(msg) {
|
|
313
|
+
const chatId = msg.chat.id.toString()
|
|
314
|
+
if (!this._checkPermission(chatId)) {
|
|
315
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
316
|
+
return
|
|
533
317
|
}
|
|
534
318
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
319
|
+
const fileName = msg.document.file_name || '未命名文件'
|
|
320
|
+
const caption = msg.caption?.trim() || ''
|
|
321
|
+
const ext = fileName.includes('.') ? fileName.split('.').pop() : 'bin'
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
const filePath = await this._downloadFile(msg.document.file_id, ext, 'telegram_documents')
|
|
325
|
+
await this._sendMessage(chatId, `收到文件: ${fileName}\n已保存至: ${filePath}${caption ? '\n\n说明: ' + caption : ''}`, msg.message_id)
|
|
326
|
+
if (caption) {
|
|
327
|
+
await this._processChat(chatId, `文件:${filePath}, ${caption}`, msg.message_id)
|
|
543
328
|
}
|
|
329
|
+
} catch (err) {
|
|
330
|
+
console.error('[Telegram] Failed to save document:', err.message)
|
|
331
|
+
await this._sendMessage(chatId, '文件保存失败: ' + err.message, msg.message_id)
|
|
544
332
|
}
|
|
333
|
+
}
|
|
545
334
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
return this._bot.sendMessage(chatId, text, {
|
|
552
|
-
reply_to_message_id: replyToMessageId,
|
|
553
|
-
})
|
|
554
|
-
}
|
|
335
|
+
async _downloadFile(fileId, ext, subDir) {
|
|
336
|
+
const path = require('path')
|
|
337
|
+
const fs = require('fs')
|
|
338
|
+
const saveDir = path.join(process.cwd(), '.agent', 'data', subDir)
|
|
339
|
+
if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true })
|
|
555
340
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
*/
|
|
559
|
-
getStatus() {
|
|
560
|
-
// 从 SessionPlugin 获取 Telegram 相关会话
|
|
561
|
-
let sessions = []
|
|
562
|
-
if (this._sessionPlugin) {
|
|
563
|
-
const allSessions = this._sessionPlugin.listSessions()
|
|
564
|
-
sessions = allSessions
|
|
565
|
-
.filter(s => s.id.startsWith('telegram_'))
|
|
566
|
-
.map(s => ({
|
|
567
|
-
chatId: s.id.replace('telegram_', ''),
|
|
568
|
-
historyLength: s.messageCount,
|
|
569
|
-
lastActive: s.lastActive
|
|
570
|
-
}))
|
|
571
|
-
}
|
|
341
|
+
const fileName = `${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`
|
|
342
|
+
const filePath = path.join(saveDir, fileName)
|
|
572
343
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
config: {
|
|
578
|
-
groupMode: this.config.groupMode,
|
|
579
|
-
prefix: this.config.prefix,
|
|
580
|
-
allowedChatsCount: this.config.allowedChats.length
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
344
|
+
const savedPath = await this._bot.downloadFile(fileId, saveDir)
|
|
345
|
+
if (savedPath !== filePath) fs.renameSync(savedPath, filePath)
|
|
346
|
+
return filePath
|
|
347
|
+
}
|
|
584
348
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
stopBot() {
|
|
589
|
-
if (this._bot) {
|
|
590
|
-
this._bot.stopPolling()
|
|
591
|
-
this._bot = null
|
|
592
|
-
console.log('[Telegram] Bot stopped')
|
|
593
|
-
}
|
|
349
|
+
_clearSession(chatId) {
|
|
350
|
+
if (this._sessionPlugin) {
|
|
351
|
+
this._sessionPlugin.deleteSession(`telegram_${chatId}`)
|
|
594
352
|
}
|
|
353
|
+
}
|
|
595
354
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
this._initBot()
|
|
601
|
-
}
|
|
602
|
-
}
|
|
355
|
+
async _sendMessage(chatId, text, replyToMessageId = null) {
|
|
356
|
+
if (!this._bot) return
|
|
357
|
+
return this._bot.sendMessage(chatId, text, { reply_to_message_id: replyToMessageId })
|
|
358
|
+
}
|
|
603
359
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
360
|
+
getStatus() {
|
|
361
|
+
let sessions = []
|
|
362
|
+
if (this._sessionPlugin) {
|
|
363
|
+
sessions = this._sessionPlugin.listSessions()
|
|
364
|
+
.filter(s => s.id.startsWith('telegram_'))
|
|
365
|
+
.map(s => ({
|
|
366
|
+
chatId: s.id.replace('telegram_', ''),
|
|
367
|
+
historyLength: s.messageCount,
|
|
368
|
+
lastActive: s.lastActive
|
|
369
|
+
}))
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
connected: !!this._bot,
|
|
373
|
+
sessionCount: sessions.length,
|
|
374
|
+
sessions,
|
|
375
|
+
config: {
|
|
376
|
+
groupMode: this.config.groupMode,
|
|
377
|
+
prefix: this.config.prefix,
|
|
378
|
+
allowedChatsCount: this.config.allowedChats.length
|
|
610
379
|
}
|
|
611
380
|
}
|
|
381
|
+
}
|
|
612
382
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
383
|
+
stopBot() {
|
|
384
|
+
if (this._bot) {
|
|
385
|
+
this._bot.stopPolling()
|
|
386
|
+
this._bot = null
|
|
387
|
+
console.log('[Telegram] Bot stopped')
|
|
388
|
+
}
|
|
389
|
+
}
|
|
619
390
|
|
|
391
|
+
reload(framework) {
|
|
392
|
+
this._framework = framework
|
|
393
|
+
if (this.config.botToken) {
|
|
620
394
|
this.stopBot()
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
395
|
+
this._initBot()
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
uninstall() {
|
|
400
|
+
for (const agent of this._sessionAgents.values()) {
|
|
401
|
+
agent.destroy()
|
|
402
|
+
}
|
|
403
|
+
this._sessionAgents.clear()
|
|
404
|
+
this.stopBot()
|
|
405
|
+
if (this._sessionPlugin && this._sessionDeleteHandler) {
|
|
406
|
+
this._sessionPlugin.off('session:deleted', this._sessionDeleteHandler)
|
|
407
|
+
this._sessionDeleteHandler = null
|
|
627
408
|
}
|
|
409
|
+
this._sessionPlugin = null
|
|
410
|
+
this._framework = null
|
|
628
411
|
}
|
|
629
412
|
}
|
|
413
|
+
|
|
414
|
+
module.exports = { TelegramPlugin }
|