@sunnoy/wecom 2.1.0 → 2.2.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.md +6 -2
- package/openclaw.plugin.json +3 -0
- package/package.json +5 -3
- package/skills/wecom-doc/SKILL.md +363 -0
- package/skills/wecom-doc/references/doc-api.md +224 -0
- package/wecom/accounts.js +1 -0
- package/wecom/callback-inbound.js +133 -33
- package/wecom/channel-plugin.js +107 -125
- package/wecom/constants.js +79 -3
- package/wecom/mcp-config.js +146 -0
- package/wecom/media-uploader.js +208 -0
- package/wecom/openclaw-compat.js +302 -0
- package/wecom/reqid-store.js +146 -0
- package/wecom/workspace-template.js +107 -21
- package/wecom/ws-monitor.js +665 -324
- package/image-processor.js +0 -175
package/image-processor.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { createHash } from "crypto";
|
|
2
|
-
import { readFile } from "fs/promises";
|
|
3
|
-
import { logger } from "./logger.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Image Processing Module for WeCom
|
|
7
|
-
*
|
|
8
|
-
* Handles loading, validating, and encoding images for WeCom msg_item
|
|
9
|
-
* Supports JPG and PNG formats up to 10MB
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
// Image format signatures (magic bytes)
|
|
13
|
-
const IMAGE_SIGNATURES = {
|
|
14
|
-
JPG: [0xff, 0xd8, 0xff],
|
|
15
|
-
PNG: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// 10MB size limit (before base64 encoding)
|
|
19
|
-
const MAX_IMAGE_SIZE = 10 * 1024 * 1024;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Process a raw image buffer into a WeCom msg_item payload.
|
|
23
|
-
* @param {Buffer} buffer
|
|
24
|
-
* @returns {{ base64: string, md5: string, format: string, size: number }}
|
|
25
|
-
*/
|
|
26
|
-
export function prepareImageBufferForMsgItem(buffer) {
|
|
27
|
-
validateImageSize(buffer);
|
|
28
|
-
const format = detectImageFormat(buffer);
|
|
29
|
-
const base64 = encodeImageToBase64(buffer);
|
|
30
|
-
const md5 = calculateMD5(buffer);
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
base64,
|
|
34
|
-
md5,
|
|
35
|
-
format,
|
|
36
|
-
size: buffer.length,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Load image file from filesystem
|
|
42
|
-
* @param {string} filePath - Absolute path to image file
|
|
43
|
-
* @returns {Promise<Buffer>} Image data buffer
|
|
44
|
-
* @throws {Error} If file not found or cannot be read
|
|
45
|
-
*/
|
|
46
|
-
export async function loadImageFromPath(filePath) {
|
|
47
|
-
try {
|
|
48
|
-
logger.debug("Loading image from path", { filePath });
|
|
49
|
-
const buffer = await readFile(filePath);
|
|
50
|
-
logger.debug("Image loaded successfully", {
|
|
51
|
-
filePath,
|
|
52
|
-
size: buffer.length,
|
|
53
|
-
});
|
|
54
|
-
return buffer;
|
|
55
|
-
} catch (error) {
|
|
56
|
-
if (error.code === "ENOENT") {
|
|
57
|
-
throw new Error(`Image file not found: ${filePath}`, { cause: error });
|
|
58
|
-
} else if (error.code === "EACCES") {
|
|
59
|
-
throw new Error(`Permission denied reading image: ${filePath}`, { cause: error });
|
|
60
|
-
} else {
|
|
61
|
-
throw new Error(`Failed to read image file: ${error.message}`, { cause: error });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Convert buffer to base64 string
|
|
68
|
-
* @param {Buffer} buffer - Image data buffer
|
|
69
|
-
* @returns {string} Base64-encoded string
|
|
70
|
-
*/
|
|
71
|
-
export function encodeImageToBase64(buffer) {
|
|
72
|
-
return buffer.toString("base64");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Calculate MD5 checksum of buffer
|
|
77
|
-
* @param {Buffer} buffer - Image data buffer
|
|
78
|
-
* @returns {string} MD5 hash in hexadecimal
|
|
79
|
-
*/
|
|
80
|
-
export function calculateMD5(buffer) {
|
|
81
|
-
return createHash("md5").update(buffer).digest("hex");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Validate image size is within limits
|
|
86
|
-
* @param {Buffer} buffer - Image data buffer
|
|
87
|
-
* @throws {Error} If size exceeds 10MB limit
|
|
88
|
-
*/
|
|
89
|
-
export function validateImageSize(buffer) {
|
|
90
|
-
const sizeBytes = buffer.length;
|
|
91
|
-
const sizeMB = (sizeBytes / 1024 / 1024).toFixed(2);
|
|
92
|
-
|
|
93
|
-
if (sizeBytes > MAX_IMAGE_SIZE) {
|
|
94
|
-
throw new Error(`Image size ${sizeMB}MB exceeds 10MB limit (actual: ${sizeBytes} bytes)`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
logger.debug("Image size validated", { sizeBytes, sizeMB });
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Detect image format from magic bytes
|
|
102
|
-
* @param {Buffer} buffer - Image data buffer
|
|
103
|
-
* @returns {string} Format: "JPG" or "PNG"
|
|
104
|
-
* @throws {Error} If format is not supported
|
|
105
|
-
*/
|
|
106
|
-
export function detectImageFormat(buffer) {
|
|
107
|
-
// Check PNG signature
|
|
108
|
-
if (buffer.length >= IMAGE_SIGNATURES.PNG.length) {
|
|
109
|
-
const isPNG = IMAGE_SIGNATURES.PNG.every((byte, index) => buffer[index] === byte);
|
|
110
|
-
if (isPNG) {
|
|
111
|
-
logger.debug("Image format detected: PNG");
|
|
112
|
-
return "PNG";
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Check JPG signature
|
|
117
|
-
if (buffer.length >= IMAGE_SIGNATURES.JPG.length) {
|
|
118
|
-
const isJPG = IMAGE_SIGNATURES.JPG.every((byte, index) => buffer[index] === byte);
|
|
119
|
-
if (isJPG) {
|
|
120
|
-
logger.debug("Image format detected: JPG");
|
|
121
|
-
return "JPG";
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Unknown format
|
|
126
|
-
const header = buffer.subarray(0, 16).toString("hex");
|
|
127
|
-
throw new Error(
|
|
128
|
-
`Unsupported image format. Only JPG and PNG are supported. File header: ${header}`,
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Complete image processing pipeline
|
|
134
|
-
*
|
|
135
|
-
* Loads image from filesystem, validates format and size,
|
|
136
|
-
* then encodes to base64 and calculates MD5 checksum.
|
|
137
|
-
*
|
|
138
|
-
* @param {string} filePath - Absolute path to image file
|
|
139
|
-
* @returns {Promise<Object>} Processed image data
|
|
140
|
-
* @returns {string} return.base64 - Base64-encoded image data
|
|
141
|
-
* @returns {string} return.md5 - MD5 checksum
|
|
142
|
-
* @returns {string} return.format - Image format (JPG or PNG)
|
|
143
|
-
* @returns {number} return.size - Original size in bytes
|
|
144
|
-
*
|
|
145
|
-
* @throws {Error} If any step fails (file not found, invalid format, size exceeded, etc.)
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
* const result = await prepareImageForMsgItem('/path/to/image.jpg');
|
|
149
|
-
* // Returns: { base64: "...", md5: "...", format: "JPG", size: 123456 }
|
|
150
|
-
*/
|
|
151
|
-
export async function prepareImageForMsgItem(filePath) {
|
|
152
|
-
logger.debug("Starting image processing pipeline", { filePath });
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
// Step 1: Load image
|
|
156
|
-
const buffer = await loadImageFromPath(filePath);
|
|
157
|
-
const result = prepareImageBufferForMsgItem(buffer);
|
|
158
|
-
|
|
159
|
-
logger.info("Image processed successfully", {
|
|
160
|
-
filePath,
|
|
161
|
-
format: result.format,
|
|
162
|
-
size: result.size,
|
|
163
|
-
md5: result.md5,
|
|
164
|
-
base64Length: result.base64.length,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
return result;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.error("Image processing failed", {
|
|
170
|
-
filePath,
|
|
171
|
-
error: error.message,
|
|
172
|
-
});
|
|
173
|
-
throw error;
|
|
174
|
-
}
|
|
175
|
-
}
|