@zzedbot/yunzhijia 1.0.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.

Potentially problematic release.


This version of @zzedbot/yunzhijia might be problematic. Click here for more details.

package/src/utils.ts ADDED
@@ -0,0 +1,119 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { YunzhiJiaAccountConfig } from "./types.js";
3
+
4
+ /**
5
+ * Verify YunzhiJia webhook signature using HmacSHA1 + Base64 (as per YunzhiJia documentation)
6
+ * Note: YunzhiJia documentation shows HmacSHA1 + Base64, but some implementations may use SHA256 + hex
7
+ * We'll implement both and allow configuration to choose
8
+ */
9
+ export function verifySignature(
10
+ body: string,
11
+ signature: string,
12
+ appSecret: string,
13
+ skipSignatureVerification?: boolean,
14
+ ): boolean {
15
+ // Explicitly skip signature verification if configured
16
+ if (skipSignatureVerification) {
17
+ return true;
18
+ }
19
+
20
+ // If no app secret configured, skip verification (for testing)
21
+ if (!appSecret) {
22
+ return true;
23
+ }
24
+
25
+ // Parse the JSON body to extract fields for signature calculation
26
+ try {
27
+ const payload = JSON.parse(body);
28
+ const summaryInfo = [
29
+ payload.robotId,
30
+ payload.robotName,
31
+ payload.operatorOpenid,
32
+ payload.operatorName,
33
+ payload.time?.toString(),
34
+ payload.msgId,
35
+ payload.content
36
+ ].join(",");
37
+
38
+ // Try HmacSHA1 + Base64 first (as per YunzhiJia documentation)
39
+ const hmacSha1 = createHmac('sha1', appSecret);
40
+ hmacSha1.update(summaryInfo);
41
+ const expectedSignatureBase64 = hmacSha1.digest('base64');
42
+
43
+ if (signature === expectedSignatureBase64) {
44
+ return true;
45
+ }
46
+
47
+ // Fallback to SHA256 + hex (current implementation)
48
+ const expectedSignatureSha256 = createHash("sha256")
49
+ .update(body + appSecret)
50
+ .digest("hex");
51
+
52
+ return signature === expectedSignatureSha256;
53
+ } catch (error) {
54
+ console.error("Error parsing payload for signature verification:", error);
55
+ return false;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Create HMAC hash (Node.js compatible)
61
+ */
62
+ function createHmac(algorithm: string, key: string) {
63
+ const crypto = require('node:crypto');
64
+ return crypto.createHmac(algorithm, key);
65
+ }
66
+
67
+ /**
68
+ * Format message for YunzhiJia webhook
69
+ */
70
+ export function formatYunzhiJiaMessage(content: string): Record<string, unknown> {
71
+ return {
72
+ content: content,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Extract user info from YunzhiJia webhook payload
78
+ */
79
+ export function extractUserInfo(payload: Record<string, unknown>) {
80
+ return {
81
+ userId: String(payload.operatorOpenid ?? "unknown"),
82
+ userName: String(payload.operatorName ?? "Unknown User"),
83
+ messageId: String(payload.msgId ?? Date.now().toString()),
84
+ content: String(payload.content ?? ""),
85
+ robotId: String(payload.robotId ?? "unknown"),
86
+ robotName: String(payload.robotName ?? "YunzhiJia Bot"),
87
+ time: Number(payload.time ?? Date.now()),
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Check if bot is mentioned in the message content
93
+ */
94
+ export function isBotMentioned(content: string, robotName: string): boolean {
95
+ // YunzhiJia mentions are typically in the format "@BotName"
96
+ const mentionPattern = new RegExp(`@${robotName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'i');
97
+ return mentionPattern.test(content) || content.includes('@ALL') || content.includes('@all');
98
+ }
99
+
100
+ /**
101
+ * Extract clean content by removing bot mention
102
+ */
103
+ export function extractCleanContent(content: string, robotName: string): string {
104
+ // Remove bot mention patterns
105
+ const cleaned = content
106
+ .replace(new RegExp(`@${robotName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'gi'), '')
107
+ .replace(/@ALL/gi, '')
108
+ .replace(/@all/gi, '')
109
+ .trim();
110
+
111
+ return cleaned || content; // Return original if nothing was cleaned
112
+ }
113
+
114
+ /**
115
+ * Create session key from sessionId and robotId
116
+ */
117
+ export function createSessionKey(sessionId: string, robotId: string): string {
118
+ return `yunzhijia:${robotId}:${sessionId}`;
119
+ }
package/src/webhook.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { YunzhiJiaAccountConfig } from "./types.js";
2
+
3
+ /**
4
+ * Send message to YunzhiJia webhook
5
+ */
6
+ export async function sendToYunzhiJiaWebhook(
7
+ content: string,
8
+ accountConfig: YunzhiJiaAccountConfig,
9
+ ): Promise<void> {
10
+ if (!accountConfig.webhookToken) {
11
+ throw new Error("webhookToken is required");
12
+ }
13
+
14
+ const webhookUrl = `https://www.yunzhijia.com/gateway/robot/webhook/send?yzjtype=12&yzjtoken=${accountConfig.webhookToken}`;
15
+ const message = {
16
+ content: content,
17
+ };
18
+
19
+ try {
20
+ // Use the runtime's fetch implementation for proper error handling and proxy support
21
+ const response = await fetch(webhookUrl, {
22
+ method: 'POST',
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ },
26
+ body: JSON.stringify(message),
27
+ timeout: 10000,
28
+ });
29
+
30
+ if (!response.ok) {
31
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
32
+ }
33
+ } catch (error) {
34
+ console.error("Failed to send message to YunzhiJia:", error);
35
+ throw error;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Send media to YunzhiJia (placeholder - YunzhiJia may not support media directly)
41
+ */
42
+ export async function sendMediaToYunzhiJia(
43
+ content: string,
44
+ mediaUrl: string,
45
+ accountConfig: YunzhiJiaAccountConfig,
46
+ ): Promise<void> {
47
+ // For now, just send the content with media URL as text
48
+ const messageWithMedia = `${content}\n${mediaUrl}`;
49
+ await sendToYunzhiJiaWebhook(messageWithMedia, accountConfig);
50
+ }
@@ -0,0 +1,3 @@
1
+ import { yunzhijiaPlugin } from './src/plugin.js';
2
+
3
+ console.log('Plugin loaded successfully:', yunzhijiaPlugin.id);
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "declaration": true,
11
+ "outDir": "./dist",
12
+ "rootDir": "./src",
13
+ "types": ["node"]
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }
Binary file
Binary file