koishi-plugin-chatluna-toolbox 0.0.12 → 0.0.13
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/lib/index.js +262 -25
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -201,6 +201,10 @@ var require_lib = __commonJS({
|
|
|
201
201
|
writable: true
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
|
+
function createMessageFingerprint(message, response) {
|
|
205
|
+
const type = String(message.type ?? message.role ?? "").trim().toLowerCase();
|
|
206
|
+
return `${type}:${response}`;
|
|
207
|
+
}
|
|
204
208
|
function restoreDispatcher(key, dispatcher) {
|
|
205
209
|
if (dispatcher.messages.push === dispatcher.patchedPush) {
|
|
206
210
|
dispatcher.messages.push = dispatcher.originalPush;
|
|
@@ -212,17 +216,22 @@ var require_lib = __commonJS({
|
|
|
212
216
|
let dispatcher = getDispatcher(messages, key);
|
|
213
217
|
if (!dispatcher) {
|
|
214
218
|
const listeners = /* @__PURE__ */ new Set();
|
|
215
|
-
const processedMessages = /* @__PURE__ */ new
|
|
219
|
+
const processedMessages = /* @__PURE__ */ new WeakMap();
|
|
216
220
|
const originalPush = messages.push;
|
|
217
221
|
const patchedPush = function patchedPush2(...items) {
|
|
218
222
|
const result = originalPush.apply(this, items);
|
|
219
223
|
for (const item of items) {
|
|
220
224
|
if (!item || typeof item !== "object") continue;
|
|
221
|
-
if (processedMessages.has(item)) continue;
|
|
222
|
-
processedMessages.add(item);
|
|
223
225
|
const message = item;
|
|
224
226
|
const response = extractAssistantText(message);
|
|
225
227
|
if (!response) continue;
|
|
228
|
+
const fingerprint = createMessageFingerprint(
|
|
229
|
+
item,
|
|
230
|
+
response
|
|
231
|
+
);
|
|
232
|
+
const previousFingerprint = processedMessages.get(item);
|
|
233
|
+
if (previousFingerprint === fingerprint) continue;
|
|
234
|
+
processedMessages.set(item, fingerprint);
|
|
226
235
|
for (const listener2 of Array.from(listeners)) {
|
|
227
236
|
try {
|
|
228
237
|
listener2(message);
|
|
@@ -706,7 +715,15 @@ var path = __toESM(require("path"));
|
|
|
706
715
|
function createLogger(ctx, config) {
|
|
707
716
|
const logger = ctx.logger("chatluna-toolbox");
|
|
708
717
|
return (level, message, meta) => {
|
|
709
|
-
if (level === "debug"
|
|
718
|
+
if (level === "debug") {
|
|
719
|
+
if (!config.debugLogging) return;
|
|
720
|
+
if (meta === void 0) {
|
|
721
|
+
logger.info(message);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
logger.info(message, meta);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
710
727
|
if (meta === void 0) {
|
|
711
728
|
logger[level](message);
|
|
712
729
|
return;
|
|
@@ -1980,6 +1997,9 @@ function createGroupInfoProvider(deps) {
|
|
|
1980
1997
|
}
|
|
1981
1998
|
|
|
1982
1999
|
// src/features/variables/providers/group-shut-list.ts
|
|
2000
|
+
var GROUP_SHUT_LIST_TIMEOUT_MS = 200;
|
|
2001
|
+
var GROUP_SHUT_LIST_TIMEOUT_ERROR = "\u8BF7\u6C42\u7FA4\u7981\u8A00\u5217\u8868\u8D85\u65F6\u3002";
|
|
2002
|
+
var GROUP_SHUT_LIST_CACHE_TTL_MS = 15e3;
|
|
1983
2003
|
function normalizeTimestamp2(raw) {
|
|
1984
2004
|
if (raw === null || raw === void 0 || raw === "" || Number(raw) === 0)
|
|
1985
2005
|
return null;
|
|
@@ -2001,13 +2021,6 @@ function pickFirst2(...values) {
|
|
|
2001
2021
|
}
|
|
2002
2022
|
return "";
|
|
2003
2023
|
}
|
|
2004
|
-
function extractList(result) {
|
|
2005
|
-
if (Array.isArray(result)) return result;
|
|
2006
|
-
if (result && typeof result === "object" && Array.isArray(result.data)) {
|
|
2007
|
-
return result.data;
|
|
2008
|
-
}
|
|
2009
|
-
return [];
|
|
2010
|
-
}
|
|
2011
2024
|
function normalizeShutItem(item) {
|
|
2012
2025
|
const userId = pickFirst2(
|
|
2013
2026
|
item.uin,
|
|
@@ -2035,32 +2048,227 @@ function normalizeShutItem(item) {
|
|
|
2035
2048
|
shutUpTimeText
|
|
2036
2049
|
};
|
|
2037
2050
|
}
|
|
2038
|
-
|
|
2051
|
+
function previewText(text, max = 180) {
|
|
2052
|
+
const raw = String(text ?? "").trim();
|
|
2053
|
+
if (!raw) return "";
|
|
2054
|
+
return raw.length > max ? `${raw.slice(0, max)}...` : raw;
|
|
2055
|
+
}
|
|
2056
|
+
function getDataKind(data) {
|
|
2057
|
+
if (Array.isArray(data)) return "array";
|
|
2058
|
+
if (data === null) return "null";
|
|
2059
|
+
if (data === void 0) return "undefined";
|
|
2060
|
+
return typeof data;
|
|
2061
|
+
}
|
|
2062
|
+
function debug(log, message, meta) {
|
|
2063
|
+
log?.("debug", message, meta);
|
|
2064
|
+
}
|
|
2065
|
+
function debugErrorMessage(error) {
|
|
2066
|
+
if (error instanceof Error) return previewText(error.message);
|
|
2067
|
+
return previewText(error);
|
|
2068
|
+
}
|
|
2069
|
+
function withTiming(promise) {
|
|
2070
|
+
const startedAt = Date.now();
|
|
2071
|
+
return promise.then((value) => ({
|
|
2072
|
+
value,
|
|
2073
|
+
elapsedMs: Date.now() - startedAt
|
|
2074
|
+
}));
|
|
2075
|
+
}
|
|
2076
|
+
function withTimeout(promise, timeoutMs) {
|
|
2077
|
+
return new Promise((resolve2, reject) => {
|
|
2078
|
+
const timer = setTimeout(() => {
|
|
2079
|
+
reject(new Error(GROUP_SHUT_LIST_TIMEOUT_ERROR));
|
|
2080
|
+
}, timeoutMs);
|
|
2081
|
+
promise.then((value) => {
|
|
2082
|
+
clearTimeout(timer);
|
|
2083
|
+
resolve2(value);
|
|
2084
|
+
}).catch((error) => {
|
|
2085
|
+
clearTimeout(timer);
|
|
2086
|
+
reject(error);
|
|
2087
|
+
});
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
function isOneBotEnvelope(result) {
|
|
2091
|
+
return !!result && typeof result === "object" && !Array.isArray(result);
|
|
2092
|
+
}
|
|
2093
|
+
function isTimeoutError(error) {
|
|
2094
|
+
return error instanceof Error && error.message === GROUP_SHUT_LIST_TIMEOUT_ERROR;
|
|
2095
|
+
}
|
|
2096
|
+
function normalizeErrorMessage(error) {
|
|
2097
|
+
if (!(error instanceof Error)) return "";
|
|
2098
|
+
return error.message.toLowerCase();
|
|
2099
|
+
}
|
|
2100
|
+
function shouldFallbackByError(error) {
|
|
2101
|
+
if (isTimeoutError(error)) return false;
|
|
2102
|
+
const message = normalizeErrorMessage(error);
|
|
2103
|
+
if (!message) return false;
|
|
2104
|
+
return message.includes("string required") || message.includes("number required") || message.includes("invalid") || message.includes("type") || message.includes("\u53C2\u6570");
|
|
2105
|
+
}
|
|
2106
|
+
function shouldFallbackByEnvelope(envelope) {
|
|
2107
|
+
const message = `${envelope.message || ""} ${envelope.wording || ""}`.toLowerCase().trim();
|
|
2108
|
+
if (!message) return false;
|
|
2109
|
+
return message.includes("string") || message.includes("number") || message.includes("type") || message.includes("\u53C2\u6570");
|
|
2110
|
+
}
|
|
2111
|
+
function debugEnvelopeMeta(result) {
|
|
2112
|
+
if (!isOneBotEnvelope(result)) {
|
|
2113
|
+
return {
|
|
2114
|
+
resultKind: Array.isArray(result) ? "array" : typeof result
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
return {
|
|
2118
|
+
resultKind: "envelope",
|
|
2119
|
+
status: previewText(result.status),
|
|
2120
|
+
retcode: typeof result.retcode === "number" ? result.retcode : void 0,
|
|
2121
|
+
dataKind: getDataKind(result.data),
|
|
2122
|
+
dataLength: Array.isArray(result.data) ? result.data.length : void 0,
|
|
2123
|
+
errorMessage: previewText(result.message || result.wording)
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
function parseShutListResult(result) {
|
|
2127
|
+
if (Array.isArray(result)) {
|
|
2128
|
+
return { list: result, shouldFallback: false };
|
|
2129
|
+
}
|
|
2130
|
+
if (!isOneBotEnvelope(result)) {
|
|
2131
|
+
return { list: [], shouldFallback: false };
|
|
2132
|
+
}
|
|
2133
|
+
const status = String(result.status || "").toLowerCase();
|
|
2134
|
+
const hasRetcode = typeof result.retcode === "number";
|
|
2135
|
+
const failedByRetcode = hasRetcode && result.retcode !== 0;
|
|
2136
|
+
const failedByStatus = !!status && status !== "ok";
|
|
2137
|
+
if (failedByRetcode || failedByStatus) {
|
|
2138
|
+
if (shouldFallbackByEnvelope(result)) {
|
|
2139
|
+
return { list: [], shouldFallback: true };
|
|
2140
|
+
}
|
|
2141
|
+
const errorText = String(result.message || result.wording || "\u63A5\u53E3\u8C03\u7528\u5931\u8D25\u3002");
|
|
2142
|
+
throw new Error(errorText);
|
|
2143
|
+
}
|
|
2144
|
+
if (Array.isArray(result.data)) {
|
|
2145
|
+
return {
|
|
2146
|
+
list: result.data,
|
|
2147
|
+
shouldFallback: false
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
if (result.data == null) {
|
|
2151
|
+
return { list: [], shouldFallback: false };
|
|
2152
|
+
}
|
|
2153
|
+
return { list: [], shouldFallback: false };
|
|
2154
|
+
}
|
|
2155
|
+
async function callGroupShutList(internal, groupId, log, preferredProtocol) {
|
|
2156
|
+
debug(log, "groupShutList: request start", {
|
|
2157
|
+
stage: "request.start",
|
|
2158
|
+
preferredProtocol,
|
|
2159
|
+
groupId,
|
|
2160
|
+
groupIdType: typeof groupId
|
|
2161
|
+
});
|
|
2162
|
+
const { value: result, elapsedMs } = await withTiming(
|
|
2163
|
+
withTimeout(
|
|
2164
|
+
callOneBotAPI(
|
|
2165
|
+
internal,
|
|
2166
|
+
"get_group_shut_list",
|
|
2167
|
+
{ group_id: groupId },
|
|
2168
|
+
["getGroupShutList"]
|
|
2169
|
+
),
|
|
2170
|
+
GROUP_SHUT_LIST_TIMEOUT_MS
|
|
2171
|
+
)
|
|
2172
|
+
);
|
|
2173
|
+
debug(log, "groupShutList: request resolved", {
|
|
2174
|
+
stage: "request.resolved",
|
|
2175
|
+
preferredProtocol,
|
|
2176
|
+
groupId,
|
|
2177
|
+
groupIdType: typeof groupId,
|
|
2178
|
+
elapsedMs,
|
|
2179
|
+
...debugEnvelopeMeta(result)
|
|
2180
|
+
});
|
|
2181
|
+
const parsed = parseShutListResult(result);
|
|
2182
|
+
debug(log, "groupShutList: response parsed", {
|
|
2183
|
+
stage: "response.parsed",
|
|
2184
|
+
preferredProtocol,
|
|
2185
|
+
groupId,
|
|
2186
|
+
groupIdType: typeof groupId,
|
|
2187
|
+
elapsedMs,
|
|
2188
|
+
shouldFallback: parsed.shouldFallback,
|
|
2189
|
+
dataLength: parsed.list.length
|
|
2190
|
+
});
|
|
2191
|
+
return parsed;
|
|
2192
|
+
}
|
|
2193
|
+
async function fetchGroupShutList(session, config, log) {
|
|
2039
2194
|
const { error, internal } = ensureOneBotSession(session);
|
|
2040
2195
|
if (error || !internal)
|
|
2041
2196
|
throw new Error(error || "\u7F3A\u5C11 OneBot internal \u63A5\u53E3\u3002");
|
|
2042
2197
|
const preferredProtocol = resolveOneBotProtocol(config);
|
|
2043
2198
|
const preferredGroupId = preferredProtocol === "llbot" ? String(session.guildId) : Number(session.guildId);
|
|
2044
2199
|
const fallbackGroupId = preferredProtocol === "llbot" ? Number(session.guildId) : String(session.guildId);
|
|
2200
|
+
const canFallback = preferredGroupId !== fallbackGroupId;
|
|
2201
|
+
debug(log, "groupShutList: fetch start", {
|
|
2202
|
+
stage: "fetch.start",
|
|
2203
|
+
preferredProtocol,
|
|
2204
|
+
canFallback,
|
|
2205
|
+
groupId: preferredGroupId,
|
|
2206
|
+
groupIdType: typeof preferredGroupId
|
|
2207
|
+
});
|
|
2045
2208
|
try {
|
|
2046
|
-
const
|
|
2209
|
+
const primary = await callGroupShutList(
|
|
2047
2210
|
internal,
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2211
|
+
preferredGroupId,
|
|
2212
|
+
log,
|
|
2213
|
+
preferredProtocol
|
|
2051
2214
|
);
|
|
2052
|
-
|
|
2215
|
+
if (!canFallback || !primary.shouldFallback) return primary.list;
|
|
2216
|
+
debug(log, "groupShutList: trigger fallback by envelope", {
|
|
2217
|
+
stage: "fallback.envelope",
|
|
2218
|
+
preferredProtocol,
|
|
2219
|
+
canFallback,
|
|
2220
|
+
groupId: fallbackGroupId,
|
|
2221
|
+
groupIdType: typeof fallbackGroupId,
|
|
2222
|
+
shouldFallback: primary.shouldFallback
|
|
2223
|
+
});
|
|
2224
|
+
const fallback = await callGroupShutList(
|
|
2225
|
+
internal,
|
|
2226
|
+
fallbackGroupId,
|
|
2227
|
+
log,
|
|
2228
|
+
preferredProtocol
|
|
2229
|
+
);
|
|
2230
|
+
return fallback.list;
|
|
2053
2231
|
} catch (error2) {
|
|
2054
|
-
|
|
2055
|
-
|
|
2232
|
+
const fallbackByError = shouldFallbackByError(error2);
|
|
2233
|
+
debug(log, "groupShutList: request failed", {
|
|
2234
|
+
stage: "request.error",
|
|
2235
|
+
preferredProtocol,
|
|
2236
|
+
canFallback,
|
|
2237
|
+
errorMessage: debugErrorMessage(error2),
|
|
2238
|
+
shouldFallback: fallbackByError
|
|
2239
|
+
});
|
|
2240
|
+
if (!canFallback || !fallbackByError) throw error2;
|
|
2241
|
+
debug(log, "groupShutList: trigger fallback by error", {
|
|
2242
|
+
stage: "fallback.error",
|
|
2243
|
+
preferredProtocol,
|
|
2244
|
+
groupId: fallbackGroupId,
|
|
2245
|
+
groupIdType: typeof fallbackGroupId,
|
|
2246
|
+
errorMessage: debugErrorMessage(error2)
|
|
2247
|
+
});
|
|
2248
|
+
const fallback = await callGroupShutList(
|
|
2056
2249
|
internal,
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2250
|
+
fallbackGroupId,
|
|
2251
|
+
log,
|
|
2252
|
+
preferredProtocol
|
|
2060
2253
|
);
|
|
2061
|
-
return
|
|
2254
|
+
return fallback.list;
|
|
2062
2255
|
}
|
|
2063
2256
|
}
|
|
2257
|
+
function readCache(cache, guildId, now) {
|
|
2258
|
+
const entry = cache.get(guildId);
|
|
2259
|
+
if (!entry) return null;
|
|
2260
|
+
if (entry.expiresAt <= now) {
|
|
2261
|
+
cache.delete(guildId);
|
|
2262
|
+
return null;
|
|
2263
|
+
}
|
|
2264
|
+
return entry.value;
|
|
2265
|
+
}
|
|
2266
|
+
function writeCache(cache, guildId, value, now) {
|
|
2267
|
+
cache.set(guildId, {
|
|
2268
|
+
value,
|
|
2269
|
+
expiresAt: now + GROUP_SHUT_LIST_CACHE_TTL_MS
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2064
2272
|
function renderShutList(items) {
|
|
2065
2273
|
if (items.length === 0) return "\u5F53\u524D\u7FA4\u6682\u65E0\u7981\u8A00\u6210\u5458\u3002";
|
|
2066
2274
|
return items.map(
|
|
@@ -2069,18 +2277,47 @@ function renderShutList(items) {
|
|
|
2069
2277
|
}
|
|
2070
2278
|
function createGroupShutListProvider(deps) {
|
|
2071
2279
|
const { config, log } = deps;
|
|
2280
|
+
const cache = /* @__PURE__ */ new Map();
|
|
2072
2281
|
return async (_args, _variables, configurable) => {
|
|
2073
2282
|
const session = configurable?.session;
|
|
2074
2283
|
if (!session) return "\u6682\u65E0\u7FA4\u7981\u8A00\u5217\u8868\u3002";
|
|
2075
2284
|
if (!session.guildId) return "";
|
|
2076
2285
|
if (session.platform !== "onebot")
|
|
2077
2286
|
return "\u5F53\u524D\u5E73\u53F0\u6682\u4E0D\u652F\u6301\u67E5\u8BE2\u7FA4\u7981\u8A00\u5217\u8868\u3002";
|
|
2287
|
+
const guildId = String(session.guildId);
|
|
2288
|
+
const cached = readCache(cache, guildId, Date.now());
|
|
2289
|
+
if (cached !== null) {
|
|
2290
|
+
debug(log, "groupShutList: cache hit", {
|
|
2291
|
+
stage: "cache.hit",
|
|
2292
|
+
groupId: guildId,
|
|
2293
|
+
groupIdType: typeof guildId
|
|
2294
|
+
});
|
|
2295
|
+
return cached;
|
|
2296
|
+
}
|
|
2297
|
+
debug(log, "groupShutList: cache miss", {
|
|
2298
|
+
stage: "cache.miss",
|
|
2299
|
+
groupId: guildId,
|
|
2300
|
+
groupIdType: typeof guildId
|
|
2301
|
+
});
|
|
2078
2302
|
try {
|
|
2079
|
-
const list = await fetchGroupShutList(session, config);
|
|
2303
|
+
const list = await fetchGroupShutList(session, config, log);
|
|
2080
2304
|
const normalized = list.map(normalizeShutItem).filter(Boolean);
|
|
2081
|
-
|
|
2305
|
+
const result = renderShutList(normalized);
|
|
2306
|
+
writeCache(cache, guildId, result, Date.now());
|
|
2307
|
+
debug(log, "groupShutList: provider success", {
|
|
2308
|
+
stage: "provider.success",
|
|
2309
|
+
groupId: guildId,
|
|
2310
|
+
dataLength: list.length
|
|
2311
|
+
});
|
|
2312
|
+
return result;
|
|
2082
2313
|
} catch (error) {
|
|
2314
|
+
debug(log, "groupShutList: provider failed", {
|
|
2315
|
+
stage: "provider.error",
|
|
2316
|
+
groupId: guildId,
|
|
2317
|
+
errorMessage: debugErrorMessage(error)
|
|
2318
|
+
});
|
|
2083
2319
|
log?.("debug", "\u7FA4\u7981\u8A00\u5217\u8868\u53D8\u91CF\u89E3\u6790\u5931\u8D25", error);
|
|
2320
|
+
if (isTimeoutError(error)) return "\u5F53\u524D\u7FA4\u6682\u65E0\u7981\u8A00\u6210\u5458\u3002";
|
|
2084
2321
|
return "\u83B7\u53D6\u7FA4\u7981\u8A00\u5217\u8868\u5931\u8D25\u3002";
|
|
2085
2322
|
}
|
|
2086
2323
|
};
|