7 Commits

Author SHA1 Message Date
2124845848 Merge pull request 'release/v1.3.3' (#10) from release/v1.3.3 into main
Some checks failed
Build and Release / build (push) Failing after 5m26s
Reviewed-on: #10
2026-02-21 04:50:07 +01:00
eca3a728fc Merge pull request 'release/v1.3.2' (#9) from release/v1.3.2 into main
All checks were successful
Build and Release / build (push) Successful in 5m33s
Reviewed-on: #9
2026-02-21 04:39:16 +01:00
60b3dd1ca1 Merge pull request 'release/v1.3.1' (#8) from release/v1.3.1 into main
Some checks failed
Build and Release / build (push) Failing after 5m36s
Reviewed-on: #8
2026-02-21 04:25:16 +01:00
589acdebcb Merge pull request 'chore: bump version to 1.3.0' (#7) from release/v1.3.0 into main
All checks were successful
Build and Release / build (push) Successful in 5m39s
Reviewed-on: #7
2026-02-21 03:37:38 +01:00
4037d7bce3 Merge pull request 'chore: update application title in index.html from frontend to Ultimate Ban Tracker' (#6) from release/v1.3.0 into main
Some checks failed
Build and Release / build (push) Has been cancelled
Reviewed-on: #6
2026-02-21 03:36:37 +01:00
5d611fd8be Merge pull request 'feat: implement comprehensive admin dashboard for server management and user oversight' (#5) from release/v1.3.0 into main
Some checks failed
Build and Release / build (push) Has been cancelled
Reviewed-on: #5
2026-02-21 03:35:22 +01:00
88d2a2133c Merge pull request 'chore: bump version to 1.2.0 and commit recent fixes/features including tray and auth isolation' (#4) from release/v1.2.0 into main
All checks were successful
Build and Release / build (push) Successful in 5m37s
Reviewed-on: #4
2026-02-21 03:21:45 +01:00
7 changed files with 86 additions and 112 deletions

View File

@@ -219,13 +219,9 @@ const scrapeAccountData = async (account) => {
} }
} }
catch (e) { catch (e) {
if (e instanceof scraper_1.SteamAuthError) { if (e.message.includes('cookie') || e.message.includes('Sign In'))
account.authError = true; account.authError = true;
} }
else {
console.error(`[Scraper] Temporary error for ${account.personaName}: ${e.message}`);
}
}
} }
if (backend && !account._id.startsWith('shared_')) { if (backend && !account._id.startsWith('shared_')) {
await backend.shareAccount(account); await backend.shareAccount(account);
@@ -263,16 +259,7 @@ const syncAccounts = async (isManual = false) => {
else { else {
const sDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0); const sDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0);
const lDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0); const lDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0);
// 1. SENSITIVE DATA SYNC (Credentials) if (sDate > lDate) {
const sSessionDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0);
const lSessionDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0);
const isLocalAccount = !exists._id.startsWith('shared_');
const isLocalSessionHealthy = exists.steamLoginSecure && !exists.authError;
// SMART OVERWRITE LOGIC:
// - If it's a remote shared account: Newest wins.
// - If it's a LOCAL account: Only overwrite if our local session is broken/missing.
const shouldOverwriteCredentials = !isLocalAccount ? (sSessionDate > lSessionDate) : (!isLocalSessionHealthy && sSessionDate > lSessionDate);
if (shouldOverwriteCredentials) {
if (s.loginName) if (s.loginName)
exists.loginName = s.loginName; exists.loginName = s.loginName;
if (s.loginConfig) if (s.loginConfig)
@@ -285,7 +272,7 @@ const syncAccounts = async (isManual = false) => {
exists.sessionUpdatedAt = s.sessionUpdatedAt; exists.sessionUpdatedAt = s.sessionUpdatedAt;
hasChanges = true; hasChanges = true;
} }
// 2. Metadata Sync (Pull) - Always "Newest Wins" // Metadata Sync (Pull)
const sMetaDate = s.lastMetadataCheck ? new Date(s.lastMetadataCheck) : new Date(0); const sMetaDate = s.lastMetadataCheck ? new Date(s.lastMetadataCheck) : new Date(0);
const lMetaDate = exists.lastBanCheck ? new Date(exists.lastBanCheck) : new Date(0); const lMetaDate = exists.lastBanCheck ? new Date(exists.lastBanCheck) : new Date(0);
if (sMetaDate > lMetaDate) { if (sMetaDate > lMetaDate) {

View File

@@ -36,17 +36,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.scrapeCooldown = exports.SteamAuthError = void 0; exports.scrapeCooldown = void 0;
const axios_1 = __importDefault(require("axios")); const axios_1 = __importDefault(require("axios"));
const cheerio = __importStar(require("cheerio")); const cheerio = __importStar(require("cheerio"));
// Custom error to identify session death
class SteamAuthError extends Error {
constructor(message) {
super(message);
this.name = "SteamAuthError";
}
}
exports.SteamAuthError = SteamAuthError;
const scrapeCooldown = async (steamId, steamLoginSecure) => { const scrapeCooldown = async (steamId, steamLoginSecure) => {
const url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`; const url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`;
try { try {
@@ -55,17 +47,13 @@ const scrapeCooldown = async (steamId, steamLoginSecure) => {
'Cookie': steamLoginSecure, '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' '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, 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); const $ = cheerio.load(response.data);
if (!response.data.includes('Personal Game Data')) { if (response.data.includes('Sign In') || !response.data.includes('Personal Game Data')) {
throw new SteamAuthError('Session invalid: Personal Game Data not accessible'); throw new Error('Invalid or expired steamLoginSecure cookie');
} }
// 1. Locate the specific table containing cooldown info
let expirationDate = undefined; let expirationDate = undefined;
$('table').each((_, table) => { $('table').each((_, table) => {
const headers = $(table).find('th').map((_, th) => $(th).text().trim()).get(); const headers = $(table).find('th').map((_, th) => $(th).text().trim()).get();
@@ -75,18 +63,25 @@ const scrapeCooldown = async (steamId, steamLoginSecure) => {
rows.each((_, row) => { rows.each((_, row) => {
const dateText = $(row).find('td').eq(expirationIndex).text().trim(); const dateText = $(row).find('td').eq(expirationIndex).text().trim();
if (dateText && dateText !== '') { if (dateText && dateText !== '') {
// Steam uses 'GMT' which some JS engines don't parse well, replace with 'UTC'
const cleanDateText = dateText.replace(' GMT', ' UTC'); const cleanDateText = dateText.replace(' GMT', ' UTC');
const parsed = new Date(cleanDateText); const parsed = new Date(cleanDateText);
if (!isNaN(parsed.getTime())) { if (!isNaN(parsed.getTime())) {
if (!expirationDate || parsed > expirationDate) // We want the newest expiration date found
if (!expirationDate || parsed > expirationDate) {
expirationDate = parsed; expirationDate = parsed;
} }
} }
}
}); });
} }
}); });
if (expirationDate && expirationDate.getTime() > Date.now()) { if (expirationDate && expirationDate.getTime() > Date.now()) {
return { isActive: true, expiresAt: expirationDate }; console.log(`[Scraper] Found active cooldown until: ${expirationDate.toISOString()}`);
return {
isActive: true,
expiresAt: expirationDate
};
} }
const content = $('#personal_game_data_content').text(); const content = $('#personal_game_data_content').text();
if (content.includes('Competitive Cooldown') || content.includes('Your account is currently')) { if (content.includes('Competitive Cooldown') || content.includes('Your account is currently')) {
@@ -95,10 +90,8 @@ const scrapeCooldown = async (steamId, steamLoginSecure) => {
return { isActive: false }; return { isActive: false };
} }
catch (error) { catch (error) {
if (error instanceof SteamAuthError) console.error(`[Scraper] Error for ${steamId}:`, error.message);
throw error; throw error;
console.error(`[Scraper] Network/Internal Error for ${steamId}:`, error.message);
throw error; // Generic errors don't trigger re-auth
} }
}; };
exports.scrapeCooldown = scrapeCooldown; exports.scrapeCooldown = scrapeCooldown;

View File

@@ -49,6 +49,10 @@ class SteamClientService {
return null; return null;
return path_1.default.join(this.steamPath, 'config', 'config.vdf'); return path_1.default.join(this.steamPath, 'config', 'config.vdf');
} }
/**
* Safe Atomic Write: Writes to a temp file and renames it.
* This prevents file corruption if the app crashes during write.
*/
safeWriteVdf(filePath, data) { safeWriteVdf(filePath, data) {
const tempPath = `${filePath}.tmp_${Date.now()}`; const tempPath = `${filePath}.tmp_${Date.now()}`;
const dir = path_1.default.dirname(filePath); const dir = path_1.default.dirname(filePath);
@@ -57,6 +61,7 @@ class SteamClientService {
fs_1.default.mkdirSync(dir, { recursive: true }); fs_1.default.mkdirSync(dir, { recursive: true });
const vdfContent = (0, simple_vdf_1.stringify)(data); const vdfContent = (0, simple_vdf_1.stringify)(data);
fs_1.default.writeFileSync(tempPath, vdfContent, 'utf-8'); fs_1.default.writeFileSync(tempPath, vdfContent, 'utf-8');
// Atomic rename
fs_1.default.renameSync(tempPath, filePath); fs_1.default.renameSync(tempPath, filePath);
} }
catch (e) { catch (e) {
@@ -72,6 +77,7 @@ class SteamClientService {
if (loginUsersPath && fs_1.default.existsSync(loginUsersPath)) { if (loginUsersPath && fs_1.default.existsSync(loginUsersPath)) {
this.readLocalAccounts(); this.readLocalAccounts();
chokidar_1.default.watch(loginUsersPath, { persistent: true, ignoreInitial: true }).on('change', () => { chokidar_1.default.watch(loginUsersPath, { persistent: true, ignoreInitial: true }).on('change', () => {
console.log(`[SteamClient] loginusers.vdf changed, re-scanning...`);
this.readLocalAccounts(); this.readLocalAccounts();
}); });
} }
@@ -83,7 +89,7 @@ class SteamClientService {
try { try {
const content = fs_1.default.readFileSync(filePath, 'utf-8'); const content = fs_1.default.readFileSync(filePath, 'utf-8');
if (!content.trim()) if (!content.trim())
return; return; // Empty file
const data = (0, simple_vdf_1.parse)(content); const data = (0, simple_vdf_1.parse)(content);
if (!data || !data.users) if (!data || !data.users)
return; return;
@@ -93,7 +99,8 @@ class SteamClientService {
if (!user || !user.AccountName) if (!user || !user.AccountName)
continue; continue;
accounts.push({ accounts.push({
steamId: steamId64, accountName: user.AccountName, steamId: steamId64,
accountName: user.AccountName,
personaName: user.PersonaName || user.AccountName, personaName: user.PersonaName || user.AccountName,
timestamp: parseInt(user.Timestamp) || 0 timestamp: parseInt(user.Timestamp) || 0
}); });
@@ -101,7 +108,9 @@ class SteamClientService {
if (this.onAccountsChanged) if (this.onAccountsChanged)
this.onAccountsChanged(accounts); this.onAccountsChanged(accounts);
} }
catch (error) { } catch (error) {
console.error('[SteamClient] Error parsing loginusers.vdf:', error);
}
} }
extractAccountConfig(accountName) { extractAccountConfig(accountName) {
const configPath = this.getConfigVdfPath(); const configPath = this.getConfigVdfPath();
@@ -114,6 +123,7 @@ class SteamClientService {
return (accounts && accounts[accountName]) ? accounts[accountName] : null; return (accounts && accounts[accountName]) ? accounts[accountName] : null;
} }
catch (e) { catch (e) {
console.error('[SteamClient] Failed to extract config.vdf data');
return null; return null;
} }
} }
@@ -121,7 +131,17 @@ class SteamClientService {
const configPath = this.getConfigVdfPath(); const configPath = this.getConfigVdfPath();
if (!configPath) if (!configPath)
return; return;
let data = { InstallConfigStore: { Software: { Valve: { Steam: { Accounts: {} } } } } }; let data = {
InstallConfigStore: {
Software: {
Valve: {
Steam: {
Accounts: {}
}
}
}
}
};
if (fs_1.default.existsSync(configPath)) { if (fs_1.default.existsSync(configPath)) {
try { try {
const content = fs_1.default.readFileSync(configPath, 'utf-8'); const content = fs_1.default.readFileSync(configPath, 'utf-8');
@@ -131,23 +151,18 @@ class SteamClientService {
} }
catch (e) { } catch (e) { }
} }
const ensurePath = (obj, keys) => { // Ensure safe nesting
let curr = obj; if (!data.InstallConfigStore)
for (const key of keys) { data.InstallConfigStore = {};
if (!curr[key] || typeof curr[key] !== 'object') if (!data.InstallConfigStore.Software)
curr[key] = {}; data.InstallConfigStore.Software = {};
curr = curr[key]; if (!data.InstallConfigStore.Software.Valve)
} data.InstallConfigStore.Software.Valve = {};
return curr; if (!data.InstallConfigStore.Software.Valve.Steam)
}; data.InstallConfigStore.Software.Valve.Steam = {};
const steamAccounts = ensurePath(data, ['InstallConfigStore', 'Software', 'Valve', 'Steam', 'Accounts']); if (!data.InstallConfigStore.Software.Valve.Steam.Accounts)
// FAILPROOF: Force crucial flags that Steam uses to decide session validity data.InstallConfigStore.Software.Valve.Steam.Accounts = {};
steamAccounts[accountName] = { data.InstallConfigStore.Software.Valve.Steam.Accounts[accountName] = accountData;
...accountData,
RememberPassword: "1",
AllowAutoLogin: "1",
Timestamp: Math.floor(Date.now() / 1000).toString()
};
try { try {
this.safeWriteVdf(configPath, data); this.safeWriteVdf(configPath, data);
console.log(`[SteamClient] Safely injected session for ${accountName}`); console.log(`[SteamClient] Safely injected session for ${accountName}`);
@@ -205,7 +220,6 @@ class SteamClientService {
} }
catch (e) { } catch (e) { }
} }
// Injection of the actual authentication blob
if (accountConfig && accountName) { if (accountConfig && accountName) {
this.injectAccountConfig(accountName, accountConfig); this.injectAccountConfig(accountName, accountConfig);
} }
@@ -218,7 +232,11 @@ class SteamClientService {
for (const regPath of regLocations) { for (const regPath of regLocations) {
if (!fs_1.default.existsSync(path_1.default.dirname(regPath))) if (!fs_1.default.existsSync(path_1.default.dirname(regPath)))
continue; continue;
let regData = { Registry: { HKCU: { Software: { Valve: { Steam: { AutoLoginUser: "", RememberPassword: "1", AlreadyLoggedIn: "1" } } } } } }; let regData = { Registry: { HKCU: { Software: { Valve: { Steam: {
AutoLoginUser: "",
RememberPassword: "1",
AlreadyLoggedIn: "1"
} } } } } };
if (fs_1.default.existsSync(regPath)) { if (fs_1.default.existsSync(regPath)) {
try { try {
const content = fs_1.default.readFileSync(regPath, 'utf-8'); const content = fs_1.default.readFileSync(regPath, 'utf-8');
@@ -228,6 +246,7 @@ class SteamClientService {
} }
catch (e) { } catch (e) { }
} }
// Deep merge helper
const ensurePath = (obj, keys) => { const ensurePath = (obj, keys) => {
let curr = obj; let curr = obj;
for (const key of keys) { for (const key of keys) {

View File

@@ -7,7 +7,7 @@ import axios from 'axios';
import fs from 'fs'; import fs from 'fs';
import { pathToFileURL } from 'url'; import { pathToFileURL } from 'url';
import { fetchProfileData, scrapeBanStatus } from './services/steam-web'; import { fetchProfileData, scrapeBanStatus } from './services/steam-web';
import { scrapeCooldown, SteamAuthError } from './services/scraper'; import { scrapeCooldown } from './services/scraper';
import { steamClient, LocalSteamAccount } from './services/steam-client'; import { steamClient, LocalSteamAccount } from './services/steam-client';
import { BackendService } from './services/backend'; import { BackendService } from './services/backend';
@@ -247,11 +247,7 @@ const scrapeAccountData = async (account: Account) => {
if (backend) await backend.pushCooldown(account.steamId, undefined, now.toISOString()); if (backend) await backend.pushCooldown(account.steamId, undefined, now.toISOString());
} }
} catch (e: any) { } catch (e: any) {
if (e instanceof SteamAuthError) { if (e.message.includes('cookie') || e.message.includes('Sign In')) account.authError = true;
account.authError = true;
} else {
console.error(`[Scraper] Temporary error for ${account.personaName}: ${e.message}`);
}
} }
} }
if (backend && !account._id.startsWith('shared_')) { if (backend && !account._id.startsWith('shared_')) {
@@ -290,31 +286,15 @@ const syncAccounts = async (isManual = false) => {
} else { } else {
const sDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0); const sDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0);
const lDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0); const lDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0);
// 1. SENSITIVE DATA SYNC (Credentials) if (sDate > lDate) {
const sSessionDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0);
const lSessionDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0);
const isLocalAccount = !exists._id.startsWith('shared_');
const isLocalSessionHealthy = exists.steamLoginSecure && !exists.authError;
// SMART OVERWRITE LOGIC:
// - If it's a remote shared account: Newest wins.
// - If it's a LOCAL account: Only overwrite if our local session is broken/missing.
const shouldOverwriteCredentials = !isLocalAccount ? (sSessionDate > lSessionDate) : (!isLocalSessionHealthy && sSessionDate > lSessionDate);
if (shouldOverwriteCredentials) {
if (s.loginName) exists.loginName = s.loginName; if (s.loginName) exists.loginName = s.loginName;
if (s.loginConfig) exists.loginConfig = s.loginConfig; if (s.loginConfig) exists.loginConfig = s.loginConfig;
if (s.steamLoginSecure) { if (s.steamLoginSecure) { exists.steamLoginSecure = s.steamLoginSecure; exists.autoCheckCooldown = true; exists.authError = false; }
exists.steamLoginSecure = s.steamLoginSecure;
exists.autoCheckCooldown = true;
exists.authError = false;
}
exists.sessionUpdatedAt = s.sessionUpdatedAt; exists.sessionUpdatedAt = s.sessionUpdatedAt;
hasChanges = true; hasChanges = true;
} }
// 2. Metadata Sync (Pull) - Always "Newest Wins" // Metadata Sync (Pull)
const sMetaDate = s.lastMetadataCheck ? new Date(s.lastMetadataCheck) : new Date(0); const sMetaDate = s.lastMetadataCheck ? new Date(s.lastMetadataCheck) : new Date(0);
const lMetaDate = exists.lastBanCheck ? new Date(exists.lastBanCheck) : new Date(0); const lMetaDate = exists.lastBanCheck ? new Date(exists.lastBanCheck) : new Date(0);
if (sMetaDate > lMetaDate) { if (sMetaDate > lMetaDate) {

View File

@@ -6,14 +6,6 @@ export interface CooldownData {
expiresAt?: Date; 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> => { export const scrapeCooldown = async (steamId: string, steamLoginSecure: string): Promise<CooldownData> => {
const url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`; const url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`;
@@ -23,21 +15,16 @@ export const scrapeCooldown = async (steamId: string, steamLoginSecure: string):
'Cookie': steamLoginSecure, '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' '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, 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); const $ = cheerio.load(response.data);
if (!response.data.includes('Personal Game Data')) { if (response.data.includes('Sign In') || !response.data.includes('Personal Game Data')) {
throw new SteamAuthError('Session invalid: Personal Game Data not accessible'); throw new Error('Invalid or expired steamLoginSecure cookie');
} }
// 1. Locate the specific table containing cooldown info
let expirationDate: Date | undefined = undefined; let expirationDate: Date | undefined = undefined;
$('table').each((_, table) => { $('table').each((_, table) => {
@@ -49,10 +36,15 @@ export const scrapeCooldown = async (steamId: string, steamLoginSecure: string):
rows.each((_, row) => { rows.each((_, row) => {
const dateText = $(row).find('td').eq(expirationIndex).text().trim(); const dateText = $(row).find('td').eq(expirationIndex).text().trim();
if (dateText && dateText !== '') { if (dateText && dateText !== '') {
// Steam uses 'GMT' which some JS engines don't parse well, replace with 'UTC'
const cleanDateText = dateText.replace(' GMT', ' UTC'); const cleanDateText = dateText.replace(' GMT', ' UTC');
const parsed = new Date(cleanDateText); const parsed = new Date(cleanDateText);
if (!isNaN(parsed.getTime())) { if (!isNaN(parsed.getTime())) {
if (!expirationDate || parsed > (expirationDate as Date)) expirationDate = parsed; // We want the newest expiration date found
if (!expirationDate || parsed > (expirationDate as Date)) {
expirationDate = parsed;
}
} }
} }
}); });
@@ -60,7 +52,11 @@ export const scrapeCooldown = async (steamId: string, steamLoginSecure: string):
}); });
if (expirationDate && (expirationDate as Date).getTime() > Date.now()) { if (expirationDate && (expirationDate as Date).getTime() > Date.now()) {
return { isActive: true, expiresAt: expirationDate }; console.log(`[Scraper] Found active cooldown until: ${(expirationDate as Date).toISOString()}`);
return {
isActive: true,
expiresAt: expirationDate
};
} }
const content = $('#personal_game_data_content').text(); const content = $('#personal_game_data_content').text();
@@ -70,8 +66,7 @@ export const scrapeCooldown = async (steamId: string, steamLoginSecure: string):
return { isActive: false }; return { isActive: false };
} catch (error: any) { } catch (error: any) {
if (error instanceof SteamAuthError) throw error; console.error(`[Scraper] Error for ${steamId}:`, error.message);
console.error(`[Scraper] Network/Internal Error for ${steamId}:`, error.message); throw error;
throw error; // Generic errors don't trigger re-auth
} }
}; };

View File

@@ -1,12 +1,12 @@
{ {
"name": "ultimate-ban-tracker-desktop", "name": "ultimate-ban-tracker-desktop",
"version": "1.3.3", "version": "1.3.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ultimate-ban-tracker-desktop", "name": "ultimate-ban-tracker-desktop",
"version": "1.3.3", "version": "1.3.2",
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
"dependencies": { "dependencies": {
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",

View File

@@ -1,7 +1,7 @@
{ {
"name": "ultimate-ban-tracker-desktop", "name": "ultimate-ban-tracker-desktop",
"description": "Professional Steam Account Manager & Ban Tracker", "description": "Professional Steam Account Manager & Ban Tracker",
"version": "1.3.3", "version": "1.3.2",
"author": "Nils Pukropp <nils@narl.io>", "author": "Nils Pukropp <nils@narl.io>",
"homepage": "https://narl.io", "homepage": "https://narl.io",
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",