@sidleo3/dsh-chat-feishu 0.0.5 → 0.0.6
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/client/index.js +14 -4
- package/host/app-manifest.mjs +71 -0
- package/host/controller.mjs +6 -1
- package/host/index.mjs +1 -1
- package/lib/client.js +15 -4
- package/lib/index.js +50 -2
- package/package.json +4 -1
package/client/index.js
CHANGED
|
@@ -153,6 +153,7 @@ const zh = {
|
|
|
153
153
|
'两种方式:扫码新建一个飞书机器人(推荐,扫码的人就是属主),或手动填已有机器人的凭据。': '两种方式:扫码新建一个飞书机器人(推荐,扫码的人就是属主),或手动填已有机器人的凭据。',
|
|
154
154
|
'扫码新建机器人': '扫码新建机器人',
|
|
155
155
|
'点下面的按钮会向飞书申请一个一次性授权链接:用飞书扫一扫,或在浏览器里打开它,就会自动创建应用并接入。': '点下面的按钮会向飞书申请一个一次性授权链接:用飞书扫一扫,或在浏览器里打开它,就会自动创建应用并接入。',
|
|
156
|
+
'扫码后的确认页会列出这台机器人需要的权限(已经替你选好),确认一次就一起开通;个别权限要管理员审批、应用发布后才生效。': '扫码后的确认页会列出这台机器人需要的权限(已经替你选好),确认一次就一起开通;个别权限要管理员审批、应用发布后才生效。',
|
|
156
157
|
'手动接入已有机器人': '手动接入已有机器人',
|
|
157
158
|
'打开授权页面': '打开授权页面',
|
|
158
159
|
'这台 Host 没能生成二维码,请点上面的链接继续。': '这台 Host 没能生成二维码,请点上面的链接继续。',
|
|
@@ -314,6 +315,7 @@ const en = {
|
|
|
314
315
|
'两种方式:扫码新建一个飞书机器人(推荐,扫码的人就是属主),或手动填已有机器人的凭据。': 'Two ways: scan to create a new Feishu bot (recommended — whoever scans becomes the owner), or paste the credentials of an existing bot.',
|
|
315
316
|
'扫码新建机器人': 'Scan to create a bot',
|
|
316
317
|
'点下面的按钮会向飞书申请一个一次性授权链接:用飞书扫一扫,或在浏览器里打开它,就会自动创建应用并接入。': 'This requests a one-time authorization link from Feishu. Scan it with Feishu (or open it in your browser) and a new app is created and connected automatically.',
|
|
318
|
+
'扫码后的确认页会列出这台机器人需要的权限(已经替你选好),确认一次就一起开通;个别权限要管理员审批、应用发布后才生效。': 'The confirmation page lists the permissions this bot needs, already selected for you — one confirmation grants them together. A few may need admin approval and take effect once the app is published.',
|
|
317
319
|
'手动接入已有机器人': 'Connect an existing bot manually',
|
|
318
320
|
'打开授权页面': 'Open the authorization page',
|
|
319
321
|
'这台 Host 没能生成二维码,请点上面的链接继续。': 'This Host could not render the QR code — use the link above instead.',
|
|
@@ -1000,6 +1002,10 @@ export function FeishuOnboard({ chatUi, connection, translate, onAdded }) {
|
|
|
1000
1002
|
h('h4', { className: 'dchat-onboardTitle' }, t('扫码新建机器人')),
|
|
1001
1003
|
h('p', { className: 'dchat-cardDescription' },
|
|
1002
1004
|
t('点下面的按钮会向飞书申请一个一次性授权链接:用飞书扫一扫,或在浏览器里打开它,就会自动创建应用并接入。')),
|
|
1005
|
+
// 权限是**预填进确认页**的(host 侧 app-manifest.mjs):用户要知道"扫完要点确认"这一步,
|
|
1006
|
+
// 否则会以为扫码就已经全部就绪,遇到还没发布的权限又找不到原因。
|
|
1007
|
+
h('p', { className: 'dchat-cardDescription' },
|
|
1008
|
+
t('扫码后的确认页会列出这台机器人需要的权限(已经替你选好),确认一次就一起开通;个别权限要管理员审批、应用发布后才生效。')),
|
|
1003
1009
|
h('div', { className: 'dchat-actions' },
|
|
1004
1010
|
h('button', {
|
|
1005
1011
|
type: 'button',
|
|
@@ -1023,14 +1029,18 @@ export function FeishuOnboard({ chatUi, connection, translate, onAdded }) {
|
|
|
1023
1029
|
height: 200,
|
|
1024
1030
|
})
|
|
1025
1031
|
: null,
|
|
1026
|
-
//
|
|
1027
|
-
|
|
1032
|
+
// 二维码与链接**两条路都给**(真机反馈:二维码出来之后链接就没了):桌面端直接点链接
|
|
1033
|
+
// 比"掏手机扫屏幕"省事,手机端扫码更快——两种都留着,谁方便用谁。
|
|
1034
|
+
// 只有 Host 编不出二维码(缺 `qrcode` 模块)时,链接才是唯一的路,这时补一句说明。
|
|
1035
|
+
scan.verificationUrl
|
|
1028
1036
|
? h('div', null,
|
|
1029
1037
|
h('a', {
|
|
1030
1038
|
className: 'dchat-onboardLink', href: scan.verificationUrl, target: '_blank', rel: 'noreferrer',
|
|
1031
1039
|
}, t('打开授权页面')),
|
|
1032
|
-
|
|
1033
|
-
|
|
1040
|
+
!scan.qrCodeDataUrl
|
|
1041
|
+
? h('p', { className: 'dchat-cardDescription' },
|
|
1042
|
+
`${t('这台 Host 没能生成二维码,请点上面的链接继续。')}`)
|
|
1043
|
+
: null)
|
|
1034
1044
|
: null,
|
|
1035
1045
|
h('p', { className: 'dchat-cardDescription', role: 'status' },
|
|
1036
1046
|
scan.remainingSeconds !== null && scan.remainingSeconds !== undefined && scanActive
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 飞书「扫码新建」时预填给用户的应用清单:权限、事件订阅、卡片回调。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要有这个文件:SDK 的 `registerApp` 支持 `addons`,它把清单**预填进扫码后的确认页**
|
|
5
|
+
* ——用户在飞书里扫一下、在确认页点一次确认,这些权限就一起开通了;不带它就只能建出一个
|
|
6
|
+
* 什么权限都没有的空应用,用户还得自己去开放平台逐个勾选(本仓库早期就是这样,
|
|
7
|
+
* 真机上表现为"扫码建好了、一发消息就缺权限",只能靠设置页那串开通链接救场)。
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ 两条平台口径(照 `@larksuiteoapi/node-sdk` 1.73.0 README 的「一键创建应用」节):
|
|
10
|
+
* - **平台只校验数据形状、不校验权限点是否存在**:写错的 id 会被确认页**静默忽略**,
|
|
11
|
+
* "明明声明了、怎么还是没权限"就是这么来的。改这份清单必须对着开放平台权限目录核实,
|
|
12
|
+
* 不能凭印象拼 id。
|
|
13
|
+
* - `addons` 只能**增量叠加公开配置**(应用/用户身份权限、事件、回调);事件**订阅方式(长连接)**、
|
|
14
|
+
* 回调地址、加密 key 属敏感配置,传不了,仍要在开放平台里人工确认。
|
|
15
|
+
*
|
|
16
|
+
* 清单口径 = **本渠道代码真的会调的接口**(见 `lark-gateway.mjs`),不"顺手多要":
|
|
17
|
+
* 多要来的数据面(通讯录全员、云文档、日历)只会扩大暴露面,也让管理员更难批。
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-chat-feishu/app-manifest
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 租户(应用身份)权限:每一行都对应 `lark-gateway.mjs` 里一处真实调用,
|
|
24
|
+
* 删掉哪一行就会出现对应的"缺权限"降级(表情打不上 / 白名单只有 id / 图片发不出)。
|
|
25
|
+
*/
|
|
26
|
+
export const FEISHU_APP_TENANT_SCOPES = Object.freeze([
|
|
27
|
+
// 收消息(事件 `im.message.receive_v1` 的准入权限,三个场景各一条)
|
|
28
|
+
'im:message.p2p_msg:readonly', // 私聊消息
|
|
29
|
+
'im:message.group_at_msg:readonly', // 群里 @ 机器人
|
|
30
|
+
'im:message.group_at_msg.include_bot:readonly', // 群里其它机器人 @ 机器人
|
|
31
|
+
// 消息本身
|
|
32
|
+
'im:message:readonly', // 读消息正文 / 下载消息里的图片与文件
|
|
33
|
+
'im:message:send_as_bot', // 发消息、回消息、更新卡片
|
|
34
|
+
'im:message.reactions:write_only', // 「在做了」表情(缺了不致命,但表情一直打不上)
|
|
35
|
+
// 资源
|
|
36
|
+
'im:resource', // 上传机器人要发的图片 / 文件
|
|
37
|
+
// 把 id 换成名字(白名单、投递目标;查不到时设置页只能显示一串 ou_…)
|
|
38
|
+
'im:chat:readonly', // 群名、群成员
|
|
39
|
+
'contact:user.base:readonly', // 人名
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/** 收消息事件(长连接推过来的就是它)。 */
|
|
43
|
+
export const FEISHU_APP_EVENTS = Object.freeze(['im.message.receive_v1']);
|
|
44
|
+
|
|
45
|
+
/** 卡片回调(卡片上的按钮/下拉点了之后要靠它回来;缺了就是"点了没反应")。 */
|
|
46
|
+
export const FEISHU_APP_CALLBACKS = Object.freeze(['card.action.trigger']);
|
|
47
|
+
|
|
48
|
+
/** 预填到确认页的应用信息(扫码的人仍可在页面上改)。`{user}` 会换成扫码人的名字。 */
|
|
49
|
+
export const FEISHU_APP_PRESET = Object.freeze({
|
|
50
|
+
name: '{user} 的 DSH 助手',
|
|
51
|
+
desc: '连接飞书与 DeepSeek Harness,在聊天里使用 AI 助手。',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 扫码新建时交给 SDK `registerApp` 的参数。
|
|
56
|
+
*
|
|
57
|
+
* `createOnly: true` 是**安全项**:没有它,落地页会让用户选"创建新应用 / 更新已有应用",
|
|
58
|
+
* 误选一台正在用的应用并确认,就会去改那台应用的配置(这条路是"新建",不该碰别人的应用)。
|
|
59
|
+
*/
|
|
60
|
+
export const FEISHU_SCAN_REGISTER_OPTIONS = Object.freeze({
|
|
61
|
+
createOnly: true,
|
|
62
|
+
appPreset: FEISHU_APP_PRESET,
|
|
63
|
+
addons: Object.freeze({
|
|
64
|
+
// `preset: false` = 不要平台的默认模板,从「仅机器人能力、无业务权限」起,
|
|
65
|
+
// 确认页上就只是上面声明的这几项(默认模板里有什么、版本间会不会变,我们不去猜)。
|
|
66
|
+
preset: false,
|
|
67
|
+
scopes: Object.freeze({ tenant: FEISHU_APP_TENANT_SCOPES }),
|
|
68
|
+
events: Object.freeze({ items: Object.freeze({ tenant: FEISHU_APP_EVENTS }) }),
|
|
69
|
+
callbacks: Object.freeze({ items: FEISHU_APP_CALLBACKS }),
|
|
70
|
+
}),
|
|
71
|
+
});
|
package/host/controller.mjs
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { createHash } from 'node:crypto';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
|
|
12
|
+
import { FEISHU_SCAN_REGISTER_OPTIONS } from './app-manifest.mjs';
|
|
12
13
|
import { createFeishuBridge } from './bridge.mjs';
|
|
13
14
|
import { createFeishuConfigStore } from './config-store.mjs';
|
|
14
15
|
import { createLarkCli, normalizeLarkUserIdentity } from './lark-cli.mjs';
|
|
@@ -1129,10 +1130,14 @@ export function createFeishuController({ deps, logger = console, config = {}, in
|
|
|
1129
1130
|
* (回显二维码/链接、剩余秒数、失败原因、成功后的机器人)、`cancel` 取消。
|
|
1130
1131
|
* 二维码由 host 转成 data URL(`qrcode` 是构建期外置依赖):拿不到就只回链接,
|
|
1131
1132
|
* 前端照样能让人打开——**不静默**,状态里会写清为什么没有二维码。
|
|
1133
|
+
*
|
|
1134
|
+
* 带上 `FEISHU_SCAN_REGISTER_OPTIONS`:把本渠道真的要用的权限/事件/回调**预填进确认页**,
|
|
1135
|
+
* 用户扫完在页面上点一次确认就一起开通;同时 `createOnly` 钉死"只能新建",
|
|
1136
|
+
* 免得误选一台正在用的应用去改它的配置。清单口径见 `app-manifest.mjs`。
|
|
1132
1137
|
*/
|
|
1133
1138
|
'bot.register.start': async () => ({
|
|
1134
1139
|
ok: true,
|
|
1135
|
-
value: await provisionStatus(provision.start()),
|
|
1140
|
+
value: await provisionStatus(provision.start(FEISHU_SCAN_REGISTER_OPTIONS)),
|
|
1136
1141
|
}),
|
|
1137
1142
|
|
|
1138
1143
|
'bot.register.status': async () => ({ ok: true, value: await provisionStatus(provision.status()) }),
|
package/host/index.mjs
CHANGED
package/lib/client.js
CHANGED
|
@@ -174,6 +174,7 @@ var zh = {
|
|
|
174
174
|
"\u4E24\u79CD\u65B9\u5F0F\uFF1A\u626B\u7801\u65B0\u5EFA\u4E00\u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\uFF08\u63A8\u8350\uFF0C\u626B\u7801\u7684\u4EBA\u5C31\u662F\u5C5E\u4E3B\uFF09\uFF0C\u6216\u624B\u52A8\u586B\u5DF2\u6709\u673A\u5668\u4EBA\u7684\u51ED\u636E\u3002": "\u4E24\u79CD\u65B9\u5F0F\uFF1A\u626B\u7801\u65B0\u5EFA\u4E00\u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\uFF08\u63A8\u8350\uFF0C\u626B\u7801\u7684\u4EBA\u5C31\u662F\u5C5E\u4E3B\uFF09\uFF0C\u6216\u624B\u52A8\u586B\u5DF2\u6709\u673A\u5668\u4EBA\u7684\u51ED\u636E\u3002",
|
|
175
175
|
"\u626B\u7801\u65B0\u5EFA\u673A\u5668\u4EBA": "\u626B\u7801\u65B0\u5EFA\u673A\u5668\u4EBA",
|
|
176
176
|
"\u70B9\u4E0B\u9762\u7684\u6309\u94AE\u4F1A\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u4E2A\u4E00\u6B21\u6027\u6388\u6743\u94FE\u63A5\uFF1A\u7528\u98DE\u4E66\u626B\u4E00\u626B\uFF0C\u6216\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u5B83\uFF0C\u5C31\u4F1A\u81EA\u52A8\u521B\u5EFA\u5E94\u7528\u5E76\u63A5\u5165\u3002": "\u70B9\u4E0B\u9762\u7684\u6309\u94AE\u4F1A\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u4E2A\u4E00\u6B21\u6027\u6388\u6743\u94FE\u63A5\uFF1A\u7528\u98DE\u4E66\u626B\u4E00\u626B\uFF0C\u6216\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u5B83\uFF0C\u5C31\u4F1A\u81EA\u52A8\u521B\u5EFA\u5E94\u7528\u5E76\u63A5\u5165\u3002",
|
|
177
|
+
"\u626B\u7801\u540E\u7684\u786E\u8BA4\u9875\u4F1A\u5217\u51FA\u8FD9\u53F0\u673A\u5668\u4EBA\u9700\u8981\u7684\u6743\u9650\uFF08\u5DF2\u7ECF\u66FF\u4F60\u9009\u597D\uFF09\uFF0C\u786E\u8BA4\u4E00\u6B21\u5C31\u4E00\u8D77\u5F00\u901A\uFF1B\u4E2A\u522B\u6743\u9650\u8981\u7BA1\u7406\u5458\u5BA1\u6279\u3001\u5E94\u7528\u53D1\u5E03\u540E\u624D\u751F\u6548\u3002": "\u626B\u7801\u540E\u7684\u786E\u8BA4\u9875\u4F1A\u5217\u51FA\u8FD9\u53F0\u673A\u5668\u4EBA\u9700\u8981\u7684\u6743\u9650\uFF08\u5DF2\u7ECF\u66FF\u4F60\u9009\u597D\uFF09\uFF0C\u786E\u8BA4\u4E00\u6B21\u5C31\u4E00\u8D77\u5F00\u901A\uFF1B\u4E2A\u522B\u6743\u9650\u8981\u7BA1\u7406\u5458\u5BA1\u6279\u3001\u5E94\u7528\u53D1\u5E03\u540E\u624D\u751F\u6548\u3002",
|
|
177
178
|
"\u624B\u52A8\u63A5\u5165\u5DF2\u6709\u673A\u5668\u4EBA": "\u624B\u52A8\u63A5\u5165\u5DF2\u6709\u673A\u5668\u4EBA",
|
|
178
179
|
"\u6253\u5F00\u6388\u6743\u9875\u9762": "\u6253\u5F00\u6388\u6743\u9875\u9762",
|
|
179
180
|
"\u8FD9\u53F0 Host \u6CA1\u80FD\u751F\u6210\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u70B9\u4E0A\u9762\u7684\u94FE\u63A5\u7EE7\u7EED\u3002": "\u8FD9\u53F0 Host \u6CA1\u80FD\u751F\u6210\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u70B9\u4E0A\u9762\u7684\u94FE\u63A5\u7EE7\u7EED\u3002",
|
|
@@ -325,6 +326,7 @@ var en = {
|
|
|
325
326
|
"\u4E24\u79CD\u65B9\u5F0F\uFF1A\u626B\u7801\u65B0\u5EFA\u4E00\u4E2A\u98DE\u4E66\u673A\u5668\u4EBA\uFF08\u63A8\u8350\uFF0C\u626B\u7801\u7684\u4EBA\u5C31\u662F\u5C5E\u4E3B\uFF09\uFF0C\u6216\u624B\u52A8\u586B\u5DF2\u6709\u673A\u5668\u4EBA\u7684\u51ED\u636E\u3002": "Two ways: scan to create a new Feishu bot (recommended \u2014 whoever scans becomes the owner), or paste the credentials of an existing bot.",
|
|
326
327
|
"\u626B\u7801\u65B0\u5EFA\u673A\u5668\u4EBA": "Scan to create a bot",
|
|
327
328
|
"\u70B9\u4E0B\u9762\u7684\u6309\u94AE\u4F1A\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u4E2A\u4E00\u6B21\u6027\u6388\u6743\u94FE\u63A5\uFF1A\u7528\u98DE\u4E66\u626B\u4E00\u626B\uFF0C\u6216\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u5B83\uFF0C\u5C31\u4F1A\u81EA\u52A8\u521B\u5EFA\u5E94\u7528\u5E76\u63A5\u5165\u3002": "This requests a one-time authorization link from Feishu. Scan it with Feishu (or open it in your browser) and a new app is created and connected automatically.",
|
|
329
|
+
"\u626B\u7801\u540E\u7684\u786E\u8BA4\u9875\u4F1A\u5217\u51FA\u8FD9\u53F0\u673A\u5668\u4EBA\u9700\u8981\u7684\u6743\u9650\uFF08\u5DF2\u7ECF\u66FF\u4F60\u9009\u597D\uFF09\uFF0C\u786E\u8BA4\u4E00\u6B21\u5C31\u4E00\u8D77\u5F00\u901A\uFF1B\u4E2A\u522B\u6743\u9650\u8981\u7BA1\u7406\u5458\u5BA1\u6279\u3001\u5E94\u7528\u53D1\u5E03\u540E\u624D\u751F\u6548\u3002": "The confirmation page lists the permissions this bot needs, already selected for you \u2014 one confirmation grants them together. A few may need admin approval and take effect once the app is published.",
|
|
328
330
|
"\u624B\u52A8\u63A5\u5165\u5DF2\u6709\u673A\u5668\u4EBA": "Connect an existing bot manually",
|
|
329
331
|
"\u6253\u5F00\u6388\u6743\u9875\u9762": "Open the authorization page",
|
|
330
332
|
"\u8FD9\u53F0 Host \u6CA1\u80FD\u751F\u6210\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u70B9\u4E0A\u9762\u7684\u94FE\u63A5\u7EE7\u7EED\u3002": "This Host could not render the QR code \u2014 use the link above instead.",
|
|
@@ -940,6 +942,13 @@ function FeishuOnboard({ chatUi, connection, translate, onAdded }) {
|
|
|
940
942
|
{ className: "dchat-cardDescription" },
|
|
941
943
|
t("\u70B9\u4E0B\u9762\u7684\u6309\u94AE\u4F1A\u5411\u98DE\u4E66\u7533\u8BF7\u4E00\u4E2A\u4E00\u6B21\u6027\u6388\u6743\u94FE\u63A5\uFF1A\u7528\u98DE\u4E66\u626B\u4E00\u626B\uFF0C\u6216\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u5B83\uFF0C\u5C31\u4F1A\u81EA\u52A8\u521B\u5EFA\u5E94\u7528\u5E76\u63A5\u5165\u3002")
|
|
942
944
|
),
|
|
945
|
+
// 权限是**预填进确认页**的(host 侧 app-manifest.mjs):用户要知道"扫完要点确认"这一步,
|
|
946
|
+
// 否则会以为扫码就已经全部就绪,遇到还没发布的权限又找不到原因。
|
|
947
|
+
h(
|
|
948
|
+
"p",
|
|
949
|
+
{ className: "dchat-cardDescription" },
|
|
950
|
+
t("\u626B\u7801\u540E\u7684\u786E\u8BA4\u9875\u4F1A\u5217\u51FA\u8FD9\u53F0\u673A\u5668\u4EBA\u9700\u8981\u7684\u6743\u9650\uFF08\u5DF2\u7ECF\u66FF\u4F60\u9009\u597D\uFF09\uFF0C\u786E\u8BA4\u4E00\u6B21\u5C31\u4E00\u8D77\u5F00\u901A\uFF1B\u4E2A\u522B\u6743\u9650\u8981\u7BA1\u7406\u5458\u5BA1\u6279\u3001\u5E94\u7528\u53D1\u5E03\u540E\u624D\u751F\u6548\u3002")
|
|
951
|
+
),
|
|
943
952
|
h(
|
|
944
953
|
"div",
|
|
945
954
|
{ className: "dchat-actions" },
|
|
@@ -966,8 +975,10 @@ function FeishuOnboard({ chatUi, connection, translate, onAdded }) {
|
|
|
966
975
|
width: 200,
|
|
967
976
|
height: 200
|
|
968
977
|
}) : null,
|
|
969
|
-
//
|
|
970
|
-
|
|
978
|
+
// 二维码与链接**两条路都给**(真机反馈:二维码出来之后链接就没了):桌面端直接点链接
|
|
979
|
+
// 比"掏手机扫屏幕"省事,手机端扫码更快——两种都留着,谁方便用谁。
|
|
980
|
+
// 只有 Host 编不出二维码(缺 `qrcode` 模块)时,链接才是唯一的路,这时补一句说明。
|
|
981
|
+
scan.verificationUrl ? h(
|
|
971
982
|
"div",
|
|
972
983
|
null,
|
|
973
984
|
h("a", {
|
|
@@ -976,11 +987,11 @@ function FeishuOnboard({ chatUi, connection, translate, onAdded }) {
|
|
|
976
987
|
target: "_blank",
|
|
977
988
|
rel: "noreferrer"
|
|
978
989
|
}, t("\u6253\u5F00\u6388\u6743\u9875\u9762")),
|
|
979
|
-
h(
|
|
990
|
+
!scan.qrCodeDataUrl ? h(
|
|
980
991
|
"p",
|
|
981
992
|
{ className: "dchat-cardDescription" },
|
|
982
993
|
`${t("\u8FD9\u53F0 Host \u6CA1\u80FD\u751F\u6210\u4E8C\u7EF4\u7801\uFF0C\u8BF7\u70B9\u4E0A\u9762\u7684\u94FE\u63A5\u7EE7\u7EED\u3002")}`
|
|
983
|
-
)
|
|
994
|
+
) : null
|
|
984
995
|
) : null,
|
|
985
996
|
h(
|
|
986
997
|
"p",
|
package/lib/index.js
CHANGED
|
@@ -126480,6 +126480,50 @@ ${lines.join("\n")}
|
|
|
126480
126480
|
import { createHash } from "node:crypto";
|
|
126481
126481
|
import { join } from "node:path";
|
|
126482
126482
|
|
|
126483
|
+
// packages/dsh-chat-feishu/host/app-manifest.mjs
|
|
126484
|
+
var FEISHU_APP_TENANT_SCOPES = Object.freeze([
|
|
126485
|
+
// 收消息(事件 `im.message.receive_v1` 的准入权限,三个场景各一条)
|
|
126486
|
+
"im:message.p2p_msg:readonly",
|
|
126487
|
+
// 私聊消息
|
|
126488
|
+
"im:message.group_at_msg:readonly",
|
|
126489
|
+
// 群里 @ 机器人
|
|
126490
|
+
"im:message.group_at_msg.include_bot:readonly",
|
|
126491
|
+
// 群里其它机器人 @ 机器人
|
|
126492
|
+
// 消息本身
|
|
126493
|
+
"im:message:readonly",
|
|
126494
|
+
// 读消息正文 / 下载消息里的图片与文件
|
|
126495
|
+
"im:message:send_as_bot",
|
|
126496
|
+
// 发消息、回消息、更新卡片
|
|
126497
|
+
"im:message.reactions:write_only",
|
|
126498
|
+
// 「在做了」表情(缺了不致命,但表情一直打不上)
|
|
126499
|
+
// 资源
|
|
126500
|
+
"im:resource",
|
|
126501
|
+
// 上传机器人要发的图片 / 文件
|
|
126502
|
+
// 把 id 换成名字(白名单、投递目标;查不到时设置页只能显示一串 ou_…)
|
|
126503
|
+
"im:chat:readonly",
|
|
126504
|
+
// 群名、群成员
|
|
126505
|
+
"contact:user.base:readonly"
|
|
126506
|
+
// 人名
|
|
126507
|
+
]);
|
|
126508
|
+
var FEISHU_APP_EVENTS = Object.freeze(["im.message.receive_v1"]);
|
|
126509
|
+
var FEISHU_APP_CALLBACKS = Object.freeze(["card.action.trigger"]);
|
|
126510
|
+
var FEISHU_APP_PRESET = Object.freeze({
|
|
126511
|
+
name: "{user} \u7684 DSH \u52A9\u624B",
|
|
126512
|
+
desc: "\u8FDE\u63A5\u98DE\u4E66\u4E0E DeepSeek Harness\uFF0C\u5728\u804A\u5929\u91CC\u4F7F\u7528 AI \u52A9\u624B\u3002"
|
|
126513
|
+
});
|
|
126514
|
+
var FEISHU_SCAN_REGISTER_OPTIONS = Object.freeze({
|
|
126515
|
+
createOnly: true,
|
|
126516
|
+
appPreset: FEISHU_APP_PRESET,
|
|
126517
|
+
addons: Object.freeze({
|
|
126518
|
+
// `preset: false` = 不要平台的默认模板,从「仅机器人能力、无业务权限」起,
|
|
126519
|
+
// 确认页上就只是上面声明的这几项(默认模板里有什么、版本间会不会变,我们不去猜)。
|
|
126520
|
+
preset: false,
|
|
126521
|
+
scopes: Object.freeze({ tenant: FEISHU_APP_TENANT_SCOPES }),
|
|
126522
|
+
events: Object.freeze({ items: Object.freeze({ tenant: FEISHU_APP_EVENTS }) }),
|
|
126523
|
+
callbacks: Object.freeze({ items: FEISHU_APP_CALLBACKS })
|
|
126524
|
+
})
|
|
126525
|
+
});
|
|
126526
|
+
|
|
126483
126527
|
// packages/dsh-chat-feishu/host/bridge.mjs
|
|
126484
126528
|
import { stat } from "node:fs/promises";
|
|
126485
126529
|
|
|
@@ -131575,10 +131619,14 @@ function createFeishuController({ deps, logger = console, config = {}, internals
|
|
|
131575
131619
|
* (回显二维码/链接、剩余秒数、失败原因、成功后的机器人)、`cancel` 取消。
|
|
131576
131620
|
* 二维码由 host 转成 data URL(`qrcode` 是构建期外置依赖):拿不到就只回链接,
|
|
131577
131621
|
* 前端照样能让人打开——**不静默**,状态里会写清为什么没有二维码。
|
|
131622
|
+
*
|
|
131623
|
+
* 带上 `FEISHU_SCAN_REGISTER_OPTIONS`:把本渠道真的要用的权限/事件/回调**预填进确认页**,
|
|
131624
|
+
* 用户扫完在页面上点一次确认就一起开通;同时 `createOnly` 钉死"只能新建",
|
|
131625
|
+
* 免得误选一台正在用的应用去改它的配置。清单口径见 `app-manifest.mjs`。
|
|
131578
131626
|
*/
|
|
131579
131627
|
"bot.register.start": async () => ({
|
|
131580
131628
|
ok: true,
|
|
131581
|
-
value: await provisionStatus(provision.start())
|
|
131629
|
+
value: await provisionStatus(provision.start(FEISHU_SCAN_REGISTER_OPTIONS))
|
|
131582
131630
|
}),
|
|
131583
131631
|
"bot.register.status": async () => ({ ok: true, value: await provisionStatus(provision.status()) }),
|
|
131584
131632
|
"bot.register.cancel": async () => ({ ok: true, value: await provisionStatus(provision.cancel()) }),
|
|
@@ -131937,7 +131985,7 @@ function createFeishuController({ deps, logger = console, config = {}, internals
|
|
|
131937
131985
|
}
|
|
131938
131986
|
|
|
131939
131987
|
// packages/dsh-chat-feishu/host/index.mjs
|
|
131940
|
-
var CHANNEL_VERSION = "0.0.
|
|
131988
|
+
var CHANNEL_VERSION = "0.0.6";
|
|
131941
131989
|
var name = "dsh-chat-feishu-host";
|
|
131942
131990
|
var inject = ["dshChat"];
|
|
131943
131991
|
var EXPECTED_CONTRACT = 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sidleo3/dsh-chat-feishu",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "dsh-chat 飞书渠道插件(凭据、长连接、私聊/群聊、任务过程展示)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@larksuiteoapi/node-sdk": "1.73.0"
|
|
41
41
|
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"qrcode": "^1.5.4"
|
|
44
|
+
},
|
|
42
45
|
"publishConfig": {
|
|
43
46
|
"access": "public"
|
|
44
47
|
},
|