Import source: src/state
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
+218
@@ -0,0 +1,218 @@
|
||||
import type { ChatSession, GroupMessage, BucketEntry, LogEntry, ModelInfo, TgUser, ExecutionContext } from './types';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let cfg: any = null as any;
|
||||
let API_URL = "";
|
||||
let BOT_INFO: TgUser | null = null;
|
||||
let BOT_SELF_ID: number | null = null;
|
||||
let isInitialized = false;
|
||||
let maintenanceCache: { value: boolean; ts: number } | null = null;
|
||||
let requestOrigin = "";
|
||||
let globalCtx: ExecutionContext | null = null;
|
||||
|
||||
const runtimeStatus = {
|
||||
startedAt: Date.now(),
|
||||
scheduler: {
|
||||
running: false,
|
||||
lastRunAt: null as number | null,
|
||||
lastSuccessAt: null as number | null,
|
||||
lastErrorAt: null as number | null,
|
||||
lastError: null as string | null,
|
||||
},
|
||||
polling: {
|
||||
running: false,
|
||||
lastPollAt: null as number | null,
|
||||
lastUpdateAt: null as number | null,
|
||||
lastUpdateId: null as number | null,
|
||||
lastErrorAt: null as number | null,
|
||||
lastError: null as string | null,
|
||||
},
|
||||
};
|
||||
|
||||
const activeRequests = new Map<number, Set<{ id: string; ts: number }>>();
|
||||
const callbackRateLimits = new Map<number, number[]>();
|
||||
const sessionLoadLocks = new Map<number, Promise<ChatSession>>();
|
||||
const groupContextCache = new Map<number, { messages: GroupMessage[]; lastCleanup: number }>();
|
||||
const modelListStates = new Map<string, { page: number; perPage: number; totalPages: number }>();
|
||||
const adminPanelStates = new Map<number, { page: number; perPage: number; sortBy: "new" | "active" | "messages" }>();
|
||||
const broadcastStates = new Map<number, { mode: "all" | "vip" | "free" | "specific"; userId?: number }>();
|
||||
|
||||
let globalDisabledKeys: Record<string, number> = {};
|
||||
let _saveDisabledKeysTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let lastDisabledKeysFetch = 0;
|
||||
|
||||
const activeProgressMessages = new Map<number, number>();
|
||||
const lastTypingSent = new Map<number, number>();
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// SECTION: LOGGER
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
const MAX_LOGS = 100;
|
||||
const recentLogs: LogEntry[] = [];
|
||||
|
||||
function log(level: LogEntry["level"], message: string, context?: unknown) {
|
||||
if (level === "info") {
|
||||
const entry: LogEntry = { timestamp: Date.now(), level, message, context };
|
||||
recentLogs.push(entry);
|
||||
if (recentLogs.length > MAX_LOGS) recentLogs.shift();
|
||||
return;
|
||||
}
|
||||
|
||||
const entry: LogEntry = { timestamp: Date.now(), level, message, context };
|
||||
const fn = level === "error" ? console.error : console.warn;
|
||||
fn(`[${level.toUpperCase()}] ${message}`, context ?? "");
|
||||
recentLogs.push(entry);
|
||||
if (recentLogs.length > MAX_LOGS) recentLogs.shift();
|
||||
}
|
||||
|
||||
const logger = {
|
||||
info: (msg: string, ctx?: unknown) => log("info", msg, ctx),
|
||||
warn: (msg: string, ctx?: unknown) => log("warn", msg, ctx),
|
||||
error: (msg: string, ctx?: unknown) => log("error", msg, ctx),
|
||||
};
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// SECTION: CACHE (LRU eviction)
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
class CacheLayer<T> {
|
||||
private store = new Map<string, { data: T; expires: number; lastAccess: number }>();
|
||||
constructor(private maxSize = 500, private defaultTTL = 5 * 60 * 1000) {}
|
||||
|
||||
set(key: string, value: T, ttl?: number): void {
|
||||
if (this.store.size >= this.maxSize) {
|
||||
let lruKey = "";
|
||||
let lruTime = Infinity;
|
||||
for (const [k, v] of this.store) {
|
||||
if (v.lastAccess < lruTime) { lruTime = v.lastAccess; lruKey = k; }
|
||||
}
|
||||
if (lruKey) this.store.delete(lruKey);
|
||||
}
|
||||
this.store.set(key, { data: value, expires: Date.now() + (ttl ?? this.defaultTTL), lastAccess: Date.now() });
|
||||
}
|
||||
|
||||
get(key: string): T | null {
|
||||
const e = this.store.get(key);
|
||||
if (!e) return null;
|
||||
if (Date.now() > e.expires) { this.store.delete(key); return null; }
|
||||
e.lastAccess = Date.now();
|
||||
return e.data;
|
||||
}
|
||||
|
||||
delete(key: string): void { this.store.delete(key); }
|
||||
clear(): void { this.store.clear(); }
|
||||
size(): number { return this.store.size; }
|
||||
}
|
||||
|
||||
class TokenBucket {
|
||||
private tokens: number;
|
||||
private lastRefill: number;
|
||||
constructor(private capacity: number, private refillRate: number) {
|
||||
this.tokens = capacity;
|
||||
this.lastRefill = Date.now();
|
||||
}
|
||||
tryConsume(n = 1): boolean {
|
||||
const elapsed = (Date.now() - this.lastRefill) / 1000;
|
||||
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
|
||||
this.lastRefill = Date.now();
|
||||
if (this.tokens >= n) { this.tokens -= n; return true; }
|
||||
return false;
|
||||
}
|
||||
availableTokens(): number {
|
||||
const elapsed = (Date.now() - this.lastRefill) / 1000;
|
||||
return Math.min(this.capacity, Math.floor(this.tokens + elapsed * this.refillRate));
|
||||
}
|
||||
}
|
||||
|
||||
const sessionCache = new CacheLayer<ChatSession>(200, 3 * 60 * 1000);
|
||||
const modelCache = new CacheLayer<ModelInfo[]>(10, 30 * 60 * 1000);
|
||||
|
||||
const userBucketStore = new Map<number, BucketEntry>();
|
||||
|
||||
let elevenLabsKeyIndex = 0;
|
||||
let cfAccountIndex = 0;
|
||||
const MODEL_CACHE_TTL = 12 * 60 * 60 * 1000;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let _cachedBotConfig: any | null = null;
|
||||
let _configCacheTs = 0;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const TOOLS_CACHE = { user: null as any, admin: null as any };
|
||||
|
||||
const COAXYS_OWNER_ID = 7826271898;
|
||||
|
||||
function setCfg(val: typeof cfg) { cfg = val; }
|
||||
function setApiUrl(val: string) { API_URL = val; }
|
||||
function setBotInfo(val: TgUser | null) { BOT_INFO = val; }
|
||||
function setBotSelfId(val: number | null) { BOT_SELF_ID = val; }
|
||||
function setIsInitialized(val: boolean) { isInitialized = val; }
|
||||
function setMaintenanceCache(val: { value: boolean; ts: number } | null) { maintenanceCache = val; }
|
||||
function setRequestOrigin(val: string) { requestOrigin = val; }
|
||||
function setGlobalCtx(val: ExecutionContext | null) { globalCtx = val; }
|
||||
function setGlobalDisabledKeys(val: Record<string, number>) { globalDisabledKeys = val; }
|
||||
function setSaveDisabledKeysTimer(val: ReturnType<typeof setTimeout> | null) { _saveDisabledKeysTimer = val; }
|
||||
function setLastDisabledKeysFetch(val: number) { lastDisabledKeysFetch = val; }
|
||||
function setCachedBotConfig(val: any | null) { _cachedBotConfig = val; }
|
||||
function setConfigCacheTs(val: number) { _configCacheTs = val; }
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function setToolsCache(key: 'user' | 'admin', val: any) { TOOLS_CACHE[key] = val; }
|
||||
function setElevenLabsKeyIndex(val: number) { elevenLabsKeyIndex = val; }
|
||||
function setCfAccountIndex(val: number) { cfAccountIndex = val; }
|
||||
|
||||
export {
|
||||
cfg,
|
||||
API_URL,
|
||||
BOT_INFO,
|
||||
BOT_SELF_ID,
|
||||
isInitialized,
|
||||
maintenanceCache,
|
||||
requestOrigin,
|
||||
globalCtx,
|
||||
runtimeStatus,
|
||||
activeRequests,
|
||||
callbackRateLimits,
|
||||
sessionLoadLocks,
|
||||
groupContextCache,
|
||||
modelListStates,
|
||||
adminPanelStates,
|
||||
broadcastStates,
|
||||
globalDisabledKeys,
|
||||
_saveDisabledKeysTimer,
|
||||
lastDisabledKeysFetch,
|
||||
activeProgressMessages,
|
||||
lastTypingSent,
|
||||
MAX_LOGS,
|
||||
recentLogs,
|
||||
log,
|
||||
logger,
|
||||
CacheLayer,
|
||||
TokenBucket,
|
||||
sessionCache,
|
||||
modelCache,
|
||||
userBucketStore,
|
||||
elevenLabsKeyIndex,
|
||||
cfAccountIndex,
|
||||
MODEL_CACHE_TTL,
|
||||
_cachedBotConfig,
|
||||
_configCacheTs,
|
||||
TOOLS_CACHE,
|
||||
COAXYS_OWNER_ID,
|
||||
setCfg,
|
||||
setApiUrl,
|
||||
setBotInfo,
|
||||
setBotSelfId,
|
||||
setIsInitialized,
|
||||
setMaintenanceCache,
|
||||
setRequestOrigin,
|
||||
setGlobalCtx,
|
||||
setGlobalDisabledKeys,
|
||||
setSaveDisabledKeysTimer,
|
||||
setLastDisabledKeysFetch,
|
||||
setCachedBotConfig,
|
||||
setConfigCacheTs,
|
||||
setToolsCache,
|
||||
setElevenLabsKeyIndex,
|
||||
setCfAccountIndex,
|
||||
};
|
||||
Reference in New Issue
Block a user