codex-weixin 0.2.1
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/NOTICE +5 -0
- package/README.en.md +200 -0
- package/README.md +210 -0
- package/dist/bridge/access.d.ts +21 -0
- package/dist/bridge/access.js +31 -0
- package/dist/bridge/access.js.map +1 -0
- package/dist/bridge/actions.d.ts +21 -0
- package/dist/bridge/actions.js +120 -0
- package/dist/bridge/actions.js.map +1 -0
- package/dist/bridge/errors.d.ts +1 -0
- package/dist/bridge/errors.js +15 -0
- package/dist/bridge/errors.js.map +1 -0
- package/dist/bridge/format.d.ts +14 -0
- package/dist/bridge/format.js +62 -0
- package/dist/bridge/format.js.map +1 -0
- package/dist/bridge/prompt-buffer.d.ts +45 -0
- package/dist/bridge/prompt-buffer.js +56 -0
- package/dist/bridge/prompt-buffer.js.map +1 -0
- package/dist/bridge/service.d.ts +38 -0
- package/dist/bridge/service.js +292 -0
- package/dist/bridge/service.js.map +1 -0
- package/dist/codex/app-server-runner.d.ts +74 -0
- package/dist/codex/app-server-runner.js +514 -0
- package/dist/codex/app-server-runner.js.map +1 -0
- package/dist/codex/exec-runner.d.ts +36 -0
- package/dist/codex/exec-runner.js +153 -0
- package/dist/codex/exec-runner.js.map +1 -0
- package/dist/codex/runner.d.ts +22 -0
- package/dist/codex/runner.js +57 -0
- package/dist/codex/runner.js.map +1 -0
- package/dist/codex/sandbox.d.ts +3 -0
- package/dist/codex/sandbox.js +15 -0
- package/dist/codex/sandbox.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/server/account-manager.d.ts +94 -0
- package/dist/server/account-manager.js +468 -0
- package/dist/server/account-manager.js.map +1 -0
- package/dist/server/arguments.d.ts +3 -0
- package/dist/server/arguments.js +25 -0
- package/dist/server/arguments.js.map +1 -0
- package/dist/server/http-server.d.ts +23 -0
- package/dist/server/http-server.js +481 -0
- package/dist/server/http-server.js.map +1 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +69 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/login-manager.d.ts +27 -0
- package/dist/server/login-manager.js +49 -0
- package/dist/server/login-manager.js.map +1 -0
- package/dist/server/process-lock.d.ts +5 -0
- package/dist/server/process-lock.js +63 -0
- package/dist/server/process-lock.js.map +1 -0
- package/dist/state/config.d.ts +19 -0
- package/dist/state/config.js +41 -0
- package/dist/state/config.js.map +1 -0
- package/dist/state/json-store.d.ts +3 -0
- package/dist/state/json-store.js +23 -0
- package/dist/state/json-store.js.map +1 -0
- package/dist/state/paths.d.ts +12 -0
- package/dist/state/paths.js +28 -0
- package/dist/state/paths.js.map +1 -0
- package/dist/state/runtime-state.d.ts +57 -0
- package/dist/state/runtime-state.js +212 -0
- package/dist/state/runtime-state.js.map +1 -0
- package/dist/web/app.js +958 -0
- package/dist/web/index.html +273 -0
- package/dist/web/styles.css +324 -0
- package/dist/web/vendor/lucide.min.js +12 -0
- package/dist/web/vendor/marked.umd.js +79 -0
- package/dist/web/vendor/purify.min.js +3 -0
- package/dist/weixin/accounts.d.ts +22 -0
- package/dist/weixin/accounts.js +66 -0
- package/dist/weixin/accounts.js.map +1 -0
- package/dist/weixin/api.d.ts +75 -0
- package/dist/weixin/api.js +191 -0
- package/dist/weixin/api.js.map +1 -0
- package/dist/weixin/login.d.ts +29 -0
- package/dist/weixin/login.js +100 -0
- package/dist/weixin/login.js.map +1 -0
- package/dist/weixin/media.d.ts +32 -0
- package/dist/weixin/media.js +266 -0
- package/dist/weixin/media.js.map +1 -0
- package/dist/weixin/messages.d.ts +24 -0
- package/dist/weixin/messages.js +87 -0
- package/dist/weixin/messages.js.map +1 -0
- package/dist/weixin/monitor.d.ts +22 -0
- package/dist/weixin/monitor.js +110 -0
- package/dist/weixin/monitor.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/weixin/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type WeixinAccount } from "./accounts.js";
|
|
2
|
+
import type { FetchLike } from "./api.js";
|
|
3
|
+
import type { StatePaths } from "../state/paths.js";
|
|
4
|
+
export type LoginOptions = {
|
|
5
|
+
paths: StatePaths;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
fetch?: FetchLike;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
pollMs?: number;
|
|
10
|
+
};
|
|
11
|
+
export type CreateQrLoginOptions = Omit<LoginOptions, "paths" | "pollMs">;
|
|
12
|
+
export type QrLoginUpdate = {
|
|
13
|
+
status: "waiting" | "scanned" | "expired";
|
|
14
|
+
} | {
|
|
15
|
+
status: "confirmed";
|
|
16
|
+
account: WeixinAccount;
|
|
17
|
+
};
|
|
18
|
+
export declare class WeixinQrLoginSession {
|
|
19
|
+
readonly qrContent: string;
|
|
20
|
+
private readonly qrToken;
|
|
21
|
+
private readonly fetchImpl;
|
|
22
|
+
private readonly deadline;
|
|
23
|
+
readonly expiresAt: string;
|
|
24
|
+
private pollBaseUrl;
|
|
25
|
+
constructor(qrContent: string, qrToken: string, baseUrl: string, fetchImpl: FetchLike, deadline: number);
|
|
26
|
+
poll(): Promise<QrLoginUpdate>;
|
|
27
|
+
}
|
|
28
|
+
export declare function createQrLoginSession(options?: CreateQrLoginOptions): Promise<WeixinQrLoginSession>;
|
|
29
|
+
export declare function loginWithQr(options: LoginOptions): Promise<WeixinAccount>;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { DEFAULT_BASE_URL, DEFAULT_CDN_BASE_URL, saveAccount } from "./accounts.js";
|
|
2
|
+
export class WeixinQrLoginSession {
|
|
3
|
+
qrContent;
|
|
4
|
+
qrToken;
|
|
5
|
+
fetchImpl;
|
|
6
|
+
deadline;
|
|
7
|
+
expiresAt;
|
|
8
|
+
pollBaseUrl;
|
|
9
|
+
constructor(qrContent, qrToken, baseUrl, fetchImpl, deadline) {
|
|
10
|
+
this.qrContent = qrContent;
|
|
11
|
+
this.qrToken = qrToken;
|
|
12
|
+
this.fetchImpl = fetchImpl;
|
|
13
|
+
this.deadline = deadline;
|
|
14
|
+
this.pollBaseUrl = baseUrl;
|
|
15
|
+
this.expiresAt = new Date(deadline).toISOString();
|
|
16
|
+
}
|
|
17
|
+
async poll() {
|
|
18
|
+
if (Date.now() >= this.deadline) {
|
|
19
|
+
return { status: "expired" };
|
|
20
|
+
}
|
|
21
|
+
const status = await get(this.fetchImpl, this.pollBaseUrl, `ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(this.qrToken)}`);
|
|
22
|
+
if (status.status === "scaned_but_redirect" && status.redirect_host?.trim()) {
|
|
23
|
+
this.pollBaseUrl = normalizeRedirectHost(status.redirect_host);
|
|
24
|
+
return { status: "waiting" };
|
|
25
|
+
}
|
|
26
|
+
if (status.status === "expired") {
|
|
27
|
+
return { status: "expired" };
|
|
28
|
+
}
|
|
29
|
+
if (status.connected || status.status === "connected" || status.status === "confirmed") {
|
|
30
|
+
return { status: "confirmed", account: accountFromStatus(status, this.pollBaseUrl) };
|
|
31
|
+
}
|
|
32
|
+
if (status.ret && status.ret !== 0) {
|
|
33
|
+
throw new Error(`WeChat QR login failed: ${status.errmsg ?? status.ret}`);
|
|
34
|
+
}
|
|
35
|
+
if (status.status === "scaned") {
|
|
36
|
+
return { status: "scanned" };
|
|
37
|
+
}
|
|
38
|
+
return { status: "waiting" };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function createQrLoginSession(options = {}) {
|
|
42
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
43
|
+
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
44
|
+
const start = await get(fetchImpl, baseUrl, `ilink/bot/get_bot_qrcode?bot_type=${encodeURIComponent("3")}`);
|
|
45
|
+
const qrToken = start.qrcode;
|
|
46
|
+
const qrContent = start.qrcode_img_content ?? start.qrcode_url ?? start.qr_code ?? start.qrcode;
|
|
47
|
+
if (!qrContent || !qrToken) {
|
|
48
|
+
throw new Error(`Unable to start WeChat QR login: ${start.errmsg ?? "missing QR response"}`);
|
|
49
|
+
}
|
|
50
|
+
return new WeixinQrLoginSession(qrContent, qrToken, baseUrl, fetchImpl, Date.now() + (options.timeoutMs ?? 480_000));
|
|
51
|
+
}
|
|
52
|
+
export async function loginWithQr(options) {
|
|
53
|
+
const session = await createQrLoginSession(options);
|
|
54
|
+
console.log(session.qrContent);
|
|
55
|
+
console.log("Open the codex-weixin management page and scan this QR content with WeChat.");
|
|
56
|
+
while (true) {
|
|
57
|
+
await delay(options.pollMs ?? 2_000);
|
|
58
|
+
const update = await session.poll();
|
|
59
|
+
if (update.status === "expired") {
|
|
60
|
+
throw new Error("WeChat QR code expired. Start login again.");
|
|
61
|
+
}
|
|
62
|
+
if (update.status === "confirmed") {
|
|
63
|
+
saveAccount(options.paths, update.account);
|
|
64
|
+
return update.account;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function accountFromStatus(status, pollBaseUrl) {
|
|
69
|
+
if (!status.bot_token || !status.ilink_bot_id) {
|
|
70
|
+
throw new Error("WeChat QR login completed but did not return bot credentials.");
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
accountId: status.ilink_bot_id,
|
|
74
|
+
token: status.bot_token,
|
|
75
|
+
userId: status.ilink_user_id,
|
|
76
|
+
baseUrl: status.baseurl ?? status.base_url ?? pollBaseUrl,
|
|
77
|
+
cdnBaseUrl: status.cdn_base_url ?? DEFAULT_CDN_BASE_URL,
|
|
78
|
+
savedAt: new Date().toISOString(),
|
|
79
|
+
enabled: true
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function normalizeRedirectHost(host) {
|
|
83
|
+
const trimmed = host.trim().replace(/\/+$/, "");
|
|
84
|
+
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
85
|
+
}
|
|
86
|
+
async function get(fetchImpl, baseUrl, endpoint) {
|
|
87
|
+
const response = await fetchImpl(`${baseUrl}/${endpoint}`, {
|
|
88
|
+
method: "GET",
|
|
89
|
+
headers: { "iLink-App-ClientVersion": "1" }
|
|
90
|
+
});
|
|
91
|
+
const text = await response.text();
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
throw new Error(`${endpoint} failed with HTTP ${response.status}: ${text}`);
|
|
94
|
+
}
|
|
95
|
+
return JSON.parse(text);
|
|
96
|
+
}
|
|
97
|
+
function delay(ms) {
|
|
98
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/weixin/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { FetchLike, WeixinApiClient } from "./api.js";
|
|
2
|
+
import type { WeixinInboundAttachment } from "./messages.js";
|
|
3
|
+
export declare function parseAesKey(raw: string): Buffer;
|
|
4
|
+
export declare function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer;
|
|
5
|
+
export declare function decryptAesEcb(ciphertext: Buffer, key: Buffer): Buffer;
|
|
6
|
+
export declare function aesEcbPaddedSize(plainSize: number): number;
|
|
7
|
+
export declare function sanitizeFileName(name: string): string;
|
|
8
|
+
export declare function inferMediaKind(fileName: string): "image" | "file" | "video";
|
|
9
|
+
export type DownloadedInboundAttachment = {
|
|
10
|
+
kind: "image" | "file" | "video" | "audio";
|
|
11
|
+
path: string;
|
|
12
|
+
label: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function downloadInboundAttachments(input: {
|
|
15
|
+
rootDir: string;
|
|
16
|
+
senderId: string;
|
|
17
|
+
messageId: string;
|
|
18
|
+
attachments: WeixinInboundAttachment[];
|
|
19
|
+
maxBytes: number;
|
|
20
|
+
fetch?: FetchLike;
|
|
21
|
+
}): Promise<DownloadedInboundAttachment[]>;
|
|
22
|
+
export declare function sendLocalMediaFile(input: {
|
|
23
|
+
client: Pick<WeixinApiClient, "getUploadUrl" | "sendFileMessage" | "sendImageMessage" | "sendVideoMessage">;
|
|
24
|
+
toUserId: string;
|
|
25
|
+
contextToken?: string;
|
|
26
|
+
filePath: string;
|
|
27
|
+
kind?: "image" | "file" | "video";
|
|
28
|
+
fetch?: FetchLike;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
messageId: string;
|
|
31
|
+
kind: "image" | "file" | "video";
|
|
32
|
+
}>;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const DEFAULT_WEIXIN_CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
|
|
5
|
+
const UPLOAD_MEDIA_TYPE = {
|
|
6
|
+
image: 1,
|
|
7
|
+
video: 2,
|
|
8
|
+
file: 3
|
|
9
|
+
};
|
|
10
|
+
export function parseAesKey(raw) {
|
|
11
|
+
const trimmed = raw.trim();
|
|
12
|
+
if (/^[0-9a-fA-F]{32}$/.test(trimmed)) {
|
|
13
|
+
return Buffer.from(trimmed, "hex");
|
|
14
|
+
}
|
|
15
|
+
const normalized = trimmed.replace(/-/g, "+").replace(/_/g, "/");
|
|
16
|
+
const decoded = Buffer.from(normalized, "base64");
|
|
17
|
+
if (decoded.length === 16) {
|
|
18
|
+
return decoded;
|
|
19
|
+
}
|
|
20
|
+
if (decoded.length === 32 && /^[0-9a-fA-F]{32}$/.test(decoded.toString("ascii"))) {
|
|
21
|
+
return Buffer.from(decoded.toString("ascii"), "hex");
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`WeChat media AES key must decode to 16 bytes, got ${decoded.length}`);
|
|
24
|
+
}
|
|
25
|
+
export function encryptAesEcb(plaintext, key) {
|
|
26
|
+
if (key.length !== 16) {
|
|
27
|
+
throw new Error("AES-128-ECB key must be 16 bytes");
|
|
28
|
+
}
|
|
29
|
+
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
|
|
30
|
+
cipher.setAutoPadding(true);
|
|
31
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
32
|
+
}
|
|
33
|
+
export function decryptAesEcb(ciphertext, key) {
|
|
34
|
+
if (key.length !== 16) {
|
|
35
|
+
throw new Error("AES-128-ECB key must be 16 bytes");
|
|
36
|
+
}
|
|
37
|
+
const decipher = crypto.createDecipheriv("aes-128-ecb", key, null);
|
|
38
|
+
decipher.setAutoPadding(true);
|
|
39
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
40
|
+
}
|
|
41
|
+
export function aesEcbPaddedSize(plainSize) {
|
|
42
|
+
return Math.ceil((plainSize + 1) / 16) * 16;
|
|
43
|
+
}
|
|
44
|
+
export function sanitizeFileName(name) {
|
|
45
|
+
const cleaned = name
|
|
46
|
+
.normalize("NFC")
|
|
47
|
+
.replace(/[<>:"/\\|?*\u0000-\u001F]/g, "_")
|
|
48
|
+
.replace(/\s+/g, " ")
|
|
49
|
+
.trim();
|
|
50
|
+
return cleaned || "attachment";
|
|
51
|
+
}
|
|
52
|
+
export function inferMediaKind(fileName) {
|
|
53
|
+
if (/\.(png|jpe?g|gif|webp|bmp|heic)$/i.test(fileName)) {
|
|
54
|
+
return "image";
|
|
55
|
+
}
|
|
56
|
+
if (/\.(mp4|mov|webm|mkv|avi|m4v)$/i.test(fileName)) {
|
|
57
|
+
return "video";
|
|
58
|
+
}
|
|
59
|
+
return "file";
|
|
60
|
+
}
|
|
61
|
+
export async function downloadInboundAttachments(input) {
|
|
62
|
+
const downloaded = [];
|
|
63
|
+
for (const [index, attachment] of input.attachments.entries()) {
|
|
64
|
+
downloaded.push(await downloadInboundAttachment({
|
|
65
|
+
rootDir: input.rootDir,
|
|
66
|
+
senderId: input.senderId,
|
|
67
|
+
messageId: `${input.messageId}-${index + 1}`,
|
|
68
|
+
attachment,
|
|
69
|
+
maxBytes: input.maxBytes,
|
|
70
|
+
fetch: input.fetch
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
return downloaded;
|
|
74
|
+
}
|
|
75
|
+
async function downloadInboundAttachment(input) {
|
|
76
|
+
const ref = inboundMediaRef(input.attachment);
|
|
77
|
+
const encrypted = await downloadMediaBuffer({
|
|
78
|
+
url: ref.fullUrl ?? downloadUrlFromParam(ref.encryptQueryParam),
|
|
79
|
+
maxBytes: input.maxBytes,
|
|
80
|
+
fetch: input.fetch
|
|
81
|
+
});
|
|
82
|
+
const key = inboundAesKey(ref);
|
|
83
|
+
const plaintext = key ? decryptAesEcb(encrypted, key) : encrypted;
|
|
84
|
+
const label = labelWithExtension(input.attachment.label, input.attachment.kind, plaintext);
|
|
85
|
+
const targetPath = uniqueInboundPath({
|
|
86
|
+
rootDir: input.rootDir,
|
|
87
|
+
senderId: input.senderId,
|
|
88
|
+
messageId: input.messageId,
|
|
89
|
+
label
|
|
90
|
+
});
|
|
91
|
+
fs.writeFileSync(targetPath, plaintext);
|
|
92
|
+
return { kind: input.attachment.kind, path: targetPath, label };
|
|
93
|
+
}
|
|
94
|
+
function inboundMediaRef(attachment) {
|
|
95
|
+
const containerName = inboundContainerName(attachment.kind);
|
|
96
|
+
const container = attachment.item[containerName];
|
|
97
|
+
const media = container?.media;
|
|
98
|
+
return {
|
|
99
|
+
encryptQueryParam: typeof media?.encrypt_query_param === "string" ? media.encrypt_query_param : undefined,
|
|
100
|
+
aesKey: typeof media?.aes_key === "string" ? media.aes_key : undefined,
|
|
101
|
+
fullUrl: typeof media?.full_url === "string" ? media.full_url : undefined,
|
|
102
|
+
aeskey: typeof container?.aeskey === "string" ? container.aeskey : undefined
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function inboundContainerName(kind) {
|
|
106
|
+
return kind === "audio" ? "voice_item" : `${kind}_item`;
|
|
107
|
+
}
|
|
108
|
+
function inboundAesKey(ref) {
|
|
109
|
+
if (ref.aeskey) {
|
|
110
|
+
return Buffer.from(ref.aeskey, "hex");
|
|
111
|
+
}
|
|
112
|
+
return ref.aesKey ? parseAesKey(ref.aesKey) : undefined;
|
|
113
|
+
}
|
|
114
|
+
async function downloadMediaBuffer(input) {
|
|
115
|
+
const response = await (input.fetch ?? globalThis.fetch.bind(globalThis))(input.url);
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
throw new Error(`CDN download failed: ${response.status} ${response.statusText}`);
|
|
118
|
+
}
|
|
119
|
+
const contentLength = response.headers.get("content-length");
|
|
120
|
+
if (contentLength && Number(contentLength) > input.maxBytes) {
|
|
121
|
+
throw new Error(`Inbound media exceeds max size ${input.maxBytes} bytes`);
|
|
122
|
+
}
|
|
123
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
124
|
+
if (buffer.length > input.maxBytes) {
|
|
125
|
+
throw new Error(`Inbound media exceeds max size ${input.maxBytes} bytes`);
|
|
126
|
+
}
|
|
127
|
+
return buffer;
|
|
128
|
+
}
|
|
129
|
+
function downloadUrlFromParam(encryptQueryParam) {
|
|
130
|
+
if (!encryptQueryParam?.trim()) {
|
|
131
|
+
throw new Error("Inbound media is missing download URL");
|
|
132
|
+
}
|
|
133
|
+
return `${DEFAULT_WEIXIN_CDN_BASE_URL}/download?encrypted_query_param=${encodeURIComponent(encryptQueryParam)}`;
|
|
134
|
+
}
|
|
135
|
+
function labelWithExtension(label, kind, buffer) {
|
|
136
|
+
const ext = path.extname(label);
|
|
137
|
+
if (ext) {
|
|
138
|
+
return sanitizeFileName(label);
|
|
139
|
+
}
|
|
140
|
+
if (kind === "image") {
|
|
141
|
+
return sanitizeFileName(`${label}${inferImageExtension(buffer)}`);
|
|
142
|
+
}
|
|
143
|
+
if (kind === "video") {
|
|
144
|
+
return sanitizeFileName(`${label}.mp4`);
|
|
145
|
+
}
|
|
146
|
+
if (kind === "audio") {
|
|
147
|
+
return sanitizeFileName(`${label}.silk`);
|
|
148
|
+
}
|
|
149
|
+
return sanitizeFileName(label);
|
|
150
|
+
}
|
|
151
|
+
function inferImageExtension(buffer) {
|
|
152
|
+
if (buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
153
|
+
return ".png";
|
|
154
|
+
}
|
|
155
|
+
if (buffer.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff]))) {
|
|
156
|
+
return ".jpg";
|
|
157
|
+
}
|
|
158
|
+
if (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a") {
|
|
159
|
+
return ".gif";
|
|
160
|
+
}
|
|
161
|
+
if (buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
162
|
+
return ".webp";
|
|
163
|
+
}
|
|
164
|
+
return ".bin";
|
|
165
|
+
}
|
|
166
|
+
function uniqueInboundPath(input) {
|
|
167
|
+
const dir = path.join(input.rootDir, sanitizePathSegment(input.senderId));
|
|
168
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
169
|
+
const parsed = path.parse(input.label);
|
|
170
|
+
const stem = sanitizeFileName(`${sanitizePathSegment(input.messageId)}-${parsed.name}`);
|
|
171
|
+
const ext = parsed.ext || "";
|
|
172
|
+
let candidate = path.join(dir, `${stem}${ext}`);
|
|
173
|
+
for (let index = 1; fs.existsSync(candidate); index += 1) {
|
|
174
|
+
candidate = path.join(dir, `${stem}-${index}${ext}`);
|
|
175
|
+
}
|
|
176
|
+
return candidate;
|
|
177
|
+
}
|
|
178
|
+
function sanitizePathSegment(value) {
|
|
179
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "_") || "unknown";
|
|
180
|
+
}
|
|
181
|
+
export async function sendLocalMediaFile(input) {
|
|
182
|
+
const plaintext = fs.readFileSync(input.filePath);
|
|
183
|
+
const rawSize = plaintext.length;
|
|
184
|
+
const rawFileMd5 = crypto.createHash("md5").update(plaintext).digest("hex");
|
|
185
|
+
const cipherSize = aesEcbPaddedSize(rawSize);
|
|
186
|
+
const aesKey = crypto.randomBytes(16);
|
|
187
|
+
const aesKeyHex = aesKey.toString("hex");
|
|
188
|
+
const fileKey = crypto.randomBytes(16).toString("hex");
|
|
189
|
+
const kind = input.kind ?? inferMediaKind(input.filePath);
|
|
190
|
+
const uploadUrl = await input.client.getUploadUrl({
|
|
191
|
+
fileKey,
|
|
192
|
+
mediaType: UPLOAD_MEDIA_TYPE[kind],
|
|
193
|
+
toUserId: input.toUserId,
|
|
194
|
+
rawSize,
|
|
195
|
+
rawFileMd5,
|
|
196
|
+
cipherSize,
|
|
197
|
+
noNeedThumb: true,
|
|
198
|
+
aesKeyHex
|
|
199
|
+
});
|
|
200
|
+
const encryptQueryParam = await uploadBufferToCdn({
|
|
201
|
+
plaintext,
|
|
202
|
+
aesKey,
|
|
203
|
+
fileKey,
|
|
204
|
+
uploadFullUrl: uploadUrl.uploadFullUrl,
|
|
205
|
+
uploadParam: uploadUrl.uploadParam,
|
|
206
|
+
fetch: input.fetch
|
|
207
|
+
});
|
|
208
|
+
const aesKeyBase64 = Buffer.from(aesKeyHex, "utf8").toString("base64");
|
|
209
|
+
if (kind === "image") {
|
|
210
|
+
const result = await input.client.sendImageMessage({
|
|
211
|
+
toUserId: input.toUserId,
|
|
212
|
+
contextToken: input.contextToken,
|
|
213
|
+
encryptQueryParam,
|
|
214
|
+
aesKeyBase64,
|
|
215
|
+
cipherSize
|
|
216
|
+
});
|
|
217
|
+
return { messageId: result.messageId, kind };
|
|
218
|
+
}
|
|
219
|
+
if (kind === "video") {
|
|
220
|
+
const result = await input.client.sendVideoMessage({
|
|
221
|
+
toUserId: input.toUserId,
|
|
222
|
+
contextToken: input.contextToken,
|
|
223
|
+
encryptQueryParam,
|
|
224
|
+
aesKeyBase64,
|
|
225
|
+
cipherSize
|
|
226
|
+
});
|
|
227
|
+
return { messageId: result.messageId, kind };
|
|
228
|
+
}
|
|
229
|
+
const result = await input.client.sendFileMessage({
|
|
230
|
+
toUserId: input.toUserId,
|
|
231
|
+
contextToken: input.contextToken,
|
|
232
|
+
fileName: sanitizeFileName(path.basename(input.filePath)),
|
|
233
|
+
encryptQueryParam,
|
|
234
|
+
aesKeyBase64,
|
|
235
|
+
plainSize: rawSize
|
|
236
|
+
});
|
|
237
|
+
return { messageId: result.messageId, kind };
|
|
238
|
+
}
|
|
239
|
+
async function uploadBufferToCdn(input) {
|
|
240
|
+
const ciphertext = encryptAesEcb(input.plaintext, input.aesKey);
|
|
241
|
+
const response = await (input.fetch ?? globalThis.fetch.bind(globalThis))(resolveUploadUrl(input), {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
244
|
+
body: new Uint8Array(ciphertext)
|
|
245
|
+
});
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
throw new Error(`CDN upload failed: ${response.status} ${response.statusText}`);
|
|
248
|
+
}
|
|
249
|
+
const encryptedParam = response.headers.get("x-encrypted-query-param") ?? response.headers.get("x-encrypted-param");
|
|
250
|
+
if (!encryptedParam) {
|
|
251
|
+
throw new Error("CDN upload response missing encrypted query param header");
|
|
252
|
+
}
|
|
253
|
+
return encryptedParam;
|
|
254
|
+
}
|
|
255
|
+
function resolveUploadUrl(input) {
|
|
256
|
+
const fullUrl = input.uploadFullUrl?.trim();
|
|
257
|
+
if (fullUrl) {
|
|
258
|
+
return fullUrl;
|
|
259
|
+
}
|
|
260
|
+
const uploadParam = input.uploadParam?.trim();
|
|
261
|
+
if (!uploadParam) {
|
|
262
|
+
throw new Error("Outbound media upload URL is missing");
|
|
263
|
+
}
|
|
264
|
+
return `${DEFAULT_WEIXIN_CDN_BASE_URL}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(input.fileKey)}`;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../../src/weixin/media.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type WeixinRawMessage = {
|
|
2
|
+
message_id?: string | number;
|
|
3
|
+
from_user_id?: string;
|
|
4
|
+
sender?: string;
|
|
5
|
+
context_token?: string;
|
|
6
|
+
item_list?: Array<Record<string, unknown>>;
|
|
7
|
+
text?: string;
|
|
8
|
+
};
|
|
9
|
+
export type WeixinInboundAttachment = {
|
|
10
|
+
kind: "image" | "file" | "video" | "audio";
|
|
11
|
+
label: string;
|
|
12
|
+
item: Record<string, unknown>;
|
|
13
|
+
};
|
|
14
|
+
export type NormalizedWeixinMessage = {
|
|
15
|
+
id: string;
|
|
16
|
+
senderId: string;
|
|
17
|
+
contextToken?: string;
|
|
18
|
+
text: string;
|
|
19
|
+
attachments: WeixinInboundAttachment[];
|
|
20
|
+
raw: WeixinRawMessage;
|
|
21
|
+
};
|
|
22
|
+
export declare function normalizeWeixinMessage(raw: WeixinRawMessage): NormalizedWeixinMessage | undefined;
|
|
23
|
+
export declare function extractTextItem(item: Record<string, unknown>): string | undefined;
|
|
24
|
+
export declare function extractAttachments(items: Array<Record<string, unknown>>): WeixinInboundAttachment[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function normalizeWeixinMessage(raw) {
|
|
2
|
+
const senderId = raw.from_user_id ?? raw.sender;
|
|
3
|
+
if (!senderId) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const textParts = [];
|
|
7
|
+
if (typeof raw.text === "string") {
|
|
8
|
+
textParts.push(raw.text);
|
|
9
|
+
}
|
|
10
|
+
for (const item of raw.item_list ?? []) {
|
|
11
|
+
const text = extractTextItem(item);
|
|
12
|
+
if (text) {
|
|
13
|
+
textParts.push(text);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const attachments = extractAttachments(raw.item_list ?? []);
|
|
17
|
+
return {
|
|
18
|
+
id: String(raw.message_id ?? `${senderId}:${Date.now()}`),
|
|
19
|
+
senderId,
|
|
20
|
+
contextToken: raw.context_token,
|
|
21
|
+
text: textParts.join("\n").trim(),
|
|
22
|
+
attachments,
|
|
23
|
+
raw
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function extractTextItem(item) {
|
|
27
|
+
const direct = item.text;
|
|
28
|
+
if (typeof direct === "string") {
|
|
29
|
+
return direct;
|
|
30
|
+
}
|
|
31
|
+
const textItem = item.text_item;
|
|
32
|
+
if (textItem && typeof textItem === "object" && typeof textItem.text === "string") {
|
|
33
|
+
return textItem.text;
|
|
34
|
+
}
|
|
35
|
+
const voiceItem = item.voice_item;
|
|
36
|
+
return extractVoiceTranscription(voiceItem);
|
|
37
|
+
}
|
|
38
|
+
export function extractAttachments(items) {
|
|
39
|
+
const attachments = [];
|
|
40
|
+
for (const item of items) {
|
|
41
|
+
const type = typeof item.type === "number" ? item.type : undefined;
|
|
42
|
+
if (type === 2 && hasMedia(item.image_item)) {
|
|
43
|
+
attachments.push({ kind: "image", label: "image", item });
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (type === 3 && hasMedia(item.voice_item)) {
|
|
47
|
+
if (!extractVoiceTranscription(item.voice_item)) {
|
|
48
|
+
attachments.push({ kind: "audio", label: "voice.silk", item });
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (type === 4 && hasMedia(item.file_item)) {
|
|
53
|
+
const fileItem = item.file_item;
|
|
54
|
+
const label = typeof fileItem.file_name === "string" && fileItem.file_name.trim()
|
|
55
|
+
? fileItem.file_name.trim()
|
|
56
|
+
: "file";
|
|
57
|
+
attachments.push({ kind: "file", label, item });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (type === 5 && hasMedia(item.video_item)) {
|
|
61
|
+
attachments.push({ kind: "video", label: "video.mp4", item });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return attachments;
|
|
65
|
+
}
|
|
66
|
+
function extractVoiceTranscription(value) {
|
|
67
|
+
if (!value || typeof value !== "object") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const text = value.text;
|
|
71
|
+
if (typeof text !== "string" || !text.trim()) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
return text;
|
|
75
|
+
}
|
|
76
|
+
function hasMedia(value) {
|
|
77
|
+
if (!value || typeof value !== "object") {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const media = value.media;
|
|
81
|
+
if (!media || typeof media !== "object") {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const candidate = media;
|
|
85
|
+
return typeof candidate.encrypt_query_param === "string" || typeof candidate.full_url === "string";
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/weixin/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WeixinApiClient } from "./api.js";
|
|
2
|
+
import { type NormalizedWeixinMessage } from "./messages.js";
|
|
3
|
+
export type MonitorOptions = {
|
|
4
|
+
client: WeixinApiClient;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
pollIntervalMs?: number;
|
|
7
|
+
maxPollRetryMs?: number;
|
|
8
|
+
initialSyncKey?: string;
|
|
9
|
+
onSyncKey?: (syncKey: string) => Promise<void> | void;
|
|
10
|
+
claimMessage?: (message: NormalizedWeixinMessage) => boolean;
|
|
11
|
+
onMessage: (message: NormalizedWeixinMessage) => Promise<void>;
|
|
12
|
+
onMessageError?: (error: unknown, message: NormalizedWeixinMessage) => Promise<void> | void;
|
|
13
|
+
};
|
|
14
|
+
export declare class PollRetryBackoff {
|
|
15
|
+
private readonly initialMs;
|
|
16
|
+
private readonly maxMs;
|
|
17
|
+
private currentMs;
|
|
18
|
+
constructor(initialMs: number, maxMs: number);
|
|
19
|
+
next(): number;
|
|
20
|
+
reset(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function monitorWeixin(options: MonitorOptions): Promise<void>;
|