@ecat/weixin-bot-cli 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 +27 -0
- package/README.md +77 -0
- package/dist/api/api.d.ts +47 -0
- package/dist/api/api.js +233 -0
- package/dist/api/api.js.map +1 -0
- package/dist/api/config-cache.d.ts +18 -0
- package/dist/api/config-cache.js +64 -0
- package/dist/api/config-cache.js.map +1 -0
- package/dist/api/session-guard.d.ts +15 -0
- package/dist/api/session-guard.js +49 -0
- package/dist/api/session-guard.js.map +1 -0
- package/dist/api/types.d.ts +201 -0
- package/dist/api/types.js +35 -0
- package/dist/api/types.js.map +1 -0
- package/dist/auth/accounts.d.ts +30 -0
- package/dist/auth/accounts.js +158 -0
- package/dist/auth/accounts.js.map +1 -0
- package/dist/auth/login-qr.d.ts +31 -0
- package/dist/auth/login-qr.js +235 -0
- package/dist/auth/login-qr.js.map +1 -0
- package/dist/cdn/aes-ecb.d.ts +6 -0
- package/dist/cdn/aes-ecb.js +19 -0
- package/dist/cdn/aes-ecb.js.map +1 -0
- package/dist/cdn/cdn-upload.d.ts +17 -0
- package/dist/cdn/cdn-upload.js +73 -0
- package/dist/cdn/cdn-upload.js.map +1 -0
- package/dist/cdn/cdn-url.d.ts +13 -0
- package/dist/cdn/cdn-url.js +14 -0
- package/dist/cdn/cdn-url.js.map +1 -0
- package/dist/cdn/pic-decrypt.d.ts +9 -0
- package/dist/cdn/pic-decrypt.js +89 -0
- package/dist/cdn/pic-decrypt.js.map +1 -0
- package/dist/cdn/upload.d.ts +42 -0
- package/dist/cdn/upload.js +106 -0
- package/dist/cdn/upload.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +127 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/config-schema.d.ts +16 -0
- package/dist/config/config-schema.js +17 -0
- package/dist/config/config-schema.js.map +1 -0
- package/dist/media/media-download.d.ts +18 -0
- package/dist/media/media-download.js +95 -0
- package/dist/media/media-download.js.map +1 -0
- package/dist/media/mime.d.ts +6 -0
- package/dist/media/mime.js +73 -0
- package/dist/media/mime.js.map +1 -0
- package/dist/media/silk-transcode.d.ts +8 -0
- package/dist/media/silk-transcode.js +64 -0
- package/dist/media/silk-transcode.js.map +1 -0
- package/dist/messaging/debug-mode.d.ts +9 -0
- package/dist/messaging/debug-mode.js +63 -0
- package/dist/messaging/debug-mode.js.map +1 -0
- package/dist/messaging/inbound.d.ts +69 -0
- package/dist/messaging/inbound.js +201 -0
- package/dist/messaging/inbound.js.map +1 -0
- package/dist/messaging/send-media.d.ts +21 -0
- package/dist/messaging/send-media.js +54 -0
- package/dist/messaging/send-media.js.map +1 -0
- package/dist/messaging/send.d.ts +70 -0
- package/dist/messaging/send.js +203 -0
- package/dist/messaging/send.js.map +1 -0
- package/dist/monitor/monitor.d.ts +12 -0
- package/dist/monitor/monitor.js +145 -0
- package/dist/monitor/monitor.js.map +1 -0
- package/dist/storage/state-dir.d.ts +2 -0
- package/dist/storage/state-dir.js +8 -0
- package/dist/storage/state-dir.js.map +1 -0
- package/dist/storage/sync-buf.d.ts +20 -0
- package/dist/storage/sync-buf.js +64 -0
- package/dist/storage/sync-buf.js.map +1 -0
- package/dist/util/logger.d.ts +14 -0
- package/dist/util/logger.js +119 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/random.d.ts +10 -0
- package/dist/util/random.js +16 -0
- package/dist/util/random.js.map +1 -0
- package/dist/util/redact.d.ts +20 -0
- package/dist/util/redact.js +54 -0
- package/dist/util/redact.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// Local standalone stripMarkdown to remove openclaw dependency
|
|
2
|
+
export function stripMarkdown(str) {
|
|
3
|
+
return str.replace(/([_*~`])/g, '');
|
|
4
|
+
}
|
|
5
|
+
import { sendMessage as sendMessageApi } from "../api/api.js";
|
|
6
|
+
import { logger } from "../util/logger.js";
|
|
7
|
+
import { generateId } from "../util/random.js";
|
|
8
|
+
import { MessageItemType, MessageState, MessageType } from "../api/types.js";
|
|
9
|
+
function generateClientId() {
|
|
10
|
+
return generateId("openclaw-weixin");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Convert markdown-formatted model reply to plain text for Weixin delivery.
|
|
14
|
+
* Preserves newlines; strips markdown syntax.
|
|
15
|
+
*/
|
|
16
|
+
export function markdownToPlainText(text) {
|
|
17
|
+
let result = text;
|
|
18
|
+
// Code blocks: strip fences, keep code content
|
|
19
|
+
result = result.replace(/```[^\n]*\n?([\s\S]*?)```/g, (_, code) => code.trim());
|
|
20
|
+
// Images: remove entirely
|
|
21
|
+
result = result.replace(/!\[[^\]]*\]\([^)]*\)/g, "");
|
|
22
|
+
// Links: keep display text only
|
|
23
|
+
result = result.replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
|
|
24
|
+
// Tables: remove separator rows, then strip leading/trailing pipes and convert inner pipes to spaces
|
|
25
|
+
result = result.replace(/^\|[\s:|-]+\|$/gm, "");
|
|
26
|
+
result = result.replace(/^\|(.+)\|$/gm, (_, inner) => inner.split("|").map((cell) => cell.trim()).join(" "));
|
|
27
|
+
result = stripMarkdown(result);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/** Build a SendMessageReq containing a single text message. */
|
|
31
|
+
function buildTextMessageReq(params) {
|
|
32
|
+
const { to, text, contextToken, clientId } = params;
|
|
33
|
+
const item_list = text
|
|
34
|
+
? [{ type: MessageItemType.TEXT, text_item: { text } }]
|
|
35
|
+
: [];
|
|
36
|
+
return {
|
|
37
|
+
msg: {
|
|
38
|
+
from_user_id: "",
|
|
39
|
+
to_user_id: to,
|
|
40
|
+
client_id: clientId,
|
|
41
|
+
message_type: MessageType.BOT,
|
|
42
|
+
message_state: MessageState.FINISH,
|
|
43
|
+
item_list: item_list.length ? item_list : undefined,
|
|
44
|
+
context_token: contextToken ?? undefined,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Build a SendMessageReq from a reply payload (text only; image send uses sendImageMessageWeixin). */
|
|
49
|
+
function buildSendMessageReq(params) {
|
|
50
|
+
const { to, contextToken, text, clientId } = params;
|
|
51
|
+
return buildTextMessageReq({
|
|
52
|
+
to,
|
|
53
|
+
text: text ?? "",
|
|
54
|
+
contextToken,
|
|
55
|
+
clientId,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Send a plain text message downstream.
|
|
60
|
+
*/
|
|
61
|
+
export async function sendMessageWeixin(params) {
|
|
62
|
+
const { to, text, opts } = params;
|
|
63
|
+
if (!opts.contextToken) {
|
|
64
|
+
logger.warn(`sendMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
65
|
+
}
|
|
66
|
+
const clientId = generateClientId();
|
|
67
|
+
const req = buildSendMessageReq({
|
|
68
|
+
to,
|
|
69
|
+
contextToken: opts.contextToken,
|
|
70
|
+
text,
|
|
71
|
+
clientId,
|
|
72
|
+
});
|
|
73
|
+
try {
|
|
74
|
+
await sendMessageApi({
|
|
75
|
+
baseUrl: opts.baseUrl,
|
|
76
|
+
token: opts.token,
|
|
77
|
+
timeoutMs: opts.timeoutMs,
|
|
78
|
+
body: req,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.error(`sendMessageWeixin: failed to=${to} clientId=${clientId} err=${String(err)}`);
|
|
83
|
+
throw err;
|
|
84
|
+
}
|
|
85
|
+
return { messageId: clientId };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Send one or more MessageItems (optionally preceded by a text caption) downstream.
|
|
89
|
+
* Each item is sent as its own request so that item_list always has exactly one entry.
|
|
90
|
+
*/
|
|
91
|
+
async function sendMediaItems(params) {
|
|
92
|
+
const { to, text, mediaItem, opts, label } = params;
|
|
93
|
+
const items = [];
|
|
94
|
+
if (text) {
|
|
95
|
+
items.push({ type: MessageItemType.TEXT, text_item: { text } });
|
|
96
|
+
}
|
|
97
|
+
items.push(mediaItem);
|
|
98
|
+
let lastClientId = "";
|
|
99
|
+
for (const item of items) {
|
|
100
|
+
lastClientId = generateClientId();
|
|
101
|
+
const req = {
|
|
102
|
+
msg: {
|
|
103
|
+
from_user_id: "",
|
|
104
|
+
to_user_id: to,
|
|
105
|
+
client_id: lastClientId,
|
|
106
|
+
message_type: MessageType.BOT,
|
|
107
|
+
message_state: MessageState.FINISH,
|
|
108
|
+
item_list: [item],
|
|
109
|
+
context_token: opts.contextToken ?? undefined,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
try {
|
|
113
|
+
await sendMessageApi({
|
|
114
|
+
baseUrl: opts.baseUrl,
|
|
115
|
+
token: opts.token,
|
|
116
|
+
timeoutMs: opts.timeoutMs,
|
|
117
|
+
body: req,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
logger.error(`${label}: failed to=${to} clientId=${lastClientId} err=${String(err)}`);
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
logger.info(`${label}: success to=${to} clientId=${lastClientId}`);
|
|
126
|
+
return { messageId: lastClientId };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Send an image message downstream using a previously uploaded file.
|
|
130
|
+
* Optionally include a text caption as a separate TEXT item before the image.
|
|
131
|
+
*
|
|
132
|
+
* ImageItem fields:
|
|
133
|
+
* - media.encrypt_query_param: CDN download param
|
|
134
|
+
* - media.aes_key: AES key, base64-encoded
|
|
135
|
+
* - mid_size: original ciphertext file size
|
|
136
|
+
*/
|
|
137
|
+
export async function sendImageMessageWeixin(params) {
|
|
138
|
+
const { to, text, uploaded, opts } = params;
|
|
139
|
+
if (!opts.contextToken) {
|
|
140
|
+
logger.warn(`sendImageMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
141
|
+
}
|
|
142
|
+
logger.info(`sendImageMessageWeixin: to=${to} filekey=${uploaded.filekey} fileSize=${uploaded.fileSize} aeskey=present`);
|
|
143
|
+
const imageItem = {
|
|
144
|
+
type: MessageItemType.IMAGE,
|
|
145
|
+
image_item: {
|
|
146
|
+
media: {
|
|
147
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
148
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
149
|
+
encrypt_type: 1,
|
|
150
|
+
},
|
|
151
|
+
mid_size: uploaded.fileSizeCiphertext,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
return sendMediaItems({ to, text, mediaItem: imageItem, opts, label: "sendImageMessageWeixin" });
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Send a video message downstream using a previously uploaded file.
|
|
158
|
+
* VideoItem: media (CDN ref), video_size (ciphertext bytes).
|
|
159
|
+
* Includes an optional text caption sent as a separate TEXT item first.
|
|
160
|
+
*/
|
|
161
|
+
export async function sendVideoMessageWeixin(params) {
|
|
162
|
+
const { to, text, uploaded, opts } = params;
|
|
163
|
+
if (!opts.contextToken) {
|
|
164
|
+
logger.warn(`sendVideoMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
165
|
+
}
|
|
166
|
+
const videoItem = {
|
|
167
|
+
type: MessageItemType.VIDEO,
|
|
168
|
+
video_item: {
|
|
169
|
+
media: {
|
|
170
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
171
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
172
|
+
encrypt_type: 1,
|
|
173
|
+
},
|
|
174
|
+
video_size: uploaded.fileSizeCiphertext,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
return sendMediaItems({ to, text, mediaItem: videoItem, opts, label: "sendVideoMessageWeixin" });
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Send a file attachment downstream using a previously uploaded file.
|
|
181
|
+
* FileItem: media (CDN ref), file_name, len (plaintext bytes as string).
|
|
182
|
+
* Includes an optional text caption sent as a separate TEXT item first.
|
|
183
|
+
*/
|
|
184
|
+
export async function sendFileMessageWeixin(params) {
|
|
185
|
+
const { to, text, fileName, uploaded, opts } = params;
|
|
186
|
+
if (!opts.contextToken) {
|
|
187
|
+
logger.warn(`sendFileMessageWeixin: contextToken missing for to=${to}, sending without context`);
|
|
188
|
+
}
|
|
189
|
+
const fileItem = {
|
|
190
|
+
type: MessageItemType.FILE,
|
|
191
|
+
file_item: {
|
|
192
|
+
media: {
|
|
193
|
+
encrypt_query_param: uploaded.downloadEncryptedQueryParam,
|
|
194
|
+
aes_key: Buffer.from(uploaded.aeskey).toString("base64"),
|
|
195
|
+
encrypt_type: 1,
|
|
196
|
+
},
|
|
197
|
+
file_name: fileName,
|
|
198
|
+
len: String(uploaded.fileSize),
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
return sendMediaItems({ to, text, mediaItem: fileItem, opts, label: "sendFileMessageWeixin" });
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=send.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/messaging/send.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type MonitorWeixinOpts = {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
cdnBaseUrl?: string;
|
|
4
|
+
token?: string;
|
|
5
|
+
accountId: string;
|
|
6
|
+
abortSignal?: AbortSignal;
|
|
7
|
+
longPollTimeoutMs?: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Long-poll loop. Runs until abort.
|
|
11
|
+
*/
|
|
12
|
+
export declare function monitorWeixinProvider(opts: MonitorWeixinOpts): Promise<void>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { getUpdates } from "../api/api.js";
|
|
2
|
+
import { WeixinConfigManager } from "../api/config-cache.js";
|
|
3
|
+
import { SESSION_EXPIRED_ERRCODE, pauseSession, getRemainingPauseMs } from "../api/session-guard.js";
|
|
4
|
+
import { getSyncBufFilePath, loadGetUpdatesBuf, saveGetUpdatesBuf } from "../storage/sync-buf.js";
|
|
5
|
+
import { logger } from "../util/logger.js";
|
|
6
|
+
import { redactBody } from "../util/redact.js";
|
|
7
|
+
import { MessageItemType } from "../api/types.js";
|
|
8
|
+
const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
|
|
9
|
+
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
10
|
+
const BACKOFF_DELAY_MS = 30_000;
|
|
11
|
+
const RETRY_DELAY_MS = 2_000;
|
|
12
|
+
/**
|
|
13
|
+
* Extract text body from item_list.
|
|
14
|
+
*/
|
|
15
|
+
function extractSummary(full) {
|
|
16
|
+
const itemList = full.item_list;
|
|
17
|
+
if (!itemList?.length)
|
|
18
|
+
return "[Empty Message]";
|
|
19
|
+
let summary = "";
|
|
20
|
+
for (const item of itemList) {
|
|
21
|
+
if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
|
|
22
|
+
summary += String(item.text_item.text);
|
|
23
|
+
}
|
|
24
|
+
else if (item.type === MessageItemType.IMAGE) {
|
|
25
|
+
summary += "[图片] ";
|
|
26
|
+
}
|
|
27
|
+
else if (item.type === MessageItemType.VIDEO) {
|
|
28
|
+
summary += "[视频] ";
|
|
29
|
+
}
|
|
30
|
+
else if (item.type === MessageItemType.FILE) {
|
|
31
|
+
summary += "[文件] ";
|
|
32
|
+
}
|
|
33
|
+
else if (item.type === MessageItemType.VOICE) {
|
|
34
|
+
summary += "[语音] ";
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
summary += `[未知类型:${item.type}] `;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return summary;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Long-poll loop. Runs until abort.
|
|
44
|
+
*/
|
|
45
|
+
export async function monitorWeixinProvider(opts) {
|
|
46
|
+
const { baseUrl, token, accountId, abortSignal, longPollTimeoutMs, } = opts;
|
|
47
|
+
const aLog = logger.withAccount(accountId);
|
|
48
|
+
console.log(`[weixin-bot-cli] 开始监听消息 (baseUrl=${baseUrl}, account=${accountId})`);
|
|
49
|
+
aLog.info(`Monitor started: baseUrl=${baseUrl} timeoutMs=${longPollTimeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS}`);
|
|
50
|
+
const syncFilePath = getSyncBufFilePath(accountId);
|
|
51
|
+
aLog.debug(`syncFilePath: ${syncFilePath}`);
|
|
52
|
+
const previousGetUpdatesBuf = loadGetUpdatesBuf(syncFilePath);
|
|
53
|
+
let getUpdatesBuf = previousGetUpdatesBuf ?? "";
|
|
54
|
+
if (previousGetUpdatesBuf) {
|
|
55
|
+
aLog.debug(`Using previous get_updates_buf (${getUpdatesBuf.length} bytes)`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
aLog.info(`No previous get_updates_buf found, starting fresh`);
|
|
59
|
+
}
|
|
60
|
+
const configManager = new WeixinConfigManager({ baseUrl, token }, (msg) => aLog.debug(msg));
|
|
61
|
+
let nextTimeoutMs = longPollTimeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;
|
|
62
|
+
let consecutiveFailures = 0;
|
|
63
|
+
while (!abortSignal?.aborted) {
|
|
64
|
+
try {
|
|
65
|
+
aLog.debug(`getUpdates: get_updates_buf=${getUpdatesBuf.substring(0, 50)}..., timeoutMs=${nextTimeoutMs}`);
|
|
66
|
+
const resp = await getUpdates({
|
|
67
|
+
baseUrl,
|
|
68
|
+
token,
|
|
69
|
+
get_updates_buf: getUpdatesBuf,
|
|
70
|
+
timeoutMs: nextTimeoutMs,
|
|
71
|
+
});
|
|
72
|
+
if (resp.longpolling_timeout_ms != null && resp.longpolling_timeout_ms > 0) {
|
|
73
|
+
nextTimeoutMs = resp.longpolling_timeout_ms;
|
|
74
|
+
}
|
|
75
|
+
const isApiError = (resp.ret !== undefined && resp.ret !== 0) ||
|
|
76
|
+
(resp.errcode !== undefined && resp.errcode !== 0);
|
|
77
|
+
if (isApiError) {
|
|
78
|
+
const isSessionExpired = resp.errcode === SESSION_EXPIRED_ERRCODE || resp.ret === SESSION_EXPIRED_ERRCODE;
|
|
79
|
+
if (isSessionExpired) {
|
|
80
|
+
pauseSession(accountId);
|
|
81
|
+
const pauseMs = getRemainingPauseMs(accountId);
|
|
82
|
+
console.error(`[Error] 凭证过期 (errcode ${SESSION_EXPIRED_ERRCODE}),暂停轮询 ${Math.ceil(pauseMs / 60_000)} 分钟`);
|
|
83
|
+
aLog.error(`getUpdates: session expired (errcode=${resp.errcode} ret=${resp.ret}), pausing all requests for ${Math.ceil(pauseMs / 60_000)} min`);
|
|
84
|
+
consecutiveFailures = 0;
|
|
85
|
+
await sleep(pauseMs, abortSignal);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
consecutiveFailures += 1;
|
|
89
|
+
aLog.error(`getUpdates failed: ret=${resp.ret} errcode=${resp.errcode} errmsg=${resp.errmsg} response=${redactBody(JSON.stringify(resp))}`);
|
|
90
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
91
|
+
aLog.error(`getUpdates: ${MAX_CONSECUTIVE_FAILURES} consecutive failures, backing off 30s`);
|
|
92
|
+
consecutiveFailures = 0;
|
|
93
|
+
await sleep(BACKOFF_DELAY_MS, abortSignal);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
await sleep(RETRY_DELAY_MS, abortSignal);
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
consecutiveFailures = 0;
|
|
101
|
+
if (resp.get_updates_buf != null && resp.get_updates_buf !== "") {
|
|
102
|
+
saveGetUpdatesBuf(syncFilePath, resp.get_updates_buf);
|
|
103
|
+
getUpdatesBuf = resp.get_updates_buf;
|
|
104
|
+
aLog.debug(`Saved new get_updates_buf (${getUpdatesBuf.length} bytes)`);
|
|
105
|
+
}
|
|
106
|
+
const list = resp.msgs ?? [];
|
|
107
|
+
for (const full of list) {
|
|
108
|
+
const summary = extractSummary(full);
|
|
109
|
+
const fromUserId = full.from_user_id ?? "unknown";
|
|
110
|
+
console.log(`\n[收到消息] 来自: ${fromUserId}`);
|
|
111
|
+
console.log(`> ${summary}`);
|
|
112
|
+
aLog.info(`inbound message: from=${fromUserId} types=${full.item_list?.map((i) => i.type).join(",") ?? "none"}`);
|
|
113
|
+
// Pre-warm the config cache just in case we add reply functionality
|
|
114
|
+
await configManager.getForUser(fromUserId, full.context_token);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (abortSignal?.aborted) {
|
|
119
|
+
aLog.info(`Monitor stopped (aborted)`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
consecutiveFailures += 1;
|
|
123
|
+
aLog.error(`getUpdates error: ${String(err)}, stack=${err.stack}`);
|
|
124
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
125
|
+
aLog.error(`getUpdates: ${MAX_CONSECUTIVE_FAILURES} consecutive failures, backing off 30s`);
|
|
126
|
+
consecutiveFailures = 0;
|
|
127
|
+
await sleep(30_000, abortSignal);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
await sleep(2000, abortSignal);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
aLog.info(`Monitor ended`);
|
|
135
|
+
}
|
|
136
|
+
function sleep(ms, signal) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const t = setTimeout(resolve, ms);
|
|
139
|
+
signal?.addEventListener("abort", () => {
|
|
140
|
+
clearTimeout(t);
|
|
141
|
+
reject(new Error("aborted"));
|
|
142
|
+
}, { once: true });
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../src/monitor/monitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Resolve the state directory for weixin-bot-cli. */
|
|
4
|
+
export function resolveStateDir() {
|
|
5
|
+
return (process.env.WEIXIN_BOT_HOME?.trim() ||
|
|
6
|
+
path.join(os.homedir(), ".weixin-bot-cli"));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=state-dir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-dir.js","sourceRoot":"","sources":["../../src/storage/state-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,sDAAsD;AACtD,MAAM,UAAU,eAAe;IAC7B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE;QACnC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,iBAAiB,CAAC,CAC3C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path to the persistent get_updates_buf file for an account.
|
|
3
|
+
* Stored alongside account data: ~/.openclaw/openclaw-weixin/accounts/{accountId}.sync.json
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSyncBufFilePath(accountId: string): string;
|
|
6
|
+
export type SyncBufData = {
|
|
7
|
+
get_updates_buf: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Load persisted get_updates_buf.
|
|
11
|
+
* Falls back in order:
|
|
12
|
+
* 1. Primary path (normalized accountId, new installs)
|
|
13
|
+
* 2. Compat path (raw accountId derived from pattern, old installs)
|
|
14
|
+
* 3. Legacy single-account path (very old installs without multi-account support)
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadGetUpdatesBuf(filePath: string): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Persist get_updates_buf. Creates parent dir if needed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function saveGetUpdatesBuf(filePath: string, getUpdatesBuf: string): void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { deriveRawAccountId } from "../auth/accounts.js";
|
|
4
|
+
import { resolveStateDir } from "./state-dir.js";
|
|
5
|
+
function resolveAccountsDir() {
|
|
6
|
+
return path.join(resolveStateDir(), "openclaw-weixin", "accounts");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Path to the persistent get_updates_buf file for an account.
|
|
10
|
+
* Stored alongside account data: ~/.openclaw/openclaw-weixin/accounts/{accountId}.sync.json
|
|
11
|
+
*/
|
|
12
|
+
export function getSyncBufFilePath(accountId) {
|
|
13
|
+
return path.join(resolveAccountsDir(), `${accountId}.sync.json`);
|
|
14
|
+
}
|
|
15
|
+
/** Legacy single-account syncbuf (pre multi-account): `.openclaw-weixin-sync/default.json`. */
|
|
16
|
+
function getLegacySyncBufDefaultJsonPath() {
|
|
17
|
+
return path.join(resolveStateDir(), "agents", "default", "sessions", ".openclaw-weixin-sync", "default.json");
|
|
18
|
+
}
|
|
19
|
+
function readSyncBufFile(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
22
|
+
const data = JSON.parse(raw);
|
|
23
|
+
if (typeof data.get_updates_buf === "string") {
|
|
24
|
+
return data.get_updates_buf;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// file not found or invalid
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load persisted get_updates_buf.
|
|
34
|
+
* Falls back in order:
|
|
35
|
+
* 1. Primary path (normalized accountId, new installs)
|
|
36
|
+
* 2. Compat path (raw accountId derived from pattern, old installs)
|
|
37
|
+
* 3. Legacy single-account path (very old installs without multi-account support)
|
|
38
|
+
*/
|
|
39
|
+
export function loadGetUpdatesBuf(filePath) {
|
|
40
|
+
const value = readSyncBufFile(filePath);
|
|
41
|
+
if (value !== undefined)
|
|
42
|
+
return value;
|
|
43
|
+
// Compat: if given path uses a normalized accountId (e.g. "b0f5860fdecb-im-bot.sync.json"),
|
|
44
|
+
// also try the old raw-ID filename (e.g. "b0f5860fdecb@im.bot.sync.json").
|
|
45
|
+
const accountId = path.basename(filePath, ".sync.json");
|
|
46
|
+
const rawId = deriveRawAccountId(accountId);
|
|
47
|
+
if (rawId) {
|
|
48
|
+
const compatPath = path.join(resolveAccountsDir(), `${rawId}.sync.json`);
|
|
49
|
+
const compatValue = readSyncBufFile(compatPath);
|
|
50
|
+
if (compatValue !== undefined)
|
|
51
|
+
return compatValue;
|
|
52
|
+
}
|
|
53
|
+
// Legacy fallback: old single-account installs stored syncbuf without accountId.
|
|
54
|
+
return readSyncBufFile(getLegacySyncBufDefaultJsonPath());
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Persist get_updates_buf. Creates parent dir if needed.
|
|
58
|
+
*/
|
|
59
|
+
export function saveGetUpdatesBuf(filePath, getUpdatesBuf) {
|
|
60
|
+
const dir = path.dirname(filePath);
|
|
61
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
62
|
+
fs.writeFileSync(filePath, JSON.stringify({ get_updates_buf: getUpdatesBuf }, null, 0), "utf-8");
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sync-buf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-buf.js","sourceRoot":"","sources":["../../src/storage/sync-buf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,SAAS,kBAAkB;IACzB,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,GAAG,SAAS,YAAY,CAAC,CAAC;AACnE,CAAC;AAED,+FAA+F;AAC/F,SAAS,+BAA+B;IACtC,OAAO,IAAI,CAAC,IAAI,CACd,eAAe,EAAE,EACjB,QAAQ,EACR,SAAS,EACT,UAAU,EACV,uBAAuB,EACvB,cAAc,CACf,CAAC;AACJ,CAAC;AAMD,SAAS,eAAe,CAAC,QAAgB;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiC,CAAC;QAC7D,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC,eAAe,CAAC;QAC9B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAEtC,4FAA4F;IAC5F,2EAA2E;IAC3E,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,GAAG,KAAK,YAAY,CAAC,CAAC;QACzE,MAAM,WAAW,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO,WAAW,CAAC;IACpD,CAAC;IAED,iFAAiF;IACjF,OAAO,eAAe,CAAC,+BAA+B,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,aAAqB;IACvE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACnG,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Dynamically change the minimum log level at runtime. */
|
|
2
|
+
export declare function setLogLevel(level: string): void;
|
|
3
|
+
export type Logger = {
|
|
4
|
+
info(message: string): void;
|
|
5
|
+
debug(message: string): void;
|
|
6
|
+
warn(message: string): void;
|
|
7
|
+
error(message: string): void;
|
|
8
|
+
/** Returns a child logger whose messages are prefixed with `[accountId]`. */
|
|
9
|
+
withAccount(accountId: string): Logger;
|
|
10
|
+
/** Returns the current main log file path. */
|
|
11
|
+
getLogFilePath(): string;
|
|
12
|
+
close(): void;
|
|
13
|
+
};
|
|
14
|
+
export declare const logger: Logger;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Plugin logger — writes JSON lines to the main openclaw log file:
|
|
6
|
+
* <tmpDir>/openclaw-YYYY-MM-DD.log
|
|
7
|
+
* Same file and format used by all other channels.
|
|
8
|
+
*/
|
|
9
|
+
const MAIN_LOG_DIR = path.join(os.homedir(), ".weixin-bot-cli", "logs");
|
|
10
|
+
const SUBSYSTEM = "weixin-bot-cli";
|
|
11
|
+
const RUNTIME = "node";
|
|
12
|
+
const RUNTIME_VERSION = process.versions.node;
|
|
13
|
+
const HOSTNAME = os.hostname() || "unknown";
|
|
14
|
+
const PARENT_NAMES = ["openclaw"];
|
|
15
|
+
/** tslog-compatible level IDs (higher = more severe). */
|
|
16
|
+
const LEVEL_IDS = {
|
|
17
|
+
TRACE: 1,
|
|
18
|
+
DEBUG: 2,
|
|
19
|
+
INFO: 3,
|
|
20
|
+
WARN: 4,
|
|
21
|
+
ERROR: 5,
|
|
22
|
+
FATAL: 6,
|
|
23
|
+
};
|
|
24
|
+
const DEFAULT_LOG_LEVEL = "INFO";
|
|
25
|
+
function resolveMinLevel() {
|
|
26
|
+
const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase();
|
|
27
|
+
if (env && env in LEVEL_IDS)
|
|
28
|
+
return LEVEL_IDS[env];
|
|
29
|
+
return LEVEL_IDS[DEFAULT_LOG_LEVEL];
|
|
30
|
+
}
|
|
31
|
+
let minLevelId = resolveMinLevel();
|
|
32
|
+
/** Dynamically change the minimum log level at runtime. */
|
|
33
|
+
export function setLogLevel(level) {
|
|
34
|
+
const upper = level.toUpperCase();
|
|
35
|
+
if (!(upper in LEVEL_IDS)) {
|
|
36
|
+
throw new Error(`Invalid log level: ${level}. Valid levels: ${Object.keys(LEVEL_IDS).join(", ")}`);
|
|
37
|
+
}
|
|
38
|
+
minLevelId = LEVEL_IDS[upper];
|
|
39
|
+
}
|
|
40
|
+
/** Shift a Date into local time so toISOString() renders local clock digits. */
|
|
41
|
+
function toLocalISO(now) {
|
|
42
|
+
const offsetMs = -now.getTimezoneOffset() * 60_000;
|
|
43
|
+
const sign = offsetMs >= 0 ? "+" : "-";
|
|
44
|
+
const abs = Math.abs(now.getTimezoneOffset());
|
|
45
|
+
const offStr = `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
|
|
46
|
+
return new Date(now.getTime() + offsetMs).toISOString().replace("Z", offStr);
|
|
47
|
+
}
|
|
48
|
+
function localDateKey(now) {
|
|
49
|
+
return toLocalISO(now).slice(0, 10);
|
|
50
|
+
}
|
|
51
|
+
function resolveMainLogPath() {
|
|
52
|
+
const dateKey = localDateKey(new Date());
|
|
53
|
+
return path.join(MAIN_LOG_DIR, `openclaw-${dateKey}.log`);
|
|
54
|
+
}
|
|
55
|
+
let logDirEnsured = false;
|
|
56
|
+
function buildLoggerName(accountId) {
|
|
57
|
+
return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
|
|
58
|
+
}
|
|
59
|
+
function writeLog(level, message, accountId) {
|
|
60
|
+
const levelId = LEVEL_IDS[level] ?? LEVEL_IDS.INFO;
|
|
61
|
+
if (levelId < minLevelId)
|
|
62
|
+
return;
|
|
63
|
+
const now = new Date();
|
|
64
|
+
const loggerName = buildLoggerName(accountId);
|
|
65
|
+
const prefixedMessage = accountId ? `[${accountId}] ${message}` : message;
|
|
66
|
+
const entry = JSON.stringify({
|
|
67
|
+
"0": loggerName,
|
|
68
|
+
"1": prefixedMessage,
|
|
69
|
+
_meta: {
|
|
70
|
+
runtime: RUNTIME,
|
|
71
|
+
runtimeVersion: RUNTIME_VERSION,
|
|
72
|
+
hostname: HOSTNAME,
|
|
73
|
+
name: loggerName,
|
|
74
|
+
parentNames: PARENT_NAMES,
|
|
75
|
+
date: now.toISOString(),
|
|
76
|
+
logLevelId: LEVEL_IDS[level] ?? LEVEL_IDS.INFO,
|
|
77
|
+
logLevelName: level,
|
|
78
|
+
},
|
|
79
|
+
time: toLocalISO(now),
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
if (!logDirEnsured) {
|
|
83
|
+
fs.mkdirSync(MAIN_LOG_DIR, { recursive: true });
|
|
84
|
+
logDirEnsured = true;
|
|
85
|
+
}
|
|
86
|
+
fs.appendFileSync(resolveMainLogPath(), `${entry}\n`, "utf-8");
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Best-effort; never block on logging failures.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Creates a logger instance, optionally bound to a specific account. */
|
|
93
|
+
function createLogger(accountId) {
|
|
94
|
+
return {
|
|
95
|
+
info(message) {
|
|
96
|
+
writeLog("INFO", message, accountId);
|
|
97
|
+
},
|
|
98
|
+
debug(message) {
|
|
99
|
+
writeLog("DEBUG", message, accountId);
|
|
100
|
+
},
|
|
101
|
+
warn(message) {
|
|
102
|
+
writeLog("WARN", message, accountId);
|
|
103
|
+
},
|
|
104
|
+
error(message) {
|
|
105
|
+
writeLog("ERROR", message, accountId);
|
|
106
|
+
},
|
|
107
|
+
withAccount(id) {
|
|
108
|
+
return createLogger(id);
|
|
109
|
+
},
|
|
110
|
+
getLogFilePath() {
|
|
111
|
+
return resolveMainLogPath();
|
|
112
|
+
},
|
|
113
|
+
close() {
|
|
114
|
+
// No-op: appendFileSync has no persistent handle to close.
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export const logger = createLogger();
|
|
119
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/util/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a prefixed unique ID using timestamp + crypto random bytes.
|
|
3
|
+
* Format: `{prefix}:{timestamp}-{8-char hex}`
|
|
4
|
+
*/
|
|
5
|
+
export declare function generateId(prefix: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Generate a temporary file name with random suffix.
|
|
8
|
+
* Format: `{prefix}-{timestamp}-{8-char hex}{ext}`
|
|
9
|
+
*/
|
|
10
|
+
export declare function tempFileName(prefix: string, ext: string): string;
|