@xuanyue202/wechat-mp 2026.3.21
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 +74 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api.d.ts +101 -0
- package/dist/src/api.d.ts.map +1 -0
- package/dist/src/api.js +142 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/channel.d.ts +296 -0
- package/dist/src/channel.d.ts.map +1 -0
- package/dist/src/channel.js +341 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/config.d.ts +69 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +167 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/crypto.d.ts +117 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +270 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/crypto.test.d.ts +2 -0
- package/dist/src/crypto.test.d.ts.map +1 -0
- package/dist/src/crypto.test.js +76 -0
- package/dist/src/crypto.test.js.map +1 -0
- package/dist/src/dispatch.d.ts +15 -0
- package/dist/src/dispatch.d.ts.map +1 -0
- package/dist/src/dispatch.js +193 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/dispatch.test.d.ts +2 -0
- package/dist/src/dispatch.test.d.ts.map +1 -0
- package/dist/src/dispatch.test.js +231 -0
- package/dist/src/dispatch.test.js.map +1 -0
- package/dist/src/inbound.d.ts +7 -0
- package/dist/src/inbound.d.ts.map +1 -0
- package/dist/src/inbound.js +82 -0
- package/dist/src/inbound.js.map +1 -0
- package/dist/src/onboarding.d.ts +25 -0
- package/dist/src/onboarding.d.ts.map +1 -0
- package/dist/src/onboarding.js +49 -0
- package/dist/src/onboarding.js.map +1 -0
- package/dist/src/outbound.d.ts +17 -0
- package/dist/src/outbound.d.ts.map +1 -0
- package/dist/src/outbound.js +55 -0
- package/dist/src/outbound.js.map +1 -0
- package/dist/src/outbound.test.d.ts +2 -0
- package/dist/src/outbound.test.d.ts.map +1 -0
- package/dist/src/outbound.test.js +175 -0
- package/dist/src/outbound.test.js.map +1 -0
- package/dist/src/probe.d.ts +15 -0
- package/dist/src/probe.d.ts.map +1 -0
- package/dist/src/probe.js +55 -0
- package/dist/src/probe.js.map +1 -0
- package/dist/src/runtime.d.ts +22 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +33 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/send.d.ts +27 -0
- package/dist/src/send.d.ts.map +1 -0
- package/dist/src/send.js +103 -0
- package/dist/src/send.js.map +1 -0
- package/dist/src/state.d.ts +7 -0
- package/dist/src/state.d.ts.map +1 -0
- package/dist/src/state.js +109 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/text.d.ts +46 -0
- package/dist/src/text.d.ts.map +1 -0
- package/dist/src/text.js +192 -0
- package/dist/src/text.js.map +1 -0
- package/dist/src/text.test.d.ts +2 -0
- package/dist/src/text.test.d.ts.map +1 -0
- package/dist/src/text.test.js +110 -0
- package/dist/src/text.test.js.map +1 -0
- package/dist/src/token.d.ts +40 -0
- package/dist/src/token.d.ts.map +1 -0
- package/dist/src/token.js +154 -0
- package/dist/src/token.js.map +1 -0
- package/dist/src/token.test.d.ts +2 -0
- package/dist/src/token.test.d.ts.map +1 -0
- package/dist/src/token.test.js +74 -0
- package/dist/src/token.test.js.map +1 -0
- package/dist/src/types.d.ts +320 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/webhook.d.ts +6 -0
- package/dist/src/webhook.d.ts.map +1 -0
- package/dist/src/webhook.js +381 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/webhook.test.d.ts +2 -0
- package/dist/src/webhook.test.d.ts.map +1 -0
- package/dist/src/webhook.test.js +737 -0
- package/dist/src/webhook.test.js.map +1 -0
- package/openclaw.plugin.json +83 -0
- package/package.json +103 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/send.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WechatMpAccountState } from "./types.js";
|
|
2
|
+
export declare function markProcessedMessage(msgId: string): Promise<boolean>;
|
|
3
|
+
export declare function getAccountState(accountId: string): Promise<WechatMpAccountState>;
|
|
4
|
+
export declare function updateAccountState(accountId: string, patch: Partial<WechatMpAccountState>): Promise<WechatMpAccountState>;
|
|
5
|
+
export declare function flushWechatMpStateForTests(): Promise<void>;
|
|
6
|
+
export declare function setWechatMpStateFilePathForTests(nextPath?: string): void;
|
|
7
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAA0B,MAAM,YAAY,CAAC;AAqE/E,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW1E;AAED,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAGtF;AAED,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAO/B;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhE;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAQxE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
const DEDUP_TTL_MS = 10 * 60 * 1000;
|
|
5
|
+
const DEFAULT_STATE_FILE = join(homedir(), ".openclaw", "wechat-mp", "data", "state.json");
|
|
6
|
+
let stateFilePath = DEFAULT_STATE_FILE;
|
|
7
|
+
let cachedState = null;
|
|
8
|
+
let loadingState = null;
|
|
9
|
+
let saveTimer = null;
|
|
10
|
+
function createEmptyState() {
|
|
11
|
+
return {
|
|
12
|
+
version: 1,
|
|
13
|
+
processedMsgIds: {},
|
|
14
|
+
accounts: {},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function pruneState(state) {
|
|
18
|
+
const cutoff = Date.now() - DEDUP_TTL_MS;
|
|
19
|
+
for (const [msgId, timestamp] of Object.entries(state.processedMsgIds)) {
|
|
20
|
+
if (timestamp < cutoff) {
|
|
21
|
+
delete state.processedMsgIds[msgId];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function saveState() {
|
|
26
|
+
if (!cachedState)
|
|
27
|
+
return;
|
|
28
|
+
pruneState(cachedState);
|
|
29
|
+
await mkdir(dirname(stateFilePath), { recursive: true });
|
|
30
|
+
await writeFile(stateFilePath, `${JSON.stringify(cachedState, null, 2)}\n`, "utf8");
|
|
31
|
+
}
|
|
32
|
+
function scheduleSave() {
|
|
33
|
+
if (saveTimer)
|
|
34
|
+
return;
|
|
35
|
+
saveTimer = setTimeout(() => {
|
|
36
|
+
saveTimer = null;
|
|
37
|
+
void saveState();
|
|
38
|
+
}, 50);
|
|
39
|
+
}
|
|
40
|
+
async function loadState() {
|
|
41
|
+
if (cachedState)
|
|
42
|
+
return cachedState;
|
|
43
|
+
if (loadingState)
|
|
44
|
+
return loadingState;
|
|
45
|
+
loadingState = (async () => {
|
|
46
|
+
try {
|
|
47
|
+
const raw = await readFile(stateFilePath, "utf8");
|
|
48
|
+
const parsed = JSON.parse(raw);
|
|
49
|
+
cachedState = {
|
|
50
|
+
version: 1,
|
|
51
|
+
processedMsgIds: parsed.processedMsgIds ?? {},
|
|
52
|
+
accounts: parsed.accounts ?? {},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
cachedState = createEmptyState();
|
|
57
|
+
}
|
|
58
|
+
pruneState(cachedState);
|
|
59
|
+
return cachedState;
|
|
60
|
+
})();
|
|
61
|
+
try {
|
|
62
|
+
return await loadingState;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
loadingState = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function markProcessedMessage(msgId) {
|
|
69
|
+
const normalized = msgId.trim();
|
|
70
|
+
if (!normalized)
|
|
71
|
+
return false;
|
|
72
|
+
const state = await loadState();
|
|
73
|
+
pruneState(state);
|
|
74
|
+
if (state.processedMsgIds[normalized]) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
state.processedMsgIds[normalized] = Date.now();
|
|
78
|
+
scheduleSave();
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
export async function getAccountState(accountId) {
|
|
82
|
+
const state = await loadState();
|
|
83
|
+
return { ...(state.accounts[accountId] ?? {}) };
|
|
84
|
+
}
|
|
85
|
+
export async function updateAccountState(accountId, patch) {
|
|
86
|
+
const state = await loadState();
|
|
87
|
+
const current = state.accounts[accountId] ?? {};
|
|
88
|
+
const next = { ...current, ...patch };
|
|
89
|
+
state.accounts[accountId] = next;
|
|
90
|
+
scheduleSave();
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
export async function flushWechatMpStateForTests() {
|
|
94
|
+
if (saveTimer) {
|
|
95
|
+
clearTimeout(saveTimer);
|
|
96
|
+
saveTimer = null;
|
|
97
|
+
}
|
|
98
|
+
await saveState();
|
|
99
|
+
}
|
|
100
|
+
export function setWechatMpStateFilePathForTests(nextPath) {
|
|
101
|
+
stateFilePath = nextPath?.trim() || DEFAULT_STATE_FILE;
|
|
102
|
+
cachedState = null;
|
|
103
|
+
loadingState = null;
|
|
104
|
+
if (saveTimer) {
|
|
105
|
+
clearTimeout(saveTimer);
|
|
106
|
+
saveTimer = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeChat MP text normalization utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides markdown-friendly text normalization that can be shared between
|
|
5
|
+
* the reply pipeline (dispatch.ts) and direct outbound (outbound.ts).
|
|
6
|
+
*
|
|
7
|
+
* The normalization behavior is controlled by the `renderMarkdown` config:
|
|
8
|
+
* - true (default): Apply markdown-friendly formatting for WeChat MP
|
|
9
|
+
* - false: Minimal passthrough, preserving original text
|
|
10
|
+
*/
|
|
11
|
+
/** WeChat MP text message byte limit (2048 bytes) */
|
|
12
|
+
export declare const WECHAT_TEXT_BYTE_LIMIT = 2048;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate UTF-8 byte length of a string.
|
|
15
|
+
* @param str - The string to measure
|
|
16
|
+
* @returns The byte length in UTF-8 encoding
|
|
17
|
+
*/
|
|
18
|
+
export declare function getUtf8ByteLength(str: string): number;
|
|
19
|
+
/**
|
|
20
|
+
* Split text by byte limit, respecting boundaries (paragraphs, sentences, spaces).
|
|
21
|
+
* Ensures no multi-byte characters are truncated.
|
|
22
|
+
*
|
|
23
|
+
* @param text - The text to split
|
|
24
|
+
* @param maxBytes - Maximum bytes per chunk (default: WECHAT_TEXT_BYTE_LIMIT)
|
|
25
|
+
* @returns Array of text chunks, each within the byte limit
|
|
26
|
+
*/
|
|
27
|
+
export declare function splitTextByByteLimit(text: string, maxBytes?: number): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Normalize text for WeChat MP delivery.
|
|
30
|
+
*
|
|
31
|
+
* When renderMarkdown is enabled (default), converts markdown to WeChat MP
|
|
32
|
+
* friendly plain text. When disabled, returns text with minimal changes.
|
|
33
|
+
*
|
|
34
|
+
* @param text - The raw text to normalize
|
|
35
|
+
* @param renderMarkdown - Whether to apply markdown-friendly formatting
|
|
36
|
+
* @returns Normalized text ready for WeChat MP delivery
|
|
37
|
+
*/
|
|
38
|
+
export declare function normalizeWechatMpText(text: string, renderMarkdown: boolean): string;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the renderMarkdown setting from account config.
|
|
41
|
+
* Defaults to true if not explicitly set to false.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveRenderMarkdown(config: {
|
|
44
|
+
renderMarkdown?: boolean;
|
|
45
|
+
}): boolean;
|
|
46
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAA+B,GACxC,MAAM,EAAE,CAmCV;AA2CD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,MAAM,CAWnF;AAiGD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAEnF"}
|
package/dist/src/text.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeChat MP text normalization utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides markdown-friendly text normalization that can be shared between
|
|
5
|
+
* the reply pipeline (dispatch.ts) and direct outbound (outbound.ts).
|
|
6
|
+
*
|
|
7
|
+
* The normalization behavior is controlled by the `renderMarkdown` config:
|
|
8
|
+
* - true (default): Apply markdown-friendly formatting for WeChat MP
|
|
9
|
+
* - false: Minimal passthrough, preserving original text
|
|
10
|
+
*/
|
|
11
|
+
/** WeChat MP text message byte limit (2048 bytes) */
|
|
12
|
+
export const WECHAT_TEXT_BYTE_LIMIT = 2048;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate UTF-8 byte length of a string.
|
|
15
|
+
* @param str - The string to measure
|
|
16
|
+
* @returns The byte length in UTF-8 encoding
|
|
17
|
+
*/
|
|
18
|
+
export function getUtf8ByteLength(str) {
|
|
19
|
+
return Buffer.byteLength(str, "utf-8");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Split text by byte limit, respecting boundaries (paragraphs, sentences, spaces).
|
|
23
|
+
* Ensures no multi-byte characters are truncated.
|
|
24
|
+
*
|
|
25
|
+
* @param text - The text to split
|
|
26
|
+
* @param maxBytes - Maximum bytes per chunk (default: WECHAT_TEXT_BYTE_LIMIT)
|
|
27
|
+
* @returns Array of text chunks, each within the byte limit
|
|
28
|
+
*/
|
|
29
|
+
export function splitTextByByteLimit(text, maxBytes = WECHAT_TEXT_BYTE_LIMIT) {
|
|
30
|
+
if (getUtf8ByteLength(text) <= maxBytes) {
|
|
31
|
+
return [text];
|
|
32
|
+
}
|
|
33
|
+
const chunks = [];
|
|
34
|
+
let remaining = text;
|
|
35
|
+
while (remaining.length > 0) {
|
|
36
|
+
// Find the maximum character length that fits within maxBytes
|
|
37
|
+
let end = remaining.length;
|
|
38
|
+
while (end > 0 && getUtf8ByteLength(remaining.slice(0, end)) > maxBytes) {
|
|
39
|
+
end--;
|
|
40
|
+
}
|
|
41
|
+
if (end === 0) {
|
|
42
|
+
// Single character exceeds limit (should not happen with 2048 bytes)
|
|
43
|
+
// Force take at least 1 character to avoid infinite loop
|
|
44
|
+
end = 1;
|
|
45
|
+
}
|
|
46
|
+
// Try to find a good boundary (paragraph > sentence > space)
|
|
47
|
+
const boundary = findBestBoundary(remaining, end);
|
|
48
|
+
if (boundary > 0) {
|
|
49
|
+
end = boundary;
|
|
50
|
+
}
|
|
51
|
+
const chunk = remaining.slice(0, end).trim();
|
|
52
|
+
if (chunk) {
|
|
53
|
+
chunks.push(chunk);
|
|
54
|
+
}
|
|
55
|
+
remaining = remaining.slice(end).trim();
|
|
56
|
+
}
|
|
57
|
+
return chunks;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Find the best boundary position for splitting text.
|
|
61
|
+
* Prioritizes: paragraphs > horizontal rules > sentences > spaces.
|
|
62
|
+
*
|
|
63
|
+
* @param text - The text to search
|
|
64
|
+
* @param maxPos - Maximum position to consider
|
|
65
|
+
* @returns Best boundary position, or 0 if none found
|
|
66
|
+
*/
|
|
67
|
+
function findBestBoundary(text, maxPos) {
|
|
68
|
+
const searchRegion = text.slice(0, maxPos);
|
|
69
|
+
// Priority: paragraph > horizontal rule > sentence > space
|
|
70
|
+
// Each tuple: [pattern, include pattern in chunk]
|
|
71
|
+
const boundaries = [
|
|
72
|
+
["\n\n", true],
|
|
73
|
+
["\n------------\n", true], // Converted markdown horizontal rule
|
|
74
|
+
["\n---\n", true], // Markdown horizontal rule
|
|
75
|
+
["\n***\n", true],
|
|
76
|
+
["\n___\n", true],
|
|
77
|
+
["\n", true],
|
|
78
|
+
["。", true],
|
|
79
|
+
["!", true],
|
|
80
|
+
["?", true],
|
|
81
|
+
["。 ", true],
|
|
82
|
+
["! ", true],
|
|
83
|
+
["? ", true],
|
|
84
|
+
[". ", true],
|
|
85
|
+
[" ", false],
|
|
86
|
+
];
|
|
87
|
+
for (const [boundary, include] of boundaries) {
|
|
88
|
+
const pos = searchRegion.lastIndexOf(boundary);
|
|
89
|
+
// Ensure split point is not too early (at least 30% of maxPos)
|
|
90
|
+
if (pos > maxPos * 0.3) {
|
|
91
|
+
return include ? pos + boundary.length : pos;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return 0; // No suitable boundary found
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Normalize text for WeChat MP delivery.
|
|
98
|
+
*
|
|
99
|
+
* When renderMarkdown is enabled (default), converts markdown to WeChat MP
|
|
100
|
+
* friendly plain text. When disabled, returns text with minimal changes.
|
|
101
|
+
*
|
|
102
|
+
* @param text - The raw text to normalize
|
|
103
|
+
* @param renderMarkdown - Whether to apply markdown-friendly formatting
|
|
104
|
+
* @returns Normalized text ready for WeChat MP delivery
|
|
105
|
+
*/
|
|
106
|
+
export function normalizeWechatMpText(text, renderMarkdown) {
|
|
107
|
+
const raw = String(text ?? "").trim();
|
|
108
|
+
if (!raw)
|
|
109
|
+
return "";
|
|
110
|
+
// When renderMarkdown is disabled, pass through with minimal normalization
|
|
111
|
+
if (!renderMarkdown) {
|
|
112
|
+
return raw;
|
|
113
|
+
}
|
|
114
|
+
// Apply markdown-friendly normalization for WeChat MP
|
|
115
|
+
return stripMarkdownForWechatMp(raw);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Strip markdown formatting and convert to WeChat MP friendly plain text.
|
|
119
|
+
*
|
|
120
|
+
* WeChat MP text messages do not support markdown, so we convert to a
|
|
121
|
+
* readable plain-text format similar to the wecom-app implementation.
|
|
122
|
+
*/
|
|
123
|
+
function stripMarkdownForWechatMp(text) {
|
|
124
|
+
let result = text;
|
|
125
|
+
// 1. Code blocks: extract content with indentation (preserve language label)
|
|
126
|
+
result = result.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
|
|
127
|
+
const trimmedCode = code.trim();
|
|
128
|
+
if (!trimmedCode)
|
|
129
|
+
return "";
|
|
130
|
+
const langLabel = lang ? `[${lang}]\n` : "";
|
|
131
|
+
const indentedCode = trimmedCode
|
|
132
|
+
.split("\n")
|
|
133
|
+
.map((line) => ` ${line}`)
|
|
134
|
+
.join("\n");
|
|
135
|
+
return `\n${langLabel}${indentedCode}\n`;
|
|
136
|
+
});
|
|
137
|
+
// 2. Headings: use brackets to mark
|
|
138
|
+
result = result.replace(/^#{1,6}\s+(.+)$/gm, "[$1]");
|
|
139
|
+
// 3. Bold/italic: preserve text (exclude underscores in URLs)
|
|
140
|
+
result = result
|
|
141
|
+
.replace(/\*\*(.*?)\*\*/g, "$1")
|
|
142
|
+
.replace(/\*(.*?)\*/g, "$1")
|
|
143
|
+
.replace(/__(.*?)__/g, "$1")
|
|
144
|
+
// Only replace standalone underscores (surrounded by space/punctuation), avoid URLs
|
|
145
|
+
.replace(/(?<![\w/])_(.+?)_(?![\w/])/g, "$1");
|
|
146
|
+
// 4. Unordered list items to bullet points
|
|
147
|
+
result = result.replace(/^[-*]\s+/gm, "- ");
|
|
148
|
+
// 5. Ordered lists keep numbering
|
|
149
|
+
result = result.replace(/^(\d+)\.\s+/gm, "$1. ");
|
|
150
|
+
// 6. Inline code: preserve content
|
|
151
|
+
result = result.replace(/`([^`]+)`/g, "$1");
|
|
152
|
+
// 7. Strikethrough
|
|
153
|
+
result = result.replace(/~~(.*?)~~/g, "$1");
|
|
154
|
+
// 8. Links: preserve text and URL
|
|
155
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)");
|
|
156
|
+
// 9. Images: display alt text
|
|
157
|
+
result = result.replace(/!\[([^\]]*)\]\([^)]+\)/g, "[image: $1]");
|
|
158
|
+
// 10. Tables: simplify to aligned text (basic table support)
|
|
159
|
+
result = result.replace(/\|(.+)\|\n\|[-:| ]+\|\n((?:\|.+\|\n?)*)/g, (_match, header, body) => {
|
|
160
|
+
const headerCells = header.split("|").map((c) => c.trim()).filter(Boolean);
|
|
161
|
+
const rows = body.trim().split("\n").map((row) => row.split("|").map((c) => c.trim()).filter(Boolean));
|
|
162
|
+
// Calculate max width per column
|
|
163
|
+
const colWidths = headerCells.map((h, i) => {
|
|
164
|
+
const maxRowWidth = Math.max(...rows.map((r) => (r[i] || "").length));
|
|
165
|
+
return Math.max(h.length, maxRowWidth);
|
|
166
|
+
});
|
|
167
|
+
// Format header
|
|
168
|
+
const formattedHeader = headerCells
|
|
169
|
+
.map((h, i) => h.padEnd(colWidths[i]))
|
|
170
|
+
.join(" ");
|
|
171
|
+
// Format data rows
|
|
172
|
+
const formattedRows = rows
|
|
173
|
+
.map((row) => headerCells.map((_, i) => (row[i] || "").padEnd(colWidths[i])).join(" "))
|
|
174
|
+
.join("\n");
|
|
175
|
+
return `${formattedHeader}\n${formattedRows}\n`;
|
|
176
|
+
});
|
|
177
|
+
// 11. Blockquotes: remove > prefix
|
|
178
|
+
result = result.replace(/^>\s?/gm, "");
|
|
179
|
+
// 12. Horizontal rules
|
|
180
|
+
result = result.replace(/^[-*_]{3,}$/gm, "------------");
|
|
181
|
+
// 13. Collapse multiple blank lines
|
|
182
|
+
result = result.replace(/\n{3,}/g, "\n\n");
|
|
183
|
+
return result.trim();
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Resolve the renderMarkdown setting from account config.
|
|
187
|
+
* Defaults to true if not explicitly set to false.
|
|
188
|
+
*/
|
|
189
|
+
export function resolveRenderMarkdown(config) {
|
|
190
|
+
return config.renderMarkdown !== false;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.test.d.ts","sourceRoot":"","sources":["../../src/text.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { getUtf8ByteLength, splitTextByByteLimit, WECHAT_TEXT_BYTE_LIMIT, } from "./text.js";
|
|
3
|
+
describe("wechat-mp text utils", () => {
|
|
4
|
+
describe("getUtf8ByteLength", () => {
|
|
5
|
+
it("calculates ASCII byte length", () => {
|
|
6
|
+
expect(getUtf8ByteLength("hello")).toBe(5);
|
|
7
|
+
expect(getUtf8ByteLength("12345")).toBe(5);
|
|
8
|
+
});
|
|
9
|
+
it("calculates Chinese character byte length (3 bytes per char)", () => {
|
|
10
|
+
expect(getUtf8ByteLength("你好")).toBe(6); // 2 chars * 3 bytes
|
|
11
|
+
expect(getUtf8ByteLength("测试")).toBe(6);
|
|
12
|
+
});
|
|
13
|
+
it("calculates mixed content byte length", () => {
|
|
14
|
+
expect(getUtf8ByteLength("hello你好")).toBe(11); // 5 + 6
|
|
15
|
+
expect(getUtf8ByteLength("abc测试123")).toBe(12); // 3 + 6 + 3
|
|
16
|
+
});
|
|
17
|
+
it("handles empty string", () => {
|
|
18
|
+
expect(getUtf8ByteLength("")).toBe(0);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("splitTextByByteLimit", () => {
|
|
22
|
+
it("returns single chunk when within limit", () => {
|
|
23
|
+
const text = "a".repeat(100);
|
|
24
|
+
const chunks = splitTextByByteLimit(text, 200);
|
|
25
|
+
expect(chunks).toHaveLength(1);
|
|
26
|
+
expect(chunks[0]).toBe(text);
|
|
27
|
+
});
|
|
28
|
+
it("splits at exact byte limit boundary", () => {
|
|
29
|
+
const text = "a".repeat(3000);
|
|
30
|
+
const chunks = splitTextByByteLimit(text, 1000);
|
|
31
|
+
expect(chunks.length).toBeGreaterThan(1);
|
|
32
|
+
// Each chunk should be within limit
|
|
33
|
+
for (const chunk of chunks) {
|
|
34
|
+
expect(getUtf8ByteLength(chunk)).toBeLessThanOrEqual(1000);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
it("splits at paragraph boundary (\\n\\n)", () => {
|
|
38
|
+
const part1 = "a".repeat(500);
|
|
39
|
+
const part2 = "b".repeat(500);
|
|
40
|
+
const text = `${part1}\n\n${part2}`;
|
|
41
|
+
const chunks = splitTextByByteLimit(text, 600);
|
|
42
|
+
expect(chunks.length).toBeGreaterThan(1);
|
|
43
|
+
expect(chunks[0]).toContain(part1);
|
|
44
|
+
expect(chunks[1]).toContain(part2);
|
|
45
|
+
});
|
|
46
|
+
it("splits at horizontal rule (---)", () => {
|
|
47
|
+
const part1 = "a".repeat(500);
|
|
48
|
+
const part2 = "b".repeat(500);
|
|
49
|
+
const text = `${part1}\n---\n${part2}`;
|
|
50
|
+
const chunks = splitTextByByteLimit(text, 600);
|
|
51
|
+
expect(chunks.length).toBeGreaterThan(1);
|
|
52
|
+
});
|
|
53
|
+
it("splits at Chinese sentence boundary", () => {
|
|
54
|
+
const part1 = "中".repeat(300); // 900 bytes
|
|
55
|
+
const part2 = "文".repeat(300); // 900 bytes
|
|
56
|
+
const text = `${part1}。${part2}`;
|
|
57
|
+
const chunks = splitTextByByteLimit(text, 1000);
|
|
58
|
+
expect(chunks.length).toBeGreaterThan(1);
|
|
59
|
+
});
|
|
60
|
+
it("does not truncate multi-byte characters", () => {
|
|
61
|
+
// Create text that would be exactly at boundary
|
|
62
|
+
const chinese = "你".repeat(683); // 2049 bytes, slightly over limit
|
|
63
|
+
const chunks = splitTextByByteLimit(chinese, WECHAT_TEXT_BYTE_LIMIT);
|
|
64
|
+
// Verify no characters are corrupted
|
|
65
|
+
const rejoined = chunks.join("");
|
|
66
|
+
expect(rejoined).toBe(chinese);
|
|
67
|
+
});
|
|
68
|
+
it("handles mixed ASCII and Chinese content", () => {
|
|
69
|
+
const part1 = "hello " + "中".repeat(300);
|
|
70
|
+
const part2 = "world " + "文".repeat(300);
|
|
71
|
+
const text = `${part1}\n\n${part2}`;
|
|
72
|
+
const chunks = splitTextByByteLimit(text, 1000);
|
|
73
|
+
// Each chunk should be within limit
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
expect(getUtf8ByteLength(chunk)).toBeLessThanOrEqual(1000);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
it("splits very long text into multiple chunks", () => {
|
|
79
|
+
const text = "a".repeat(10000);
|
|
80
|
+
const chunks = splitTextByByteLimit(text, WECHAT_TEXT_BYTE_LIMIT);
|
|
81
|
+
expect(chunks.length).toBeGreaterThan(4);
|
|
82
|
+
// Verify all content is preserved
|
|
83
|
+
expect(chunks.join("")).toBe(text);
|
|
84
|
+
});
|
|
85
|
+
it("handles text with multiple paragraph breaks", () => {
|
|
86
|
+
const paragraphs = [
|
|
87
|
+
"First paragraph with some content here.",
|
|
88
|
+
"Second paragraph with more content here.",
|
|
89
|
+
"Third paragraph with even more content here.",
|
|
90
|
+
];
|
|
91
|
+
const text = paragraphs.join("\n\n");
|
|
92
|
+
const chunks = splitTextByByteLimit(text, 50);
|
|
93
|
+
// Should split into multiple chunks at boundaries
|
|
94
|
+
expect(chunks.length).toBeGreaterThanOrEqual(3);
|
|
95
|
+
// Verify content preserved
|
|
96
|
+
const rejoined = chunks.join(" ");
|
|
97
|
+
expect(rejoined).toContain("First paragraph");
|
|
98
|
+
expect(rejoined).toContain("Second paragraph");
|
|
99
|
+
expect(rejoined).toContain("Third paragraph");
|
|
100
|
+
});
|
|
101
|
+
it("handles leading/trailing whitespace in short text", () => {
|
|
102
|
+
const text = " hello world ";
|
|
103
|
+
const chunks = splitTextByByteLimit(text, 100);
|
|
104
|
+
// Short text within limit is returned as-is
|
|
105
|
+
expect(chunks).toHaveLength(1);
|
|
106
|
+
expect(chunks[0]).toBe(" hello world ");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=text.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.test.js","sourceRoot":"","sources":["../../src/text.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* access_token lifecycle management for wechat-mp
|
|
3
|
+
*
|
|
4
|
+
* Provides centralized token caching, proactive refresh, and error handling.
|
|
5
|
+
*/
|
|
6
|
+
import type { ResolvedWechatMpAccount } from "./types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Get a cached access_token or fetch a new one.
|
|
9
|
+
* @throws Error if appId/appSecret not configured or API call fails
|
|
10
|
+
*/
|
|
11
|
+
export declare function getAccessToken(account: ResolvedWechatMpAccount): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* Clear token cache for an account
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearAccessTokenCache(account: ResolvedWechatMpAccount): void;
|
|
16
|
+
/**
|
|
17
|
+
* Clear all token caches
|
|
18
|
+
*/
|
|
19
|
+
export declare function clearAllAccessTokenCache(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a token needs refresh (expires within 5 minutes)
|
|
22
|
+
*/
|
|
23
|
+
export declare function shouldRefreshToken(account: ResolvedWechatMpAccount): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get token cache status
|
|
26
|
+
*/
|
|
27
|
+
export declare function getTokenCacheStatus(account: ResolvedWechatMpAccount): {
|
|
28
|
+
cached: boolean;
|
|
29
|
+
expiresAt?: number;
|
|
30
|
+
valid?: boolean;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Force refresh token (clear cache and fetch new)
|
|
34
|
+
*/
|
|
35
|
+
export declare function refreshToken(account: ResolvedWechatMpAccount): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if error code indicates invalid token
|
|
38
|
+
*/
|
|
39
|
+
export declare function isInvalidTokenError(errcode: number): boolean;
|
|
40
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/token.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAyB,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAsBjF;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BtF;AAgCD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAU5E;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAO5E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,uBAAuB,GAC/B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAW1D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF;AA+BD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D"}
|