@zzedbot/yunzhijia 2.0.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.
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 云之家 Channel 插件类型定义
3
+ */
4
+ export interface YunzhiJiaChannelConfig {
5
+ enabled?: boolean;
6
+ accounts?: Record<string, YunzhiJiaAccountConfig>;
7
+ }
8
+ export interface YunzhiJiaAccountConfig {
9
+ enabled?: boolean;
10
+ name?: string;
11
+ appId: string;
12
+ appSecret: string;
13
+ token: string;
14
+ webhookToken?: string;
15
+ encodingAesKey?: string;
16
+ webhookPath?: string;
17
+ webhookPort?: number;
18
+ webhookHost?: string;
19
+ dmPolicy?: "pairing" | "allowlist" | "open" | "disabled";
20
+ allowFrom?: string[];
21
+ groupPolicy?: "open" | "allowlist" | "disabled";
22
+ groupAllowFrom?: string[];
23
+ groups?: Record<string, YunzhiJiaGroupConfig>;
24
+ }
25
+ export interface YunzhiJiaGroupConfig {
26
+ requireMention?: boolean;
27
+ groupPolicy?: "open" | "allowlist";
28
+ allowFrom?: string[];
29
+ }
30
+ /**
31
+ * 云之家消息接收格式 (POST body)
32
+ */
33
+ export interface YunzhiJiaMessage {
34
+ type: number;
35
+ robotId: string;
36
+ robotName: string;
37
+ operatorOpenid: string;
38
+ operatorName: string;
39
+ time: number;
40
+ msgId: string;
41
+ content: string;
42
+ }
43
+ export interface YunzhiJiaWebhookRequest {
44
+ signature: string;
45
+ timestamp: number;
46
+ nonce: string;
47
+ encrypt?: string;
48
+ msg_signature?: string;
49
+ }
50
+ export interface YunzhiJiaSendOptions {
51
+ webhookToken: string;
52
+ content: string;
53
+ notifyParams?: Array<{
54
+ type: string;
55
+ values: string[];
56
+ }>;
57
+ }
58
+ export interface YunzhiJiaSendResult {
59
+ messageId: string;
60
+ success: boolean;
61
+ error?: string;
62
+ }
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IAChD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACnC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 云之家 Channel 插件类型定义
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,28 @@
1
+ import type { YunzhiJiaMessage } from "./types.js";
2
+ export interface WebhookServerOptions {
3
+ accountId: string;
4
+ appId: string;
5
+ appSecret: string;
6
+ token: string;
7
+ webhookPath: string;
8
+ webhookPort: number;
9
+ webhookHost: string;
10
+ onMessage: (message: YunzhiJiaMessage, sessionId: string) => Promise<{
11
+ reply?: string;
12
+ }>;
13
+ log: {
14
+ info: (msg: string) => void;
15
+ warn: (msg: string) => void;
16
+ error: (msg: string) => void;
17
+ };
18
+ abortSignal?: AbortSignal;
19
+ }
20
+ export interface WebhookServer {
21
+ close: () => Promise<void>;
22
+ isRunning: () => boolean;
23
+ }
24
+ /**
25
+ * 启动 Webhook 服务器
26
+ */
27
+ export declare function startWebhookServer(options: WebhookServerOptions): Promise<WebhookServer>;
28
+ //# sourceMappingURL=webhook-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-server.d.ts","sourceRoot":"","sources":["../src/webhook-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzF,GAAG,EAAE;QACH,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC9B,CAAC;IACF,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CA2G9F"}
@@ -0,0 +1,96 @@
1
+ import http from "http";
2
+ /**
3
+ * 启动 Webhook 服务器
4
+ */
5
+ export async function startWebhookServer(options) {
6
+ const { accountId, appSecret, webhookPath, webhookPort, webhookHost, onMessage, log, abortSignal, } = options;
7
+ let server;
8
+ // 创建 HTTP 服务器
9
+ server = http.createServer(async (req, res) => {
10
+ // 只处理 POST 请求
11
+ if (req.method !== "POST" || req.url !== webhookPath) {
12
+ res.statusCode = 404;
13
+ res.end("Not Found");
14
+ return;
15
+ }
16
+ const sessionId = req.headers.sessionid;
17
+ const sign = req.headers.sign;
18
+ if (!sessionId) {
19
+ log.warn(`[${accountId}] Missing sessionId`);
20
+ res.statusCode = 400;
21
+ res.end(JSON.stringify({ success: false, error: "Missing sessionId" }));
22
+ return;
23
+ }
24
+ if (!sign) {
25
+ log.warn(`[${accountId}] Missing sign header`);
26
+ res.statusCode = 401;
27
+ res.end(JSON.stringify({ success: false, error: "Missing sign" }));
28
+ return;
29
+ }
30
+ // 读取请求体
31
+ let body = "";
32
+ for await (const chunk of req) {
33
+ body += chunk;
34
+ }
35
+ try {
36
+ const message = JSON.parse(body);
37
+ // 验证签名 (跳过测试请求)
38
+ if (!isTestRequest(message)) {
39
+ const { verifySignature } = await import("./crypto.js");
40
+ const isValid = verifySignature(sign, message, appSecret);
41
+ if (!isValid) {
42
+ log.warn(`[${accountId}] Invalid signature`);
43
+ res.statusCode = 401;
44
+ res.end(JSON.stringify({ success: false, error: "Invalid signature" }));
45
+ return;
46
+ }
47
+ }
48
+ log.info(`[${accountId}] Received message from ${message.operatorName}: ${message.content}`);
49
+ // 立即返回空响应 (3 秒内)
50
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
51
+ res.end(JSON.stringify({ success: true, data: { type: 2, content: "" } }));
52
+ // 异步处理消息并获取回复
53
+ const result = await onMessage(message, sessionId);
54
+ // 如果有回复,记录日志(实际发送由 channel.ts 处理)
55
+ if (result.reply) {
56
+ log.info(`[${accountId}] AI 回复:${result.reply}`);
57
+ }
58
+ }
59
+ catch (err) {
60
+ log.error(`[${accountId}] Parse error: ${err}`);
61
+ res.statusCode = 400;
62
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON" }));
63
+ }
64
+ });
65
+ return new Promise((resolve, reject) => {
66
+ server.listen(webhookPort, webhookHost, () => {
67
+ log.info(`[${accountId}] Webhook server running at http://${webhookHost}:${webhookPort}${webhookPath}`);
68
+ resolve({
69
+ close: async () => {
70
+ return new Promise((resolveClose) => {
71
+ server.close(() => {
72
+ log.info(`[${accountId}] Webhook server stopped`);
73
+ resolveClose();
74
+ });
75
+ });
76
+ },
77
+ isRunning: () => server.listening,
78
+ });
79
+ });
80
+ server.on("error", (err) => {
81
+ log.error(`[${accountId}] Webhook server error: ${err.message}`);
82
+ reject(err);
83
+ });
84
+ // 监听 abort 信号
85
+ abortSignal?.addEventListener("abort", () => {
86
+ server.close();
87
+ });
88
+ });
89
+ }
90
+ /**
91
+ * 判断是否是测试请求
92
+ */
93
+ function isTestRequest(message) {
94
+ return message.robotId?.startsWith("test-") ?? false;
95
+ }
96
+ //# sourceMappingURL=webhook-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-server.js","sourceRoot":"","sources":["../src/webhook-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "yunzhijia",
3
+ "name": "金蝶云之家",
4
+ "description": "金蝶云之家 (YunzhiJia) 消息渠道插件",
5
+ "channels": ["yunzhijia"],
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "accounts": {
11
+ "type": "object",
12
+ "additionalProperties": {
13
+ "type": "object",
14
+ "properties": {
15
+ "enabled": { "type": "boolean" },
16
+ "name": { "type": "string" },
17
+ "appId": { "type": "string", "description": "云之家应用 ID" },
18
+ "appSecret": { "type": "string", "description": "云之家应用密钥" },
19
+ "token": { "type": "string", "description": "消息接收 Token" },
20
+ "encodingAesKey": { "type": "string", "description": "消息加密密钥" },
21
+ "webhookPath": { "type": "string", "description": "Webhook 路径" },
22
+ "webhookPort": { "type": "integer", "minimum": 1, "maximum": 65535 },
23
+ "webhookHost": { "type": "string" }
24
+ },
25
+ "required": ["appId", "appSecret", "token"]
26
+ }
27
+ }
28
+ }
29
+ },
30
+ "uiHints": {
31
+ "accounts": {
32
+ "label": "账户配置",
33
+ "description": "支持多账户配置"
34
+ }
35
+ }
36
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@zzedbot/yunzhijia",
3
+ "version": "2.0.0",
4
+ "description": "金蝶云之家 Channel 插件",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "test": "vitest"
11
+ },
12
+ "dependencies": {
13
+ "openclaw": "^2026.2.0",
14
+ "axios": "^1.6.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^20.0.0",
18
+ "typescript": "^5.0.0",
19
+ "vitest": "^1.0.0"
20
+ },
21
+ "openclaw": {
22
+ "extensions": ["./dist/index.js"]
23
+ }
24
+ }