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.
Files changed (2) hide show
  1. package/lib/index.js +262 -25
  2. 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 WeakSet();
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" && !config.debugLogging) return;
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
- async function fetchGroupShutList(session, config) {
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 result = await callOneBotAPI(
2209
+ const primary = await callGroupShutList(
2047
2210
  internal,
2048
- "get_group_shut_list",
2049
- { group_id: preferredGroupId },
2050
- ["getGroupShutList"]
2211
+ preferredGroupId,
2212
+ log,
2213
+ preferredProtocol
2051
2214
  );
2052
- return extractList(result);
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
- if (preferredGroupId === fallbackGroupId) throw error2;
2055
- const result = await callOneBotAPI(
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
- "get_group_shut_list",
2058
- { group_id: fallbackGroupId },
2059
- ["getGroupShutList"]
2250
+ fallbackGroupId,
2251
+ log,
2252
+ preferredProtocol
2060
2253
  );
2061
- return extractList(result);
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
- return renderShutList(normalized);
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
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chatluna-toolbox",
3
3
  "description": "为 ChatLuna 提供更多原生工具、XML 工具与变量,仅支持 onebot 平台。",
4
- "version": "0.0.12",
4
+ "version": "0.0.13",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
7
  "files": [