@wenbin_wb/dsh-bridge 2.3.3 → 2.4.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 +41 -1
- package/README.md +41 -1
- package/client/client.js +102 -45
- package/client/index.js +103 -46
- package/docs/telegram-usage.md +89 -0
- package/lib/feishu/gateway.js +166 -7
- package/lib/feishu/node.js +55 -10
- package/lib/index.js +56 -1
- 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/README.en.md
CHANGED
|
@@ -20,6 +20,7 @@ Seamlessly extend your local DeepSeek Harness to mobile phones, tablets, public
|
|
|
20
20
|
- **WeChat Bot (ClawBot / iLink)**: Scan a QR code to log in a WeChat personal account, then chat with, control, and approve your DeepSeek Harness agents right inside WeChat. **Multi-workspace selection, restart-persistent sessions, grouped session listing with titles, media (image/file/voice) transfer, and permission approvals** — over Tencent's official iLink Bot API, no public server or tunnel required ([Usage Guide](docs/wechat-usage.md))
|
|
21
21
|
- **QQ Bot (OpenAPI v2)**: Connect your QQ Bot to receive private/group messages, send Markdown, button keyboards, and rich media. **Full event coverage (C2C / GROUP_AT_MESSAGE_CREATE), auto token refresh, reconnection with backoff, message deduplication** — over Tencent's official QQ Bot OpenAPI v2 ([Usage Guide](docs/qq-usage.md))
|
|
22
22
|
- **Feishu / Lark Bot (Official WebSocket)**: Connect enterprise self-built apps via Feishu's official WebSocket protocol. **No public IP / no Webhook required, Markdown table formatting, native interactive card permission approvals with 1-click button actions** ([Usage Guide](docs/feishu-usage.md))
|
|
23
|
+
- **Telegram Bot (Official Bot API + Proxy Support)**: Connect official Telegram bots for private and group interactions. **No public IP required (Long Polling getUpdates), built-in zero-dependency HTTP/HTTPS proxy tunnel, smooth typewriter streaming output, native command menu (Menu button), and Inline Keyboard interactive approval cards** ([Usage Guide](docs/telegram-usage.md))
|
|
23
24
|
- **Official Brand SVG Icons**: Authentic vector brand icons for WeChat, QQ, Feishu, Telegram with real-time status indicators
|
|
24
25
|
- **Fast Version Check & 1-Click Upgrade**: Dual-channel registry check (npmmirror + npmjs fallback in ~200ms) with seamless **1-click in-app upgrade**, no terminal copying required
|
|
25
26
|
- **Dark Mode Support**: Deep integration with DeepSeek Harness Design Tokens (`--dsw-alias-*`), QR code background protection for safe dark mode scanning
|
|
@@ -40,7 +41,7 @@ Seamlessly extend your local DeepSeek Harness to mobile phones, tablets, public
|
|
|
40
41
|
| **WeChat** | Chat with your Agent directly in WeChat | ✅ Supported (workspaces / persisted sessions / media / approvals) |
|
|
41
42
|
| **QQ Bot** | QQ bot integration for group/private chat | ✅ **Completed** (v2.1.0) — Markdown / buttons / rich media |
|
|
42
43
|
| **Feishu** | Feishu / Lark official persistent WebSocket bot | ✅ **Completed** (v2.3.0) — No-public-IP WS / Card Approvals |
|
|
43
|
-
| **Telegram** | IM channel suited for self-hosting and overseas |
|
|
44
|
+
| **Telegram** | IM channel suited for self-hosting and overseas | ✅ **Completed** (v2.4.0) — Long Polling / Proxy / Native Menu / Inline Cards / Streaming |
|
|
44
45
|
| **OpenClaw** | Connect with the OpenClaw ecosystem | Planned |
|
|
45
46
|
|
|
46
47
|
---
|
|
@@ -283,6 +284,45 @@ Connect your enterprise self-built app via Feishu's official WebSocket protocol.
|
|
|
283
284
|
|
|
284
285
|
---
|
|
285
286
|
|
|
287
|
+
### Telegram Bot (Official Bot API + Proxy Support)
|
|
288
|
+
|
|
289
|
+
Connect official Telegram Bot API for real-time private and group interactions. Powered by official Long Polling (`getUpdates`), **no public IP / no Webhook required**, built-in **zero-dependency HTTP/HTTPS CONNECT proxy tunnel**, ready to use in any network environment.
|
|
290
|
+
|
|
291
|
+
**Key Features**
|
|
292
|
+
|
|
293
|
+
- ⚡ **100% No Public IP Needed**: Official Long Polling mechanism allows local machines or private servers to connect directly
|
|
294
|
+
- 🌐 **Built-in HTTP/HTTPS Proxy Support**: Easily configure local proxies like Clash / v2ray (`http://127.0.0.1:7890`) with zero external dependencies
|
|
295
|
+
- 📜 **Typewriter Streaming Output**: Integrated turn lifecycle updates existing message in-place with `editMessageText`, eliminating message fragmentation
|
|
296
|
+
- 🎯 **Native Command Menu (`Menu` Button)**: Automatically registered with `setMyCommands` & `setChatMenuButton`, type `/` or tap `[Menu]` for 1-click command navigation
|
|
297
|
+
- 🛡️ **Inline Keyboard Interactive Cards**: Permission approvals send `[✓ Approve]` / `[✕ Reject]` buttons for 1-second approval actions
|
|
298
|
+
- 🖼️ **Multimodal & File Transfers**: Inbound images/files automatically saved and sent to Agent; generated artifacts sent back to Telegram
|
|
299
|
+
- 🔄 **Session & Workspace Management**: Manage multiple sessions with `/sessions`, switch with `/use N`, view workspaces with `/workspaces`
|
|
300
|
+
|
|
301
|
+
**Quick Start**
|
|
302
|
+
|
|
303
|
+
1. Send `/newbot` to [@BotFather](https://t.me/BotFather) on Telegram to create your bot and obtain the **Bot Token**
|
|
304
|
+
2. Open DSH Settings → "Remote Access" → "IM Bots" → select "**Telegram**"
|
|
305
|
+
3. Enter your **Bot Token** (and optional proxy address like `http://127.0.0.1:7890`), click "Save and Connect"
|
|
306
|
+
4. Scan the QR code with Telegram on your phone, send the first message (e.g. `/help`) to **automatically authorize your account into the allowlist**
|
|
307
|
+
|
|
308
|
+
**Commands in Telegram** (Full guide in [Telegram Bot Guide](docs/telegram-usage.md))
|
|
309
|
+
|
|
310
|
+
| Command | Description | Interactive Card |
|
|
311
|
+
|---------|-------------|------------------|
|
|
312
|
+
| *(plain text)* | Send to current active agent | Real-time typewriter stream |
|
|
313
|
+
| `/new <prompt>` | Create and start a new session in current workspace | Start fresh turn |
|
|
314
|
+
| `/new <prompt> @N` | Create a new session in workspace N | Multi-workspace routing |
|
|
315
|
+
| `/sessions` (or `/list`) | List all sessions | 1-click switch buttons |
|
|
316
|
+
| `/use N` (or `/resume N`) | Switch to/resume session N | Instant context switch |
|
|
317
|
+
| `/workspaces` | List all available workspaces | View workspace paths |
|
|
318
|
+
| `/status` | View agent status dashboard | Refresh/Stop/End buttons |
|
|
319
|
+
| `/stop` | Stop currently executing task | Immediate abort |
|
|
320
|
+
| `/end` | End current active session | Quick-start button attached |
|
|
321
|
+
| `/yes` `/no` (or `1`/`2`) | Respond to permission approvals | Click inline buttons directly |
|
|
322
|
+
| `/help` | Display quick buttons and help | Full navigation buttons |
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
286
326
|
## Optional Configuration
|
|
287
327
|
|
|
288
328
|
The plugin is ready to use out of the box. To customize the proxy port, add to `cordis.yml`:
|
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- **微信 Bot(ClawBot / iLink)**:扫码登录微信个人号后,直接在微信里对话、控制 DeepSeek Harness 的 agent。**支持多工作区选择、会话跨重启持久化、按工作区分组查看、媒体(图片/文件/语音)收发、权限审批**——走腾讯官方 iLink Bot API,无需公网([使用说明](docs/wechat-usage.md))
|
|
21
21
|
- **QQ Bot(OpenAPI v2)**:接入 QQ 机器人,私聊/群聊接收消息,发送 Markdown、按钮键盘和富媒体。**完整事件覆盖(C2C / GROUP_AT_MESSAGE_CREATE)、Token 自动刷新、断线重连、消息去重**——走腾讯官方 QQ Bot OpenAPI v2([使用说明](docs/qq-usage.md))
|
|
22
22
|
- **飞书 Bot(官方 WebSocket 长连接)**:接入飞书开放平台企业自建应用,私聊/群聊实时交互。**无需公网 IP / 无需 Webhook、支持飞书 Markdown 表格排版、原生交互卡片权限审批一键点击确认**——走飞书官方最新 WebSocket 长连接协议([使用说明](docs/feishu-usage.md))
|
|
23
|
+
- **Telegram Bot(官方 Bot API + 代理支持)**:接入官方 Telegram 机器人,单聊/群聊实时交互。**无需公网 IP(长轮询 getUpdates)、内置零依赖 HTTP/HTTPS 代理隧道、打字机平滑流式输出、原生快捷指令菜单(Menu 按钮)与 Inline 交互卡片审批**([使用说明](docs/telegram-usage.md))
|
|
23
24
|
- **IM 官方品牌矢量图标**:微信、QQ、飞书、Telegram 官方矢量图标与状态展示,直接在聊天软件里呼唤你的 Agent
|
|
24
25
|
- **极速版本检查与一键升级**:国内高速镜像(npmmirror)优先 + 官方源毫秒级双通道检查,检测到新版本支持**界面一键直接升级**,无需手动打开终端
|
|
25
26
|
- **深色模式深度适配**:完美适配 DeepSeek Harness 设计系统明暗主题切换,二维码自带白底安全垫,暗光下手机扫码 100% 极速识别
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
| **微信** | 在微信里直接与你的 Agent 对话 | ✅ 已支持(多工作区 / 会话持久化 / 媒体 / 审批) |
|
|
41
42
|
| **QQ Bot** | 接入 QQ 机器人,群聊/私聊唤起 Agent | ✅ **已完成**(v2.1.0)— Markdown / 按钮 / 富媒体 |
|
|
42
43
|
| **飞书** | 飞书开放平台长连接机器人,办公场景直接调用 | ✅ **已完成**(v2.3.0)— 免公网 WS / 卡片审批 |
|
|
43
|
-
| **Telegram** | 适合自托管与海外的 IM 渠道 |
|
|
44
|
+
| **Telegram** | 适合自托管与海外的 IM 渠道 | ✅ **已完成**(v2.4.0)— 免公网长轮询 / 代理支持 / 原生菜单 / Inline 卡片 / 流式打字机 |
|
|
44
45
|
| **OpenClaw** | 与 OpenClaw 生态打通 | 规划中 |
|
|
45
46
|
|
|
46
47
|
---
|
|
@@ -283,6 +284,45 @@ dsh plugin --profile web add @wenbin_wb/dsh-bridge@latest
|
|
|
283
284
|
|
|
284
285
|
---
|
|
285
286
|
|
|
287
|
+
### Telegram Bot(官方 Bot API + 代理支持)
|
|
288
|
+
|
|
289
|
+
接入 Telegram 官方 Bot API,单聊与群聊实时交互。采用官方 Long Polling(长轮询)机制,**无需公网 IP / 免 Webhook**,内置**零依赖 HTTP/HTTPS CONNECT 代理隧道**,国内网络即开即连。
|
|
290
|
+
|
|
291
|
+
**功能亮点**
|
|
292
|
+
|
|
293
|
+
- ⚡ **100% 免公网 IP**:官方 Long Polling 长轮询,本地电脑或内网服务器即可直连通信
|
|
294
|
+
- 🌐 **内置 HTTP/HTTPS 代理支持**:支持填写本地 Clash / v2ray 代理(如 `http://127.0.0.1:7890`),零外部依赖
|
|
295
|
+
- 📜 **实时打字机流式输出**:接入轮次生命周期,单条气泡原地 `editMessageText` 增量刷新,告别频繁发碎消息
|
|
296
|
+
- 🎯 **原生快捷指令菜单(Menu 按钮)**:自动注册全范围指令,输入 `/` 或点击左下角 `[Menu]` 按钮一键直达常用命令
|
|
297
|
+
- 🛡️ **Inline Keyboard 交互卡片**:权限审批下发 `[✓ 批准执行]` / `[✕ 拒绝执行]` 按键,一秒点击即时放行
|
|
298
|
+
- 🖼️ **多模态与文件传输**:支持入站图片/文档自动转存交付 Agent,出站产物文件自动推回 Telegram
|
|
299
|
+
- 🔄 **会话与工作区管理**:支持 `/sessions` 列出历史会话、`/use N` 切换、`/workspaces` 调度工作区
|
|
300
|
+
|
|
301
|
+
**使用步骤**
|
|
302
|
+
|
|
303
|
+
1. 在 Telegram 中向 [@BotFather](https://t.me/BotFather) 发送 `/newbot` 创建机器人并获取 **Bot Token**
|
|
304
|
+
2. 打开 DSH 设置页「远程访问」→「IM 机器人」→ 选中「**Telegram**」
|
|
305
|
+
3. 填入 **Bot Token**(国内网络可按需填入代理地址如 `http://127.0.0.1:7890`),点击「保存并连接」
|
|
306
|
+
4. 手机 Telegram 扫码打开机器人,发送第一条消息(如 `/help`)即**自动完成白名单授权**
|
|
307
|
+
|
|
308
|
+
**Telegram 里的命令**(完整说明见 [Telegram Bot 使用说明](docs/telegram-usage.md))
|
|
309
|
+
|
|
310
|
+
| 命令 | 说明 | 交互卡片 |
|
|
311
|
+
|------|------|------|
|
|
312
|
+
| *(普通文本)* | 发给当前活动 agent | 实时打字机流式输出 |
|
|
313
|
+
| `/new <提示词>` | 在当前工作区新建会话并开始 | 立即启动新轮次 |
|
|
314
|
+
| `/new <提示词> @N` | 在指定工作区新建会话 | 多工作区调度 |
|
|
315
|
+
| `/sessions`(或 `/list`) | 查看所有会话列表 | 挂载一键切换按键 |
|
|
316
|
+
| `/use N`(或 `/resume N`) | 切换到会话 N | 快速切换上下文 |
|
|
317
|
+
| `/workspaces` | 列出可用工作区 | 查看工作区路径 |
|
|
318
|
+
| `/status` | 查看 agent 状态看板 | 挂载刷新/停止/结束按键 |
|
|
319
|
+
| `/stop` | 停止当前正在运行的任务 | 即刻中断执行 |
|
|
320
|
+
| `/end` | 结束当前活动会话 | 挂载快捷开始按键 |
|
|
321
|
+
| `/yes` `/no`(或 `1`/`2`) | 响应权限审批请求 | 支持直接点击卡片按钮 |
|
|
322
|
+
| `/help` | 显示快捷按键与完整帮助 | 挂载全套功能导航按键 |
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
286
326
|
## 可选配置
|
|
287
327
|
|
|
288
328
|
插件开箱即用,无需配置。如需修改代理端口,在 cordis.yml 中添加:
|
package/client/client.js
CHANGED
|
@@ -421,16 +421,18 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
421
421
|
setCfgDraft({
|
|
422
422
|
digestIntervalSec: String(platform.config.digestIntervalSec ?? 300),
|
|
423
423
|
approvalTimeoutSec: String(platform.config.approvalTimeoutSec ?? 600),
|
|
424
|
-
maxMessageChars: String(platform.config.maxMessageChars ?? 2e3),
|
|
424
|
+
maxMessageChars: String(platform.config.maxMessageChars ?? (platformId === "telegram" ? 4096 : 2e3)),
|
|
425
425
|
sendChunkDelayMs: String(platform.config.sendChunkDelayMs ?? 1500),
|
|
426
426
|
appId: platform.config.appId ?? "",
|
|
427
427
|
// Secret 不由后端回传;空值表示沿用已保存密钥
|
|
428
428
|
clientSecret: "",
|
|
429
429
|
appSecret: "",
|
|
430
|
-
domain: platform.config.domain ?? "feishu"
|
|
430
|
+
domain: platform.config.domain ?? "feishu",
|
|
431
|
+
botToken: "",
|
|
432
|
+
proxy: platform.config.proxy ?? ""
|
|
431
433
|
});
|
|
432
434
|
}
|
|
433
|
-
}, [platform?.config]);
|
|
435
|
+
}, [platform?.config, platformId]);
|
|
434
436
|
React.useEffect(() => {
|
|
435
437
|
const connected2 = platform?.status === "connected" || platform?.status === "starting" || platform?.status === "reconnecting";
|
|
436
438
|
onStatusChange?.(connected2);
|
|
@@ -488,10 +490,10 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
488
490
|
...d,
|
|
489
491
|
digestIntervalSec: "300",
|
|
490
492
|
approvalTimeoutSec: "600",
|
|
491
|
-
maxMessageChars: "2000",
|
|
493
|
+
maxMessageChars: platformId === "telegram" ? "4096" : "2000",
|
|
492
494
|
sendChunkDelayMs: "1500"
|
|
493
495
|
}));
|
|
494
|
-
}, []);
|
|
496
|
+
}, [platformId]);
|
|
495
497
|
const saveConfig = React.useCallback(async () => {
|
|
496
498
|
if (!cfgDraft) return;
|
|
497
499
|
const payload = {
|
|
@@ -507,10 +509,13 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
507
509
|
payload.appId = cfgDraft.appId.trim();
|
|
508
510
|
payload.appSecret = cfgDraft.appSecret.trim();
|
|
509
511
|
payload.domain = cfgDraft.domain || "feishu";
|
|
512
|
+
} else if (platformId === "telegram") {
|
|
513
|
+
payload.botToken = cfgDraft.botToken.trim();
|
|
514
|
+
payload.proxy = cfgDraft.proxy.trim();
|
|
510
515
|
}
|
|
511
516
|
await act(BRIDGE_ENDPOINTS.platformSetConfig, payload);
|
|
512
517
|
}, [act, cfgDraft, platformId]);
|
|
513
|
-
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")) || platformId === "feishu" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.appSecret !== (platform.config.appSecret ?? "")));
|
|
518
|
+
const cfgDirty = cfgDraft && platform?.config && (Number(cfgDraft.digestIntervalSec) !== platform.config.digestIntervalSec || Number(cfgDraft.approvalTimeoutSec) !== platform.config.approvalTimeoutSec || Number(cfgDraft.maxMessageChars) !== platform.config.maxMessageChars || Number(cfgDraft.sendChunkDelayMs) !== platform.config.sendChunkDelayMs || platformId === "qq" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.clientSecret !== (platform.config.clientSecret ?? "")) || platformId === "feishu" && (cfgDraft.appId !== (platform.config.appId ?? "") || cfgDraft.appSecret !== (platform.config.appSecret ?? "")) || platformId === "telegram" && (cfgDraft.botToken !== "" || cfgDraft.proxy !== (platform.config.proxy ?? "")));
|
|
514
519
|
if (!platform && !err) {
|
|
515
520
|
return React.createElement(
|
|
516
521
|
"div",
|
|
@@ -579,6 +584,18 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
579
584
|
rel: "noopener noreferrer",
|
|
580
585
|
style: s.btnGhost
|
|
581
586
|
}, "\u{1F310} \u98DE\u4E66\u5F00\u653E\u5E73\u53F0"),
|
|
587
|
+
platformId === "telegram" && React.createElement("a", {
|
|
588
|
+
href: "https://github.com/wenbin-wb/dsh-bridge/blob/main/docs/telegram-usage.md",
|
|
589
|
+
target: "_blank",
|
|
590
|
+
rel: "noopener noreferrer",
|
|
591
|
+
style: s.btnGhost
|
|
592
|
+
}, "\u{1F4D6} Telegram \u4F7F\u7528\u8BF4\u660E"),
|
|
593
|
+
platformId === "telegram" && React.createElement("a", {
|
|
594
|
+
href: "https://t.me/BotFather",
|
|
595
|
+
target: "_blank",
|
|
596
|
+
rel: "noopener noreferrer",
|
|
597
|
+
style: s.btnGhost
|
|
598
|
+
}, "\u{1F310} @BotFather \u7533\u8BF7 Bot"),
|
|
582
599
|
React.createElement("button", {
|
|
583
600
|
style: s.btnGhost,
|
|
584
601
|
onClick: () => setShowHelp((v) => !v)
|
|
@@ -724,8 +741,8 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
724
741
|
title: "\u6E05\u9664\u767B\u5F55\u51ED\u8BC1\uFF0C\u4E0B\u6B21\u9700\u91CD\u65B0\u914D\u7F6E"
|
|
725
742
|
}, "\u89E3\u7ED1\u8D26\u53F7")
|
|
726
743
|
),
|
|
727
|
-
//
|
|
728
|
-
platformId === "feishu" && platform.botQr && React.createElement(
|
|
744
|
+
// 飞书 / Telegram 扫码直达对话引导卡片
|
|
745
|
+
(platformId === "feishu" || platformId === "telegram") && platform.botQr && React.createElement(
|
|
729
746
|
"div",
|
|
730
747
|
{
|
|
731
748
|
style: {
|
|
@@ -740,15 +757,15 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
740
757
|
flexWrap: "wrap"
|
|
741
758
|
}
|
|
742
759
|
},
|
|
743
|
-
React.createElement("img", { src: platform.botQr, alt:
|
|
760
|
+
React.createElement("img", { src: platform.botQr, alt: `${platformName} Bot QR`, style: { width: 110, height: 110, borderRadius: 8, border: "1px solid var(--dsw-alias-border-l2,#e5e7eb)", padding: 4, background: "#fff" } }),
|
|
744
761
|
React.createElement(
|
|
745
762
|
"div",
|
|
746
763
|
{ style: { flex: 1, minWidth: 160 } },
|
|
747
|
-
React.createElement("div", { style: { ...s.label, fontSize: 13, fontWeight: 600 } },
|
|
764
|
+
React.createElement("div", { style: { ...s.label, fontSize: 13, fontWeight: 600 } }, `\u{1F4F1} \u624B\u673A ${platformName} \u626B\u7801\u76F4\u8FBE\u5BF9\u8BDD`),
|
|
748
765
|
React.createElement(
|
|
749
766
|
"div",
|
|
750
767
|
{ style: { ...s.muted, fontSize: 12, marginTop: 4, lineHeight: 1.5 } },
|
|
751
|
-
|
|
768
|
+
`\u7528 ${platformName} \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
769
|
),
|
|
753
770
|
platform.botLink && React.createElement(
|
|
754
771
|
"div",
|
|
@@ -758,12 +775,12 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
758
775
|
target: "_blank",
|
|
759
776
|
rel: "noopener noreferrer",
|
|
760
777
|
style: { ...s.btnGhost, textDecoration: "none", display: "inline-flex", alignItems: "center", gap: 4, padding: "4px 10px", fontSize: 12 }
|
|
761
|
-
},
|
|
778
|
+
}, `\u5728 ${platformName} \u5BA2\u6237\u7AEF\u6253\u5F00 \u2197`)
|
|
762
779
|
)
|
|
763
780
|
)
|
|
764
781
|
)
|
|
765
782
|
),
|
|
766
|
-
// 未配置 / 登录中:表单(QQ /
|
|
783
|
+
// 未配置 / 登录中:表单(QQ / 飞书 / Telegram)或二维码(微信)
|
|
767
784
|
(!platform?.configured || showQr) && React.createElement(
|
|
768
785
|
"div",
|
|
769
786
|
{ style: s.block },
|
|
@@ -777,7 +794,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
777
794
|
login.phase === "scaned" ? "\u5DF2\u626B\u7801\uFF0C\u8BF7\u5728\u624B\u673A\u4E0A\u786E\u8BA4\u2026" : platformId === "wechat" ? "\u8BF7\u4F7F\u7528\u5FAE\u4FE1\u626B\u7801\u767B\u5F55\uFF08ClawBot\uFF09" : "\u8BF7\u626B\u7801\u767B\u5F55"
|
|
778
795
|
),
|
|
779
796
|
login.error && React.createElement("div", { style: { ...s.muted, marginTop: 4, color: "var(--dsw-alias-state-warn-primary,#92400e)" } }, login.error)
|
|
780
|
-
) : platformId === "qq" || platformId === "feishu" ? React.createElement(
|
|
797
|
+
) : platformId === "qq" || platformId === "feishu" || platformId === "telegram" ? React.createElement(
|
|
781
798
|
"div",
|
|
782
799
|
{ style: { display: "flex", flexDirection: "column", gap: 10, marginTop: 4 } },
|
|
783
800
|
platformId === "feishu" && React.createElement(
|
|
@@ -797,52 +814,92 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
797
814
|
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
815
|
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
816
|
),
|
|
800
|
-
React.createElement(
|
|
801
|
-
|
|
817
|
+
platformId === "telegram" ? React.createElement(
|
|
818
|
+
React.Fragment,
|
|
802
819
|
null,
|
|
803
820
|
React.createElement(
|
|
804
821
|
"div",
|
|
805
|
-
|
|
806
|
-
|
|
822
|
+
null,
|
|
823
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "Bot Token \u2014 Telegram @BotFather \u4E0B\u53D1\u7684\u673A\u5668\u4EBA Token"),
|
|
824
|
+
React.createElement(
|
|
825
|
+
"div",
|
|
826
|
+
{ style: { display: "flex", gap: 6, alignItems: "center" } },
|
|
827
|
+
React.createElement("input", {
|
|
828
|
+
style: { ...s.input, flex: 1 },
|
|
829
|
+
type: showSecret ? "text" : "password",
|
|
830
|
+
placeholder: "\u8BF7\u8F93\u5165 Telegram Bot Token (\u5982 123456789:ABCdef...)",
|
|
831
|
+
value: cfgDraft?.botToken ?? "",
|
|
832
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, botToken: e.target.value }))
|
|
833
|
+
}),
|
|
834
|
+
React.createElement("button", {
|
|
835
|
+
style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
|
|
836
|
+
onClick: () => setShowSecret((v) => !v),
|
|
837
|
+
type: "button",
|
|
838
|
+
title: showSecret ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u660E\u6587"
|
|
839
|
+
}, showSecret ? "\u{1F648} \u9690\u85CF" : "\u{1F441}\uFE0F \u663E\u793A")
|
|
840
|
+
)
|
|
807
841
|
),
|
|
808
|
-
React.createElement(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
842
|
+
React.createElement(
|
|
843
|
+
"div",
|
|
844
|
+
null,
|
|
845
|
+
React.createElement("div", { style: { ...s.muted, marginBottom: 4 } }, "\u7F51\u7EDC\u4EE3\u7406 (\u53EF\u9009) \u2014 \u652F\u6301\u56FD\u5185 HTTP / HTTPS \u4EE3\u7406"),
|
|
846
|
+
React.createElement("input", {
|
|
847
|
+
style: { ...s.input, width: "100%" },
|
|
848
|
+
placeholder: "\u53EF\u9009\uFF0C\u4F8B\u5982 http://127.0.0.1:7890\uFF08\u4E3A\u7A7A\u5219\u76F4\u8FDE\u6216\u8BFB\u53D6\u73AF\u5883\u53D8\u91CF\uFF09",
|
|
849
|
+
value: cfgDraft?.proxy ?? "",
|
|
850
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, proxy: e.target.value }))
|
|
851
|
+
})
|
|
852
|
+
)
|
|
853
|
+
) : React.createElement(
|
|
854
|
+
React.Fragment,
|
|
817
855
|
null,
|
|
818
856
|
React.createElement(
|
|
819
857
|
"div",
|
|
820
|
-
|
|
821
|
-
|
|
858
|
+
null,
|
|
859
|
+
React.createElement(
|
|
860
|
+
"div",
|
|
861
|
+
{ style: { ...s.muted, marginBottom: 4 } },
|
|
862
|
+
platformId === "qq" ? "AppID \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5E94\u7528 ID" : "App ID \u2014 \u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u81EA\u5EFA\u5E94\u7528 ID (cli_xxx)"
|
|
863
|
+
),
|
|
864
|
+
React.createElement("input", {
|
|
865
|
+
style: { ...s.input, width: "100%" },
|
|
866
|
+
placeholder: platformId === "qq" ? "\u8BF7\u8F93\u5165 AppID" : "\u8BF7\u8F93\u5165 App ID (\u5982 cli_a1b2c3d4...)",
|
|
867
|
+
value: cfgDraft?.appId ?? "",
|
|
868
|
+
onChange: (e) => setCfgDraft((d) => ({ ...d, appId: e.target.value }))
|
|
869
|
+
})
|
|
822
870
|
),
|
|
823
871
|
React.createElement(
|
|
824
872
|
"div",
|
|
825
|
-
|
|
826
|
-
React.createElement(
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
873
|
+
null,
|
|
874
|
+
React.createElement(
|
|
875
|
+
"div",
|
|
876
|
+
{ style: { ...s.muted, marginBottom: 4 } },
|
|
877
|
+
platformId === "qq" ? "ClientSecret \u2014 QQ \u5F00\u653E\u5E73\u53F0\u673A\u5668\u4EBA\u5BC6\u94A5" : "App Secret \u2014 \u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u5E94\u7528\u5BC6\u94A5"
|
|
878
|
+
),
|
|
879
|
+
React.createElement(
|
|
880
|
+
"div",
|
|
881
|
+
{ style: { display: "flex", gap: 6, alignItems: "center" } },
|
|
882
|
+
React.createElement("input", {
|
|
883
|
+
style: { ...s.input, flex: 1 },
|
|
884
|
+
type: showSecret ? "text" : "password",
|
|
885
|
+
placeholder: platformId === "qq" ? "\u8BF7\u8F93\u5165 ClientSecret" : "\u8BF7\u8F93\u5165 App Secret",
|
|
886
|
+
value: platformId === "qq" ? cfgDraft?.clientSecret ?? "" : cfgDraft?.appSecret ?? "",
|
|
887
|
+
onChange: (e) => setCfgDraft((d) => platformId === "qq" ? { ...d, clientSecret: e.target.value } : { ...d, appSecret: e.target.value })
|
|
888
|
+
}),
|
|
889
|
+
React.createElement("button", {
|
|
890
|
+
style: { ...s.btnGhost, height: 32, padding: "0 10px", fontSize: 13, flexShrink: 0 },
|
|
891
|
+
onClick: () => setShowSecret((v) => !v),
|
|
892
|
+
type: "button",
|
|
893
|
+
title: showSecret ? "\u9690\u85CF\u5BC6\u94A5" : "\u663E\u793A\u660E\u6587"
|
|
894
|
+
}, showSecret ? "\u{1F648} \u9690\u85CF" : "\u{1F441}\uFE0F \u663E\u793A")
|
|
895
|
+
)
|
|
839
896
|
)
|
|
840
897
|
),
|
|
841
898
|
React.createElement(
|
|
842
899
|
"div",
|
|
843
900
|
null,
|
|
844
901
|
React.createElement("a", {
|
|
845
|
-
href: platformId === "qq" ? "https://bot.q.qq.com/wiki/develop/api-v2/" : "https://open.feishu.cn/app",
|
|
902
|
+
href: platformId === "qq" ? "https://bot.q.qq.com/wiki/develop/api-v2/" : platformId === "feishu" ? "https://open.feishu.cn/app" : "https://t.me/BotFather",
|
|
846
903
|
target: "_blank",
|
|
847
904
|
rel: "noopener noreferrer",
|
|
848
905
|
style: s.btnLink
|
|
@@ -854,7 +911,7 @@ function PlatformCard({ platformId, platformName, platformDesc, rpcCall, onStatu
|
|
|
854
911
|
React.createElement("button", {
|
|
855
912
|
style: { ...s.btnPri, opacity: busy ? 0.5 : 1 },
|
|
856
913
|
onClick: saveConfig,
|
|
857
|
-
disabled: busy || !cfgDraft?.appId?.trim() || (platformId === "qq" ? !cfgDraft?.clientSecret?.trim() : !cfgDraft?.appSecret?.trim())
|
|
914
|
+
disabled: busy || (platformId === "telegram" ? !cfgDraft?.botToken?.trim() : !cfgDraft?.appId?.trim() || (platformId === "qq" ? !cfgDraft?.clientSecret?.trim() : !cfgDraft?.appSecret?.trim()))
|
|
858
915
|
}, busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5E76\u8FDE\u63A5"),
|
|
859
916
|
login.phase === "error" && React.createElement("div", { style: { ...s.muted, fontSize: 12 } }, login.error ?? "\u8FDE\u63A5\u5931\u8D25")
|
|
860
917
|
)
|