@xmanrui/dsh-im 0.1.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/LICENSE +21 -0
- package/README.md +143 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/bin/dsh-im.mjs +99 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +6211 -0
- package/lib/index.js +13339 -0
- package/package.json +73 -0
- package/plugin-src/client/build.mjs +38 -0
- package/plugin-src/client/channel-logos.js +107 -0
- package/plugin-src/client/channels/dingtalk/api.js +203 -0
- package/plugin-src/client/channels/dingtalk/index.js +709 -0
- package/plugin-src/client/channels/dingtalk/styles.js +140 -0
- package/plugin-src/client/channels/discord/api.js +11 -0
- package/plugin-src/client/channels/discord/index.js +25 -0
- package/plugin-src/client/channels/discord/styles.js +19 -0
- package/plugin-src/client/channels/feishu/api.js +325 -0
- package/plugin-src/client/channels/feishu/index.js +986 -0
- package/plugin-src/client/channels/feishu/styles.js +505 -0
- package/plugin-src/client/channels/qq/api.js +119 -0
- package/plugin-src/client/channels/qq/index.js +402 -0
- package/plugin-src/client/channels/qq/styles.js +19 -0
- package/plugin-src/client/channels/shared/token-api.js +88 -0
- package/plugin-src/client/channels/shared/token-channel.js +277 -0
- package/plugin-src/client/channels/telegram/api.js +11 -0
- package/plugin-src/client/channels/telegram/index.js +25 -0
- package/plugin-src/client/channels/telegram/styles.js +19 -0
- package/plugin-src/client/channels/wecom/api.js +119 -0
- package/plugin-src/client/channels/wecom/index.js +402 -0
- package/plugin-src/client/channels/wecom/styles.js +19 -0
- package/plugin-src/client/channels/weixin/api.js +156 -0
- package/plugin-src/client/channels/weixin/index.js +577 -0
- package/plugin-src/client/channels/weixin/styles.js +120 -0
- package/plugin-src/client/credential-binding.js +117 -0
- package/plugin-src/client/index.js +192 -0
- package/plugin-src/client/lifecycle.js +86 -0
- package/plugin-src/client/styles.js +192 -0
- package/plugin-src/host/build.mjs +24 -0
- package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
- package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
- package/plugin-src/host/channels/discord/index.mjs +30 -0
- package/plugin-src/host/channels/discord/production.mjs +17 -0
- package/plugin-src/host/channels/discord/rpc.mjs +21 -0
- package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
- package/plugin-src/host/channels/feishu/controller.mjs +172 -0
- package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
- package/plugin-src/host/channels/feishu/index.mjs +73 -0
- package/plugin-src/host/channels/feishu/production.mjs +138 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
- package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/qq/index.mjs +30 -0
- package/plugin-src/host/channels/qq/production.mjs +109 -0
- package/plugin-src/host/channels/qq/rpc.mjs +146 -0
- package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
- package/plugin-src/host/channels/shared/production.mjs +104 -0
- package/plugin-src/host/channels/shared/rpc.mjs +119 -0
- package/plugin-src/host/channels/telegram/index.mjs +30 -0
- package/plugin-src/host/channels/telegram/production.mjs +17 -0
- package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
- package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
- package/plugin-src/host/channels/wecom/index.mjs +36 -0
- package/plugin-src/host/channels/wecom/production.mjs +113 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
- package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
- package/plugin-src/host/channels/weixin/index.mjs +39 -0
- package/plugin-src/host/channels/weixin/production.mjs +119 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
- package/plugin-src/host/index.mjs +44 -0
- package/plugin-src/host/rpc-authority.mjs +11 -0
- package/scripts/verify-package.mjs +97 -0
- package/src/channels/dingtalk/config-store.mjs +282 -0
- package/src/channels/dingtalk/device-auth.mjs +237 -0
- package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
- package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
- package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
- package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
- package/src/channels/dingtalk/harness-client.mjs +299 -0
- package/src/channels/dingtalk/state-store.mjs +212 -0
- package/src/channels/discord/config-store.mjs +24 -0
- package/src/channels/discord/discord-api.mjs +153 -0
- package/src/channels/discord/discord-bridge.mjs +15 -0
- package/src/channels/discord/discord-controller.mjs +16 -0
- package/src/channels/discord/discord-runtime.mjs +460 -0
- package/src/channels/discord/harness-client.mjs +3 -0
- package/src/channels/discord/state-store.mjs +3 -0
- package/src/channels/feishu/bridge.mjs +216 -0
- package/src/channels/feishu/config.mjs +71 -0
- package/src/channels/feishu/feishu-app.mjs +51 -0
- package/src/channels/feishu/feishu-channel.mjs +153 -0
- package/src/channels/feishu/feishu-runtime.mjs +222 -0
- package/src/channels/feishu/harness-client.mjs +268 -0
- package/src/channels/feishu/message-utils.mjs +51 -0
- package/src/channels/feishu/multi-bot-controller.mjs +686 -0
- package/src/channels/feishu/plugin-config-store.mjs +204 -0
- package/src/channels/feishu/plugin-controller.mjs +248 -0
- package/src/channels/feishu/registration-manager.mjs +345 -0
- package/src/channels/feishu/state-store.mjs +71 -0
- package/src/channels/qq/config-store.mjs +171 -0
- package/src/channels/qq/harness-client.mjs +3 -0
- package/src/channels/qq/qq-bridge.mjs +177 -0
- package/src/channels/qq/qq-controller.mjs +452 -0
- package/src/channels/qq/qq-runtime.mjs +208 -0
- package/src/channels/qq/qr-auth.mjs +24 -0
- package/src/channels/qq/state-store.mjs +96 -0
- package/src/channels/shared/conversation-state-store.mjs +107 -0
- package/src/channels/shared/editable-message-stream.mjs +81 -0
- package/src/channels/shared/text-harness-bridge.mjs +180 -0
- package/src/channels/shared/token-bot-controller.mjs +298 -0
- package/src/channels/shared/token-config-store.mjs +180 -0
- package/src/channels/telegram/config-store.mjs +24 -0
- package/src/channels/telegram/harness-client.mjs +3 -0
- package/src/channels/telegram/state-store.mjs +3 -0
- package/src/channels/telegram/telegram-api.mjs +120 -0
- package/src/channels/telegram/telegram-bridge.mjs +15 -0
- package/src/channels/telegram/telegram-controller.mjs +16 -0
- package/src/channels/telegram/telegram-runtime.mjs +278 -0
- package/src/channels/wecom/config-store.mjs +170 -0
- package/src/channels/wecom/harness-client.mjs +3 -0
- package/src/channels/wecom/qr-auth.mjs +95 -0
- package/src/channels/wecom/state-store.mjs +90 -0
- package/src/channels/wecom/wecom-bridge.mjs +243 -0
- package/src/channels/wecom/wecom-controller.mjs +474 -0
- package/src/channels/wecom/wecom-runtime.mjs +209 -0
- package/src/channels/weixin/config-store.mjs +182 -0
- package/src/channels/weixin/harness-client.mjs +259 -0
- package/src/channels/weixin/state-store.mjs +112 -0
- package/src/channels/weixin/weixin-api.mjs +319 -0
- package/src/channels/weixin/weixin-bridge.mjs +173 -0
- package/src/channels/weixin/weixin-controller.mjs +545 -0
- package/src/channels/weixin/weixin-runtime.mjs +204 -0
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xmanrui/dsh-im",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "通过扫码或机器人凭据把IM机器人接入DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Telegram和Discord)。",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/xmanrui/dsh-im.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/xmanrui/dsh-im#readme",
|
|
12
|
+
"bugs": "https://github.com/xmanrui/dsh-im/issues",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"dsh-im": "./bin/dsh-im.mjs"
|
|
18
|
+
},
|
|
19
|
+
"main": "./lib/index.js",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": "./lib/index.js",
|
|
22
|
+
"./client": "./lib/client.js",
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"bin",
|
|
27
|
+
"cordis.patch.yml",
|
|
28
|
+
"lib",
|
|
29
|
+
"plugin-src",
|
|
30
|
+
"scripts",
|
|
31
|
+
"src",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md",
|
|
34
|
+
"THIRD_PARTY_NOTICES.md"
|
|
35
|
+
],
|
|
36
|
+
"dsh": {
|
|
37
|
+
"bundle": {
|
|
38
|
+
"patch": "./cordis.patch.yml"
|
|
39
|
+
},
|
|
40
|
+
"client": {
|
|
41
|
+
"inject": [
|
|
42
|
+
"@deepseek-ai/dsh-client-connection",
|
|
43
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
44
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
45
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
46
|
+
"@deepseek-ai/dsh-client-locale"
|
|
47
|
+
],
|
|
48
|
+
"platform": "web"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "node plugin-src/client/build.mjs && node plugin-src/host/build.mjs",
|
|
53
|
+
"test": "node --test test/*.test.mjs test/channels/*/*.test.mjs",
|
|
54
|
+
"check": "npm test && npm run build && node scripts/verify-package.mjs"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22.19"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@larksuiteoapi/node-sdk": "1.73.0",
|
|
61
|
+
"@tencent-connect/qqbot-connector": "1.2.0",
|
|
62
|
+
"@tencent-connect/qqbot-nodejs": "1.0.4",
|
|
63
|
+
"@wecom/aibot-node-sdk": "1.0.7",
|
|
64
|
+
"dingtalk-stream": "2.1.4",
|
|
65
|
+
"qrcode": "1.5.4"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"esbuild": "0.25.9",
|
|
69
|
+
"react": "18.3.1",
|
|
70
|
+
"react-dom": "18.3.1",
|
|
71
|
+
"react-test-renderer": "18.3.1"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
import { build } from 'esbuild';
|
|
6
|
+
|
|
7
|
+
const sourceDirectory = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = resolve(sourceDirectory, '../..');
|
|
9
|
+
const outputPath = resolve(packageRoot, 'lib/client.js');
|
|
10
|
+
const loaderId = process.env.DSH_IM_CLIENT_ID ?? '@xmanrui/dsh-im';
|
|
11
|
+
|
|
12
|
+
const result = await build({
|
|
13
|
+
entryPoints: [resolve(sourceDirectory, 'index.js')],
|
|
14
|
+
bundle: true,
|
|
15
|
+
format: 'cjs',
|
|
16
|
+
platform: 'browser',
|
|
17
|
+
target: ['chrome100'],
|
|
18
|
+
external: ['react'],
|
|
19
|
+
write: false,
|
|
20
|
+
minify: process.env.NODE_ENV === 'production',
|
|
21
|
+
legalComments: 'none',
|
|
22
|
+
});
|
|
23
|
+
const bundled = result.outputFiles?.[0]?.text;
|
|
24
|
+
if (!bundled) throw new Error('esbuild did not produce a client bundle');
|
|
25
|
+
|
|
26
|
+
const wrapped = `window.__ModuleLoader__.load({
|
|
27
|
+
id: ${JSON.stringify(loaderId)},
|
|
28
|
+
factory: (require) => {
|
|
29
|
+
var module = { exports: {} };
|
|
30
|
+
var exports = module.exports;
|
|
31
|
+
${bundled}
|
|
32
|
+
return module.exports;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
`;
|
|
36
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
37
|
+
await writeFile(outputPath, wrapped, 'utf8');
|
|
38
|
+
console.log(`Wrote ${outputPath}`);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
const h = React.createElement;
|
|
4
|
+
|
|
5
|
+
function dimensions(size) {
|
|
6
|
+
return size === undefined ? {} : { width: size, height: size };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function WeixinLogoGlyph({ size } = {}) {
|
|
10
|
+
return h('svg', {
|
|
11
|
+
...dimensions(size),
|
|
12
|
+
viewBox: '0 0 24 24',
|
|
13
|
+
focusable: 'false',
|
|
14
|
+
'aria-hidden': 'true',
|
|
15
|
+
'data-im-channel-logo': 'weixin',
|
|
16
|
+
}, h('path', {
|
|
17
|
+
fill: 'currentColor',
|
|
18
|
+
d: 'M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178A1.17 1.17 0 0 1 4.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 0 1-1.162 1.178 1.17 1.17 0 0 1-1.162-1.178c0-.651.52-1.18 1.162-1.18zm5.34 2.867c-1.797-.052-3.746.512-5.28 1.786-1.72 1.428-2.687 3.72-1.78 6.22.942 2.453 3.666 4.229 6.884 4.229.826 0 1.622-.12 2.361-.336a.722.722 0 0 1 .598.082l1.584.926a.272.272 0 0 0 .14.047c.134 0 .24-.111.24-.247 0-.06-.023-.12-.038-.177l-.327-1.233a.582.582 0 0 1-.023-.156.49.49 0 0 1 .201-.398C23.024 18.48 24 16.82 24 14.98c0-3.21-2.931-5.837-6.656-6.088V8.89c-.135-.01-.27-.027-.407-.03zm-2.53 3.274c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.97-.982zm4.844 0c.535 0 .969.44.969.982a.976.976 0 0 1-.969.983.976.976 0 0 1-.969-.983c0-.542.434-.982.969-.982z',
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function FeishuLogoGlyph({ size } = {}) {
|
|
23
|
+
return h('svg', {
|
|
24
|
+
...dimensions(size),
|
|
25
|
+
viewBox: '0 0 24 24',
|
|
26
|
+
focusable: 'false',
|
|
27
|
+
'aria-hidden': 'true',
|
|
28
|
+
'data-im-channel-logo': 'feishu',
|
|
29
|
+
},
|
|
30
|
+
h('path', { fill: '#00D6B9', d: 'M7.2 4.5h7.6c1.2 0 2.1.55 2.7 1.58 1.05 1.8 1.55 3.45 1.58 4.95-2.04-.62-4.2-.15-6.22 1.45C11.3 9.7 9.42 7.04 7.2 4.5Z' }),
|
|
31
|
+
h('path', { fill: '#1456B8', d: 'M10.8 13.55c3.3-2.93 5.72-4.24 9.47-2.52-1.2 1.45-2.27 4.18-3.86 5.43-1.67 1.31-3.9.5-5.61-.64v-2.27Z' }),
|
|
32
|
+
h('path', { fill: '#3370FF', d: 'M4.4 8.35c3.47 3.61 7.25 6.1 10.33 5.7 1.06-.14 2.2-.72 3.4-1.72-1.04 2.65-2.6 4.8-5.06 6-2.46 1.2-5.56.52-7.42-.72A2.76 2.76 0 0 1 4.4 15.3V8.35Z' }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function DingtalkLogoGlyph({ size } = {}) {
|
|
36
|
+
return h('svg', {
|
|
37
|
+
...dimensions(size),
|
|
38
|
+
viewBox: '0 0 48 48',
|
|
39
|
+
focusable: 'false',
|
|
40
|
+
'aria-hidden': 'true',
|
|
41
|
+
'data-im-channel-logo': 'dingtalk',
|
|
42
|
+
}, h('path', {
|
|
43
|
+
fill: 'currentColor',
|
|
44
|
+
d: 'M37.05 22.783c-6.758-5.216-14.378-12.128-22.73-19.538-.655-.585-1.242-.354-1.536.42-1.88 4.973-.058 9.386 2.889 11.932s7.368 4.912 10.058 6.155c.105.049.013.203-.093.163-4.953-2.182-8.397-3.765-13.07-7.368-.497-.388-1.01-.242-1.07.521-.384 4.748 2.657 8.483 6.058 9.745 2.1.781 4.398 1.212 6.53 1.474.109.015.084.178-.027.178-2.747.01-6.058-.654-8.935-1.751-.606-.233-.818.25-.722.633.491 2.008 2.974 5.076 6.926 5.73a12 12 0 0 0 2.228.115c.164 0 .208.089.154.217q-2.685 4.6-2.803 4.797c-.091.152-.036.275.156.275h3.543c.164 0 .264.106.18.246l-4.958 8.196c-.191.328.035.565.395.301s15.212-11.133 15.636-11.448c.195-.142.148-.327-.124-.327h-3.18c-.206 0-.252-.14-.111-.28.14-.141 3.602-3.594 4.837-4.888 1.283-1.35 1.938-3.825-.231-5.498',
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function QqLogoGlyph({ size } = {}) {
|
|
49
|
+
return h('svg', {
|
|
50
|
+
...dimensions(size),
|
|
51
|
+
viewBox: '0 0 24 24',
|
|
52
|
+
focusable: 'false',
|
|
53
|
+
'aria-hidden': 'true',
|
|
54
|
+
'data-im-channel-logo': 'qq',
|
|
55
|
+
}, h('path', {
|
|
56
|
+
fill: 'currentColor',
|
|
57
|
+
d: 'M21.395 15.035a40 40 0 0 0-.803-2.264l-1.079-2.695c.001-.032.014-.562.014-.836C19.526 4.632 17.351 0 12 0S4.474 4.632 4.474 9.241c0 .274.013.804.014.836l-1.08 2.695a39 39 0 0 0-.802 2.264c-1.021 3.283-.69 4.643-.438 4.673.54.065 2.103-2.472 2.103-2.472 0 1.469.756 3.387 2.394 4.771-.612.188-1.363.479-1.845.835-.434.32-.379.646-.301.778.343.578 5.883.369 7.482.189 1.6.18 7.14.389 7.483-.189.078-.132.132-.458-.301-.778-.483-.356-1.233-.646-1.846-.836 1.637-1.384 2.393-3.302 2.393-4.771 0 0 1.563 2.537 2.103 2.472.251-.03.581-1.39-.438-4.673',
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function WecomLogoGlyph({ size } = {}) {
|
|
62
|
+
return h('svg', {
|
|
63
|
+
...dimensions(size),
|
|
64
|
+
viewBox: '0 0 24 24',
|
|
65
|
+
focusable: 'false',
|
|
66
|
+
'aria-hidden': 'true',
|
|
67
|
+
'data-im-channel-logo': 'wecom',
|
|
68
|
+
},
|
|
69
|
+
h('path', {
|
|
70
|
+
fill: 'none',
|
|
71
|
+
stroke: '#3370FF',
|
|
72
|
+
strokeWidth: '2.35',
|
|
73
|
+
strokeLinecap: 'round',
|
|
74
|
+
strokeLinejoin: 'round',
|
|
75
|
+
d: 'M17.7 14.5c1.05-1.12 1.65-2.52 1.65-4.03 0-3.82-3.58-6.92-8-6.92s-8 3.1-8 6.92 3.58 6.92 8 6.92c1.17 0 2.28-.22 3.28-.62',
|
|
76
|
+
}),
|
|
77
|
+
h('path', { fill: '#07C160', d: 'M16.1 15.15c.7-.7 1.83-.7 2.53 0s.7 1.83 0 2.53-1.83.7-2.53 0-.7-1.83 0-2.53Z' }),
|
|
78
|
+
h('path', { fill: '#FFB800', d: 'M19.25 13.45a1.36 1.36 0 1 1 1.92 1.92 1.36 1.36 0 0 1-1.92-1.92Z' }),
|
|
79
|
+
h('path', { fill: '#FF7A00', d: 'M19.55 18.05a1.16 1.16 0 1 1 1.64 1.64 1.16 1.16 0 0 1-1.64-1.64Z' }),
|
|
80
|
+
h('path', { fill: '#3370FF', d: 'M15.25 18.75a.92.92 0 1 1 1.3 1.3.92.92 0 0 1-1.3-1.3Z' }));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function TelegramLogoGlyph({ size } = {}) {
|
|
84
|
+
return h('svg', {
|
|
85
|
+
...dimensions(size),
|
|
86
|
+
viewBox: '0 0 24 24',
|
|
87
|
+
focusable: 'false',
|
|
88
|
+
'aria-hidden': 'true',
|
|
89
|
+
'data-im-channel-logo': 'telegram',
|
|
90
|
+
}, h('path', {
|
|
91
|
+
fill: 'currentColor',
|
|
92
|
+
d: 'M23.95 4.57c-.36-1.45-1.43-1.76-2.82-1.24L1.5 10.9c-1.34.52-1.32 1.27-.24 1.6l5.03 1.57 11.66-7.36c.55-.34 1.05-.16.64.21l-9.44 8.52-.37 5.12c.54 0 .78-.24 1.08-.53l2.59-2.51 5.38 3.97c.99.55 1.7.27 1.95-.92L23.95 4.57Z',
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function DiscordLogoGlyph({ size } = {}) {
|
|
97
|
+
return h('svg', {
|
|
98
|
+
...dimensions(size),
|
|
99
|
+
viewBox: '0 0 24 24',
|
|
100
|
+
focusable: 'false',
|
|
101
|
+
'aria-hidden': 'true',
|
|
102
|
+
'data-im-channel-logo': 'discord',
|
|
103
|
+
}, h('path', {
|
|
104
|
+
fill: 'currentColor',
|
|
105
|
+
d: 'M20.32 4.37a19.8 19.8 0 0 0-4.89-1.51c-.21.38-.46.89-.63 1.29a18.4 18.4 0 0 0-5.59 0 13 13 0 0 0-.64-1.29c-1.71.29-3.36.8-4.89 1.52C.59 9.09-.25 13.68.17 18.2a19.9 19.9 0 0 0 6 3.04c.48-.66.91-1.36 1.28-2.1-.7-.26-1.37-.58-2-.96.17-.12.33-.25.49-.38 3.86 1.79 8.04 1.79 11.86 0 .16.13.32.26.49.38-.64.38-1.31.7-2.01.97.37.73.8 1.44 1.28 2.09a19.8 19.8 0 0 0 6-3.04c.49-5.24-.84-9.79-3.24-13.83ZM8.02 15.42c-1.16 0-2.11-1.07-2.11-2.38s.93-2.38 2.11-2.38c1.18 0 2.13 1.08 2.11 2.38 0 1.31-.93 2.38-2.11 2.38Zm7.95 0c-1.16 0-2.11-1.07-2.11-2.38s.93-2.38 2.11-2.38c1.18 0 2.13 1.08 2.11 2.38 0 1.31-.93 2.38-2.11 2.38Z',
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
export const DINGTALK_RPC_CHANNEL = '/dingtalk';
|
|
2
|
+
|
|
3
|
+
export const DINGTALK_ENDPOINTS = Object.freeze({
|
|
4
|
+
status: 'connection.status',
|
|
5
|
+
beginProvisioning: 'provision.begin',
|
|
6
|
+
pollProvisioning: 'provision.poll',
|
|
7
|
+
cancelProvisioning: 'provision.cancel',
|
|
8
|
+
bindCredentials: 'bot.bind-credentials',
|
|
9
|
+
reconnectBot: 'bot.reconnect',
|
|
10
|
+
deleteBot: 'bot.delete',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
|
|
14
|
+
const SNAPSHOT_STATES = new Set(['disconnected', 'offline', 'provisioning', 'connected', 'degraded']);
|
|
15
|
+
const PROVISION_STATES = new Set([
|
|
16
|
+
'starting',
|
|
17
|
+
'pending',
|
|
18
|
+
'scanned',
|
|
19
|
+
'authorizing',
|
|
20
|
+
'creating',
|
|
21
|
+
'connecting',
|
|
22
|
+
'connected',
|
|
23
|
+
'expired',
|
|
24
|
+
'failed',
|
|
25
|
+
'cancelled',
|
|
26
|
+
]);
|
|
27
|
+
const HEALTH_STATES = new Set(['healthy', 'checking', 'degraded', 'offline']);
|
|
28
|
+
const FORBIDDEN_ERROR_FIELDS = /(client[_-]?secret|secret[_-]?ref|device[_-]?code|app[_-]?secret|access[_-]?token|token)/i;
|
|
29
|
+
const QR_DATA_URL = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
|
|
30
|
+
const MAX_QR_SOURCE_LENGTH = 2 * 1024 * 1024;
|
|
31
|
+
|
|
32
|
+
function isRecord(value) {
|
|
33
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function optionalString(value, maxLength = 240) {
|
|
37
|
+
if (typeof value !== 'string') return undefined;
|
|
38
|
+
const trimmed = value.trim();
|
|
39
|
+
return trimmed ? trimmed.slice(0, maxLength) : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function opaqueId(value) {
|
|
43
|
+
const id = optionalString(value, 128);
|
|
44
|
+
return id && /^[a-z\d_-]+$/i.test(id) ? id : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function timestamp(value) {
|
|
48
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
49
|
+
if (typeof value === 'string' && value.trim()) {
|
|
50
|
+
const parsed = Date.parse(value);
|
|
51
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function nonNegativeInteger(value) {
|
|
57
|
+
const number = Number(value);
|
|
58
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function clamp(value, min, max, fallback) {
|
|
62
|
+
const number = Number(value);
|
|
63
|
+
return Number.isFinite(number) ? Math.min(max, Math.max(min, number)) : fallback;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function safeErrorCode(value, fallback) {
|
|
67
|
+
const code = optionalString(value, 80);
|
|
68
|
+
return code && /^[a-z][a-z\d_.:-]*$/i.test(code) && !FORBIDDEN_ERROR_FIELDS.test(code)
|
|
69
|
+
? code
|
|
70
|
+
: fallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sanitizeMessage(value, fallback) {
|
|
74
|
+
const message = optionalString(value, 480) ?? fallback;
|
|
75
|
+
if (FORBIDDEN_ERROR_FIELDS.test(message)) return fallback;
|
|
76
|
+
return message.replace(/([=:]\s*)[^\s,;,。]+/g, '$1••••••').slice(0, 240);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeError(value, fallbackCode, fallbackMessage) {
|
|
80
|
+
if (!isRecord(value)) return undefined;
|
|
81
|
+
return {
|
|
82
|
+
code: safeErrorCode(value.code, fallbackCode),
|
|
83
|
+
message: sanitizeMessage(value.message, fallbackMessage),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function unwrapRpcResult(result) {
|
|
88
|
+
if (!isRecord(result) || typeof result.ok !== 'boolean') {
|
|
89
|
+
throw new Error('钉钉服务返回了无法识别的响应');
|
|
90
|
+
}
|
|
91
|
+
if (!result.ok) {
|
|
92
|
+
const error = new Error(sanitizeMessage(result.error?.message, '钉钉操作失败'));
|
|
93
|
+
error.code = safeErrorCode(result.error?.code, 'DINGTALK_RPC_ERROR');
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
return result.value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function safeQrSource(value) {
|
|
100
|
+
if (typeof value !== 'string' || value.length > MAX_QR_SOURCE_LENGTH) return undefined;
|
|
101
|
+
return QR_DATA_URL.test(value) ? value : undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function normalizeProvisioning(value, now = Date.now()) {
|
|
105
|
+
const source = isRecord(value?.provisioning) ? value.provisioning : value;
|
|
106
|
+
if (!isRecord(source)) throw new Error('钉钉服务没有返回扫码绑定进度');
|
|
107
|
+
const attemptId = opaqueId(source.attemptId);
|
|
108
|
+
if (!attemptId) throw new Error('钉钉扫码服务没有返回有效的绑定任务');
|
|
109
|
+
|
|
110
|
+
const reportedStatus = optionalString(source.status, 32) ?? optionalString(source.state, 32);
|
|
111
|
+
const status = PROVISION_STATES.has(reportedStatus) ? reportedStatus : 'failed';
|
|
112
|
+
const expiresAt = timestamp(source.expiresAt)
|
|
113
|
+
?? now + clamp(source.expiresIn, 1, 2 * 60 * 60, 10 * 60) * 1_000;
|
|
114
|
+
const result = {
|
|
115
|
+
attemptId,
|
|
116
|
+
status,
|
|
117
|
+
expiresAt,
|
|
118
|
+
pollIntervalMs: clamp(source.pollIntervalMs, 1_000, 10_000, 3_000),
|
|
119
|
+
};
|
|
120
|
+
const qrCodeDataUrl = safeQrSource(source.qrCodeDataUrl);
|
|
121
|
+
if (qrCodeDataUrl) result.qrCodeDataUrl = qrCodeDataUrl;
|
|
122
|
+
if (opaqueId(source.botId)) result.botId = opaqueId(source.botId);
|
|
123
|
+
if (source.alreadyConnected === true) result.alreadyConnected = true;
|
|
124
|
+
const error = normalizeError(
|
|
125
|
+
source.error,
|
|
126
|
+
'DINGTALK_PROVISION_FAILED',
|
|
127
|
+
'钉钉机器人没有接入完成',
|
|
128
|
+
);
|
|
129
|
+
if (error) result.error = error;
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeBot(value) {
|
|
134
|
+
if (!isRecord(value)) return undefined;
|
|
135
|
+
const botId = opaqueId(value.botId);
|
|
136
|
+
if (!botId) return undefined;
|
|
137
|
+
const bot = isRecord(value.bot) ? value.bot : {};
|
|
138
|
+
const connected = value.connected === true;
|
|
139
|
+
const reportedState = ACCOUNT_STATES.has(value.state) ? value.state : 'offline';
|
|
140
|
+
const state = connected ? 'connected' : reportedState === 'connected' ? 'connecting' : reportedState;
|
|
141
|
+
const health = isRecord(value.health) ? value.health : {};
|
|
142
|
+
const stats = isRecord(value.stats) ? value.stats : {};
|
|
143
|
+
return {
|
|
144
|
+
botId,
|
|
145
|
+
state,
|
|
146
|
+
connected,
|
|
147
|
+
configured: value.configured !== false,
|
|
148
|
+
bot: {
|
|
149
|
+
name: optionalString(bot.name, 100) ?? '钉钉机器人',
|
|
150
|
+
clientIdMasked: optionalString(bot.clientIdMasked, 140) ?? '已安全保存',
|
|
151
|
+
},
|
|
152
|
+
health: {
|
|
153
|
+
status: HEALTH_STATES.has(health.status)
|
|
154
|
+
? health.status
|
|
155
|
+
: connected ? 'healthy' : 'offline',
|
|
156
|
+
summary: optionalString(health.summary, 200)
|
|
157
|
+
?? (connected ? '钉钉 Stream 长连接运行正常' : '钉钉连接尚未就绪'),
|
|
158
|
+
lastCheckedAt: timestamp(health.lastCheckedAt),
|
|
159
|
+
lastConnectedAt: timestamp(health.lastConnectedAt),
|
|
160
|
+
},
|
|
161
|
+
stats: {
|
|
162
|
+
messagesReceived: nonNegativeInteger(stats.messagesReceived),
|
|
163
|
+
messagesReplied: nonNegativeInteger(stats.messagesReplied),
|
|
164
|
+
},
|
|
165
|
+
error: normalizeError(value.error, 'DINGTALK_ACCOUNT_ERROR', '钉钉连接尚未就绪') ?? null,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function normalizeSnapshot(value) {
|
|
170
|
+
const source = isRecord(value?.snapshot) ? value.snapshot : value;
|
|
171
|
+
if (!isRecord(source) || !Array.isArray(source.bots)) {
|
|
172
|
+
throw new Error('钉钉服务没有返回有效的机器人列表');
|
|
173
|
+
}
|
|
174
|
+
const seen = new Set();
|
|
175
|
+
const bots = source.bots.map(normalizeBot).filter((bot) => {
|
|
176
|
+
if (!bot || seen.has(bot.botId)) return false;
|
|
177
|
+
seen.add(bot.botId);
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
return {
|
|
181
|
+
schemaVersion: Number.isSafeInteger(source.schemaVersion) ? source.schemaVersion : 1,
|
|
182
|
+
revision: nonNegativeInteger(source.revision),
|
|
183
|
+
state: SNAPSHOT_STATES.has(source.state) ? source.state : 'offline',
|
|
184
|
+
bots,
|
|
185
|
+
totals: {
|
|
186
|
+
configured: bots.length,
|
|
187
|
+
connected: bots.filter((bot) => bot.connected).length,
|
|
188
|
+
},
|
|
189
|
+
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function presentError(error) {
|
|
194
|
+
return {
|
|
195
|
+
code: safeErrorCode(error?.code, 'DINGTALK_ERROR'),
|
|
196
|
+
message: sanitizeMessage(error?.message, '钉钉操作失败,请稍后重试'),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function formatRemaining(milliseconds) {
|
|
201
|
+
const seconds = Math.max(0, Math.ceil(Number(milliseconds) / 1_000) || 0);
|
|
202
|
+
return `${String(Math.floor(seconds / 60)).padStart(2, '0')}:${String(seconds % 60).padStart(2, '0')}`;
|
|
203
|
+
}
|