chore: bump version to 1.3.3
This commit is contained in:
@@ -219,8 +219,12 @@ const scrapeAccountData = async (account) => {
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e.message.includes('cookie') || e.message.includes('Sign In'))
|
||||
if (e instanceof scraper_1.SteamAuthError) {
|
||||
account.authError = true;
|
||||
}
|
||||
else {
|
||||
console.error(`[Scraper] Temporary error for ${account.personaName}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (backend && !account._id.startsWith('shared_')) {
|
||||
@@ -259,7 +263,16 @@ const syncAccounts = async (isManual = false) => {
|
||||
else {
|
||||
const sDate = s.sessionUpdatedAt ? new Date(s.sessionUpdatedAt) : new Date(0);
|
||||
const lDate = exists.sessionUpdatedAt ? new Date(exists.sessionUpdatedAt) : new Date(0);
|
||||
if (sDate > lDate) {
|
||||
// 1. SENSITIVE DATA SYNC (Credentials)
|
||||
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.loginConfig)
|
||||
@@ -272,7 +285,7 @@ const syncAccounts = async (isManual = false) => {
|
||||
exists.sessionUpdatedAt = s.sessionUpdatedAt;
|
||||
hasChanges = true;
|
||||
}
|
||||
// Metadata Sync (Pull)
|
||||
// 2. Metadata Sync (Pull) - Always "Newest Wins"
|
||||
const sMetaDate = s.lastMetadataCheck ? new Date(s.lastMetadataCheck) : new Date(0);
|
||||
const lMetaDate = exists.lastBanCheck ? new Date(exists.lastBanCheck) : new Date(0);
|
||||
if (sMetaDate > lMetaDate) {
|
||||
|
||||
@@ -36,9 +36,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.scrapeCooldown = void 0;
|
||||
exports.scrapeCooldown = exports.SteamAuthError = void 0;
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
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 url = `https://steamcommunity.com/profiles/${steamId}/gcpd/730?tab=matchmaking`;
|
||||
try {
|
||||
@@ -47,13 +55,17 @@ const scrapeCooldown = async (steamId, 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'
|
||||
},
|
||||
timeout: 10000
|
||||
timeout: 10000,
|
||||
validateStatus: (status) => status < 500 // Allow redirects to handle them manually
|
||||
});
|
||||
const $ = cheerio.load(response.data);
|
||||
if (response.data.includes('Sign In') || !response.data.includes('Personal Game Data')) {
|
||||
throw new Error('Invalid or expired steamLoginSecure cookie');
|
||||
// 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');
|
||||
}
|
||||
// 1. Locate the specific table containing cooldown info
|
||||
let expirationDate = undefined;
|
||||
$('table').each((_, table) => {
|
||||
const headers = $(table).find('th').map((_, th) => $(th).text().trim()).get();
|
||||
@@ -63,25 +75,18 @@ const scrapeCooldown = async (steamId, steamLoginSecure) => {
|
||||
rows.each((_, row) => {
|
||||
const dateText = $(row).find('td').eq(expirationIndex).text().trim();
|
||||
if (dateText && dateText !== '') {
|
||||
// Steam uses 'GMT' which some JS engines don't parse well, replace with 'UTC'
|
||||
const cleanDateText = dateText.replace(' GMT', ' UTC');
|
||||
const parsed = new Date(cleanDateText);
|
||||
if (!isNaN(parsed.getTime())) {
|
||||
// We want the newest expiration date found
|
||||
if (!expirationDate || parsed > expirationDate) {
|
||||
if (!expirationDate || parsed > expirationDate)
|
||||
expirationDate = parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (expirationDate && expirationDate.getTime() > Date.now()) {
|
||||
console.log(`[Scraper] Found active cooldown until: ${expirationDate.toISOString()}`);
|
||||
return {
|
||||
isActive: true,
|
||||
expiresAt: expirationDate
|
||||
};
|
||||
return { isActive: true, expiresAt: expirationDate };
|
||||
}
|
||||
const content = $('#personal_game_data_content').text();
|
||||
if (content.includes('Competitive Cooldown') || content.includes('Your account is currently')) {
|
||||
@@ -90,8 +95,10 @@ const scrapeCooldown = async (steamId, steamLoginSecure) => {
|
||||
return { isActive: false };
|
||||
}
|
||||
catch (error) {
|
||||
console.error(`[Scraper] Error for ${steamId}:`, error.message);
|
||||
throw error;
|
||||
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
|
||||
}
|
||||
};
|
||||
exports.scrapeCooldown = scrapeCooldown;
|
||||
|
||||
@@ -49,10 +49,6 @@ class SteamClientService {
|
||||
return null;
|
||||
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) {
|
||||
const tempPath = `${filePath}.tmp_${Date.now()}`;
|
||||
const dir = path_1.default.dirname(filePath);
|
||||
@@ -61,7 +57,6 @@ class SteamClientService {
|
||||
fs_1.default.mkdirSync(dir, { recursive: true });
|
||||
const vdfContent = (0, simple_vdf_1.stringify)(data);
|
||||
fs_1.default.writeFileSync(tempPath, vdfContent, 'utf-8');
|
||||
// Atomic rename
|
||||
fs_1.default.renameSync(tempPath, filePath);
|
||||
}
|
||||
catch (e) {
|
||||
@@ -77,7 +72,6 @@ class SteamClientService {
|
||||
if (loginUsersPath && fs_1.default.existsSync(loginUsersPath)) {
|
||||
this.readLocalAccounts();
|
||||
chokidar_1.default.watch(loginUsersPath, { persistent: true, ignoreInitial: true }).on('change', () => {
|
||||
console.log(`[SteamClient] loginusers.vdf changed, re-scanning...`);
|
||||
this.readLocalAccounts();
|
||||
});
|
||||
}
|
||||
@@ -89,7 +83,7 @@ class SteamClientService {
|
||||
try {
|
||||
const content = fs_1.default.readFileSync(filePath, 'utf-8');
|
||||
if (!content.trim())
|
||||
return; // Empty file
|
||||
return;
|
||||
const data = (0, simple_vdf_1.parse)(content);
|
||||
if (!data || !data.users)
|
||||
return;
|
||||
@@ -99,8 +93,7 @@ class SteamClientService {
|
||||
if (!user || !user.AccountName)
|
||||
continue;
|
||||
accounts.push({
|
||||
steamId: steamId64,
|
||||
accountName: user.AccountName,
|
||||
steamId: steamId64, accountName: user.AccountName,
|
||||
personaName: user.PersonaName || user.AccountName,
|
||||
timestamp: parseInt(user.Timestamp) || 0
|
||||
});
|
||||
@@ -108,9 +101,7 @@ class SteamClientService {
|
||||
if (this.onAccountsChanged)
|
||||
this.onAccountsChanged(accounts);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[SteamClient] Error parsing loginusers.vdf:', error);
|
||||
}
|
||||
catch (error) { }
|
||||
}
|
||||
extractAccountConfig(accountName) {
|
||||
const configPath = this.getConfigVdfPath();
|
||||
@@ -123,7 +114,6 @@ class SteamClientService {
|
||||
return (accounts && accounts[accountName]) ? accounts[accountName] : null;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('[SteamClient] Failed to extract config.vdf data');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -131,17 +121,7 @@ class SteamClientService {
|
||||
const configPath = this.getConfigVdfPath();
|
||||
if (!configPath)
|
||||
return;
|
||||
let data = {
|
||||
InstallConfigStore: {
|
||||
Software: {
|
||||
Valve: {
|
||||
Steam: {
|
||||
Accounts: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let data = { InstallConfigStore: { Software: { Valve: { Steam: { Accounts: {} } } } } };
|
||||
if (fs_1.default.existsSync(configPath)) {
|
||||
try {
|
||||
const content = fs_1.default.readFileSync(configPath, 'utf-8');
|
||||
@@ -151,18 +131,23 @@ class SteamClientService {
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
// Ensure safe nesting
|
||||
if (!data.InstallConfigStore)
|
||||
data.InstallConfigStore = {};
|
||||
if (!data.InstallConfigStore.Software)
|
||||
data.InstallConfigStore.Software = {};
|
||||
if (!data.InstallConfigStore.Software.Valve)
|
||||
data.InstallConfigStore.Software.Valve = {};
|
||||
if (!data.InstallConfigStore.Software.Valve.Steam)
|
||||
data.InstallConfigStore.Software.Valve.Steam = {};
|
||||
if (!data.InstallConfigStore.Software.Valve.Steam.Accounts)
|
||||
data.InstallConfigStore.Software.Valve.Steam.Accounts = {};
|
||||
data.InstallConfigStore.Software.Valve.Steam.Accounts[accountName] = accountData;
|
||||
const ensurePath = (obj, keys) => {
|
||||
let curr = obj;
|
||||
for (const key of keys) {
|
||||
if (!curr[key] || typeof curr[key] !== 'object')
|
||||
curr[key] = {};
|
||||
curr = curr[key];
|
||||
}
|
||||
return curr;
|
||||
};
|
||||
const steamAccounts = ensurePath(data, ['InstallConfigStore', 'Software', 'Valve', 'Steam', 'Accounts']);
|
||||
// FAILPROOF: Force crucial flags that Steam uses to decide session validity
|
||||
steamAccounts[accountName] = {
|
||||
...accountData,
|
||||
RememberPassword: "1",
|
||||
AllowAutoLogin: "1",
|
||||
Timestamp: Math.floor(Date.now() / 1000).toString()
|
||||
};
|
||||
try {
|
||||
this.safeWriteVdf(configPath, data);
|
||||
console.log(`[SteamClient] Safely injected session for ${accountName}`);
|
||||
@@ -220,6 +205,7 @@ class SteamClientService {
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
// Injection of the actual authentication blob
|
||||
if (accountConfig && accountName) {
|
||||
this.injectAccountConfig(accountName, accountConfig);
|
||||
}
|
||||
@@ -232,11 +218,7 @@ class SteamClientService {
|
||||
for (const regPath of regLocations) {
|
||||
if (!fs_1.default.existsSync(path_1.default.dirname(regPath)))
|
||||
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)) {
|
||||
try {
|
||||
const content = fs_1.default.readFileSync(regPath, 'utf-8');
|
||||
@@ -246,7 +228,6 @@ class SteamClientService {
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
// Deep merge helper
|
||||
const ensurePath = (obj, keys) => {
|
||||
let curr = obj;
|
||||
for (const key of keys) {
|
||||
|
||||
4
frontend/package-lock.json
generated
4
frontend/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ultimate-ban-tracker-desktop",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ultimate-ban-tracker-desktop",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ultimate-ban-tracker-desktop",
|
||||
"description": "Professional Steam Account Manager & Ban Tracker",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.3",
|
||||
"author": "Nils Pukropp <nils@narl.io>",
|
||||
"homepage": "https://narl.io",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
|
||||
Reference in New Issue
Block a user