foliko 1.0.40 → 1.0.43
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/commands/chat.js +5 -5
- 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 +298 -537
- package/plugins/scheduler-plugin.js +1 -1
- package/plugins/subagent-plugin.js +4 -4
- package/plugins/telegram-plugin.js +314 -522
- package/plugins/think-plugin.js +2 -2
- package/plugins/web-plugin.js +542 -0
- package/plugins/weixin-plugin.js +327 -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,407 @@ 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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
+
**命令执行规范:**
|
|
32
|
+
- 执行 npx skills add / npx skills remove / npx skills list 命令时,必须自动添加参数:-a openclaw -y
|
|
33
|
+
- 禁止添加 -g 参数
|
|
34
|
+
- 例如:npx skills add xxx 应该执行为 npx skills add xxx -a openclaw -y`
|
|
35
|
+
|
|
36
|
+
this.config = {
|
|
37
|
+
botToken: config.botToken || process.env.TELEGRAM_BOT_TOKEN,
|
|
38
|
+
allowedChats: config.allowedChats || [],
|
|
39
|
+
groupMode: config.groupMode || false,
|
|
40
|
+
prefix: config.prefix || '/'
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
this._framework = null
|
|
44
|
+
this._bot = null
|
|
45
|
+
this._sessionPlugin = null
|
|
46
|
+
this._sessionDeleteHandler = null
|
|
47
|
+
this._sessionAgents = new Map()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
install(framework) {
|
|
51
|
+
this._framework = framework
|
|
52
|
+
return this
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
start(framework) {
|
|
56
|
+
if (!this.config.botToken) {
|
|
57
|
+
console.warn('[Telegram] No bot token. Set TELEGRAM_BOT_TOKEN env var.')
|
|
50
58
|
return this
|
|
51
59
|
}
|
|
52
60
|
|
|
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')
|
|
61
|
+
this._framework = framework
|
|
62
|
+
this._sessionPlugin = framework.pluginManager.get('session')
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (sessionId.startsWith('telegram_')) {
|
|
67
|
-
console.log(`[Telegram] Session deleted: ${sessionId}`)
|
|
68
|
-
}
|
|
64
|
+
if (this._sessionPlugin) {
|
|
65
|
+
this._sessionDeleteHandler = (sessionId) => {
|
|
66
|
+
if (sessionId.startsWith('telegram_')) {
|
|
67
|
+
console.log(`[Telegram] Session deleted: ${sessionId}`)
|
|
69
68
|
}
|
|
70
|
-
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
71
69
|
}
|
|
72
|
-
|
|
73
|
-
this._initBot()
|
|
74
|
-
return this
|
|
70
|
+
this._sessionPlugin.on('session:deleted', this._sessionDeleteHandler)
|
|
75
71
|
}
|
|
76
72
|
|
|
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
|
-
})
|
|
73
|
+
this._initBot()
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
|
|
77
|
+
_initBot() {
|
|
78
|
+
try {
|
|
79
|
+
const TelegramBot = require('node-telegram-bot-api')
|
|
80
|
+
this._bot = new TelegramBot(this.config.botToken, { polling: true })
|
|
104
81
|
|
|
105
|
-
|
|
106
|
-
this._bot.on('edit_message', (msg) => this._handleEdit(msg))
|
|
82
|
+
console.log('[Telegram] Bot started successfully')
|
|
107
83
|
|
|
108
|
-
|
|
109
|
-
|
|
84
|
+
this._bot.setMyCommands([
|
|
85
|
+
{ command: 'start', description: '显示帮助信息' },
|
|
86
|
+
{ command: 'clear', description: '清除对话历史' },
|
|
87
|
+
{ command: 'history', description: '查看对话状态' }
|
|
88
|
+
]).catch((err) => {
|
|
89
|
+
console.error('[Telegram] Failed to register commands:', err.message)
|
|
90
|
+
})
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
92
|
+
this._bot.on('message', (msg) => this._handleMessage(msg))
|
|
93
|
+
this._bot.on('edit_message', (msg) => this._handleEdit(msg))
|
|
94
|
+
this._bot.on('callback_query', (query) => this._handleCallback(query))
|
|
95
|
+
this._bot.on('polling_error', (err) => console.error('[Telegram] Polling error:', err.message))
|
|
96
|
+
this._bot.on('error', (err) => console.error('[Telegram] Bot error:', err.message))
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
if (this._framework) {
|
|
99
|
+
this._framework.on('agent:created', (agent) => {
|
|
100
|
+
console.log('[Telegram] New agent created:', agent.name)
|
|
101
|
+
})
|
|
102
|
+
this._framework.on('scheduler:reminder', async (data) => {
|
|
103
|
+
await this._handleScheduledReminder(data)
|
|
118
104
|
})
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error('[Telegram] Failed to initialize bot:', err.message)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
119
110
|
|
|
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
|
-
}
|
|
111
|
+
async _handleScheduledReminder(data) {
|
|
112
|
+
const { taskName, message, sessionId } = data
|
|
113
|
+
const reminderText = `🔔 [${taskName}]\n\n${message}`
|
|
132
114
|
|
|
115
|
+
if (sessionId && sessionId.startsWith('telegram_')) {
|
|
116
|
+
const chatId = sessionId.replace('telegram_', '')
|
|
117
|
+
try {
|
|
118
|
+
await this._bot.sendMessage(chatId, reminderText)
|
|
119
|
+
console.log(`[Telegram] Reminder sent to chat ${chatId}`)
|
|
133
120
|
} catch (err) {
|
|
134
|
-
console.error(
|
|
121
|
+
console.error(`[Telegram] Failed to send reminder:`, err.message)
|
|
135
122
|
}
|
|
123
|
+
return
|
|
136
124
|
}
|
|
137
125
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const { taskName, message, sessionId } = data
|
|
143
|
-
|
|
144
|
-
// 构建提醒消息
|
|
145
|
-
const reminderText = `🔔 [${taskName}]\n\n${message}`
|
|
126
|
+
if (this._sessionPlugin) {
|
|
127
|
+
const sessions = this._sessionPlugin.listSessions()
|
|
128
|
+
.filter(s => s.id.startsWith('telegram_'))
|
|
129
|
+
.sort((a, b) => new Date(b.lastActive).getTime() - new Date(a.lastActive).getTime())
|
|
146
130
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const chatId = sessionId.replace('telegram_', '')
|
|
131
|
+
if (sessions.length > 0) {
|
|
132
|
+
const chatId = sessions[0].id.replace('telegram_', '')
|
|
150
133
|
try {
|
|
151
134
|
await this._bot.sendMessage(chatId, reminderText)
|
|
152
|
-
console.log(`[Telegram] Reminder sent to chat ${chatId}`)
|
|
135
|
+
console.log(`[Telegram] Reminder sent to recent chat ${chatId}`)
|
|
153
136
|
} catch (err) {
|
|
154
137
|
console.error(`[Telegram] Failed to send reminder:`, err.message)
|
|
155
138
|
}
|
|
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
139
|
}
|
|
180
140
|
}
|
|
141
|
+
}
|
|
181
142
|
|
|
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
|
|
143
|
+
_getMainAgent() {
|
|
144
|
+
if (this._framework._mainAgent) return this._framework._mainAgent
|
|
145
|
+
const agents = this._framework._agents || []
|
|
146
|
+
return agents.length > 0 ? agents[agents.length - 1] : null
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_getSessionAgent(chatId) {
|
|
150
|
+
if (this._sessionAgents.has(chatId)) {
|
|
151
|
+
return { agent: this._sessionAgents.get(chatId), sessionId: `telegram_${chatId}` }
|
|
198
152
|
}
|
|
199
153
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
}
|
|
154
|
+
const agent = this._framework.createSessionAgent(`telegram_${chatId}`, {
|
|
155
|
+
systemPrompt: this.systemPrompt,
|
|
156
|
+
sharedPrompt: `工作目录: {{WORK_DIR}}`,
|
|
157
|
+
metadata: { WORK_DIR: process.cwd() }
|
|
158
|
+
})
|
|
159
|
+
this._sessionAgents.set(chatId, agent)
|
|
214
160
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
161
|
+
if (this._sessionPlugin) {
|
|
162
|
+
this._sessionPlugin.getOrCreateSession(`telegram_${chatId}`, {
|
|
163
|
+
metadata: { platform: 'telegram', chatId }
|
|
218
164
|
})
|
|
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
165
|
}
|
|
233
166
|
|
|
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
|
|
167
|
+
return { agent, sessionId: `telegram_${chatId}` }
|
|
168
|
+
}
|
|
255
169
|
|
|
256
|
-
|
|
170
|
+
async _handleMessage(msg) {
|
|
171
|
+
if (!msg || !msg.chat) return
|
|
172
|
+
const chatId = msg.chat.id.toString()
|
|
257
173
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return
|
|
262
|
-
}
|
|
174
|
+
if (msg.photo) { await this._handlePhoto(msg); return }
|
|
175
|
+
if (msg.document) { await this._handleDocument(msg); return }
|
|
176
|
+
if (!msg.text) return
|
|
263
177
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
178
|
+
const text = msg.text.trim()
|
|
179
|
+
if (text.startsWith(this.config.prefix)) {
|
|
180
|
+
await this._handleCommand(msg)
|
|
181
|
+
return
|
|
182
|
+
}
|
|
268
183
|
|
|
269
|
-
|
|
270
|
-
if (!this.
|
|
271
|
-
|
|
272
|
-
return
|
|
273
|
-
}
|
|
184
|
+
if (msg.chat.type === 'group' || msg.chat.type === 'supergroup') {
|
|
185
|
+
if (!this.config.groupMode) return
|
|
186
|
+
}
|
|
274
187
|
|
|
275
|
-
|
|
276
|
-
await this.
|
|
188
|
+
if (!this._checkPermission(chatId)) {
|
|
189
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
190
|
+
return
|
|
277
191
|
}
|
|
278
192
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
193
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async _handleCommand(msg) {
|
|
197
|
+
const chatId = msg.chat.id.toString()
|
|
198
|
+
const text = msg.text.trim()
|
|
199
|
+
const parts = text.split(' ')
|
|
200
|
+
const command = parts[0].substring(1)
|
|
201
|
+
const args = parts.slice(1).join(' ')
|
|
202
|
+
|
|
203
|
+
switch (command.toLowerCase()) {
|
|
204
|
+
case 'start':
|
|
205
|
+
await this._sendMessage(chatId,
|
|
206
|
+
'👋 欢迎使用 AI 助手!\n\n直接发送消息即可与我对话。\n\n可用命令:\n/start - 显示帮助\n/clear - 清除对话历史\n/history - 查看历史消息数',
|
|
207
|
+
msg.message_id)
|
|
208
|
+
break
|
|
209
|
+
case 'clear':
|
|
210
|
+
this._clearSession(chatId)
|
|
211
|
+
await this._sendMessage(chatId, '✅ 对话历史已清除', msg.message_id)
|
|
212
|
+
break
|
|
213
|
+
case 'history':
|
|
214
|
+
if (this._sessionPlugin) {
|
|
215
|
+
const session = this._sessionPlugin.getSession(`telegram_${chatId}`)
|
|
216
|
+
const sessions = this._sessionPlugin.listSessions().filter(s => s.id.startsWith('telegram_'))
|
|
291
217
|
await this._sendMessage(chatId,
|
|
292
|
-
|
|
293
|
-
'直接发送消息即可与我对话,我会尽力帮助您。\n\n' +
|
|
294
|
-
'可用命令:\n' +
|
|
295
|
-
'/start - 显示帮助\n' +
|
|
296
|
-
'/clear - 清除对话历史\n' +
|
|
297
|
-
'/history - 查看历史消息数',
|
|
218
|
+
`📊 当前状态\n\n会话数:${sessions.length}\n历史消息:${session?.messages?.length || 0}\n最后活跃:${session?.lastActive?.toLocaleString() || '无'}`,
|
|
298
219
|
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
|
-
}
|
|
220
|
+
} else {
|
|
221
|
+
await this._sendMessage(chatId, '❌ 会话服务不可用', msg.message_id)
|
|
222
|
+
}
|
|
223
|
+
break
|
|
224
|
+
default:
|
|
225
|
+
await this._processChat(chatId, text, msg.message_id)
|
|
326
226
|
}
|
|
227
|
+
}
|
|
327
228
|
|
|
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
|
-
}
|
|
229
|
+
async _processChat(chatId, text, replyToMessageId) {
|
|
230
|
+
const sessionInfo = this._getSessionAgent(chatId)
|
|
231
|
+
if (!sessionInfo) {
|
|
232
|
+
await this._sendMessage(chatId, '❌ AI 服务未初始化', replyToMessageId)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
339
235
|
|
|
340
|
-
|
|
236
|
+
const { agent, sessionId } = sessionInfo
|
|
341
237
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
238
|
+
if (this._sessionPlugin) {
|
|
239
|
+
this._sessionPlugin.addMessage(sessionId, { role: 'user', content: text })
|
|
240
|
+
}
|
|
346
241
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
242
|
+
let thinkingMsg
|
|
243
|
+
try {
|
|
244
|
+
thinkingMsg = await this._bot.sendMessage(chatId, '🤔 思考中...', {
|
|
245
|
+
reply_to_message_id: replyToMessageId
|
|
246
|
+
})
|
|
247
|
+
} catch (err) {
|
|
248
|
+
console.error('[Telegram] Send thinking error:', err.message)
|
|
249
|
+
return
|
|
250
|
+
}
|
|
357
251
|
|
|
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
|
-
}
|
|
252
|
+
try {
|
|
253
|
+
let fullResponse = ''
|
|
254
|
+
for await (const chunk of agent.chatStream(text, { sessionId })) {
|
|
255
|
+
if (chunk.type === 'text' && chunk.text) {
|
|
256
|
+
fullResponse += chunk.text
|
|
257
|
+
if (fullResponse.length % 100 === 0) {
|
|
258
|
+
try {
|
|
259
|
+
await this._bot.editMessageText(`📝 ${escapeMarkdown(fullResponse)}▌`, {
|
|
260
|
+
chat_id: chatId,
|
|
261
|
+
message_id: thinkingMsg.message_id
|
|
262
|
+
})
|
|
263
|
+
} catch (e) { /* ignore */ }
|
|
380
264
|
}
|
|
381
265
|
}
|
|
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
266
|
}
|
|
403
|
-
}
|
|
404
267
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
async _handleEdit(msg) {
|
|
409
|
-
// 支持编辑后重新处理
|
|
410
|
-
}
|
|
268
|
+
if (this._sessionPlugin) {
|
|
269
|
+
this._sessionPlugin.addMessage(sessionId, { role: 'assistant', content: fullResponse })
|
|
270
|
+
}
|
|
411
271
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
272
|
+
const safeResponse = escapeMarkdown(fullResponse) || '抱歉,我没有收到有效的回复。'
|
|
273
|
+
await this._bot.editMessageText(safeResponse, {
|
|
274
|
+
chat_id: chatId,
|
|
275
|
+
message_id: thinkingMsg.message_id,
|
|
276
|
+
parse_mode: 'MarkdownV2'
|
|
277
|
+
})
|
|
278
|
+
} catch (err) {
|
|
279
|
+
console.error('[Telegram] Chat error:', err)
|
|
280
|
+
await this._bot.editMessageText(escapeMarkdown(`❌ 发生错误:${err.message}`), {
|
|
281
|
+
chat_id: chatId,
|
|
282
|
+
message_id: thinkingMsg.message_id
|
|
418
283
|
})
|
|
419
284
|
}
|
|
285
|
+
}
|
|
420
286
|
|
|
421
|
-
|
|
422
|
-
* 权限检查
|
|
423
|
-
*/
|
|
424
|
-
_checkPermission(chatId) {
|
|
425
|
-
if (this.config.allowedChats.length === 0) return true
|
|
426
|
-
return this.config.allowedChats.includes(chatId)
|
|
427
|
-
}
|
|
287
|
+
async _handleEdit(msg) { /* 支持编辑后重新处理 */ }
|
|
428
288
|
|
|
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
|
-
}
|
|
289
|
+
async _handleCallback(query) {
|
|
290
|
+
await this._bot.answerCallbackQuery(query.id, { text: '处理中...' })
|
|
291
|
+
}
|
|
441
292
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
293
|
+
_checkPermission(chatId) {
|
|
294
|
+
return this.config.allowedChats.length === 0 || this.config.allowedChats.includes(chatId)
|
|
295
|
+
}
|
|
445
296
|
|
|
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
|
-
}
|
|
297
|
+
async _handlePhoto(msg) {
|
|
298
|
+
const chatId = msg.chat.id.toString()
|
|
299
|
+
if (!this._checkPermission(chatId)) {
|
|
300
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
301
|
+
return
|
|
463
302
|
}
|
|
464
303
|
|
|
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}`)
|
|
304
|
+
const caption = msg.caption?.trim() || ''
|
|
305
|
+
const photo = msg.photo[msg.photo.length - 1]
|
|
480
306
|
|
|
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)
|
|
307
|
+
try {
|
|
308
|
+
const filePath = await this._downloadFile(photo.file_id, '.jpg', 'telegram_images')
|
|
309
|
+
await this._sendMessage(chatId, `收到图片${caption ? ': ' + caption : ''}\n已保存至: ${filePath}`, msg.message_id)
|
|
310
|
+
if (caption) {
|
|
311
|
+
await this._processChat(chatId, `图片:${filePath}, ${caption}`, msg.message_id)
|
|
496
312
|
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
console.error('[Telegram] Failed to save photo:', err.message)
|
|
315
|
+
await this._sendMessage(chatId, '图片保存失败: ' + err.message, msg.message_id)
|
|
497
316
|
}
|
|
317
|
+
}
|
|
498
318
|
|
|
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
|
-
})
|
|
319
|
+
async _handleDocument(msg) {
|
|
320
|
+
const chatId = msg.chat.id.toString()
|
|
321
|
+
if (!this._checkPermission(chatId)) {
|
|
322
|
+
await this._sendMessage(chatId, '抱歉,您没有权限使用此 Bot。', msg.message_id)
|
|
323
|
+
return
|
|
533
324
|
}
|
|
534
325
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
326
|
+
const fileName = msg.document.file_name || '未命名文件'
|
|
327
|
+
const caption = msg.caption?.trim() || ''
|
|
328
|
+
const ext = fileName.includes('.') ? fileName.split('.').pop() : 'bin'
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
const filePath = await this._downloadFile(msg.document.file_id, ext, 'telegram_documents')
|
|
332
|
+
await this._sendMessage(chatId, `收到文件: ${fileName}\n已保存至: ${filePath}${caption ? '\n\n说明: ' + caption : ''}`, msg.message_id)
|
|
333
|
+
if (caption) {
|
|
334
|
+
await this._processChat(chatId, `文件:${filePath}, ${caption}`, msg.message_id)
|
|
543
335
|
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.error('[Telegram] Failed to save document:', err.message)
|
|
338
|
+
await this._sendMessage(chatId, '文件保存失败: ' + err.message, msg.message_id)
|
|
544
339
|
}
|
|
340
|
+
}
|
|
545
341
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
return this._bot.sendMessage(chatId, text, {
|
|
552
|
-
reply_to_message_id: replyToMessageId,
|
|
553
|
-
})
|
|
554
|
-
}
|
|
342
|
+
async _downloadFile(fileId, ext, subDir) {
|
|
343
|
+
const path = require('path')
|
|
344
|
+
const fs = require('fs')
|
|
345
|
+
const saveDir = path.join(process.cwd(), '.agent', 'data', subDir)
|
|
346
|
+
if (!fs.existsSync(saveDir)) fs.mkdirSync(saveDir, { recursive: true })
|
|
555
347
|
|
|
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
|
-
}
|
|
348
|
+
const fileName = `${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`
|
|
349
|
+
const filePath = path.join(saveDir, fileName)
|
|
572
350
|
|
|
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
|
-
}
|
|
351
|
+
const savedPath = await this._bot.downloadFile(fileId, saveDir)
|
|
352
|
+
if (savedPath !== filePath) fs.renameSync(savedPath, filePath)
|
|
353
|
+
return filePath
|
|
354
|
+
}
|
|
584
355
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
stopBot() {
|
|
589
|
-
if (this._bot) {
|
|
590
|
-
this._bot.stopPolling()
|
|
591
|
-
this._bot = null
|
|
592
|
-
console.log('[Telegram] Bot stopped')
|
|
593
|
-
}
|
|
356
|
+
_clearSession(chatId) {
|
|
357
|
+
if (this._sessionPlugin) {
|
|
358
|
+
this._sessionPlugin.deleteSession(`telegram_${chatId}`)
|
|
594
359
|
}
|
|
360
|
+
}
|
|
595
361
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
this._initBot()
|
|
601
|
-
}
|
|
602
|
-
}
|
|
362
|
+
async _sendMessage(chatId, text, replyToMessageId = null) {
|
|
363
|
+
if (!this._bot) return
|
|
364
|
+
return this._bot.sendMessage(chatId, text, { reply_to_message_id: replyToMessageId })
|
|
365
|
+
}
|
|
603
366
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
367
|
+
getStatus() {
|
|
368
|
+
let sessions = []
|
|
369
|
+
if (this._sessionPlugin) {
|
|
370
|
+
sessions = this._sessionPlugin.listSessions()
|
|
371
|
+
.filter(s => s.id.startsWith('telegram_'))
|
|
372
|
+
.map(s => ({
|
|
373
|
+
chatId: s.id.replace('telegram_', ''),
|
|
374
|
+
historyLength: s.messageCount,
|
|
375
|
+
lastActive: s.lastActive
|
|
376
|
+
}))
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
connected: !!this._bot,
|
|
380
|
+
sessionCount: sessions.length,
|
|
381
|
+
sessions,
|
|
382
|
+
config: {
|
|
383
|
+
groupMode: this.config.groupMode,
|
|
384
|
+
prefix: this.config.prefix,
|
|
385
|
+
allowedChatsCount: this.config.allowedChats.length
|
|
610
386
|
}
|
|
611
387
|
}
|
|
388
|
+
}
|
|
612
389
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
390
|
+
stopBot() {
|
|
391
|
+
if (this._bot) {
|
|
392
|
+
this._bot.stopPolling()
|
|
393
|
+
this._bot = null
|
|
394
|
+
console.log('[Telegram] Bot stopped')
|
|
395
|
+
}
|
|
396
|
+
}
|
|
619
397
|
|
|
398
|
+
reload(framework) {
|
|
399
|
+
this._framework = framework
|
|
400
|
+
if (this.config.botToken) {
|
|
620
401
|
this.stopBot()
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
402
|
+
this._initBot()
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
uninstall() {
|
|
407
|
+
for (const agent of this._sessionAgents.values()) {
|
|
408
|
+
agent.destroy()
|
|
409
|
+
}
|
|
410
|
+
this._sessionAgents.clear()
|
|
411
|
+
this.stopBot()
|
|
412
|
+
if (this._sessionPlugin && this._sessionDeleteHandler) {
|
|
413
|
+
this._sessionPlugin.off('session:deleted', this._sessionDeleteHandler)
|
|
414
|
+
this._sessionDeleteHandler = null
|
|
627
415
|
}
|
|
416
|
+
this._sessionPlugin = null
|
|
417
|
+
this._framework = null
|
|
628
418
|
}
|
|
629
419
|
}
|
|
420
|
+
|
|
421
|
+
module.exports = { TelegramPlugin }
|