'use strict'; import type { CallbackStyleFunction, Persona, UnknownRecord } from './types'; import type { IncomingMessage } from 'node:http'; import { errorMessage, isRecord } from './types'; const winston = require('winston'); const { createAuthStore } = require('./auth/store'); const { createSessionManager } = require('./auth/session'); const { loadConfig } = require('./config/load'); const { AUTH_DB_PATH, REFRESH_TOKEN_PATH } = require('./paths'); const { getClientIp } = require('./server/auth'); const { createChatService } = require('./server/chat-service'); const { createSteamLoginService, readRefreshToken } = require('./steam/lifecycle'); const { steamIdToString } = require('./storage/chat-log'); const { createSteamMessageLogger } = require('./steam/message-logger'); type PersonaResponse = UnknownRecord & { personas?: Persona[]; users?: Record; }; type SteamUserMain = { users?: Record; steamID?: unknown; getPersonas?: (ids: string[], callback: (error: unknown, response: PersonaResponse) => void) => void; chat?: { sendFriendMessage?: CallbackStyleFunction; getEmoticonList?: CallbackStyleFunction; }; sendFriendMessage?: CallbackStyleFunction; getEmoticonList?: CallbackStyleFunction; myFriends?: UnknownRecord; myGroups?: unknown; groups?: unknown; on: (event: string, listener: (...args: unknown[]) => void) => void; off?: (event: string, listener: (...args: unknown[]) => void) => void; logOn: (options: unknown) => void; webLogOn: () => void; setPersona?: (state: number) => void; EPersonaState?: { Online?: number }; logOff?: () => void; }; type SteamCommunityMain = { setCookies?: (cookies: string[]) => void; startConfirmationChecker?: (intervalMs: number, identitySecret: string) => void; sendImageToUser?: CallbackStyleFunction; }; type SteamUserConstructor = new (options: { renewRefreshTokens: boolean }) => SteamUserMain; type SteamCommunityConstructor = new () => SteamCommunityMain; type ChatServiceRuntime = { start: () => unknown; }; type LogInfo = { timestamp?: string; level: string; message: string; stack?: string; }; const SteamCommunity = require('steamcommunity') as SteamCommunityConstructor; const SteamUser = require('steam-user') as SteamUserConstructor; const config = loadConfig(); const steamUser = new SteamUser({ renewRefreshTokens: true }); const steamCommunity = new SteamCommunity(); const users: Record = {}; const authStore = createAuthStore({ dbPath: AUTH_DB_PATH }); const chatAuthConfig = isRecord(config.chat) && isRecord(config.chat.auth) ? config.chat.auth : {}; const sessionManager = createSessionManager({ store: authStore, getClientIp: (req: IncomingMessage) => getClientIp(req, chatAuthConfig) }); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.printf((info: LogInfo) => `${info.timestamp} ${info.level}: ${info.message}${info.stack ? `\n${info.stack}` : ''}`) ), transports: [ new winston.transports.Console() ] }); function getPersonaFromCache(id: string) { return users[id] || steamUser.users?.[id] || null; } function personaFromUnknown(value: unknown): Persona | null { return isRecord(value) ? value : null; } async function getUserInfo(value: unknown): Promise { const id = steamIdToString(value); if (!id) return { player_name: 'Unknown' }; const cached = getPersonaFromCache(id); if (cached) return cached; if (typeof steamUser.getPersonas !== 'function') { return { player_name: 'Unknown' }; } try { const personas = await new Promise((resolve, reject) => { steamUser.getPersonas?.([id], (error: unknown, response: PersonaResponse) => { if (error) reject(error); else resolve(response); }); }); const persona = personas.personas?.[0] || personaFromUnknown(personas[id]) || personas.users?.[id] || null; if (persona) { users[id] = persona; return persona; } } catch (error) { logger.warn('Steam persona lookup failed', { id, error: errorMessage(error) }); } return { player_name: 'Unknown' }; } async function getSelfName() { const selfId = steamIdToString(steamUser.steamID || config.steamID); if (!selfId) return 'Me'; const info = await getUserInfo(selfId); return info.player_name || info.personaName || 'Me'; } const lifecycle = createSteamLoginService({ steamUser, steamCommunity, config: { steamID: typeof config.steamID === 'string' ? config.steamID : undefined, identitySecret: typeof config.identitySecret === 'string' ? config.identitySecret : undefined }, logger, refreshTokenPath: REFRESH_TOKEN_PATH, getDefaultLogonID: authStore.getOrCreateSteamLogonID, onLoggedOn(steamId: string | null) { if (!steamId) return; authStore.upsertSteamAccount({ steamId, refreshToken: readRefreshToken(REFRESH_TOKEN_PATH) || undefined, setActive: true }); }, onRefreshToken(refreshToken: string, steamId: string | null) { if (!steamId) return; authStore.upsertSteamAccount({ steamId, refreshToken, setActive: true }); } }); createSteamMessageLogger({ steamUser, getUserInfo, getSelfName, getSteamAccountId: () => authStore.getActiveSteamAccount(false)?.steamId || steamIdToString(steamUser.steamID || ''), logger }); let steamLoginPromise = lifecycle.waitForLogin(); let steamWebLoginPromise = lifecycle.waitForWebSession(); let chatService: ChatServiceRuntime | null = null; function start() { const tokenLoginPromise = lifecycle.start().catch((error: unknown) => { logger.warn('Steam token login failed', { error: errorMessage(error) }); return false; }); steamLoginPromise = lifecycle.waitForLogin(); steamWebLoginPromise = lifecycle.waitForWebSession(); chatService = createChatService({ config, steamUser, steamCommunity, logger, getUserInfo, getSelfName, waitForLogin: () => lifecycle.waitForLogin(), waitForWebSession: () => lifecycle.waitForWebSession(), refreshWebSession: lifecycle.refreshWebSession, authStore, sessionManager, steamLoginService: lifecycle }); chatService.start(); return tokenLoginPromise; } if (process.env.STEAM_CHAT_DISABLE_AUTOSTART !== '1') { start().catch((error: unknown) => { logger.error('Steam startup failed', { error: errorMessage(error) }); }); } module.exports = { authStore, chatService, config, getSelfName, getUserInfo, lifecycle, logger, sessionManager, start, steamCommunity, steamLoginPromise, steamUser, steamWebLoginPromise, users };