/** * Known user tracking — JSON file-based store. * * Migrated from src/known-users.ts. Dependencies are only Node.js * built-ins + log + platform (both zero plugin-sdk).
*/
import fs from "node:fs"; import path from "node:path"; import type { ChatScope } from "../types.js"; import { formatErrorMessage } from "../utils/format.js"; import { debugLog, debugError } from "../utils/log.js"; import { getQQBotDataDir, getQQBotDataPath } from "../utils/platform.js";
/** Persisted record for a user who has interacted with the bot. */
export interface KnownUser {
openid: string;
type: ChatScope;
nickname?: string;
groupOpenid?: string;
accountId: string;
firstSeenAt: number;
lastSeenAt: number;
interactionCount: number;
}
let usersCache: Map<string, KnownUser> | null = null; const SAVE_THROTTLE_MS = 5000;
let saveTimer: ReturnType<typeof setTimeout> | null = null;
let isDirty = false;
function ensureDir(): void {
getQQBotDataDir("data");
}
function getKnownUsersFile(): string { return path.join(getQQBotDataPath("data"), "known-users.json");
}
function makeUserKey(user: Partial<KnownUser>): string { const base = `${user.accountId}:${user.type}:${user.openid}`; return user.type === "group" && user.groupOpenid ? `${base}:${user.groupOpenid}` : base;
}
function loadUsersFromFile(): Map<string, KnownUser> { if (usersCache !== null) { return usersCache;
}
usersCache = new Map(); try { const knownUsersFile = getKnownUsersFile(); if (fs.existsSync(knownUsersFile)) { const data = fs.readFileSync(knownUsersFile, "utf-8"); const users = JSON.parse(data) as KnownUser[]; for (const user of users) {
usersCache.set(makeUserKey(user), user);
}
debugLog(`[known-users] Loaded ${usersCache.size} users`);
}
} catch (err) {
debugError(`[known-users] Failed to load users: ${formatErrorMessage(err)}`);
usersCache = new Map();
} return usersCache;
}
/** Clear all user records, optionally scoped to one account. */
export function clearKnownUsers(accountId?: string): number { const cache = loadUsersFromFile();
let count = 0; if (accountId) { for (const [key, user] of cache.entries()) { if (user.accountId === accountId) {
cache.delete(key);
count++;
}
}
} else {
count = cache.size;
cache.clear();
} if (count > 0) {
isDirty = true;
doSaveUsersToFile();
debugLog(`[known-users] Cleared ${count} users`);
} return count;
}
/** Return all groups in which a user has interacted. */
export function getUserGroups(accountId: string, openid: string): string[] { return listKnownUsers({ accountId, type: "group" })
.filter((u) => u.openid === openid && u.groupOpenid)
.map((u) => u.groupOpenid!);
}
/** Return all recorded members for one group. */
export function getGroupMembers(accountId: string, groupOpenid: string): KnownUser[] { return listKnownUsers({ accountId, type: "group" }).filter((u) => u.groupOpenid === groupOpenid);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-05)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.