78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
import axios from 'axios';
|
|
import * as cheerio from 'cheerio';
|
|
|
|
export interface CooldownData {
|
|
isActive: boolean;
|
|
expiresAt?: Date;
|
|
}
|
|
|
|
// Custom error to identify session death
|
|
export class SteamAuthError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = "SteamAuthError";
|
|
}
|
|
}
|
|
|
|
export const scrapeCooldown = async (steamId: string, steamLoginSecure: string): Promise<CooldownData> => {
|
|
const url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`;
|
|
|
|
try {
|
|
const response = await axios.get(url, {
|
|
headers: {
|
|
'Cookie': steamLoginSecure,
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
|
},
|
|
timeout: 10000,
|
|
validateStatus: (status) => status < 500 // Allow redirects to handle them manually
|
|
});
|
|
|
|
// If Steam redirects us to the login page, the cookie is dead
|
|
if (response.data.includes('Sign In') || response.request.path.includes('/login')) {
|
|
throw new SteamAuthError('Invalid or expired steamLoginSecure cookie');
|
|
}
|
|
|
|
const $ = cheerio.load(response.data);
|
|
|
|
if (!response.data.includes('Personal Game Data')) {
|
|
throw new SteamAuthError('Session invalid: Personal Game Data not accessible');
|
|
}
|
|
|
|
let expirationDate: Date | undefined = undefined;
|
|
|
|
$('table').each((_, table) => {
|
|
const headers = $(table).find('th').map((_, th) => $(th).text().trim()).get();
|
|
const expirationIndex = headers.findIndex(h => h.includes('Competitive Cooldown Expiration') || h.includes('Cooldown Expiration'));
|
|
|
|
if (expirationIndex !== -1) {
|
|
const rows = $(table).find('tr').not(':has(th)');
|
|
rows.each((_, row) => {
|
|
const dateText = $(row).find('td').eq(expirationIndex).text().trim();
|
|
if (dateText && dateText !== '') {
|
|
const cleanDateText = dateText.replace(' GMT', ' UTC');
|
|
const parsed = new Date(cleanDateText);
|
|
if (!isNaN(parsed.getTime())) {
|
|
if (!expirationDate || parsed > (expirationDate as Date)) expirationDate = parsed;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
if (expirationDate && (expirationDate as Date).getTime() > Date.now()) {
|
|
return { isActive: true, expiresAt: expirationDate };
|
|
}
|
|
|
|
const content = $('#personal_game_data_content').text();
|
|
if (content.includes('Competitive Cooldown') || content.includes('Your account is currently')) {
|
|
return { isActive: true };
|
|
}
|
|
|
|
return { isActive: false };
|
|
} catch (error: any) {
|
|
if (error instanceof SteamAuthError) throw error;
|
|
console.error(`[Scraper] Network/Internal Error for ${steamId}:`, error.message);
|
|
throw error; // Generic errors don't trigger re-auth
|
|
}
|
|
};
|