@lanbaolu/dsh-wechat-bridge 0.1.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/LICENSE +21 -0
- package/README.md +152 -0
- package/cordis.patch.yml +12 -0
- package/docs/feasibility-plan.md +62 -0
- package/lib/bridge/commands/handlers.js +155 -0
- package/lib/bridge/commands/handlers.js.map +1 -0
- package/lib/bridge/commands/router.js +52 -0
- package/lib/bridge/commands/router.js.map +1 -0
- package/lib/bridge/config.js +40 -0
- package/lib/bridge/config.js.map +1 -0
- package/lib/bridge/constants.js +14 -0
- package/lib/bridge/constants.js.map +1 -0
- package/lib/bridge/dsh-client.js +100 -0
- package/lib/bridge/dsh-client.js.map +1 -0
- package/lib/bridge/logger.js +75 -0
- package/lib/bridge/logger.js.map +1 -0
- package/lib/bridge/main.js +546 -0
- package/lib/bridge/main.js.map +1 -0
- package/lib/bridge/pending-queue.js +55 -0
- package/lib/bridge/pending-queue.js.map +1 -0
- package/lib/bridge/session.js +84 -0
- package/lib/bridge/session.js.map +1 -0
- package/lib/bridge/store.js +38 -0
- package/lib/bridge/store.js.map +1 -0
- package/lib/bridge/wechat/accounts.js +50 -0
- package/lib/bridge/wechat/accounts.js.map +1 -0
- package/lib/bridge/wechat/api.js +197 -0
- package/lib/bridge/wechat/api.js.map +1 -0
- package/lib/bridge/wechat/cdn.js +45 -0
- package/lib/bridge/wechat/cdn.js.map +1 -0
- package/lib/bridge/wechat/crypto.js +17 -0
- package/lib/bridge/wechat/crypto.js.map +1 -0
- package/lib/bridge/wechat/login.js +139 -0
- package/lib/bridge/wechat/login.js.map +1 -0
- package/lib/bridge/wechat/media.js +138 -0
- package/lib/bridge/wechat/media.js.map +1 -0
- package/lib/bridge/wechat/monitor.js +104 -0
- package/lib/bridge/wechat/monitor.js.map +1 -0
- package/lib/bridge/wechat/send.js +172 -0
- package/lib/bridge/wechat/send.js.map +1 -0
- package/lib/bridge/wechat/sync-buf.js +11 -0
- package/lib/bridge/wechat/sync-buf.js.map +1 -0
- package/lib/bridge/wechat/types.js +35 -0
- package/lib/bridge/wechat/types.js.map +1 -0
- package/lib/bridge/wechat/upload.js +111 -0
- package/lib/bridge/wechat/upload.js.map +1 -0
- package/lib/client/Panel.js +205 -0
- package/lib/client/Panel.js.map +1 -0
- package/lib/client/index.js +11 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client.js +376 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +828 -0
- package/lib/index.js.map +1 -0
- package/lib/types/bridge/commands/handlers.d.ts +18 -0
- package/lib/types/bridge/commands/router.d.ts +26 -0
- package/lib/types/bridge/config.d.ts +7 -0
- package/lib/types/bridge/constants.d.ts +10 -0
- package/lib/types/bridge/dsh-client.d.ts +39 -0
- package/lib/types/bridge/logger.d.ts +13 -0
- package/lib/types/bridge/main.d.ts +1 -0
- package/lib/types/bridge/pending-queue.d.ts +10 -0
- package/lib/types/bridge/session.d.ts +22 -0
- package/lib/types/bridge/store.d.ts +11 -0
- package/lib/types/bridge/wechat/accounts.d.ts +14 -0
- package/lib/types/bridge/wechat/api.d.ts +32 -0
- package/lib/types/bridge/wechat/cdn.d.ts +2 -0
- package/lib/types/bridge/wechat/crypto.d.ts +4 -0
- package/lib/types/bridge/wechat/login.d.ts +31 -0
- package/lib/types/bridge/wechat/media.d.ts +20 -0
- package/lib/types/bridge/wechat/monitor.d.ts +10 -0
- package/lib/types/bridge/wechat/send.d.ts +6 -0
- package/lib/types/bridge/wechat/sync-buf.d.ts +2 -0
- package/lib/types/bridge/wechat/types.d.ts +135 -0
- package/lib/types/bridge/wechat/upload.d.ts +11 -0
- package/lib/types/client/Panel.d.ts +3 -0
- package/lib/types/client/index.d.ts +9 -0
- package/lib/types/index.d.ts +39 -0
- package/package.json +92 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { DATA_DIR } from './constants.js';
|
|
4
|
+
import { logger } from './logger.js';
|
|
5
|
+
const QUEUE_DIR = join(DATA_DIR, 'pending-queue');
|
|
6
|
+
function queuePath(accountId) {
|
|
7
|
+
return join(QUEUE_DIR, `${accountId}.json`);
|
|
8
|
+
}
|
|
9
|
+
function ensureDir() {
|
|
10
|
+
if (!existsSync(QUEUE_DIR)) {
|
|
11
|
+
mkdirSync(QUEUE_DIR, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function loadPendingQueue(accountId) {
|
|
15
|
+
try {
|
|
16
|
+
const path = queuePath(accountId);
|
|
17
|
+
if (!existsSync(path))
|
|
18
|
+
return [];
|
|
19
|
+
const raw = readFileSync(path, 'utf-8');
|
|
20
|
+
const data = JSON.parse(raw);
|
|
21
|
+
return Array.isArray(data) ? data : [];
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
logger.warn('Failed to load pending queue', {
|
|
25
|
+
accountId,
|
|
26
|
+
error: err instanceof Error ? err.message : String(err),
|
|
27
|
+
});
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function savePendingQueue(accountId, items) {
|
|
32
|
+
try {
|
|
33
|
+
ensureDir();
|
|
34
|
+
writeFileSync(queuePath(accountId), JSON.stringify(items, null, 2), 'utf-8');
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
logger.warn('Failed to save pending queue', {
|
|
38
|
+
accountId,
|
|
39
|
+
error: err instanceof Error ? err.message : String(err),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function appendPending(accountId, item) {
|
|
44
|
+
const items = loadPendingQueue(accountId);
|
|
45
|
+
items.push(item);
|
|
46
|
+
savePendingQueue(accountId, items);
|
|
47
|
+
return items;
|
|
48
|
+
}
|
|
49
|
+
export function clearPending(accountId) {
|
|
50
|
+
savePendingQueue(accountId, []);
|
|
51
|
+
}
|
|
52
|
+
export function hasPending(accountId) {
|
|
53
|
+
return loadPendingQueue(accountId).length > 0;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=pending-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-queue.js","sourceRoot":"","sources":["../../src/bridge/pending-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AAElD,SAAS,SAAS,CAAC,SAAiB;IAClC,OAAO,IAAI,CAAC,SAAS,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS;IAChB,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE;YAC1C,SAAS;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,SAAiB,EAAE,KAAoB;IACtE,IAAI,CAAC;QACH,SAAS,EAAE,CAAC;QACZ,aAAa,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE;YAC1C,SAAS;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,SAAiB,EAAE,IAAiB;IAChE,MAAM,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { loadJson, saveJson, validateAccountId } from './store.js';
|
|
2
|
+
import { mkdirSync } from 'node:fs';
|
|
3
|
+
import { DATA_DIR, DEFAULT_WORKING_DIR } from './constants.js';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const SESSIONS_DIR = join(DATA_DIR, 'sessions');
|
|
6
|
+
const DEFAULT_MAX_HISTORY = 100;
|
|
7
|
+
export function createSessionStore() {
|
|
8
|
+
function getSessionPath(accountId) {
|
|
9
|
+
validateAccountId(accountId);
|
|
10
|
+
return join(SESSIONS_DIR, `${accountId}.json`);
|
|
11
|
+
}
|
|
12
|
+
function load(accountId) {
|
|
13
|
+
validateAccountId(accountId);
|
|
14
|
+
const session = loadJson(getSessionPath(accountId), {
|
|
15
|
+
workingDirectory: DEFAULT_WORKING_DIR,
|
|
16
|
+
state: 'idle',
|
|
17
|
+
chatHistory: [],
|
|
18
|
+
maxHistoryLength: DEFAULT_MAX_HISTORY,
|
|
19
|
+
});
|
|
20
|
+
// Backward compatibility: ensure chatHistory exists
|
|
21
|
+
if (!session.chatHistory) {
|
|
22
|
+
session.chatHistory = [];
|
|
23
|
+
}
|
|
24
|
+
if (!session.maxHistoryLength) {
|
|
25
|
+
session.maxHistoryLength = DEFAULT_MAX_HISTORY;
|
|
26
|
+
}
|
|
27
|
+
return session;
|
|
28
|
+
}
|
|
29
|
+
function save(accountId, session) {
|
|
30
|
+
mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
31
|
+
// Trim chat history if it exceeds max length before saving
|
|
32
|
+
const maxLen = session.maxHistoryLength || DEFAULT_MAX_HISTORY;
|
|
33
|
+
if (session.chatHistory.length > maxLen) {
|
|
34
|
+
session.chatHistory = session.chatHistory.slice(-maxLen);
|
|
35
|
+
}
|
|
36
|
+
saveJson(getSessionPath(accountId), session);
|
|
37
|
+
}
|
|
38
|
+
function clear(accountId, currentSession) {
|
|
39
|
+
const session = {
|
|
40
|
+
sdkSessionId: undefined, // explicitly clear so Object.assign removes it
|
|
41
|
+
previousSdkSessionId: undefined,
|
|
42
|
+
workingDirectory: currentSession?.workingDirectory ?? DEFAULT_WORKING_DIR,
|
|
43
|
+
model: currentSession?.model,
|
|
44
|
+
state: 'idle',
|
|
45
|
+
chatHistory: [],
|
|
46
|
+
maxHistoryLength: currentSession?.maxHistoryLength || DEFAULT_MAX_HISTORY,
|
|
47
|
+
};
|
|
48
|
+
save(accountId, session);
|
|
49
|
+
return session;
|
|
50
|
+
}
|
|
51
|
+
function addChatMessage(session, role, content) {
|
|
52
|
+
if (!session.chatHistory) {
|
|
53
|
+
session.chatHistory = [];
|
|
54
|
+
}
|
|
55
|
+
session.chatHistory.push({
|
|
56
|
+
role,
|
|
57
|
+
content,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
});
|
|
60
|
+
// Trim if exceeds max length
|
|
61
|
+
const maxLen = session.maxHistoryLength || DEFAULT_MAX_HISTORY;
|
|
62
|
+
if (session.chatHistory.length > maxLen) {
|
|
63
|
+
session.chatHistory = session.chatHistory.slice(-maxLen);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function getChatHistoryText(session, limit) {
|
|
67
|
+
const history = session.chatHistory || [];
|
|
68
|
+
const messages = limit ? history.slice(-limit) : history;
|
|
69
|
+
if (messages.length === 0) {
|
|
70
|
+
return '暂无对话记录';
|
|
71
|
+
}
|
|
72
|
+
const lines = [];
|
|
73
|
+
for (const msg of messages) {
|
|
74
|
+
const time = new Date(msg.timestamp).toLocaleString('zh-CN');
|
|
75
|
+
const role = msg.role === 'user' ? '用户' : 'DSH';
|
|
76
|
+
lines.push(`[${time}] ${role}:`);
|
|
77
|
+
lines.push(msg.content);
|
|
78
|
+
lines.push('');
|
|
79
|
+
}
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
return { load, save, clear, addChatMessage, getChatHistoryText };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/bridge/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, chmodSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { logger } from "./logger.js";
|
|
4
|
+
export function validateAccountId(accountId) {
|
|
5
|
+
if (!/^[a-zA-Z0-9_.@=-]+$/.test(accountId)) {
|
|
6
|
+
throw new Error(`Invalid accountId: "${accountId}"`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Load a JSON file, returning a typed object or the fallback if the file
|
|
11
|
+
* does not exist or cannot be parsed.
|
|
12
|
+
*/
|
|
13
|
+
export function loadJson(filePath, fallback) {
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const code = err.code;
|
|
20
|
+
if (code !== 'ENOENT') {
|
|
21
|
+
logger.warn('loadJson failed, using fallback', { filePath, error: err instanceof Error ? err.message : String(err) });
|
|
22
|
+
}
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Persist an object as pretty-printed JSON.
|
|
28
|
+
* File is written with mode 0o600 (owner read/write only).
|
|
29
|
+
*/
|
|
30
|
+
export function saveJson(filePath, data) {
|
|
31
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
32
|
+
const raw = JSON.stringify(data, null, 2) + "\n";
|
|
33
|
+
writeFileSync(filePath, raw, "utf-8");
|
|
34
|
+
if (process.platform !== 'win32') {
|
|
35
|
+
chmodSync(filePath, 0o600);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/bridge/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,GAAG,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAI,QAAgB,EAAE,QAAW;IACvD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,QAAgB,EAAE,IAAa;IACtD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;IACjD,aAAa,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
3
|
+
import { loadJson, saveJson, validateAccountId } from '../store.js';
|
|
4
|
+
import { logger } from '../logger.js';
|
|
5
|
+
import { DATA_DIR } from '../constants.js';
|
|
6
|
+
export const DEFAULT_BASE_URL = 'https://ilinkai.weixin.qq.com';
|
|
7
|
+
const ACCOUNTS_DIR = join(DATA_DIR, 'accounts');
|
|
8
|
+
function accountPath(accountId) {
|
|
9
|
+
validateAccountId(accountId);
|
|
10
|
+
return join(ACCOUNTS_DIR, `${accountId}.json`);
|
|
11
|
+
}
|
|
12
|
+
/** Persist account credentials to disk. */
|
|
13
|
+
export function saveAccount(data) {
|
|
14
|
+
const filePath = accountPath(data.accountId);
|
|
15
|
+
saveJson(filePath, data);
|
|
16
|
+
logger.info('Account saved', { accountId: data.accountId });
|
|
17
|
+
}
|
|
18
|
+
/** Load account credentials by ID. Returns null if not found. */
|
|
19
|
+
export function loadAccount(accountId) {
|
|
20
|
+
const filePath = accountPath(accountId);
|
|
21
|
+
const data = loadJson(filePath, null);
|
|
22
|
+
if (data) {
|
|
23
|
+
logger.info('Account loaded', { accountId });
|
|
24
|
+
}
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
/** Load the most recently modified account. Returns null if none exist. */
|
|
28
|
+
export function loadLatestAccount() {
|
|
29
|
+
try {
|
|
30
|
+
const files = readdirSync(ACCOUNTS_DIR).filter((f) => f.endsWith('.json'));
|
|
31
|
+
if (files.length === 0)
|
|
32
|
+
return null;
|
|
33
|
+
let latestFile = files[0];
|
|
34
|
+
let latestMtime = 0;
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
const stat = statSync(join(ACCOUNTS_DIR, file));
|
|
37
|
+
if (stat.mtimeMs > latestMtime) {
|
|
38
|
+
latestMtime = stat.mtimeMs;
|
|
39
|
+
latestFile = file;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const accountId = latestFile.replace(/\.json$/, '');
|
|
43
|
+
return loadAccount(accountId);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Directory does not exist or is unreadable
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../../src/bridge/wechat/accounts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,+BAA+B,CAAC;AAUhE,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAEhD,SAAS,WAAW,CAAC,SAAiB;IACpC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC,YAAY,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,WAAW,CAAC,IAAiB;IAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzB,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAqB,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEpC,IAAI,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;YAChD,IAAI,IAAI,CAAC,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC/B,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC;gBAC3B,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QACpD,OAAO,WAAW,CAAC,SAAS,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,4CAA4C;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { logger } from '../logger.js';
|
|
2
|
+
/** Generate a random base64 identifier. */
|
|
3
|
+
function generateUin() {
|
|
4
|
+
const buf = new Uint8Array(4);
|
|
5
|
+
crypto.getRandomValues(buf);
|
|
6
|
+
return Buffer.from(buf).toString('base64');
|
|
7
|
+
}
|
|
8
|
+
export class WeChatApi {
|
|
9
|
+
token;
|
|
10
|
+
baseUrl;
|
|
11
|
+
uin;
|
|
12
|
+
nextSendTime = new Map();
|
|
13
|
+
static MIN_SEND_INTERVAL = 2500;
|
|
14
|
+
// Cooldown applied after a rate-limit (ret:-2). Aligned with the circuit
|
|
15
|
+
// breaker window so they don't fight each other.
|
|
16
|
+
static RATE_LIMIT_COOLDOWN_MS = 30_000;
|
|
17
|
+
// ── Circuit breaker ────────────────────────────────────────────────────
|
|
18
|
+
// Borrowed from Hermes WeChat adapter: trip after the first genuine
|
|
19
|
+
// rate-limit in a 30s window, stay open 30s. While open, all sends fail
|
|
20
|
+
// fast without hitting the API — breaking the 14-minute "head-banging"
|
|
21
|
+
// loop we observed in production logs.
|
|
22
|
+
static CIRCUIT_THRESHOLD = 1;
|
|
23
|
+
static CIRCUIT_WINDOW_MS = 30_000;
|
|
24
|
+
static CIRCUIT_OPEN_MS = 30_000;
|
|
25
|
+
_rateLimitEvents = [];
|
|
26
|
+
_circuitUntil = 0;
|
|
27
|
+
// ret:-2 + errmsg="unknown error" is a stale-session signal (same family
|
|
28
|
+
// as errcode:-14), not a real rate-limit. Pause that user 10 minutes
|
|
29
|
+
// instead of cycling through the rate-limit path.
|
|
30
|
+
static STALE_SESSION_PAUSE_MS = 10 * 60 * 1000;
|
|
31
|
+
constructor(token, baseUrl = 'https://ilinkai.weixin.qq.com') {
|
|
32
|
+
if (baseUrl) {
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(baseUrl);
|
|
35
|
+
const allowedHosts = ['weixin.qq.com', 'wechat.com'];
|
|
36
|
+
const isAllowed = allowedHosts.some(h => url.hostname === h || url.hostname.endsWith('.' + h));
|
|
37
|
+
if (url.protocol !== 'https:' || !isAllowed) {
|
|
38
|
+
logger.warn('Untrusted baseUrl, using default', { baseUrl });
|
|
39
|
+
baseUrl = 'https://ilinkai.weixin.qq.com';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
logger.warn('Invalid baseUrl, using default', { baseUrl });
|
|
44
|
+
baseUrl = 'https://ilinkai.weixin.qq.com';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
this.token = token;
|
|
48
|
+
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
49
|
+
this.uin = generateUin();
|
|
50
|
+
}
|
|
51
|
+
headers() {
|
|
52
|
+
return {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'Authorization': `Bearer ${this.token}`,
|
|
55
|
+
'AuthorizationType': 'ilink_bot_token',
|
|
56
|
+
'X-WECHAT-UIN': this.uin,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async request(path, body, timeoutMs = 15_000) {
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
62
|
+
const url = `${this.baseUrl}/${path}`;
|
|
63
|
+
logger.debug('API request', { url, body });
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch(url, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: this.headers(),
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
signal: controller.signal,
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
throw new Error(`HTTP ${res.status}: ${text}`);
|
|
74
|
+
}
|
|
75
|
+
const json = (await res.json());
|
|
76
|
+
logger.debug('API response', json);
|
|
77
|
+
return json;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
81
|
+
throw new Error(`Request to ${url} timed out after ${timeoutMs}ms`);
|
|
82
|
+
}
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Long-poll for new messages. Timeout 35s for long-polling. */
|
|
90
|
+
async getUpdates(buf) {
|
|
91
|
+
return this.request('ilink/bot/getupdates', buf ? { get_updates_buf: buf } : {}, 35_000);
|
|
92
|
+
}
|
|
93
|
+
/** Send a message to a user. Per-user rate limited, retries on rate-limit (ret: -2). */
|
|
94
|
+
async sendMessage(req) {
|
|
95
|
+
// Circuit breaker: fail fast without calling the API while open.
|
|
96
|
+
// This is what breaks the 14-minute head-banging loop.
|
|
97
|
+
if (this._isCircuitOpen()) {
|
|
98
|
+
const remainingSec = Math.ceil((this._circuitUntil - Date.now()) / 1000);
|
|
99
|
+
logger.warn('sendMessage rejected by circuit breaker', { remainingSec });
|
|
100
|
+
throw new Error(`circuit breaker open, ${remainingSec}s remaining`);
|
|
101
|
+
}
|
|
102
|
+
const userId = req.msg?.to_user_id;
|
|
103
|
+
if (userId) {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const nextAvailable = (this.nextSendTime.get(userId) ?? 0) + WeChatApi.MIN_SEND_INTERVAL;
|
|
106
|
+
const sendAt = Math.max(now, nextAvailable);
|
|
107
|
+
this.nextSendTime.set(userId, sendAt);
|
|
108
|
+
const waitMs = sendAt - now;
|
|
109
|
+
if (waitMs > 0) {
|
|
110
|
+
logger.debug('Rate limiter waiting', { userId, waitMs });
|
|
111
|
+
await new Promise(r => setTimeout(r, waitMs));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const MAX_RETRIES = 2;
|
|
115
|
+
let delay = 3_000;
|
|
116
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
117
|
+
// Re-check the circuit on each retry — a prior attempt may have just tripped it.
|
|
118
|
+
if (this._isCircuitOpen()) {
|
|
119
|
+
const remainingSec = Math.ceil((this._circuitUntil - Date.now()) / 1000);
|
|
120
|
+
logger.warn('sendMessage aborted mid-retry by circuit breaker', { attempt, remainingSec });
|
|
121
|
+
throw new Error(`circuit breaker open during retry, ${remainingSec}s remaining`);
|
|
122
|
+
}
|
|
123
|
+
const res = await this.request('ilink/bot/sendmessage', req);
|
|
124
|
+
if (res.ret === -2) {
|
|
125
|
+
// Distinguish stale-session (ret:-2 + errmsg "unknown error") from a real rate-limit.
|
|
126
|
+
// Hermes WeChat adapter established this pattern: the stale-session case behaves
|
|
127
|
+
// like errcode:-14 and is fixed by re-login, not by retry.
|
|
128
|
+
const errmsg = (res.errmsg ?? '').toLowerCase();
|
|
129
|
+
if (errmsg === 'unknown error') {
|
|
130
|
+
logger.warn('sendMessage stale session detected (ret:-2 + unknown error)', { userId });
|
|
131
|
+
if (userId) {
|
|
132
|
+
this.nextSendTime.set(userId, Date.now() + WeChatApi.STALE_SESSION_PAUSE_MS);
|
|
133
|
+
}
|
|
134
|
+
throw new Error('stale session — user must send a message to refresh context_token');
|
|
135
|
+
}
|
|
136
|
+
// Real rate-limit: trip the circuit breaker so subsequent sends fail fast.
|
|
137
|
+
this._tripCircuit();
|
|
138
|
+
if (userId) {
|
|
139
|
+
this.nextSendTime.set(userId, Date.now() + WeChatApi.RATE_LIMIT_COOLDOWN_MS);
|
|
140
|
+
}
|
|
141
|
+
if (attempt === MAX_RETRIES) {
|
|
142
|
+
logger.warn('sendMessage rate-limited after max retries', { attempts: MAX_RETRIES });
|
|
143
|
+
throw new Error(`sendMessage rate-limited after ${MAX_RETRIES} retries`);
|
|
144
|
+
}
|
|
145
|
+
logger.warn('sendMessage rate-limited (ret:-2), retrying', { attempt, delayMs: delay });
|
|
146
|
+
await new Promise(r => setTimeout(r, delay));
|
|
147
|
+
delay = Math.min(delay * 2, 15_000);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ── Circuit breaker helpers ────────────────────────────────────────────
|
|
154
|
+
/** True while the breaker is open (sends should fail fast). */
|
|
155
|
+
_isCircuitOpen() {
|
|
156
|
+
if (this._circuitUntil === 0)
|
|
157
|
+
return false;
|
|
158
|
+
if (Date.now() >= this._circuitUntil) {
|
|
159
|
+
this._circuitUntil = 0;
|
|
160
|
+
this._rateLimitEvents.length = 0;
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
/** Record a rate-limit event and open the breaker if threshold is met. */
|
|
166
|
+
_tripCircuit() {
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const windowStart = now - WeChatApi.CIRCUIT_WINDOW_MS;
|
|
169
|
+
while (this._rateLimitEvents.length > 0 && this._rateLimitEvents[0] < windowStart) {
|
|
170
|
+
this._rateLimitEvents.shift();
|
|
171
|
+
}
|
|
172
|
+
this._rateLimitEvents.push(now);
|
|
173
|
+
if (this._rateLimitEvents.length >= WeChatApi.CIRCUIT_THRESHOLD) {
|
|
174
|
+
const openUntil = Math.max(this._circuitUntil, now + WeChatApi.CIRCUIT_OPEN_MS);
|
|
175
|
+
if (openUntil > this._circuitUntil) {
|
|
176
|
+
logger.warn('Circuit breaker tripped', {
|
|
177
|
+
events: this._rateLimitEvents.length,
|
|
178
|
+
openMs: WeChatApi.CIRCUIT_OPEN_MS,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
this._circuitUntil = openUntil;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Fetch bot config (includes typing_ticket). */
|
|
185
|
+
async getConfig(ilinkUserId, contextToken) {
|
|
186
|
+
return this.request('ilink/bot/getconfig', { ilink_user_id: ilinkUserId, context_token: contextToken }, 10_000);
|
|
187
|
+
}
|
|
188
|
+
/** Send a typing indicator to a user. */
|
|
189
|
+
async sendTyping(req) {
|
|
190
|
+
await this.request('ilink/bot/sendtyping', req, 10_000);
|
|
191
|
+
}
|
|
192
|
+
/** Get a presigned upload URL for media files. */
|
|
193
|
+
async getUploadUrl(req) {
|
|
194
|
+
return this.request('ilink/bot/getuploadurl', req);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/bridge/wechat/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { decryptAesEcb } from "./crypto.js";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
import { CDN_BASE_URL } from "../constants.js";
|
|
4
|
+
export function buildCdnDownloadUrl(encryptQueryParam) {
|
|
5
|
+
if (!/^[A-Za-z0-9%=&+._~\-/]+$/.test(encryptQueryParam)) {
|
|
6
|
+
throw new Error('Invalid CDN query parameter');
|
|
7
|
+
}
|
|
8
|
+
return `${CDN_BASE_URL}/download?encrypted_query_param=${encodeURIComponent(encryptQueryParam)}`;
|
|
9
|
+
}
|
|
10
|
+
export async function downloadAndDecrypt(encryptQueryParam, aesKeyBase64) {
|
|
11
|
+
const url = buildCdnDownloadUrl(encryptQueryParam);
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timer = setTimeout(() => controller.abort(), 30_000);
|
|
14
|
+
let response;
|
|
15
|
+
try {
|
|
16
|
+
response = await fetch(url, { signal: controller.signal });
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
throw new Error(`CDN download failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21
|
+
}
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`CDN download failed: ${response.status} ${response.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
const encrypted = Buffer.from(await response.arrayBuffer());
|
|
27
|
+
// Handle both formats:
|
|
28
|
+
// 1. base64-of-raw-16-bytes (16 raw bytes encoded as base64)
|
|
29
|
+
// 2. base64-of-hex-string (32 hex chars encoded as base64)
|
|
30
|
+
let aesKey;
|
|
31
|
+
const raw = Buffer.from(aesKeyBase64, "base64");
|
|
32
|
+
if (raw.length === 16) {
|
|
33
|
+
// base64-of-raw-16-bytes
|
|
34
|
+
aesKey = raw;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// base64-of-hex-string: decode the string as hex to get the 16-byte key
|
|
38
|
+
const hexStr = raw.toString("utf-8");
|
|
39
|
+
aesKey = Buffer.from(hexStr, "hex");
|
|
40
|
+
}
|
|
41
|
+
const decrypted = decryptAesEcb(aesKey, encrypted);
|
|
42
|
+
logger.info("CDN download and decrypt succeeded", { size: decrypted.length });
|
|
43
|
+
return decrypted;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cdn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdn.js","sourceRoot":"","sources":["../../../src/bridge/wechat/cdn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,UAAU,mBAAmB,CAAC,iBAAyB;IAC3D,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,GAAG,YAAY,mCAAmC,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,CAAC;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,iBAAyB,EACzB,YAAoB;IAEpB,MAAM,GAAG,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,YAAY,CAAC,KAAK,CAAC,CAAC;IAEpB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAE5D,uBAAuB;IACvB,6DAA6D;IAC7D,2DAA2D;IAC3D,IAAI,MAAc,CAAC;IACnB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAEhD,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,yBAAyB;QACzB,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;SAAM,CAAC;QACN,wEAAwE;QACxE,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAE9E,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
|
|
2
|
+
export function generateAesKey() {
|
|
3
|
+
return randomBytes(16).toString("base64");
|
|
4
|
+
}
|
|
5
|
+
export function aesEcbPaddedSize(size) {
|
|
6
|
+
const block = 16;
|
|
7
|
+
return Math.floor((size + block - 1) / block) * block;
|
|
8
|
+
}
|
|
9
|
+
export function encryptAesEcb(key, plaintext) {
|
|
10
|
+
const cipher = createCipheriv("aes-128-ecb", key, null);
|
|
11
|
+
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
12
|
+
}
|
|
13
|
+
export function decryptAesEcb(key, ciphertext) {
|
|
14
|
+
const decipher = createDecipheriv("aes-128-ecb", key, null);
|
|
15
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/bridge/wechat/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAEvE,MAAM,UAAU,cAAc;IAC5B,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,SAAiB;IAC1D,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,UAAkB;IAC3D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACxE,CAAC"}
|