@wenbin_wb/dsh-bridge 2.3.0 → 2.3.2
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 +37 -0
- package/README.md +37 -0
- package/client/client.js +55 -0
- package/client/index.js +45 -0
- package/lib/feishu/gateway.js +17 -1
- package/lib/feishu/index.js +16 -0
- package/lib/platform/conversation-bridge.js +2 -1
- package/lib/qq/node.js +36 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -246,6 +246,43 @@ Integrates Tencent's official QQ Bot platform. Supports private chat, group chat
|
|
|
246
246
|
|
|
247
247
|
---
|
|
248
248
|
|
|
249
|
+
### Feishu / Lark Bot (Official WebSocket)
|
|
250
|
+
|
|
251
|
+
Connect your enterprise self-built app via Feishu's official WebSocket protocol. Supports private chat and group mentions without public IP, domain name, or webhook configuration.
|
|
252
|
+
|
|
253
|
+
**Key Highlights**
|
|
254
|
+
|
|
255
|
+
- ⚡ **100% No Public IP Required**: Direct duplex WebSocket connection to Feishu Open Platform
|
|
256
|
+
- 📜 **Card JSON 2.0 Streaming**: In-place single-card incremental streaming updates, eliminating message bubble fragmentation
|
|
257
|
+
- 🛡️ **Card 2.0 Interactive Approvals**: Native orange approval card with `[✓ Approve]` / `[✕ Reject]` action buttons for 1-click execution
|
|
258
|
+
- 📝 **Full Markdown Rendering**: Native support for headings, tables, syntax highlighting, blockquotes, and lists
|
|
259
|
+
- 🔄 **Workspace & Session Management**: Table-formatted `/sessions`, `/use N` switching, and `/workspaces` listing
|
|
260
|
+
|
|
261
|
+
**Setup Steps**
|
|
262
|
+
|
|
263
|
+
1. Go to [Feishu Open Platform](https://open.feishu.cn/app) to create a self-built app, enable "Bot" capability, and publish a version ([Detailed Guide](docs/feishu-usage.md))
|
|
264
|
+
2. Under "Events & Callbacks", select "Use WebSocket to receive events", and add `im.message.receive_v1` & `card.action.trigger`
|
|
265
|
+
3. Open DSH Settings → "Remote Access" → "IM Bots" → select "Feishu"
|
|
266
|
+
4. Fill in App ID and App Secret, then click "Save & Connect"
|
|
267
|
+
|
|
268
|
+
**Feishu Bot Commands** (Full guide in [Feishu Bot Usage Guide](docs/feishu-usage.md))
|
|
269
|
+
|
|
270
|
+
| Command | Description |
|
|
271
|
+
|---------|-------------|
|
|
272
|
+
| *(plain text)* | Send to current active agent |
|
|
273
|
+
| `/new <prompt>` | Create and start a new session in current workspace |
|
|
274
|
+
| `/new <prompt> @N` | Create a new session in workspace N |
|
|
275
|
+
| `/sessions` (or `/list`) | List all sessions in a structured Markdown table |
|
|
276
|
+
| `/use N` (or `/resume N`) | Switch to/resume session N |
|
|
277
|
+
| `/workspaces` | List all available workspaces |
|
|
278
|
+
| `/end` | End current session |
|
|
279
|
+
| `/stop` | Stop currently executing task |
|
|
280
|
+
| `/status` | View agent status dashboard |
|
|
281
|
+
| `/yes` `/no` (or `1`/`2`) | Respond to permission approval requests (or click card buttons) |
|
|
282
|
+
| `/help` | Display full command help |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
249
286
|
## Optional Configuration
|
|
250
287
|
|
|
251
288
|
The plugin is ready to use out of the box. To customize the proxy port, add to `cordis.yml`:
|
package/README.md
CHANGED
|
@@ -246,6 +246,43 @@ dsh plugin --profile web add @wenbin_wb/dsh-bridge@latest
|
|
|
246
246
|
|
|
247
247
|
---
|
|
248
248
|
|
|
249
|
+
### 飞书 Bot(官方 WebSocket 长连接)
|
|
250
|
+
|
|
251
|
+
接入飞书开放平台企业自建应用,支持单聊与群聊(群聊需 @机器人)。走飞书官方最新 WebSocket 长连接协议,无需公网 IP、无需域名、免配置 Webhook。
|
|
252
|
+
|
|
253
|
+
**功能亮点**
|
|
254
|
+
|
|
255
|
+
- ⚡ **100% 免公网 IP**:官方 WebSocket 全双工长连接,本地电脑即可直连飞书开放平台
|
|
256
|
+
- 📜 **Card JSON 2.0 原生流式打字机**:单条卡片原地增量打字机更新,彻底告别气泡拆分碎片化
|
|
257
|
+
- 🛡️ **Card 2.0 交互卡片审批**:敏感操作触发审批时下发橙色告警卡片,手机/电脑端点击 `[✓ 批准执行]` / `[✕ 拒绝执行]` 按钮一键处理
|
|
258
|
+
- 📝 **全量 Markdown 渲染**:支持多级标题、表格、代码高亮、引用块与列表
|
|
259
|
+
- 🔄 **会话与工作区管理**:支持 `/sessions` 表格化查看、`/use N` 切换、`/workspaces` 查看工作区
|
|
260
|
+
|
|
261
|
+
**使用步骤**
|
|
262
|
+
|
|
263
|
+
1. 前往 [飞书开放平台](https://open.feishu.cn/app) 创建企业自建应用,开启「机器人」能力并发布版本(详见 [飞书接入指南](docs/feishu-usage.md))
|
|
264
|
+
2. 在「事件与回调」中开启「使用长连接接收事件」,添加 `im.message.receive_v1` 和 `card.action.trigger` 事件
|
|
265
|
+
3. 打开 DSH 设置页「远程访问」→「IM 机器人」→ 选中「飞书」
|
|
266
|
+
4. 填入 App ID 和 App Secret,点击「保存并连接」即可
|
|
267
|
+
|
|
268
|
+
**飞书里的命令**(完整说明见 [飞书 Bot 使用说明](docs/feishu-usage.md))
|
|
269
|
+
|
|
270
|
+
| 命令 | 说明 |
|
|
271
|
+
|------|------|
|
|
272
|
+
| *(普通文本)* | 发给当前活动 agent |
|
|
273
|
+
| `/new <提示词>` | 在当前工作区新建会话并开始 |
|
|
274
|
+
| `/new <提示词> @N` | 在指定工作区新建会话 |
|
|
275
|
+
| `/sessions`(或 `/list`) | 查看所有历史会话(结构化表格排版) |
|
|
276
|
+
| `/use N`(或 `/resume N`) | 切换到会话 N |
|
|
277
|
+
| `/workspaces` | 列出可用工作区 |
|
|
278
|
+
| `/end` | 结束当前会话 |
|
|
279
|
+
| `/stop` | 停止当前任务 |
|
|
280
|
+
| `/status` | 查看 agent 状态看板 |
|
|
281
|
+
| `/yes` `/no`(或 `1`/`2`) | 响应权限审批请求(或直接点击卡片按钮) |
|
|
282
|
+
| `/help` | 查看全部命令 |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
249
286
|
## 可选配置
|
|
250
287
|
|
|
251
288
|
插件开箱即用,无需配置。如需修改代理端口,在 cordis.yml 中添加:
|
package/client/client.js
CHANGED
|
@@ -723,6 +723,44 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
723
723
|
},
|
|
724
724
|
title: "\u6E05\u9664\u767B\u5F55\u51ED\u8BC1\uFF0C\u4E0B\u6B21\u9700\u91CD\u65B0\u914D\u7F6E"
|
|
725
725
|
}, "\u89E3\u7ED1\u8D26\u53F7")
|
|
726
|
+
),
|
|
727
|
+
// 飞书专属:手机扫码直达 Bot 对话
|
|
728
|
+
platformId === "feishu" && platform.botQr && React.createElement(
|
|
729
|
+
"div",
|
|
730
|
+
{
|
|
731
|
+
style: {
|
|
732
|
+
marginTop: 12,
|
|
733
|
+
padding: 12,
|
|
734
|
+
background: "var(--dsw-alias-bg-layer-1,#ffffff)",
|
|
735
|
+
border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)",
|
|
736
|
+
borderRadius: 8,
|
|
737
|
+
display: "flex",
|
|
738
|
+
gap: 14,
|
|
739
|
+
alignItems: "center",
|
|
740
|
+
flexWrap: "wrap"
|
|
741
|
+
}
|
|
742
|
+
},
|
|
743
|
+
React.createElement("img", { src: platform.botQr, alt: "Feishu Bot QR", style: { width: 110, height: 110, borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", padding: 4, background: "#fff" } }),
|
|
744
|
+
React.createElement(
|
|
745
|
+
"div",
|
|
746
|
+
{ style: { flex: 1, minWidth: 160 } },
|
|
747
|
+
React.createElement("div", { style: { ...s.label, fontSize: 13, fontWeight: 600 } }, "\u{1F4F1} \u624B\u673A\u98DE\u4E66\u626B\u7801\u76F4\u8FBE\u5BF9\u8BDD"),
|
|
748
|
+
React.createElement(
|
|
749
|
+
"div",
|
|
750
|
+
{ style: { ...s.muted, fontSize: 12, marginTop: 4, lineHeight: 1.5 } },
|
|
751
|
+
"\u7528\u98DE\u4E66\u624B\u673A App \u626B\u63CF\u5DE6\u4FA7\u4E8C\u7EF4\u7801\uFF0C\u7ACB\u5373\u6253\u5F00\u4E0E Bot \u5BF9\u8BDD\uFF1B\u53D1\u9001\u9996\u6761\u6D88\u606F\u81EA\u52A8\u5B8C\u6210\u767D\u540D\u5355\u6388\u6743\u3002"
|
|
752
|
+
),
|
|
753
|
+
platform.botLink && React.createElement(
|
|
754
|
+
"div",
|
|
755
|
+
{ style: { display: "flex", gap: 8, marginTop: 8 } },
|
|
756
|
+
React.createElement("a", {
|
|
757
|
+
href: platform.botLink,
|
|
758
|
+
target: "_blank",
|
|
759
|
+
rel: "noopener noreferrer",
|
|
760
|
+
style: { ...s.btnGhost, textDecoration: "none", display: "inline-flex", alignItems: "center", gap: 4, padding: "4px 10px", fontSize: 12 }
|
|
761
|
+
}, "\u5728\u98DE\u4E66\u5BA2\u6237\u7AEF\u6253\u5F00 \u2197")
|
|
762
|
+
)
|
|
763
|
+
)
|
|
726
764
|
)
|
|
727
765
|
),
|
|
728
766
|
// 未配置 / 登录中:表单(QQ / 飞书)或二维码(微信)
|
|
@@ -742,6 +780,23 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
742
780
|
) : platformId === "qq" || platformId === "feishu" ? React.createElement(
|
|
743
781
|
"div",
|
|
744
782
|
{ style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4 } },
|
|
783
|
+
platformId === "feishu" && React.createElement(
|
|
784
|
+
"div",
|
|
785
|
+
{
|
|
786
|
+
style: {
|
|
787
|
+
background: "var(--dsw-alias-bg-layer-1,#ffffff)",
|
|
788
|
+
border: "1px dashed var(--dsw-alias-border-l2,#e5e7eb)",
|
|
789
|
+
borderRadius: 6,
|
|
790
|
+
padding: "8px 10px",
|
|
791
|
+
fontSize: 12,
|
|
792
|
+
lineHeight: 1.5
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
React.createElement("span", { style: s.label }, "\u{1F4A1} \u626B\u7801\u81EA\u52A8\u521B\u5EFA\u5F15\u5BFC\uFF1A"),
|
|
796
|
+
React.createElement("span", { style: s.muted }, " \u53EF\u5728\u7EC8\u7AEF\u8FD0\u884C "),
|
|
797
|
+
React.createElement("code", { style: { ...s.code, fontSize: 11, background: "var(--dsw-alias-bg-layer-2,#f3f4f6)", padding: "2px 4px", borderRadius: 4 } }, "npx feishu-bot-bootstrap"),
|
|
798
|
+
React.createElement("span", { style: s.muted }, " \u624B\u673A\u626B\u7801\u4E00\u952E\u81EA\u52A8\u521B\u5EFA\u5E94\u7528\u5E76\u8F93\u51FA\u51ED\u8BC1\uFF1B\u6216\u5728\u4E0B\u65B9\u624B\u52A8\u586B\u5165\u51ED\u8BC1\u3002")
|
|
799
|
+
),
|
|
745
800
|
React.createElement(
|
|
746
801
|
"div",
|
|
747
802
|
null,
|
package/client/index.js
CHANGED
|
@@ -643,6 +643,36 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
643
643
|
title: '清除登录凭证,下次需重新配置',
|
|
644
644
|
}, '解绑账号'),
|
|
645
645
|
),
|
|
646
|
+
// 飞书专属:手机扫码直达 Bot 对话
|
|
647
|
+
platformId === 'feishu' && platform.botQr && React.createElement('div', {
|
|
648
|
+
style: {
|
|
649
|
+
marginTop: 12,
|
|
650
|
+
padding: 12,
|
|
651
|
+
background: 'var(--dsw-alias-bg-layer-1,#ffffff)',
|
|
652
|
+
border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)',
|
|
653
|
+
borderRadius: 8,
|
|
654
|
+
display: 'flex',
|
|
655
|
+
gap: 14,
|
|
656
|
+
alignItems: 'center',
|
|
657
|
+
flexWrap: 'wrap',
|
|
658
|
+
},
|
|
659
|
+
},
|
|
660
|
+
React.createElement('img', { src: platform.botQr, alt: 'Feishu Bot QR', style: { width: 110, height: 110, borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2,#e5e7eb)', padding: 4, background: '#fff' } }),
|
|
661
|
+
React.createElement('div', { style: { flex: 1, minWidth: 160 } },
|
|
662
|
+
React.createElement('div', { style: { ...s.label, fontSize: 13, fontWeight: 600 } }, '📱 手机飞书扫码直达对话'),
|
|
663
|
+
React.createElement('div', { style: { ...s.muted, fontSize: 12, marginTop: 4, lineHeight: 1.5 } },
|
|
664
|
+
'用飞书手机 App 扫描左侧二维码,立即打开与 Bot 对话;发送首条消息自动完成白名单授权。'
|
|
665
|
+
),
|
|
666
|
+
platform.botLink && React.createElement('div', { style: { display: 'flex', gap: 8, marginTop: 8 } },
|
|
667
|
+
React.createElement('a', {
|
|
668
|
+
href: platform.botLink,
|
|
669
|
+
target: '_blank',
|
|
670
|
+
rel: 'noopener noreferrer',
|
|
671
|
+
style: { ...s.btnGhost, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 4, padding: '4px 10px', fontSize: 12 },
|
|
672
|
+
}, '在飞书客户端打开 ↗'),
|
|
673
|
+
),
|
|
674
|
+
),
|
|
675
|
+
),
|
|
646
676
|
),
|
|
647
677
|
|
|
648
678
|
// 未配置 / 登录中:表单(QQ / 飞书)或二维码(微信)
|
|
@@ -659,6 +689,21 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
659
689
|
)
|
|
660
690
|
: (platformId === 'qq' || platformId === 'feishu')
|
|
661
691
|
? React.createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 10, marginTop: 4 } },
|
|
692
|
+
platformId === 'feishu' && React.createElement('div', {
|
|
693
|
+
style: {
|
|
694
|
+
background: 'var(--dsw-alias-bg-layer-1,#ffffff)',
|
|
695
|
+
border: '1px dashed var(--dsw-alias-border-l2,#e5e7eb)',
|
|
696
|
+
borderRadius: 6,
|
|
697
|
+
padding: '8px 10px',
|
|
698
|
+
fontSize: 12,
|
|
699
|
+
lineHeight: 1.5,
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
React.createElement('span', { style: s.label }, '💡 扫码自动创建引导:'),
|
|
703
|
+
React.createElement('span', { style: s.muted }, ' 可在终端运行 '),
|
|
704
|
+
React.createElement('code', { style: { ...s.code, fontSize: 11, background: 'var(--dsw-alias-bg-layer-2,#f3f4f6)', padding: '2px 4px', borderRadius: 4 } }, 'npx feishu-bot-bootstrap'),
|
|
705
|
+
React.createElement('span', { style: s.muted }, ' 手机扫码一键自动创建应用并输出凭证;或在下方手动填入凭证。')
|
|
706
|
+
),
|
|
662
707
|
React.createElement('div', null,
|
|
663
708
|
React.createElement('div', { style: { ...s.muted, marginBottom: 4 } },
|
|
664
709
|
platformId === 'qq' ? 'AppID — QQ 开放平台机器人应用 ID' : 'App ID — 飞书开放平台自建应用 ID (cli_xxx)'
|
package/lib/feishu/gateway.js
CHANGED
|
@@ -69,12 +69,22 @@ export class FeishuGateway extends Service {
|
|
|
69
69
|
const isLark = this.config.domain === 'lark'
|
|
70
70
|
const domain = isLark ? Lark.Domain.Lark : Lark.Domain.Feishu
|
|
71
71
|
|
|
72
|
+
const sdkLogger = {
|
|
73
|
+
debug: (...args) => this.logger?.debug?.('[lark-sdk debug]', ...args),
|
|
74
|
+
info: (...args) => this.logger?.debug?.('[lark-sdk info]', ...args),
|
|
75
|
+
warn: (...args) => this.logger?.debug?.('[lark-sdk warn]', ...args),
|
|
76
|
+
error: (...args) => this.logger?.error?.('[lark-sdk error]', ...args),
|
|
77
|
+
trace: () => {},
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
// 1. 初始化 REST API Client
|
|
73
81
|
this.client = new Lark.Client({
|
|
74
82
|
appId: this.config.appId,
|
|
75
83
|
appSecret: this.config.appSecret,
|
|
76
84
|
domain,
|
|
77
85
|
appType: Lark.AppType.SelfBuild,
|
|
86
|
+
logger: sdkLogger,
|
|
87
|
+
loggerLevel: Lark.LoggerLevel.error,
|
|
78
88
|
})
|
|
79
89
|
|
|
80
90
|
// 2. 初始化长连接 WSClient
|
|
@@ -82,11 +92,14 @@ export class FeishuGateway extends Service {
|
|
|
82
92
|
appId: this.config.appId,
|
|
83
93
|
appSecret: this.config.appSecret,
|
|
84
94
|
domain,
|
|
95
|
+
logger: sdkLogger,
|
|
85
96
|
loggerLevel: Lark.LoggerLevel.error,
|
|
86
97
|
})
|
|
87
98
|
|
|
88
99
|
// 3. 构建事件分发器
|
|
89
|
-
this.eventDispatcher = new Lark.EventDispatcher({
|
|
100
|
+
this.eventDispatcher = new Lark.EventDispatcher({
|
|
101
|
+
logger: sdkLogger,
|
|
102
|
+
})
|
|
90
103
|
|
|
91
104
|
// 注册接收消息事件
|
|
92
105
|
this.eventDispatcher.register({
|
|
@@ -108,6 +121,9 @@ export class FeishuGateway extends Service {
|
|
|
108
121
|
'im.message.message_read_v1': async () => {
|
|
109
122
|
// 消息已读回执事件,忽略
|
|
110
123
|
},
|
|
124
|
+
'im.chat.access_event.bot_p2p_chat_entered_v1': async () => {
|
|
125
|
+
// 用户进入与机器人的单聊窗口事件,忽略
|
|
126
|
+
},
|
|
111
127
|
})
|
|
112
128
|
|
|
113
129
|
// 4. 尝试获取机器人信息(验证凭证,非阻断)
|
package/lib/feishu/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// 编排 FeishuGateway(官方 OpenAPI/WSClient 网关)+ FeishuConversationNode(飞书⇄DSH 会话桥)。
|
|
3
3
|
// 作为 Platform 子类,注册进 PlatformManager 统一管理。
|
|
4
4
|
|
|
5
|
+
import QRCode from 'qrcode'
|
|
5
6
|
import { Platform } from '../platform/base.js'
|
|
6
7
|
import { FeishuGateway } from './gateway.js'
|
|
7
8
|
import { FeishuConversationNode } from './node.js'
|
|
@@ -18,6 +19,7 @@ export class FeishuService extends Platform {
|
|
|
18
19
|
super({ ctx, logger, config, onPersist })
|
|
19
20
|
this.id = 'feishu'
|
|
20
21
|
this.name = 'Feishu'
|
|
22
|
+
this._botQrCache = { appId: '', qr: '' }
|
|
21
23
|
|
|
22
24
|
this.gateway = new FeishuGateway(ctx, {
|
|
23
25
|
appId: config.appId ?? '',
|
|
@@ -125,6 +127,18 @@ export class FeishuService extends Platform {
|
|
|
125
127
|
|
|
126
128
|
getStatus() {
|
|
127
129
|
const allowFrom = [...(this.node?.config?.allowFrom ?? [])]
|
|
130
|
+
const appId = this.gateway.config.appId || ''
|
|
131
|
+
const botLink = appId ? `https://applink.feishu.cn/client/bot/open?appId=${encodeURIComponent(appId)}` : null
|
|
132
|
+
if (botLink && this._botQrCache.appId !== appId) {
|
|
133
|
+
this._botQrCache.appId = appId
|
|
134
|
+
void QRCode.toDataURL(botLink, {
|
|
135
|
+
width: 260,
|
|
136
|
+
margin: 2,
|
|
137
|
+
color: { dark: '#1F2421', light: '#FFFFFF' },
|
|
138
|
+
}).then((qr) => {
|
|
139
|
+
this._botQrCache.qr = qr
|
|
140
|
+
}).catch(() => {})
|
|
141
|
+
}
|
|
128
142
|
return {
|
|
129
143
|
id: this.id,
|
|
130
144
|
name: this.name,
|
|
@@ -151,6 +165,8 @@ export class FeishuService extends Platform {
|
|
|
151
165
|
domain: this.gateway.config.domain,
|
|
152
166
|
},
|
|
153
167
|
botInfo: this.gateway.botInfo,
|
|
168
|
+
botLink,
|
|
169
|
+
botQr: this._botQrCache.qr,
|
|
154
170
|
error: this.gateway.error,
|
|
155
171
|
}
|
|
156
172
|
}
|
|
@@ -818,12 +818,13 @@ function getWorkspaceBasename(cwd) {
|
|
|
818
818
|
|
|
819
819
|
async function routeCommand(node, text) {
|
|
820
820
|
const trimmed = text.trim()
|
|
821
|
-
if (!trimmed.startsWith('/')) return false
|
|
822
821
|
|
|
823
822
|
if (trimmed === '/yes' || trimmed === '/no' || /^[12]$/.test(trimmed)) {
|
|
824
823
|
if (node.resolveApproval(trimmed)) return true
|
|
825
824
|
}
|
|
826
825
|
|
|
826
|
+
if (!trimmed.startsWith('/')) return false
|
|
827
|
+
|
|
827
828
|
const [command, ...rest] = trimmed.slice(1).split(/\s+/)
|
|
828
829
|
switch (command) {
|
|
829
830
|
case 'help':
|
package/lib/qq/node.js
CHANGED
|
@@ -205,6 +205,38 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
205
205
|
return this._sendMarkdown(peerId, text, { scope, msgId: replyMsgId, keyboard })
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
// 审批申请:发送带「✓ 批准执行」与「✕ 拒绝执行」交互按钮的 Markdown 卡片
|
|
209
|
+
const isApprovalMessage = text.includes('操作权限确认')
|
|
210
|
+
if (isApprovalMessage) {
|
|
211
|
+
const keyboard = {
|
|
212
|
+
content: {
|
|
213
|
+
rows: [
|
|
214
|
+
{
|
|
215
|
+
buttons: [
|
|
216
|
+
{
|
|
217
|
+
id: 'approve_yes',
|
|
218
|
+
render_data: { label: '✓ 批准执行', visited_label: '已批准', style: 1 },
|
|
219
|
+
action: { type: 1, permission: { type: 2 }, data: '/yes', unsupport_tips: '请升级QQ客户端后使用' },
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: 'approve_no',
|
|
223
|
+
render_data: { label: '✕ 拒绝执行', visited_label: '已拒绝', style: 0 },
|
|
224
|
+
action: { type: 1, permission: { type: 2 }, data: '/no', unsupport_tips: '请升级QQ客户端后使用' },
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
return this._sendMarkdown(peerId, sanitizeQQMarkdown(text), { scope, keyboard })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 审批决议通知(已批准/已拒绝):直接单条 Markdown 发送,无需流式分片
|
|
235
|
+
const isResolutionMessage = text.includes('已批准执行') || text.includes('已拒绝执行')
|
|
236
|
+
if (isResolutionMessage) {
|
|
237
|
+
return this._sendMarkdown(peerId, sanitizeQQMarkdown(text), { scope })
|
|
238
|
+
}
|
|
239
|
+
|
|
208
240
|
// 其他回复统一走「流式 Markdown」:content_type=markdown 让手机端渲染
|
|
209
241
|
const content = String(text || '').trim()
|
|
210
242
|
if (content.length === 0) return { success: true }
|
|
@@ -421,6 +453,10 @@ export class QqConversationNode extends ConversationBridge {
|
|
|
421
453
|
await this.handleInbound({ senderId: authId, text: '/list', isGroup })
|
|
422
454
|
} else if (buttonId === 'help') {
|
|
423
455
|
await this.handleInbound({ senderId: authId, text: '/help', isGroup })
|
|
456
|
+
} else if (buttonId === 'approve_yes') {
|
|
457
|
+
await this.handleInbound({ senderId: authId, text: '/yes', isGroup })
|
|
458
|
+
} else if (buttonId === 'approve_no') {
|
|
459
|
+
await this.handleInbound({ senderId: authId, text: '/no', isGroup })
|
|
424
460
|
} else {
|
|
425
461
|
this.logger?.info?.(`[dsh-bridge qq] unknown button interaction: ${buttonId}`)
|
|
426
462
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenbin_wb/dsh-bridge",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "手机扫码即可在移动端/公网继续用 DeepSeek Harness,人不在电脑前也能接着干。一键局域网二维码、Cloudflare 公网隧道、自建隧道与微信 / QQ / 飞书 Bot(多工作区/会话持久化/媒体/卡片审批/流式输出),无需自己搭公网服务器。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|