148 lines
No EOL
4.1 KiB
TypeScript
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;
|
|
}
|
|
} |