refactor: trim qqbot known user store

This commit is contained in:
Peter Steinberger
2026-05-02 09:21:53 +01:00
parent 1de74bdc59
commit e0f2973d20

View File

@@ -13,7 +13,7 @@ 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 {
interface KnownUser {
openid: string;
type: ChatScope;
nickname?: string;
@@ -135,120 +135,3 @@ export function recordKnownUser(user: {
isDirty = true;
saveUsersToFile();
}
/** Look up one known user. */
export function getKnownUser(
accountId: string,
openid: string,
type: ChatScope = "c2c",
groupOpenid?: string,
): KnownUser | undefined {
return loadUsersFromFile().get(makeUserKey({ accountId, openid, type, groupOpenid }));
}
/** List known users with optional filtering and sorting. */
export function listKnownUsers(options?: {
accountId?: string;
type?: ChatScope;
activeWithin?: number;
limit?: number;
sortBy?: "lastSeenAt" | "firstSeenAt" | "interactionCount";
sortOrder?: "asc" | "desc";
}): KnownUser[] {
let users = Array.from(loadUsersFromFile().values());
if (options?.accountId) {
users = users.filter((u) => u.accountId === options.accountId);
}
if (options?.type) {
users = users.filter((u) => u.type === options.type);
}
if (options?.activeWithin) {
const cutoff = Date.now() - options.activeWithin;
users = users.filter((u) => u.lastSeenAt >= cutoff);
}
const sortBy = options?.sortBy ?? "lastSeenAt";
const sortOrder = options?.sortOrder ?? "desc";
users.sort((a, b) => {
const aV = a[sortBy] ?? 0;
const bV = b[sortBy] ?? 0;
return sortOrder === "asc" ? aV - bV : bV - aV;
});
if (options?.limit && options.limit > 0) {
users = users.slice(0, options.limit);
}
return users;
}
/** Return summary stats for known users. */
export function getKnownUsersStats(accountId?: string): {
totalUsers: number;
c2cUsers: number;
groupUsers: number;
activeIn24h: number;
activeIn7d: number;
} {
const users = listKnownUsers({ accountId });
const now = Date.now();
const day = 86400000;
return {
totalUsers: users.length,
c2cUsers: users.filter((u) => u.type === "c2c").length,
groupUsers: users.filter((u) => u.type === "group").length,
activeIn24h: users.filter((u) => now - u.lastSeenAt < day).length,
activeIn7d: users.filter((u) => now - u.lastSeenAt < 7 * day).length,
};
}
/** Remove one user record. */
export function removeKnownUser(
accountId: string,
openid: string,
type: ChatScope = "c2c",
groupOpenid?: string,
): boolean {
const cache = loadUsersFromFile();
const key = makeUserKey({ accountId, openid, type, groupOpenid });
if (cache.has(key)) {
cache.delete(key);
isDirty = true;
saveUsersToFile();
debugLog(`[known-users] Removed user ${openid}`);
return true;
}
return false;
}
/** 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);
}