@wenbin_wb/dsh-bridge 2.3.3 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +85 -8
- package/README.md +85 -8
- package/client/client.js +832 -57
- package/client/index.js +718 -61
- package/docs/telegram-usage.md +89 -0
- package/lib/auth/login-template.js +378 -0
- package/lib/auth/manager.js +425 -0
- package/lib/bridge-rpc-constants.js +6 -0
- package/lib/bridge-rpc.js +34 -1
- package/lib/feishu/gateway.js +166 -7
- package/lib/feishu/node.js +55 -10
- package/lib/index.js +258 -14
- package/lib/platform/base.js +13 -0
- package/lib/platform/conversation-bridge.js +32 -4
- package/lib/qq/gateway.js +94 -14
- package/lib/qq/node.js +49 -9
- package/lib/telegram/gateway.js +619 -0
- package/lib/telegram/index.js +210 -0
- package/lib/telegram/node.js +342 -0
- package/lib/wechat/gateway.js +54 -0
- package/lib/wechat/node.js +1 -0
- package/package.json +4 -3
package/lib/qq/node.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
// - 出站:sendText 走 gateway.sendText;长文本用 Markdown 分块
|
|
7
7
|
// - 群聊:@提及消息仅在命中机器人才处理(GROUP_AT_MESSAGE_CREATE 已保证)
|
|
8
8
|
|
|
9
|
+
import fs from 'node:fs'
|
|
10
|
+
import path from 'node:path'
|
|
9
11
|
import { ConversationBridge, conversationBridgeHelpers } from '../platform/conversation-bridge.js'
|
|
10
12
|
import { gatewayConstants } from './gateway.js'
|
|
11
13
|
|
|
@@ -114,6 +116,7 @@ function makePlatform(gateway) {
|
|
|
114
116
|
get capabilities() { return gateway.capabilities },
|
|
115
117
|
// sendText / sendTyping 由 QqConversationNode 覆盖,这里仅提供兜底
|
|
116
118
|
sendText: (peer, text) => gateway.sendText(peer, text, {}),
|
|
119
|
+
sendMediaFile: (peer, filePath, opts = {}) => gateway.sendMediaFile(peer, filePath, opts),
|
|
117
120
|
sendTyping: () => Promise.resolve({ ok: true }),
|
|
118
121
|
sendKeyboard: (peer, content, keyboard) => gateway.sendKeyboard(peer, content, keyboard, {}),
|
|
119
122
|
}
|
|
@@ -144,12 +147,8 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
144
147
|
this._replyMsgId = null // 被动回复用的用户消息 ID(事件 d.id)
|
|
145
148
|
|
|
146
149
|
// 订阅网关入站事件
|
|
147
|
-
this.ctx.on('qq/message', (event) =>
|
|
148
|
-
|
|
149
|
-
})
|
|
150
|
-
this.ctx.on('qq/interaction', (event) => {
|
|
151
|
-
void this._handleInteraction(event)
|
|
152
|
-
})
|
|
150
|
+
this.ctx.on('qq/message', (event) => this._handleInbound(event))
|
|
151
|
+
this.ctx.on('qq/interaction', (event) => this._handleInteraction(event))
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
// ---- 出站:覆盖 sendText / sendTyping,正确处理 scope 与被动回复 ----
|
|
@@ -373,7 +372,16 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
373
372
|
}
|
|
374
373
|
|
|
375
374
|
const text = String(event.text ?? '').trim()
|
|
376
|
-
|
|
375
|
+
const attachments = event.attachments || []
|
|
376
|
+
|
|
377
|
+
let mediaFiles = []
|
|
378
|
+
if (attachments.length > 0) {
|
|
379
|
+
this.logger?.info?.(`[dsh-bridge qq] processing ${attachments.length} attachment(s) from ${sender}...`)
|
|
380
|
+
mediaFiles = await this._processAttachments(attachments, this.config.cwd)
|
|
381
|
+
this.logger?.info?.(`[dsh-bridge qq] downloaded ${mediaFiles.length} file(s)`)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!text && mediaFiles.length === 0) {
|
|
377
385
|
this.logger?.info?.(`[dsh-bridge qq] ignore empty message from ${sender}`)
|
|
378
386
|
return
|
|
379
387
|
}
|
|
@@ -384,10 +392,16 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
384
392
|
if (event.msgSeq !== undefined) this._msgSeq = event.msgSeq
|
|
385
393
|
}
|
|
386
394
|
|
|
395
|
+
let fullText = text
|
|
396
|
+
if (mediaFiles.length > 0) {
|
|
397
|
+
const mediaDesc = mediaFiles.map((f) => `[文件: ${f.path}]`).join('\n')
|
|
398
|
+
fullText = fullText ? `${text}\n\n${mediaDesc}` : mediaDesc
|
|
399
|
+
}
|
|
400
|
+
|
|
387
401
|
// 检测消息引用(文本交互):如果用户回复了机器人的消息,且当前没有活动会话或消息以 /new 开头,
|
|
388
402
|
// 则自动创建新会话并发送消息
|
|
389
403
|
const messageReference = event.messageReference
|
|
390
|
-
if (messageReference && !this.activeSessionId && !
|
|
404
|
+
if (messageReference && !this.activeSessionId && !fullText.startsWith('/')) {
|
|
391
405
|
this.logger?.info?.(`[dsh-bridge qq] detected message reference from ${sender}, auto-starting conversation`)
|
|
392
406
|
// 先创建新会话,再处理消息
|
|
393
407
|
await this.handleInbound({ senderId: authId, text: '/new', isGroup })
|
|
@@ -396,7 +410,33 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
396
410
|
}
|
|
397
411
|
|
|
398
412
|
// 交给平台无关核心:白名单/群消息/命令路由/agent 分发
|
|
399
|
-
await this.handleInbound({ senderId: authId, text, isGroup })
|
|
413
|
+
await this.handleInbound({ senderId: authId, text: fullText, isGroup })
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** 下载附件(图片/文件)并保存到本地工作目录 */
|
|
417
|
+
async _processAttachments(attachments, sessionCwd) {
|
|
418
|
+
if (!Array.isArray(attachments) || attachments.length === 0) return []
|
|
419
|
+
const mediaDir = path.join(sessionCwd || this.config.cwd || process.cwd(), '.qq-media')
|
|
420
|
+
try { await fs.promises.mkdir(mediaDir, { recursive: true }) } catch {}
|
|
421
|
+
|
|
422
|
+
const downloaded = []
|
|
423
|
+
for (const att of attachments) {
|
|
424
|
+
if (!att?.url) continue
|
|
425
|
+
try {
|
|
426
|
+
const rawUrl = att.url.startsWith('http') ? att.url : `https://${att.url}`
|
|
427
|
+
const res = await fetch(rawUrl, { signal: AbortSignal.timeout(15000) })
|
|
428
|
+
if (!res.ok) continue
|
|
429
|
+
const buf = Buffer.from(await res.arrayBuffer())
|
|
430
|
+
const ext = att.filename ? path.extname(att.filename) : (att.content_type?.includes('image') ? '.png' : '.bin')
|
|
431
|
+
const safeName = att.filename ? path.basename(att.filename) : `qq_${Date.now()}_${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
432
|
+
const filePath = path.join(mediaDir, safeName)
|
|
433
|
+
await fs.promises.writeFile(filePath, buf)
|
|
434
|
+
downloaded.push({ filename: safeName, path: filePath, size: buf.length })
|
|
435
|
+
} catch (err) {
|
|
436
|
+
this.logger?.warn?.('[dsh-bridge qq] download attachment error: %s', err?.message ?? err)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return downloaded
|
|
400
440
|
}
|
|
401
441
|
|
|
402
442
|
// ---- 互动事件 ----
|