stock-bot/apps/data-ingestion/src/handlers/qm/shared/session-manager.ts

148 lines
No EOL
4.1 KiB
TypeScript

/**
* QM Session Manager - Centralized session state management
*/
import { getRandomUserAgent } from '@stock-bot/utils';
import { QM_SESSION_IDS, SESSION_CONFIG } from './config';
import type { QMSession } from './types';
export class QMSessionManager {
private static instance: QMSessionManager | null = null;
private sessionCache: Record<string, QMSession[]> = {};
private isInitialized = false;
private constructor() {
// Initialize session cache with known session IDs
Object.values(QM_SESSION_IDS).forEach(sessionId => {
this.sessionCache[sessionId] = [];
});
}
static getInstance(): QMSessionManager {
if (!QMSessionManager.instance) {
QMSessionManager.instance = new QMSessionManager();
}
return QMSessionManager.instance;
}
/**
* Get a random session for the given session ID
*/
getSession(sessionId: string): QMSession | null {
const sessions = this.sessionCache[sessionId];
if (!sessions || sessions.length === 0) {
return null;
}
// Filter out sessions with excessive failures
const validSessions = sessions.filter(session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS);
if (validSessions.length === 0) {
return null;
}
return validSessions[Math.floor(Math.random() * validSessions.length)];
}
/**
* Add a session to the cache
*/
addSession(sessionId: string, session: QMSession): void {
if (!this.sessionCache[sessionId]) {
this.sessionCache[sessionId] = [];
}
this.sessionCache[sessionId].push(session);
}
/**
* Get all sessions for a session ID
*/
getSessions(sessionId: string): QMSession[] {
return this.sessionCache[sessionId] || [];
}
/**
* Get session count for all session IDs
*/
getSessionCount(): number {
return Object.values(this.sessionCache).reduce((total, sessions) => total + sessions.length, 0);
}
/**
* Clean up failed sessions
*/
cleanupFailedSessions(): number {
let removedCount = 0;
Object.keys(this.sessionCache).forEach(sessionId => {
const initialCount = this.sessionCache[sessionId].length;
this.sessionCache[sessionId] = this.sessionCache[sessionId].filter(
session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS
);
removedCount += initialCount - this.sessionCache[sessionId].length;
});
return removedCount;
}
getQmHeaders(): Record<string, string> {
return {
'User-Agent': getRandomUserAgent(),
Accept: '*/*',
'Accept-Language': 'en',
'Sec-Fetch-Mode': 'cors',
Origin: 'https://www.quotemedia.com',
Referer: 'https://www.quotemedia.com/',
};
}
/**
* Check if more sessions are needed for a session ID
*/
needsMoreSessions(sessionId: string): boolean {
const sessions = this.sessionCache[sessionId] || [];
const validSessions = sessions.filter(session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS);
return validSessions.length < SESSION_CONFIG.MIN_SESSIONS;
}
/**
* Check if session ID is at capacity
*/
isAtCapacity(sessionId: string): boolean {
const sessions = this.sessionCache[sessionId] || [];
return sessions.length >= SESSION_CONFIG.MAX_SESSIONS;
}
/**
* Get session cache statistics
*/
getStats() {
const stats: Record<string, { total: number; valid: number; failed: number }> = {};
Object.entries(this.sessionCache).forEach(([sessionId, sessions]) => {
const validSessions = sessions.filter(session => session.failedCalls <= SESSION_CONFIG.MAX_FAILED_CALLS);
const failedSessions = sessions.filter(session => session.failedCalls > SESSION_CONFIG.MAX_FAILED_CALLS);
stats[sessionId] = {
total: sessions.length,
valid: validSessions.length,
failed: failedSessions.length
};
});
return stats;
}
/**
* Mark manager as initialized
*/
setInitialized(initialized: boolean = true): void {
this.isInitialized = initialized;
}
/**
* Check if manager is initialized
*/
getInitialized(): boolean {
return this.isInitialized;
}
}