omp-wechat 1.6.0 → 1.8.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/dist/index.js +95 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1188,6 +1188,11 @@ var logger = {
|
|
|
1188
1188
|
var STATE_DIR = join3(homedir2(), ".omp-wechat");
|
|
1189
1189
|
var CREDENTIALS_FILE = join3(STATE_DIR, "credentials.json");
|
|
1190
1190
|
var SYNC_BUF_FILE = join3(STATE_DIR, "sync_buf.txt");
|
|
1191
|
+
var CHANNEL_VERSION = "2.2.0";
|
|
1192
|
+
var BOT_AGENT = "OMP-Wechat/1.7.0";
|
|
1193
|
+
function baseInfo() {
|
|
1194
|
+
return { channel_version: CHANNEL_VERSION, bot_agent: BOT_AGENT };
|
|
1195
|
+
}
|
|
1191
1196
|
function loadCredentials() {
|
|
1192
1197
|
try {
|
|
1193
1198
|
return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf8"));
|
|
@@ -1241,7 +1246,15 @@ async function apiFetch(creds, endpoint, body, timeoutMs = 15000) {
|
|
|
1241
1246
|
const text = await res.text();
|
|
1242
1247
|
if (!res.ok)
|
|
1243
1248
|
throw new Error(`${endpoint} ${res.status}: ${text}`);
|
|
1244
|
-
|
|
1249
|
+
const data = JSON.parse(text);
|
|
1250
|
+
const ret = typeof data.ret === "number" ? data.ret : undefined;
|
|
1251
|
+
const errcode = typeof data.errcode === "number" ? data.errcode : undefined;
|
|
1252
|
+
if (ret !== undefined && ret !== 0 || errcode !== undefined && errcode !== 0) {
|
|
1253
|
+
const code = errcode ?? ret ?? 0;
|
|
1254
|
+
const errmsg = typeof data.errmsg === "string" ? data.errmsg : "";
|
|
1255
|
+
throw new Error(`${endpoint} error ret=${ret} errcode=${errcode}${errmsg ? `: ${errmsg}` : ""} (code ${code})`);
|
|
1256
|
+
}
|
|
1257
|
+
return data;
|
|
1245
1258
|
} catch (err) {
|
|
1246
1259
|
clearTimeout(timer);
|
|
1247
1260
|
throw err;
|
|
@@ -1251,7 +1264,7 @@ async function getUpdates(creds, buf) {
|
|
|
1251
1264
|
try {
|
|
1252
1265
|
const resp = await apiFetch(creds, "ilink/bot/getupdates", {
|
|
1253
1266
|
get_updates_buf: buf,
|
|
1254
|
-
base_info:
|
|
1267
|
+
base_info: baseInfo()
|
|
1255
1268
|
}, 35000);
|
|
1256
1269
|
return resp;
|
|
1257
1270
|
} catch (err) {
|
|
@@ -1272,7 +1285,7 @@ async function sendMessage(creds, to, text, contextToken, clientId) {
|
|
|
1272
1285
|
item_list: [{ type: 1, text_item: { text } }],
|
|
1273
1286
|
context_token: contextToken
|
|
1274
1287
|
},
|
|
1275
|
-
base_info:
|
|
1288
|
+
base_info: baseInfo()
|
|
1276
1289
|
}, 15000);
|
|
1277
1290
|
}
|
|
1278
1291
|
var typingTicketCache = new Map;
|
|
@@ -1284,7 +1297,7 @@ async function ensureTypingTicket(creds, userId) {
|
|
|
1284
1297
|
try {
|
|
1285
1298
|
const resp = await apiFetch(creds, "ilink/bot/getconfig", {
|
|
1286
1299
|
ilink_user_id: userId,
|
|
1287
|
-
base_info:
|
|
1300
|
+
base_info: baseInfo()
|
|
1288
1301
|
}, 15000);
|
|
1289
1302
|
const data = resp;
|
|
1290
1303
|
if (data.typing_ticket) {
|
|
@@ -1306,10 +1319,9 @@ async function sendTyping(creds, userId, command) {
|
|
|
1306
1319
|
try {
|
|
1307
1320
|
await apiFetch(creds, "ilink/bot/sendtyping", {
|
|
1308
1321
|
ilink_user_id: userId,
|
|
1309
|
-
to_user_id: userId,
|
|
1310
1322
|
typing_ticket: ticket,
|
|
1311
|
-
command,
|
|
1312
|
-
base_info:
|
|
1323
|
+
status: command,
|
|
1324
|
+
base_info: baseInfo()
|
|
1313
1325
|
}, 1e4);
|
|
1314
1326
|
} catch (err) {
|
|
1315
1327
|
logger.debug(`sendTyping failed for ${userId}: ${err}`);
|
|
@@ -1873,7 +1885,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1873
1885
|
filesize: ciphertext.length,
|
|
1874
1886
|
no_need_thumb: true,
|
|
1875
1887
|
aeskey: aesKey.toString("hex"),
|
|
1876
|
-
base_info:
|
|
1888
|
+
base_info: baseInfo()
|
|
1877
1889
|
}, 15000);
|
|
1878
1890
|
const uploadFullUrl = uploadParams.upload_full_url?.trim();
|
|
1879
1891
|
if (!uploadFullUrl && !uploadParams.upload_param) {
|
|
@@ -1920,7 +1932,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1920
1932
|
}
|
|
1921
1933
|
const media = {
|
|
1922
1934
|
encrypt_query_param: encryptQueryParam,
|
|
1923
|
-
aes_key: aesKey.toString("base64"),
|
|
1935
|
+
aes_key: Buffer.from(aesKey.toString("hex"), "utf8").toString("base64"),
|
|
1924
1936
|
encrypt_type: 1
|
|
1925
1937
|
};
|
|
1926
1938
|
const item = mediaType === 1 /* IMAGE */ ? {
|
|
@@ -1931,7 +1943,6 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1931
1943
|
file_item: {
|
|
1932
1944
|
media,
|
|
1933
1945
|
file_name: fileName,
|
|
1934
|
-
md5: rawMd5,
|
|
1935
1946
|
len: String(data.length)
|
|
1936
1947
|
}
|
|
1937
1948
|
};
|
|
@@ -1948,7 +1959,7 @@ async function uploadAndSendFile(creds, to, contextToken, filePath, maxSizeBytes
|
|
|
1948
1959
|
item_list: [item],
|
|
1949
1960
|
context_token: contextToken
|
|
1950
1961
|
},
|
|
1951
|
-
base_info:
|
|
1962
|
+
base_info: baseInfo()
|
|
1952
1963
|
}, 15000);
|
|
1953
1964
|
logger.info(`Sent ${mediaType === 1 /* IMAGE */ ? "image" : "file"} to ${to}: ${fileName} (${data.length} bytes)`);
|
|
1954
1965
|
return { status: "sent", mediaType, bytes: data.length };
|
|
@@ -2006,13 +2017,20 @@ import { homedir as homedir6 } from "os";
|
|
|
2006
2017
|
import { existsSync as existsSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync3, rmSync } from "fs";
|
|
2007
2018
|
var STATE_DIR4 = join7(homedir6(), ".omp-wechat");
|
|
2008
2019
|
var SESSIONS_DIR = join7(STATE_DIR4, "sessions");
|
|
2020
|
+
var DEFAULT_OUTBOX_BASE = join7(STATE_DIR4, "outbox");
|
|
2009
2021
|
var STALE_THRESHOLD_MS = 30 * 24 * 60 * 60 * 1000;
|
|
2010
2022
|
function sanitizeChatId(chatId) {
|
|
2011
2023
|
return chatId.replace(/[^a-zA-Z0-9_@.-]/g, "_").slice(0, 200);
|
|
2012
2024
|
}
|
|
2025
|
+
function sanitizeOutboxChatId(chatId) {
|
|
2026
|
+
return chatId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2027
|
+
}
|
|
2013
2028
|
function sessionDirFor(chatId) {
|
|
2014
2029
|
return join7(SESSIONS_DIR, sanitizeChatId(chatId));
|
|
2015
2030
|
}
|
|
2031
|
+
function outboxDirFor(chatId, outboxBase) {
|
|
2032
|
+
return join7(outboxBase ?? DEFAULT_OUTBOX_BASE, sanitizeOutboxChatId(chatId));
|
|
2033
|
+
}
|
|
2016
2034
|
function ensureSessionsDir() {
|
|
2017
2035
|
mkdirSync5(SESSIONS_DIR, { recursive: true, mode: 448 });
|
|
2018
2036
|
}
|
|
@@ -2028,6 +2046,54 @@ function removeSessionDir(chatId) {
|
|
|
2028
2046
|
return false;
|
|
2029
2047
|
}
|
|
2030
2048
|
}
|
|
2049
|
+
function removeOutboxDir(chatId, outboxBase) {
|
|
2050
|
+
const dir = outboxDirFor(chatId, outboxBase);
|
|
2051
|
+
if (!existsSync2(dir))
|
|
2052
|
+
return false;
|
|
2053
|
+
try {
|
|
2054
|
+
rmSync(dir, { recursive: true, force: true });
|
|
2055
|
+
logger.info(`Removed outbox dir: ${dir}`);
|
|
2056
|
+
return true;
|
|
2057
|
+
} catch (err) {
|
|
2058
|
+
logger.warn(`Failed to remove outbox dir ${dir}: ${err}`);
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function cleanupStaleOutboxes(outboxBase) {
|
|
2063
|
+
const base = outboxBase ?? DEFAULT_OUTBOX_BASE;
|
|
2064
|
+
if (!existsSync2(base))
|
|
2065
|
+
return 0;
|
|
2066
|
+
const now = Date.now();
|
|
2067
|
+
let removed = 0;
|
|
2068
|
+
for (const entry of readdirSync2(base, { withFileTypes: true })) {
|
|
2069
|
+
if (!entry.isDirectory())
|
|
2070
|
+
continue;
|
|
2071
|
+
const dir = join7(base, entry.name);
|
|
2072
|
+
let newestMtime = 0;
|
|
2073
|
+
try {
|
|
2074
|
+
for (const file of readdirSync2(dir)) {
|
|
2075
|
+
const mtime = statSync3(join7(dir, file)).mtimeMs;
|
|
2076
|
+
if (mtime > newestMtime)
|
|
2077
|
+
newestMtime = mtime;
|
|
2078
|
+
}
|
|
2079
|
+
} catch {
|
|
2080
|
+
continue;
|
|
2081
|
+
}
|
|
2082
|
+
if (newestMtime === 0 || now - newestMtime > STALE_THRESHOLD_MS) {
|
|
2083
|
+
try {
|
|
2084
|
+
rmSync(dir, { recursive: true, force: true });
|
|
2085
|
+
removed++;
|
|
2086
|
+
logger.info(`Cleaned up stale outbox dir: ${entry.name}`);
|
|
2087
|
+
} catch (err) {
|
|
2088
|
+
logger.warn(`Failed to cleanup outbox dir ${entry.name}: ${err}`);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (removed > 0) {
|
|
2093
|
+
logger.info(`Outbox cleanup: removed ${removed} stale outbox(es)`);
|
|
2094
|
+
}
|
|
2095
|
+
return removed;
|
|
2096
|
+
}
|
|
2031
2097
|
function cleanupStaleSessions() {
|
|
2032
2098
|
if (!existsSync2(SESSIONS_DIR))
|
|
2033
2099
|
return 0;
|
|
@@ -2081,15 +2147,7 @@ function clearAllSessions() {
|
|
|
2081
2147
|
|
|
2082
2148
|
// src/engine/session.ts
|
|
2083
2149
|
import { mkdirSync as mkdirSync6, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
2084
|
-
import { homedir as homedir7 } from "os";
|
|
2085
2150
|
import { join as join8 } from "path";
|
|
2086
|
-
var DEFAULT_OUTBOX_BASE = join8(homedir7(), ".omp-wechat", "outbox");
|
|
2087
|
-
function sanitizeChatId2(chatId) {
|
|
2088
|
-
return chatId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2089
|
-
}
|
|
2090
|
-
function outboxDirFor(chatId, config) {
|
|
2091
|
-
return join8(config.outboxDir ?? DEFAULT_OUTBOX_BASE, sanitizeChatId2(chatId));
|
|
2092
|
-
}
|
|
2093
2151
|
function outboxInstructions(outboxDir) {
|
|
2094
2152
|
return `## File delivery to WeChat
|
|
2095
2153
|
|
|
@@ -2137,7 +2195,7 @@ class ChatSession {
|
|
|
2137
2195
|
}
|
|
2138
2196
|
static async create(chatId, contextToken, config, onReply, onFiles) {
|
|
2139
2197
|
logger.info(`Creating session: ${chatId}`);
|
|
2140
|
-
const outboxDir = outboxDirFor(chatId, config);
|
|
2198
|
+
const outboxDir = outboxDirFor(chatId, config.outboxDir);
|
|
2141
2199
|
const sendFilesEnabled = config.sendFiles !== false;
|
|
2142
2200
|
if (sendFilesEnabled) {
|
|
2143
2201
|
try {
|
|
@@ -2270,13 +2328,14 @@ class SessionPool {
|
|
|
2270
2328
|
get(chatId) {
|
|
2271
2329
|
return this.pool.get(chatId);
|
|
2272
2330
|
}
|
|
2273
|
-
async resetSession(chatId) {
|
|
2331
|
+
async resetSession(chatId, config) {
|
|
2274
2332
|
const entry = this.pool.get(chatId);
|
|
2275
2333
|
if (entry) {
|
|
2276
2334
|
await entry.dispose();
|
|
2277
2335
|
this.pool.delete(chatId);
|
|
2278
2336
|
}
|
|
2279
2337
|
removeSessionDir(chatId);
|
|
2338
|
+
removeOutboxDir(chatId, config.outboxDir);
|
|
2280
2339
|
}
|
|
2281
2340
|
getContextToken(chatId) {
|
|
2282
2341
|
return this.pool.get(chatId)?.getContextToken() ?? "";
|
|
@@ -2479,7 +2538,7 @@ class ModelCommand {
|
|
|
2479
2538
|
// src/command/new-session-command.ts
|
|
2480
2539
|
class NewSessionInvocation {
|
|
2481
2540
|
async execute(ctx) {
|
|
2482
|
-
await ctx.pool.resetSession(ctx.chatId);
|
|
2541
|
+
await ctx.pool.resetSession(ctx.chatId, ctx.config);
|
|
2483
2542
|
return "Session reset. Your next message starts a fresh conversation.";
|
|
2484
2543
|
}
|
|
2485
2544
|
}
|
|
@@ -2583,7 +2642,11 @@ class WeChatBridge {
|
|
|
2583
2642
|
this.releaseLock();
|
|
2584
2643
|
});
|
|
2585
2644
|
cleanupStaleSessions();
|
|
2586
|
-
|
|
2645
|
+
cleanupStaleOutboxes(config.outboxDir);
|
|
2646
|
+
this.cleanupTimer = setInterval(() => {
|
|
2647
|
+
cleanupStaleSessions();
|
|
2648
|
+
cleanupStaleOutboxes(config.outboxDir);
|
|
2649
|
+
}, 6 * 60 * 60 * 1000);
|
|
2587
2650
|
return this.state;
|
|
2588
2651
|
}
|
|
2589
2652
|
async stop() {
|
|
@@ -2835,7 +2898,7 @@ ${truncated}
|
|
|
2835
2898
|
}
|
|
2836
2899
|
|
|
2837
2900
|
// src/service.ts
|
|
2838
|
-
import { platform, homedir as
|
|
2901
|
+
import { platform, homedir as homedir7 } from "os";
|
|
2839
2902
|
import { join as join9, basename as basename4 } from "path";
|
|
2840
2903
|
import { existsSync as existsSync3, mkdirSync as mkdirSync7, writeFileSync as writeFileSync4, rmSync as rmSync3 } from "fs";
|
|
2841
2904
|
var PLIST_LABEL = "com.omp-wechat";
|
|
@@ -2851,14 +2914,14 @@ function detectPlatform() {
|
|
|
2851
2914
|
return "other";
|
|
2852
2915
|
}
|
|
2853
2916
|
function getLogDir() {
|
|
2854
|
-
return join9(
|
|
2917
|
+
return join9(homedir7(), ".omp", "logs");
|
|
2855
2918
|
}
|
|
2856
2919
|
function resolveHostBinary() {
|
|
2857
2920
|
const exe = process.execPath || "omp";
|
|
2858
2921
|
return basename4(exe);
|
|
2859
2922
|
}
|
|
2860
2923
|
function plistPath() {
|
|
2861
|
-
return join9(
|
|
2924
|
+
return join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
2862
2925
|
}
|
|
2863
2926
|
function generatePlist() {
|
|
2864
2927
|
const omp = resolveHostBinary();
|
|
@@ -2894,14 +2957,14 @@ function generatePlist() {
|
|
|
2894
2957
|
<key>PATH</key>
|
|
2895
2958
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
2896
2959
|
<key>HOME</key>
|
|
2897
|
-
<string>${
|
|
2960
|
+
<string>${homedir7()}</string>
|
|
2898
2961
|
</dict>
|
|
2899
2962
|
</dict>
|
|
2900
2963
|
</plist>
|
|
2901
2964
|
`;
|
|
2902
2965
|
}
|
|
2903
2966
|
function installLaunchd() {
|
|
2904
|
-
const dir = join9(
|
|
2967
|
+
const dir = join9(homedir7(), "Library", "LaunchAgents");
|
|
2905
2968
|
mkdirSync7(dir, { recursive: true });
|
|
2906
2969
|
mkdirSync7(getLogDir(), { recursive: true });
|
|
2907
2970
|
const plist = plistPath();
|
|
@@ -2943,7 +3006,7 @@ ExecStart=/bin/sh -c 'while true; do echo "{\\"id\\":\\"ka\\",\\"type\\":\\"get_
|
|
|
2943
3006
|
Restart=always
|
|
2944
3007
|
RestartSec=10
|
|
2945
3008
|
|
|
2946
|
-
Environment=HOME=${
|
|
3009
|
+
Environment=HOME=${homedir7()}
|
|
2947
3010
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
2948
3011
|
|
|
2949
3012
|
StandardOutput=null
|
|
@@ -2952,7 +3015,7 @@ StandardError=append:${logDir}/rpc.log
|
|
|
2952
3015
|
NoNewPrivileges=true
|
|
2953
3016
|
ProtectSystem=strict
|
|
2954
3017
|
ProtectHome=read-only
|
|
2955
|
-
ReadWritePaths=${logDir} ${join9(
|
|
3018
|
+
ReadWritePaths=${logDir} ${join9(homedir7(), ".omp-wechat")} ${join9(homedir7(), ".omp")}
|
|
2956
3019
|
PrivateTmp=true
|
|
2957
3020
|
|
|
2958
3021
|
[Install]
|
|
@@ -2993,7 +3056,7 @@ function uninstallSystemd() {
|
|
|
2993
3056
|
}
|
|
2994
3057
|
var WIN_TASK_NAME = "OMP-Wechat";
|
|
2995
3058
|
function winScriptPath() {
|
|
2996
|
-
return join9(
|
|
3059
|
+
return join9(homedir7(), ".omp-wechat", "omp-wechat-rpc.ps1");
|
|
2997
3060
|
}
|
|
2998
3061
|
function generateWinScript() {
|
|
2999
3062
|
const omp = process.execPath || "omp";
|
|
@@ -3036,7 +3099,7 @@ while ($true) {
|
|
|
3036
3099
|
`;
|
|
3037
3100
|
}
|
|
3038
3101
|
function installWinTask() {
|
|
3039
|
-
mkdirSync7(join9(
|
|
3102
|
+
mkdirSync7(join9(homedir7(), ".omp-wechat"), { recursive: true });
|
|
3040
3103
|
mkdirSync7(getLogDir(), { recursive: true });
|
|
3041
3104
|
writeFileSync4(winScriptPath(), generateWinScript());
|
|
3042
3105
|
Bun.spawnSync(["schtasks", "/end", "/tn", WIN_TASK_NAME], { stderr: "ignore" });
|