@krischoichoi/channel-weixin 0.6.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 +101 -0
- package/lib/adapter.d.ts +90 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +247 -0
- package/lib/adapter.js.map +1 -0
- package/lib/auth/account-store.d.ts +60 -0
- package/lib/auth/account-store.d.ts.map +1 -0
- package/lib/auth/account-store.js +84 -0
- package/lib/auth/account-store.js.map +1 -0
- package/lib/auth/login.d.ts +83 -0
- package/lib/auth/login.d.ts.map +1 -0
- package/lib/auth/login.js +202 -0
- package/lib/auth/login.js.map +1 -0
- package/lib/config.d.ts +52 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +36 -0
- package/lib/config.js.map +1 -0
- package/lib/definition.d.ts +59 -0
- package/lib/definition.d.ts.map +1 -0
- package/lib/definition.js +146 -0
- package/lib/definition.js.map +1 -0
- package/lib/ilink/base-info.d.ts +22 -0
- package/lib/ilink/base-info.d.ts.map +1 -0
- package/lib/ilink/base-info.js +11 -0
- package/lib/ilink/base-info.js.map +1 -0
- package/lib/ilink/client.d.ts +110 -0
- package/lib/ilink/client.d.ts.map +1 -0
- package/lib/ilink/client.js +308 -0
- package/lib/ilink/client.js.map +1 -0
- package/lib/ilink/constants.d.ts +59 -0
- package/lib/ilink/constants.d.ts.map +1 -0
- package/lib/ilink/constants.js +59 -0
- package/lib/ilink/constants.js.map +1 -0
- package/lib/ilink/errors.d.ts +79 -0
- package/lib/ilink/errors.d.ts.map +1 -0
- package/lib/ilink/errors.js +121 -0
- package/lib/ilink/errors.js.map +1 -0
- package/lib/ilink/headers.d.ts +40 -0
- package/lib/ilink/headers.d.ts.map +1 -0
- package/lib/ilink/headers.js +70 -0
- package/lib/ilink/headers.js.map +1 -0
- package/lib/ilink/schema.d.ts +154 -0
- package/lib/ilink/schema.d.ts.map +1 -0
- package/lib/ilink/schema.js +140 -0
- package/lib/ilink/schema.js.map +1 -0
- package/lib/ilink/types.d.ts +235 -0
- package/lib/ilink/types.d.ts.map +1 -0
- package/lib/ilink/types.js +21 -0
- package/lib/ilink/types.js.map +1 -0
- package/lib/index.d.ts +50 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +91 -0
- package/lib/index.js.map +1 -0
- package/lib/manifest.d.ts +54 -0
- package/lib/manifest.d.ts.map +1 -0
- package/lib/manifest.js +32 -0
- package/lib/manifest.js.map +1 -0
- package/lib/media/decrypt.d.ts +3 -0
- package/lib/media/decrypt.d.ts.map +1 -0
- package/lib/media/decrypt.js +16 -0
- package/lib/media/decrypt.js.map +1 -0
- package/lib/media/download.d.ts +22 -0
- package/lib/media/download.d.ts.map +1 -0
- package/lib/media/download.js +62 -0
- package/lib/media/download.js.map +1 -0
- package/lib/media/encrypt.d.ts +3 -0
- package/lib/media/encrypt.d.ts.map +1 -0
- package/lib/media/encrypt.js +16 -0
- package/lib/media/encrypt.js.map +1 -0
- package/lib/media/send-media.d.ts +41 -0
- package/lib/media/send-media.d.ts.map +1 -0
- package/lib/media/send-media.js +52 -0
- package/lib/media/send-media.js.map +1 -0
- package/lib/media/silk-transcode.d.ts +38 -0
- package/lib/media/silk-transcode.d.ts.map +1 -0
- package/lib/media/silk-transcode.js +88 -0
- package/lib/media/silk-transcode.js.map +1 -0
- package/lib/media/upload.d.ts +38 -0
- package/lib/media/upload.d.ts.map +1 -0
- package/lib/media/upload.js +122 -0
- package/lib/media/upload.js.map +1 -0
- package/lib/messaging/dedup.d.ts +87 -0
- package/lib/messaging/dedup.d.ts.map +1 -0
- package/lib/messaging/dedup.js +127 -0
- package/lib/messaging/dedup.js.map +1 -0
- package/lib/messaging/mapper.d.ts +26 -0
- package/lib/messaging/mapper.d.ts.map +1 -0
- package/lib/messaging/mapper.js +107 -0
- package/lib/messaging/mapper.js.map +1 -0
- package/lib/messaging/monitor.d.ts +109 -0
- package/lib/messaging/monitor.d.ts.map +1 -0
- package/lib/messaging/monitor.js +234 -0
- package/lib/messaging/monitor.js.map +1 -0
- package/lib/messaging/quote.d.ts +9 -0
- package/lib/messaging/quote.d.ts.map +1 -0
- package/lib/messaging/quote.js +35 -0
- package/lib/messaging/quote.js.map +1 -0
- package/lib/messaging/send.d.ts +54 -0
- package/lib/messaging/send.d.ts.map +1 -0
- package/lib/messaging/send.js +173 -0
- package/lib/messaging/send.js.map +1 -0
- package/lib/messaging/typing.d.ts +34 -0
- package/lib/messaging/typing.d.ts.map +1 -0
- package/lib/messaging/typing.js +56 -0
- package/lib/messaging/typing.js.map +1 -0
- package/lib/storage/context-token.d.ts +26 -0
- package/lib/storage/context-token.d.ts.map +1 -0
- package/lib/storage/context-token.js +26 -0
- package/lib/storage/context-token.js.map +1 -0
- package/lib/storage/sync-cursor.d.ts +27 -0
- package/lib/storage/sync-cursor.d.ts.map +1 -0
- package/lib/storage/sync-cursor.js +25 -0
- package/lib/storage/sync-cursor.js.map +1 -0
- package/lib/transport.d.ts +38 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +68 -0
- package/lib/transport.js.map +1 -0
- package/lib/upstream/compat.d.ts +54 -0
- package/lib/upstream/compat.d.ts.map +1 -0
- package/lib/upstream/compat.js +54 -0
- package/lib/upstream/compat.js.map +1 -0
- package/lib/upstream/config-manager.d.ts +35 -0
- package/lib/upstream/config-manager.d.ts.map +1 -0
- package/lib/upstream/config-manager.js +91 -0
- package/lib/upstream/config-manager.js.map +1 -0
- package/lib/upstream/index.d.ts +42 -0
- package/lib/upstream/index.d.ts.map +1 -0
- package/lib/upstream/index.js +13 -0
- package/lib/upstream/index.js.map +1 -0
- package/lib/upstream/port.d.ts +153 -0
- package/lib/upstream/port.d.ts.map +1 -0
- package/lib/upstream/port.js +15 -0
- package/lib/upstream/port.js.map +1 -0
- package/lib/upstream/tencent-upstream.d.ts +116 -0
- package/lib/upstream/tencent-upstream.d.ts.map +1 -0
- package/lib/upstream/tencent-upstream.js +518 -0
- package/lib/upstream/tencent-upstream.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/media/download.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAqB7C,0DAA0D;AAC1D,MAAM,UAAU,kBAAkB,CAAC,KAAoB,EAAE,UAAkB;IACzE,IAAI,KAAK,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,CAAC;IAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,mBAAmB;QAAE,OAAO,GAAG,IAAI,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;IAC7E,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAoB,EAAE,IAA0B;IAClF,MAAM,GAAG,GAAG,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC;IACrD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtG,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAA0B,EAAE,KAAoB;IACpE,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC;IAClD,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../src/media/encrypt.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAK7E"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WX5 media — AES-128-ECB encrypt.
|
|
3
|
+
*
|
|
4
|
+
* Tencent's published plugin is OpenClaw-coupled and cannot be used as a DSH
|
|
5
|
+
* runtime dependency. This small source-port intentionally uses Node crypto.
|
|
6
|
+
*/
|
|
7
|
+
import { createCipheriv } from 'node:crypto';
|
|
8
|
+
/** AES-128-ECB encrypt (PKCS#7 padding). `key` may be 16 raw bytes or hex. */
|
|
9
|
+
export function aes128Encrypt(plaintext, key) {
|
|
10
|
+
const keyBuf = typeof key === 'string' ? Buffer.from(key, 'hex') : key;
|
|
11
|
+
if (keyBuf.length !== 16)
|
|
12
|
+
throw new Error('aes128Encrypt: AES-128 key must be 16 bytes');
|
|
13
|
+
const cipher = createCipheriv('aes-128-ecb', keyBuf, null);
|
|
14
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=encrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.js","sourceRoot":"","sources":["../../src/media/encrypt.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,8EAA8E;AAC9E,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,GAAoB;IACnE,MAAM,MAAM,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACvE,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACzF,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification: C — duplicate send-media payload [@deprecated upstream-gap].
|
|
3
|
+
*
|
|
4
|
+
* Builds an iLink sendmessage image item + dispatches. The official composed
|
|
5
|
+
* messaging/send.js (sendImageMessageWeixin) is OpenClaw coupled (imports
|
|
6
|
+
* api/api + util/logger). Kept behind the facade as an upstream-gap; send
|
|
7
|
+
* routing now lives in upstream/tencent-upstream.ts sendImage().
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* WX5 media — send a media sendmessage (WX5.1 Image outbound).
|
|
11
|
+
*
|
|
12
|
+
* Embeds a CDN media reference (encrypt_query_param / aes_key) into a real
|
|
13
|
+
* iLink sendmessage item. `sendMedia` builds the full request and `dispatch`
|
|
14
|
+
* performs the send through the client; the split keeps payload construction
|
|
15
|
+
* pure for tests.
|
|
16
|
+
*/
|
|
17
|
+
import type { ILinkClient } from '../ilink/client.js';
|
|
18
|
+
export interface SendMediaOptions {
|
|
19
|
+
kind?: 'image' | 'file' | 'video';
|
|
20
|
+
to: string;
|
|
21
|
+
media: {
|
|
22
|
+
encrypt_query_param?: string;
|
|
23
|
+
aes_key?: string;
|
|
24
|
+
full_url?: string;
|
|
25
|
+
};
|
|
26
|
+
/** Plaintext length, required by the file wire shape. */
|
|
27
|
+
fileSize?: number;
|
|
28
|
+
/** Ciphertext length, required by the image/video wire shapes. */
|
|
29
|
+
fileSizeCiphertext?: number;
|
|
30
|
+
/** Original file name, required for a file item. */
|
|
31
|
+
fileName?: string;
|
|
32
|
+
contextToken?: string;
|
|
33
|
+
runId?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Build the iLink sendmessage payload for an image, file, or video item. */
|
|
36
|
+
export declare function buildSendMediaPayload(options: SendMediaOptions): Record<string, unknown>;
|
|
37
|
+
/** Send a media message through the client; returns the client_id. */
|
|
38
|
+
export declare function sendMedia(client: ILinkClient, options: SendMediaOptions): Promise<{
|
|
39
|
+
messageId: string;
|
|
40
|
+
}>;
|
|
41
|
+
//# sourceMappingURL=send-media.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-media.d.ts","sourceRoot":"","sources":["../../src/media/send-media.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAUtD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE;QACL,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsCxF;AAED,sEAAsE;AACtE,wBAAsB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAI9G"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { MESSAGE_ITEM_TYPE_FILE, MESSAGE_ITEM_TYPE_IMAGE, MESSAGE_ITEM_TYPE_VIDEO, MESSAGE_STATE_FINISH, MESSAGE_TYPE_BOT, } from '../ilink/types.js';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
/** Build the iLink sendmessage payload for an image, file, or video item. */
|
|
4
|
+
export function buildSendMediaPayload(options) {
|
|
5
|
+
const media = {};
|
|
6
|
+
if (options.media.encrypt_query_param)
|
|
7
|
+
media.encrypt_query_param = options.media.encrypt_query_param;
|
|
8
|
+
if (options.media.aes_key)
|
|
9
|
+
media.aes_key = options.media.aes_key;
|
|
10
|
+
if (options.media.full_url)
|
|
11
|
+
media.full_url = options.media.full_url;
|
|
12
|
+
// Tencent's media send items mark the encrypted media envelope explicitly.
|
|
13
|
+
media.encrypt_type = 1;
|
|
14
|
+
const kind = options.kind ?? 'image';
|
|
15
|
+
const item = kind === 'file'
|
|
16
|
+
? {
|
|
17
|
+
type: MESSAGE_ITEM_TYPE_FILE,
|
|
18
|
+
file_item: {
|
|
19
|
+
media,
|
|
20
|
+
file_name: options.fileName ?? 'attachment',
|
|
21
|
+
len: String(options.fileSize ?? 0),
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
: kind === 'video'
|
|
25
|
+
? {
|
|
26
|
+
type: MESSAGE_ITEM_TYPE_VIDEO,
|
|
27
|
+
video_item: { media, video_size: options.fileSizeCiphertext ?? 0 },
|
|
28
|
+
}
|
|
29
|
+
: {
|
|
30
|
+
type: MESSAGE_ITEM_TYPE_IMAGE,
|
|
31
|
+
image_item: { media, mid_size: options.fileSizeCiphertext ?? 0 },
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
msg: {
|
|
35
|
+
from_user_id: '',
|
|
36
|
+
to_user_id: options.to,
|
|
37
|
+
client_id: randomUUID(),
|
|
38
|
+
message_type: MESSAGE_TYPE_BOT,
|
|
39
|
+
message_state: MESSAGE_STATE_FINISH,
|
|
40
|
+
item_list: [item],
|
|
41
|
+
context_token: options.contextToken ?? undefined,
|
|
42
|
+
run_id: options.runId ?? undefined,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Send a media message through the client; returns the client_id. */
|
|
47
|
+
export async function sendMedia(client, options) {
|
|
48
|
+
const payload = buildSendMediaPayload(options);
|
|
49
|
+
await client.sendMessage(payload);
|
|
50
|
+
return { messageId: payload.msg.client_id };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=send-media.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-media.js","sourceRoot":"","sources":["../../src/media/send-media.ts"],"names":[],"mappings":"AAiBA,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoBzC,6EAA6E;AAC7E,MAAM,UAAU,qBAAqB,CAAC,OAAyB;IAC7D,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,IAAI,OAAO,CAAC,KAAK,CAAC,mBAAmB;QAAE,KAAK,CAAC,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC;IACrG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO;QAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;IACjE,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ;QAAE,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;IACpE,2EAA2E;IAC3E,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,KAAK,MAAM;QAC1B,CAAC,CAAC;YACE,IAAI,EAAE,sBAAsB;YAC5B,SAAS,EAAE;gBACT,KAAK;gBACL,SAAS,EAAE,OAAO,CAAC,QAAQ,IAAI,YAAY;gBAC3C,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;aACnC;SACF;QACH,CAAC,CAAC,IAAI,KAAK,OAAO;YAChB,CAAC,CAAC;gBACE,IAAI,EAAE,uBAAuB;gBAC7B,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,kBAAkB,IAAI,CAAC,EAAE;aACnE;YACH,CAAC,CAAC;gBACE,IAAI,EAAE,uBAAuB;gBAC7B,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,kBAAkB,IAAI,CAAC,EAAE;aACjE,CAAC;IACR,OAAO;QACL,GAAG,EAAE;YACH,YAAY,EAAE,EAAE;YAChB,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,SAAS,EAAE,UAAU,EAAE;YACvB,YAAY,EAAE,gBAAgB;YAC9B,aAAa,EAAE,oBAAoB;YACnC,SAAS,EAAE,CAAC,IAAI,CAAC;YACjB,aAAa,EAAE,OAAO,CAAC,YAAY,IAAI,SAAS;YAChD,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS;SACnC;KACF,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAmB,EAAE,OAAyB;IAC5E,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/C,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,EAAE,SAAS,EAAG,OAAO,CAAC,GAA6B,CAAC,SAAS,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort SILK voice decoding.
|
|
3
|
+
*
|
|
4
|
+
* `silk-wasm` is intentionally loaded at runtime: voice conversion must not
|
|
5
|
+
* make the Weixin adapter unavailable when the optional codec is absent.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SILK_ENCODE_TYPE = 6;
|
|
8
|
+
export declare const DEFAULT_SILK_SAMPLE_RATE = 24000;
|
|
9
|
+
export interface SilkWasmDecodedAudio {
|
|
10
|
+
data: Uint8Array;
|
|
11
|
+
duration?: number;
|
|
12
|
+
}
|
|
13
|
+
export type SilkDecoder = (input: Buffer, sampleRate: number) => Promise<SilkWasmDecodedAudio> | SilkWasmDecodedAudio;
|
|
14
|
+
export type SilkModuleLoader = () => Promise<unknown>;
|
|
15
|
+
export interface SilkTranscodeOptions {
|
|
16
|
+
/** iLink `voice_item.encode_type`; only `6` is SILK. */
|
|
17
|
+
encodeType?: number;
|
|
18
|
+
/** iLink's sample rate when available; invalid values use the iLink default. */
|
|
19
|
+
sampleRate?: number;
|
|
20
|
+
/** Test seam for the optional `silk-wasm` module. */
|
|
21
|
+
loadModule?: SilkModuleLoader;
|
|
22
|
+
}
|
|
23
|
+
export interface SilkTranscodeResult {
|
|
24
|
+
data: Buffer;
|
|
25
|
+
mimeType: 'audio/silk' | 'audio/wav';
|
|
26
|
+
/** True only when a WAV container was produced from decoded PCM. */
|
|
27
|
+
transcoded: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Decode a SILK iLink voice message where possible.
|
|
31
|
+
*
|
|
32
|
+
* The raw bytes are deliberately returned for every optional-codec failure,
|
|
33
|
+
* including a missing module or an unexpected decoder result. This keeps
|
|
34
|
+
* inbound voice media available to downstream consumers without turning a
|
|
35
|
+
* codec gap into an ingress failure.
|
|
36
|
+
*/
|
|
37
|
+
export declare function transcodeSilkVoice(input: Buffer, options?: SilkTranscodeOptions): Promise<SilkTranscodeResult>;
|
|
38
|
+
//# sourceMappingURL=silk-transcode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silk-transcode.d.ts","sourceRoot":"","sources":["../../src/media/silk-transcode.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,oBAAoB,CAAC,GAAG,oBAAoB,CAAC;AAE1D,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,GAAG,WAAW,CAAC;IACrC,oEAAoE;IACpE,UAAU,EAAE,OAAO,CAAC;CACrB;AAID;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAmB9B"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort SILK voice decoding.
|
|
3
|
+
*
|
|
4
|
+
* `silk-wasm` is intentionally loaded at runtime: voice conversion must not
|
|
5
|
+
* make the Weixin adapter unavailable when the optional codec is absent.
|
|
6
|
+
*/
|
|
7
|
+
export const SILK_ENCODE_TYPE = 6;
|
|
8
|
+
export const DEFAULT_SILK_SAMPLE_RATE = 24_000;
|
|
9
|
+
/**
|
|
10
|
+
* Decode a SILK iLink voice message where possible.
|
|
11
|
+
*
|
|
12
|
+
* The raw bytes are deliberately returned for every optional-codec failure,
|
|
13
|
+
* including a missing module or an unexpected decoder result. This keeps
|
|
14
|
+
* inbound voice media available to downstream consumers without turning a
|
|
15
|
+
* codec gap into an ingress failure.
|
|
16
|
+
*/
|
|
17
|
+
export async function transcodeSilkVoice(input, options = {}) {
|
|
18
|
+
if (options.encodeType !== SILK_ENCODE_TYPE)
|
|
19
|
+
return rawSilk(input);
|
|
20
|
+
try {
|
|
21
|
+
const module = await (options.loadModule ?? loadSilkWasm)();
|
|
22
|
+
const decode = getDecoder(module);
|
|
23
|
+
if (!decode)
|
|
24
|
+
return rawSilk(input);
|
|
25
|
+
const decoded = await decode(input, normalizeSampleRate(options.sampleRate));
|
|
26
|
+
if (!isDecodedAudio(decoded))
|
|
27
|
+
return rawSilk(input);
|
|
28
|
+
return {
|
|
29
|
+
data: pcmS16LeToWav(decoded.data, normalizeSampleRate(options.sampleRate)),
|
|
30
|
+
mimeType: 'audio/wav',
|
|
31
|
+
transcoded: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return rawSilk(input);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Load the optional decoder without making TypeScript resolve it at build time. */
|
|
39
|
+
function loadSilkWasm() {
|
|
40
|
+
const specifier = 'silk-wasm';
|
|
41
|
+
return import(specifier);
|
|
42
|
+
}
|
|
43
|
+
function getDecoder(module) {
|
|
44
|
+
if (!isSilkWasmModule(module) || typeof module.decode !== 'function')
|
|
45
|
+
return undefined;
|
|
46
|
+
return module.decode;
|
|
47
|
+
}
|
|
48
|
+
function isSilkWasmModule(value) {
|
|
49
|
+
return typeof value === 'object' && value !== null;
|
|
50
|
+
}
|
|
51
|
+
function isDecodedAudio(value) {
|
|
52
|
+
return typeof value === 'object'
|
|
53
|
+
&& value !== null
|
|
54
|
+
&& 'data' in value
|
|
55
|
+
&& value.data instanceof Uint8Array;
|
|
56
|
+
}
|
|
57
|
+
function normalizeSampleRate(value) {
|
|
58
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0
|
|
59
|
+
? value
|
|
60
|
+
: DEFAULT_SILK_SAMPLE_RATE;
|
|
61
|
+
}
|
|
62
|
+
function rawSilk(data) {
|
|
63
|
+
return { data, mimeType: 'audio/silk', transcoded: false };
|
|
64
|
+
}
|
|
65
|
+
/** Wrap mono signed 16-bit little-endian PCM in a WAV container. */
|
|
66
|
+
function pcmS16LeToWav(pcm, sampleRate) {
|
|
67
|
+
const pcmBytes = pcm.byteLength;
|
|
68
|
+
if (pcmBytes > 0xffff_ffff - 36) {
|
|
69
|
+
throw new Error('SILK PCM payload is too large for a WAV container');
|
|
70
|
+
}
|
|
71
|
+
const wav = Buffer.allocUnsafe(44 + pcmBytes);
|
|
72
|
+
wav.write('RIFF', 0, 'ascii');
|
|
73
|
+
wav.writeUInt32LE(wav.length - 8, 4);
|
|
74
|
+
wav.write('WAVE', 8, 'ascii');
|
|
75
|
+
wav.write('fmt ', 12, 'ascii');
|
|
76
|
+
wav.writeUInt32LE(16, 16);
|
|
77
|
+
wav.writeUInt16LE(1, 20); // PCM
|
|
78
|
+
wav.writeUInt16LE(1, 22); // mono
|
|
79
|
+
wav.writeUInt32LE(sampleRate, 24);
|
|
80
|
+
wav.writeUInt32LE(sampleRate * 2, 28); // 16-bit mono
|
|
81
|
+
wav.writeUInt16LE(2, 32);
|
|
82
|
+
wav.writeUInt16LE(16, 34);
|
|
83
|
+
wav.write('data', 36, 'ascii');
|
|
84
|
+
wav.writeUInt32LE(pcmBytes, 40);
|
|
85
|
+
Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength).copy(wav, 44);
|
|
86
|
+
return wav;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=silk-transcode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silk-transcode.js","sourceRoot":"","sources":["../../src/media/silk-transcode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ILinkGetUploadUrlResponse } from '../ilink/types.js';
|
|
2
|
+
export interface UploadMediaOptions {
|
|
3
|
+
cdnBaseUrl: string;
|
|
4
|
+
/** Base URL for the getuploadurl CGI. */
|
|
5
|
+
apiBaseUrl: string;
|
|
6
|
+
token?: string;
|
|
7
|
+
toUserId?: string;
|
|
8
|
+
filekey?: string;
|
|
9
|
+
mediaType?: number;
|
|
10
|
+
getUploadUrl?: (request: Record<string, unknown>) => Promise<ILinkGetUploadUrlResponse>;
|
|
11
|
+
upload?: (url: string, form: FormData) => Promise<Response>;
|
|
12
|
+
now?: () => number;
|
|
13
|
+
}
|
|
14
|
+
export interface UploadedMedia {
|
|
15
|
+
filekey: string;
|
|
16
|
+
uploadFullUrl?: string;
|
|
17
|
+
downloadEncryptedQueryParam: string;
|
|
18
|
+
/** AES-128 key encoded as 32 lowercase hex characters. */
|
|
19
|
+
aeskey: string;
|
|
20
|
+
/** Plaintext file size in bytes. */
|
|
21
|
+
fileSize: number;
|
|
22
|
+
/** AES-128-ECB PKCS#7 ciphertext size in bytes. */
|
|
23
|
+
fileSizeCiphertext: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const WX5_MEDIA_TYPE_IMAGE = 1;
|
|
26
|
+
export declare const WX5_MEDIA_TYPE_VIDEO = 2;
|
|
27
|
+
export declare const WX5_MEDIA_TYPE_FILE = 3;
|
|
28
|
+
export declare const WX5_MEDIA_TYPE_VOICE = 4;
|
|
29
|
+
interface PreparedUpload {
|
|
30
|
+
aeskey: Buffer;
|
|
31
|
+
ciphertext: Buffer;
|
|
32
|
+
}
|
|
33
|
+
/** AES-128-ECB always emits one PKCS#7 block, including for block-aligned input. */
|
|
34
|
+
export declare function aesEcbPaddedSize(size: number): number;
|
|
35
|
+
export declare function buildUploadUrlRequest(file: Buffer, opts: UploadMediaOptions, prepared?: PreparedUpload): Record<string, unknown>;
|
|
36
|
+
export declare function uploadMedia(file: Buffer, opts: UploadMediaOptions): Promise<UploadedMedia>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/media/upload.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAInE,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACxF,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B,EAAE,MAAM,CAAC;IACpC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,kBAAkB,EACxB,QAAQ,GAAE,cAAoC,GAC7C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CA4DhG"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification: C — duplicate media upload pipeline [@deprecated upstream-gap].
|
|
3
|
+
*
|
|
4
|
+
* getuploadurl → AES-128-ECB encrypt → CDN POST. The official composed flow
|
|
5
|
+
* cdn/upload.js + cdn/cdn-upload.js is OpenClaw coupled (imports api/api +
|
|
6
|
+
* util/logger). The crypto step is a local source-port. NOTE: the official
|
|
7
|
+
* media_type values and upload sizing follow Tencent/openclaw-weixin v2.4.6:
|
|
8
|
+
* IMAGE=1 and `filesize` is the AES-128-ECB PKCS#7 ciphertext length. Marked
|
|
9
|
+
* upstream-gap because the Tencent package itself remains OpenClaw-coupled.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* WX5 media — CDN upload (WX5.1 Image outbound).
|
|
13
|
+
*
|
|
14
|
+
* 1. Ask the getuploadurl CGI for an upload slot (upload_full_url + upload_param).
|
|
15
|
+
* 2. AES-128-ECB encrypt the file body with a fresh random 16-byte key.
|
|
16
|
+
* 3. Upload the ciphertext to the slot; the encrypted size is recorded.
|
|
17
|
+
* 4. Return the reference fields the sendmessage item embeds.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
20
|
+
import { ChannelError } from '@krischoichoi/channel-core';
|
|
21
|
+
import { aes128Encrypt } from './encrypt.js';
|
|
22
|
+
import { getUploadUrlResponseSchema, responseEnvelopeSchema } from '../ilink/schema.js';
|
|
23
|
+
import { buildHeaders } from '../ilink/headers.js';
|
|
24
|
+
export const WX5_MEDIA_TYPE_IMAGE = 1;
|
|
25
|
+
export const WX5_MEDIA_TYPE_VIDEO = 2;
|
|
26
|
+
export const WX5_MEDIA_TYPE_FILE = 3;
|
|
27
|
+
export const WX5_MEDIA_TYPE_VOICE = 4;
|
|
28
|
+
/** AES-128-ECB always emits one PKCS#7 block, including for block-aligned input. */
|
|
29
|
+
export function aesEcbPaddedSize(size) {
|
|
30
|
+
if (!Number.isSafeInteger(size) || size < 0) {
|
|
31
|
+
throw new RangeError('aesEcbPaddedSize: size must be a non-negative safe integer');
|
|
32
|
+
}
|
|
33
|
+
return (Math.floor(size / 16) + 1) * 16;
|
|
34
|
+
}
|
|
35
|
+
export function buildUploadUrlRequest(file, opts, prepared = prepareUpload(file)) {
|
|
36
|
+
const md5 = createHash('md5').update(file).digest('hex');
|
|
37
|
+
const ts = opts.now ? opts.now() : Date.now();
|
|
38
|
+
return {
|
|
39
|
+
filekey: opts.filekey ?? ('wx5-' + md5 + '-' + ts),
|
|
40
|
+
media_type: opts.mediaType ?? WX5_MEDIA_TYPE_IMAGE,
|
|
41
|
+
to_user_id: opts.toUserId,
|
|
42
|
+
rawsize: file.length,
|
|
43
|
+
rawfilemd5: md5,
|
|
44
|
+
filesize: prepared.ciphertext.length,
|
|
45
|
+
no_need_thumb: true,
|
|
46
|
+
aeskey: prepared.aeskey.toString('hex'),
|
|
47
|
+
base_info: {},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export async function uploadMedia(file, opts) {
|
|
51
|
+
if (file.length === 0) {
|
|
52
|
+
throw new ChannelError('CHANNEL_UNSUPPORTED', 'uploadMedia: cannot upload an empty file');
|
|
53
|
+
}
|
|
54
|
+
// The upload slot is bound to the exact AES key and padded ciphertext size.
|
|
55
|
+
const prepared = prepareUpload(file);
|
|
56
|
+
const request = buildUploadUrlRequest(file, opts, prepared);
|
|
57
|
+
const getUploadUrl = opts.getUploadUrl ?? (async (req) => {
|
|
58
|
+
const url = opts.apiBaseUrl.replace(/\/$/, '') + '/ilink/bot/getuploadurl';
|
|
59
|
+
const response = await globalThis.fetch(url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'content-type': 'application/json', ...buildHeaders({ token: opts.token }) },
|
|
62
|
+
body: JSON.stringify(req),
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok)
|
|
65
|
+
throw new ChannelError('CHANNEL_ERROR', 'getuploadurl http ' + response.status);
|
|
66
|
+
return response.json();
|
|
67
|
+
});
|
|
68
|
+
const rawUploadSlot = await getUploadUrl(request);
|
|
69
|
+
const envelope = responseEnvelopeSchema.safeParse(rawUploadSlot);
|
|
70
|
+
if (envelope.success &&
|
|
71
|
+
((envelope.data.ret !== undefined && envelope.data.ret !== 0) ||
|
|
72
|
+
(envelope.data.errcode !== undefined && envelope.data.errcode !== 0))) {
|
|
73
|
+
const code = envelope.data.ret !== undefined && envelope.data.ret !== 0
|
|
74
|
+
? `ret=${envelope.data.ret}`
|
|
75
|
+
: `errcode=${envelope.data.errcode}`;
|
|
76
|
+
throw new ChannelError('CHANNEL_ERROR', `uploadMedia: getuploadurl returned ${code}`);
|
|
77
|
+
}
|
|
78
|
+
const parsedUploadSlot = getUploadUrlResponseSchema.safeParse(rawUploadSlot);
|
|
79
|
+
if (!parsedUploadSlot.success) {
|
|
80
|
+
throw new ChannelError('CHANNEL_ERROR', 'uploadMedia: getuploadurl returned an invalid response shape');
|
|
81
|
+
}
|
|
82
|
+
const uploadSlot = parsedUploadSlot.data;
|
|
83
|
+
if (!uploadSlot.upload_full_url && !uploadSlot.upload_param) {
|
|
84
|
+
throw new ChannelError('CHANNEL_UNSUPPORTED', 'uploadMedia: getuploadurl returned no upload slot');
|
|
85
|
+
}
|
|
86
|
+
const form = new FormData();
|
|
87
|
+
if (uploadSlot.thumb_upload_param)
|
|
88
|
+
form.append('thumb_upload_param', uploadSlot.thumb_upload_param);
|
|
89
|
+
if (uploadSlot.upload_param)
|
|
90
|
+
form.append('upload_param', uploadSlot.upload_param);
|
|
91
|
+
form.append('file', new Blob([prepared.ciphertext]));
|
|
92
|
+
const upload = opts.upload ?? ((url, body) => globalThis.fetch(url, { method: 'POST', body }));
|
|
93
|
+
const uploadUrl = uploadSlot.upload_full_url ?? buildCdnUploadUrl(opts.cdnBaseUrl, uploadSlot.upload_param, String(request.filekey));
|
|
94
|
+
const response = await upload(uploadUrl, form);
|
|
95
|
+
if (!response.ok)
|
|
96
|
+
throw new ChannelError('CHANNEL_ERROR', 'cdn upload http ' + response.status);
|
|
97
|
+
const downloadEncryptedQueryParam = response.headers.get('x-encrypted-param');
|
|
98
|
+
if (!downloadEncryptedQueryParam) {
|
|
99
|
+
throw new ChannelError('CHANNEL_ERROR', 'cdn upload response missing x-encrypted-param header');
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
filekey: String(request.filekey),
|
|
103
|
+
uploadFullUrl: uploadSlot.upload_full_url,
|
|
104
|
+
downloadEncryptedQueryParam,
|
|
105
|
+
aeskey: prepared.aeskey.toString('hex'),
|
|
106
|
+
fileSize: file.length,
|
|
107
|
+
fileSizeCiphertext: prepared.ciphertext.length,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function prepareUpload(file) {
|
|
111
|
+
const aeskey = randomBytes(16);
|
|
112
|
+
const ciphertext = aes128Encrypt(file, aeskey);
|
|
113
|
+
if (ciphertext.length !== aesEcbPaddedSize(file.length)) {
|
|
114
|
+
throw new Error('uploadMedia: unexpected AES-128-ECB ciphertext size');
|
|
115
|
+
}
|
|
116
|
+
return { aeskey, ciphertext };
|
|
117
|
+
}
|
|
118
|
+
function buildCdnUploadUrl(cdnBaseUrl, uploadParam, filekey) {
|
|
119
|
+
const base = cdnBaseUrl.replace(/\/+$/, '');
|
|
120
|
+
return `${base}/upload?encrypted_query_param=${encodeURIComponent(uploadParam)}&filekey=${encodeURIComponent(filekey)}`;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/media/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification: A — DSH glue [keep].
|
|
3
|
+
*
|
|
4
|
+
* Two-phase inbound dedup (message_id/seq/hash) + durable store + stable hash.
|
|
5
|
+
* DSH-specific receive reliability, not a platform protocol. Keep.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Dedup identity for inbound iLink messages.
|
|
9
|
+
*
|
|
10
|
+
* Priority: message_id (first-class) -> seq -> stable hash of the raw payload.
|
|
11
|
+
* We NEVER primarily key on sender+content, so two identical 'hello' messages
|
|
12
|
+
* must not collide.
|
|
13
|
+
*
|
|
14
|
+
* The dedup store is TWO-PHASE (R1): a message only becomes a committed dedup
|
|
15
|
+
* entry AFTER it has successfully entered the Channel pipeline (emit resolved).
|
|
16
|
+
* This way a failed emit is replayed on the next round instead of being
|
|
17
|
+
* dropped, while a crash after emit but before cursor commit is still dropped
|
|
18
|
+
* on replay (no duplicate Agent trigger).
|
|
19
|
+
*/
|
|
20
|
+
import type { ChannelStorage } from '@krischoichoi/channel-core';
|
|
21
|
+
/** Optional injectable clock for window pruning. */
|
|
22
|
+
export interface DedupOptions {
|
|
23
|
+
/** Dedup window in ms. */
|
|
24
|
+
windowMs: number;
|
|
25
|
+
/** Injectable clock (tests). */
|
|
26
|
+
now?: () => number;
|
|
27
|
+
}
|
|
28
|
+
/** A committed dedup entry. */
|
|
29
|
+
export interface DedupRecord {
|
|
30
|
+
key: string;
|
|
31
|
+
committedAt: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Stable hash (FNV-1a-ish) used as the final fallback for a missing
|
|
35
|
+
* message_id/seq. Never the primary identity.
|
|
36
|
+
*/
|
|
37
|
+
export declare function stableHash(input: string): string;
|
|
38
|
+
/** Compute a dedup identity for a raw iLink message. */
|
|
39
|
+
export declare function dedupKey(raw: unknown): string;
|
|
40
|
+
/**
|
|
41
|
+
* Two-phase dedup boundary (R1). has() answers "already committed?" without
|
|
42
|
+
* recording anything; commit() records AFTER the caller has successfully
|
|
43
|
+
* processed the message.
|
|
44
|
+
*/
|
|
45
|
+
export interface DedupStore {
|
|
46
|
+
/** Whether the key was committed within the window. */
|
|
47
|
+
has(key: string): Promise<boolean>;
|
|
48
|
+
/** Persist the key as processed. */
|
|
49
|
+
commit(key: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
/** In-memory two-phase dedup (tests / no persistence). */
|
|
52
|
+
export declare class MemoryDedupStore implements DedupStore {
|
|
53
|
+
private readonly seen;
|
|
54
|
+
private readonly windowMs;
|
|
55
|
+
private readonly now;
|
|
56
|
+
constructor(options: DedupOptions);
|
|
57
|
+
has(key: string): Promise<boolean>;
|
|
58
|
+
commit(key: string): Promise<void>;
|
|
59
|
+
private prune;
|
|
60
|
+
}
|
|
61
|
+
export interface PersistentDedupStoreOptions extends DedupOptions {
|
|
62
|
+
storage: ChannelStorage;
|
|
63
|
+
/** Local DSH account alias; defaults to 'main'. */
|
|
64
|
+
accountId?: string;
|
|
65
|
+
/** Max entries persisted (defaults to 5000). */
|
|
66
|
+
maxRecords?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Durable two-phase dedup store. Committed records survive a restart so a
|
|
70
|
+
* cursor crash-replay window never re-triggers the Agent. State is a JSON
|
|
71
|
+
* array of DedupRecord under weixin:dedup:<accountId>, loaded lazily and
|
|
72
|
+
* rewritten (pruned + capped) on each commit.
|
|
73
|
+
*/
|
|
74
|
+
export declare class PersistentDedupStore implements DedupStore {
|
|
75
|
+
private readonly storage;
|
|
76
|
+
private readonly key;
|
|
77
|
+
private readonly windowMs;
|
|
78
|
+
private readonly now;
|
|
79
|
+
private readonly maxRecords;
|
|
80
|
+
private records?;
|
|
81
|
+
constructor(options: PersistentDedupStoreOptions);
|
|
82
|
+
private load;
|
|
83
|
+
private persist;
|
|
84
|
+
has(key: string): Promise<boolean>;
|
|
85
|
+
commit(key: string): Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../../src/messaging/dedup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,+BAA+B;AAC/B,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED,wDAAwD;AACxD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAU7C;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,oCAAoC;IACpC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAKD,0DAA0D;AAC1D,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,OAAO,EAAE,YAAY;IAK3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAMlC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxC,OAAO,CAAC,KAAK;CAKd;AAED,MAAM,WAAW,2BAA4B,SAAQ,YAAY;IAC/D,OAAO,EAAE,cAAc,CAAC;IACxB,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,UAAU;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,OAAO,CAAC,CAAsB;gBAE1B,OAAO,EAAE,2BAA2B;YAQlC,IAAI;YAoBJ,OAAO;IAcf,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOlC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKzC"}
|