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:
arsam
2026-09-18 23:14:56 +03:30
parent 28148fc681
commit 284e60c4cc
+218
View File
@@ -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,
};