Jump to content
McKay Development

Recommended Posts

Posted

I wrote an application that idles all of my games to x amount of minutes. My problem is that after logging in and passing some appIds to gamesPlayed, I am being prompted to enter my Steam Guard code after a while. I have had it run fine for over 30 minutes sometimes before being asked, but it's usually within 5-10 minutes.

I'm listening to both sessionExpired and error, but it doesn't seem to be either of them. The goal is to log on just once and have it idle games until the application is closed. I call webLogOn every 15 minutes, but that doesn't seem to be the problem, as I'm usually prompted before that. I've also tried assigning the loginKey to my log in options after first logging in, which supposedly omits the need for a guard code, but that doesn't work either.

Why is this happening? Any help would be greatly appreciated.

Code:

 

global._mckay_statistics_opt_out = true;
const SteamClient = require('steam-user');
const SteamCommunity = require('steamcommunity');
const community = new SteamCommunity();
const client = new SteamClient();
let apiKey = ('');
const minutesToIdle = 180;
let gamesToIdle = [];
let gamesTimePlayed = [];
let appNames;
let steamID;

const logInOptions = {
    accountName: '',
    password: '',
    logonID: Date.now(),
    rememberPassword: true
};

// Initialize 
(function () {
    console.log('Loading game names...')
    community.request('some api', (error, response, data) => {
        if (!error && response.statusCode == 200) {
            appNames = JSON.parse(data).data;
            client.logOn(logInOptions);
        } else {
            console.log('Failed to get game names, listing AppIDs instead.')
            setTimeout(function() {
                client.logOn(logInOptions);
            }, 4000);
        }
    })
})()

client.on("webSession", (sessionID, cookies) => {
    console.clear();
    console.log("Got web session");
    community.setCookies(cookies);
    getGamesInfo();
});

// Get owned games and time information.
function getGamesInfo () {
    gamesToIdle.length = 0;
    gamesTimePlayed.length = 0;
    console.log('\x1b[36m%s\x1b[0m', 'Idle all Steam games');
community.request('https://api.steampowered.com/IPlayerService/GetOwnedGames/v0001/?key=' + apiKey + '&steamid=' + steamID, (error, response, data) => {
    if (!error && response.statusCode == 200) {
        const gamesData = JSON.parse(data);
        const gamesTotal = gamesData.response.game_count;
        console.log('\x1b[32m%s\x1b[0m', '\nAmount of games: ' + gamesTotal);
        // Check playtime for past 2 weeks and forever.
        for (var i = 0; i < gamesTotal; i++) {
                if (gamesData.response.games[i].playtime_forever < minutesToIdle) {
                        if (gamesData.response.games[i].playtime_2weeks == null || gamesData.response.games[i].playtime_2weeks < minutesToIdle) {
                            gamesToIdle.push(gamesData.response.games[i].appid);
                            gamesTimePlayed.push(getMostTime(gamesData.response.games[i].playtime_forever, gamesData.response.games[i].playtime_2weeks));
                        }
                    }
                }
        if (gamesToIdle.length > 1 ) {
            console.log('\x1b[32m%s\x1b[0m', 'Games left to idle: ' + gamesToIdle.length);
            assignAppNames();
        } else {
            console.log('Done idling.')
        }
        } else {
            console.log('Error loading games, retrying in 1 minute...');
            setTimeout(function() {
                client.webLogOn(); 
            }, 600000);
        }
    })
}

// Returns most time played between last 2 weeks and forever.
function getMostTime (a,  {
    if (a >  {
        return a;
    } else {
        return b;
    }
}
// Assigns app names to display, or appIDs if none found.
function assignAppNames () {
    let gameNames = [];
    for (var i = 0; i < 29 && i < gamesToIdle.length; i++) {
        if (appNames != null && appNames.hasOwnProperty(gamesToIdle[i])) {
            gameNames.push(appNames[gamesToIdle[i]]);
        } else {
            gameNames.push(gamesToIdle[i]);
        }
    }
    idleGames(gameNames);
}

// Idle the games.
function idleGames (gameNames) {
    let idleNow = [];
    for (var i = 0; i < 29 && idleNow.length < gamesToIdle.length; i++) { 
        idleNow.push(gamesToIdle[i]);
    }
    client.gamesPlayed(idleNow);
    console.log('\x1b[32m%s\x1b[0m', 'Now idling: ' + idleNow.length + '\n')
    gameNames.forEach(function(element) {
        console.log(element + '\x1b[33m%s\x1b[0m', ' [' + (gamesTimePlayed[gameNames.indexOf(element)] / 60).toFixed(2) + ']');
    })
    console.log('\n' + 'Last update: ' + '\x1b[33m%s\x1b[0m', getDateTime());
    setTimeout(function() {
        client.webLogOn(); 
    }, 900000);
}

community.on("sessionExpired", error => {
    console.log("Steam web session expired: " + error.message);
    if (client.steamID) {
        client.webLogOn();
    } else {
        client.logInOptions.logonID = Date.now();
        client.logOn(logInOptions);
    }
});

client.on("error", error => {
    console.log("An error has occurred: " + error.message);
    }
);

client.on('loggedOn', (details) => {
    console.log('Logged on Steam.');
    client.setPersona(SteamClient.EPersonaState.Invisible);
    steamID = details.client_supplied_steamid;
});

function getDateTime() {

    var date = new Date();
  
    var hour = date.getHours();
    hour = (hour < 10 ? "0" : "") + hour;
  
    var min  = date.getMinutes();
    min = (min < 10 ? "0" : "") + min;
  
    var sec  = date.getSeconds();
    sec = (sec < 10 ? "0" : "") + sec;
  
    var year = date.getFullYear();
  
    var month = date.getMonth() + 1;
    month = (month < 10 ? "0" : "") + month;
  
    var day  = date.getDate();
    day = (day < 10 ? "0" : "") + day;

    return hour + ":" + min + ":" + sec;
}
Posted

I don't see anything obvious in your code that would cause that. Though I do notice that if this is your entire code, you don't need to use SteamCommunity or web sessions at all. You aren't making any authenticated requests to steamcommunity.com.

 

Could you add this and see what happens before you get prompted for a code?

client.on('debug', (msg) => console.log('[debug] ' + msg));
Posted (edited)

 

I don't see anything obvious in your code that would cause that. Though I do notice that if this is your entire code, you don't need to use SteamCommunity or web sessions at all. You aren't making any authenticated requests to steamcommunity.com.

 

Could you add this and see what happens before you get prompted for a code?

client.on('debug', (msg) => console.log('[debug] ' + msg));

 

Oh, right.

 

Here's the log:

[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] API POST request to https://api.steampowered.com/ISteamUserAuth/AuthenticateUser/v0001/: 200
Got web session
Idle all Steam games

Amount of games: 20707
Games left to idle: 18065
[debug] Sending message: 5410
Now idling: 29

Eets [2.95]
Jade Empire: Special Edition [2.62]
Peggle Deluxe [0.95]
Making History: The Calm & The Storm [0.95]
Silverfall [0.95]
FlatOut 2 [0.95]
Just Cause [0.93]
Space Empires IV Deluxe [0.93]
Space Empires V [0.93]
Vampire: The Masquerade - Bloodlines [0.93]
Joint Task Force [0.02]
Rogue Trooper [0.02]
Arx Fatalis [0.02]
Tomb Raider: Anniversary [0.02]
Sam & Max 101: Culture Shock [0.02]
Sam & Max 102: Situation: Comedy [0.02]
Sam & Max 103: The Mole, the Mob and the Meatball [0.02]
Sam & Max 105: Reality 2.0 [0.02]
Sam & Max 106: Bright Side of the Moon [0.02]
Geometry Wars: Retro Evolved [0.02]
Lost Planet: Extreme Condition [0.02]
Venice [0.02]
Nexus: The Jupiter Incident [0.02]
ThreadSpace: Hyperbol [0.02]
Full Spectrum Warrior [0.02]
Full Spectrum Warrior: Ten Hammers [0.02]
Commander Keen Complete Pack [0.02]
Wolfenstein 3D [0.02]
Wolfenstein 3D: Spear of Destiny [0.02]

Last update: 15:29:38
[debug] WebSocket disconnected with error: Ping timeout
[debug] Using TCP; we rolled 56 and percent to use WS is 50
[debug] Connecting to TCP CM: 146.66.152.10:27018
[debug] TCP connection established
[debug] Handled message: 1303
[debug] Channel encrypt request: protocol 1, universe 1, nonce xxxx, 0 remaining bytes
[debug] Sending message: 1304
[debug] Handled message: 1305
[debug] Sending message: 5514
[debug] Handled message: 751
Steam Guard App Code:

I suppose I'm being timed out, but why?

Edited by ktfo
Posted (edited)

I now noticed it happens every time I run the script after first running it 1-2 times (passing an array of 29 appIDs to gamesPlayed each time).

  • Am I passing too many appIDs to gamesPlayed in a short time span?
  • Am I exiting the process wrongly? Should I pass an emtpy array to gamesPlayed first?
Edited by ktfo
Posted

The WebSocket ping timeout means that the Steam server did not send respond to a ping we sent in a timely manner (or more accurately, it didn't respond to 3 consecutive pings within 10 seconds). If Steam isn't down, that means you have network issues.

 

As for why you're being prompted for another app code, I have absolutely no idea. I tried replicating the issue with fake errors and it just re-logged with no issues each time. Maybe try removing or dividing by 1000 your logonID? It's a 32-bit int.

Posted (edited)

The WebSocket ping timeout means that the Steam server did not send respond to a ping we sent in a timely manner (or more accurately, it didn't respond to 3 consecutive pings within 10 seconds). If Steam isn't down, that means you have network issues.

 

As for why you're being prompted for another app code, I have absolutely no idea. I tried replicating the issue with fake errors and it just re-logged with no issues each time. Maybe try removing or dividing by 1000 your logonID? It's a 32-bit int.

 

Thanks for taking the time to look into the issue.

 

I've been trying to find the cause for the issue for the past two days, and have come to the conclusion that it's simply the account in question. As you can see in the original post, it has over 20K apps, causing sporadic timeouts after logging in, for some reason. The piece of code below yielded this result:

...
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] API POST request to https://api.steampowered.com/ISteamUserAuth/Authenti
cateUser/v0001/: 200
[debug] Sending message: 8901
[debug] WebSocket disconnected with error: Ping timeout
[debug] Using WebSocket; we rolled 26 and percent to use WS is 50
[debug] WebSocket connection success; now logging in
[debug] Sending message: 5514
[debug] Handled message: 751
Steam Guard App Code:

Code:

const SteamClient = require('steam-user');
const client = new SteamClient({"enablePicsCache": true});

const logInOptions = {
    accountName: '',
    password: ''
};

client.logOn(logInOptions);

client.on('appOwnershipCached', function () {
  console.log('Loaded.');
});

client.on('debug', (msg) => console.log('[debug] ' + msg));

Do you have any idea what might be causing this? Is there anything that can be done about it?

Edited by ktfo
Posted

8901 is ClientPICSChangesSinceRequest, so I suppose it stands to reason that Steam is sending a whole lot of product data in a single message, which is transferring really slowly, thus blocking the pong frames from being received in a timely manner. But it would have to be a looooot of data, and in my experience really large PICS changes responses are broken up into multiple messages, so pong frames should make it through. ‍‍¯\_(ツ)_/¯

 

That would explain your timeouts, but not why the login key is being rejected for session resumption. Perhaps Steam doesn't believe that you've disconnected yet? Or maybe Steam saw the WS close frame and treated it as a logoff and thus invalidated the login key. But that wouldn't explain why it was being rejected when you were using rememberPassword. Perhaps using the previous session ID is invalid when trying to use a remember-password login key and not for session resumption purposes. So again, ‍‍¯\_(ツ)_/¯

 

Maybe to fix this, websocket13 should avoid tearing down a connection for failure to respond to ping while data is actively coming in. Dunno.

 

But to (finally) answer your question, you should be able to work around this quite well by adding "protocol": SteamUser.EConnectionProtocol.TCP to your constructor options. Plain TCP doesn't use pings to make sure the connection is still alive.

Posted (edited)

That did the trick, thank you.

 

On another somewhat related note - am I doing something wrong, or is it simply not possible to not have to enter the Steam Guard Code a second time for a new session after being kicked with kickPlayingSession? I'm looking to have the script handle the 'error' and resume idling after the user kicked the session by launching a game. I figured relog() would do, but it does nothing. Archie Steam Farm does this, so I guess it should be possible somehow?

 

Code:

const SteamClient = require('steam-user');
const client = new SteamClient({"enablePicsCache": true, "protocol": SteamClient.EConnectionProtocol.TCP});

const logInOptions = {
    accountName: '',
    password: '',
    rememberPassword: true
};

client.logOn(logInOptions);

client.on('appOwnershipCached', function () {
  client.gamesPlayed([9400]);
});

client.on('loggedOn', function () {
  console.log('Logged on Steam.');
  client.setPersona(SteamClient.EPersonaState.Online);
})

client.on('loginKey', function (key) {
  console.log('Got login key ' + key);
})

client.on("error", error => {
  console.log("An error has occurred: " + error.message);
  if (error.message == 'LoggedInElsewhere') {
      console.log('Logging back in.')
      client.relog();
      }
  }
);

Log:

[debug] Sending message: 5410
[debug] Handled message: 9600
[debug] Handled message: 779
[debug] Received 1 game connect tokens
[debug] Unhandled message: PlayerClient.NotifyLastPlayedTimes#1
[debug] Handled message: 766
[debug] Sending message: 8903
[debug] Handled message: 8904
[debug] Handled message: 766
[debug] Handled message: 822
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 822
[debug] Handled message: 766
[debug] Handled message: 822
[debug] Handled message: 822
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 766
[debug] Handled message: 822
[debug] Handled message: 822
[debug] Handled message: 766
[debug] Handled message: 757
[debug] Logged off: LoggedInElsewhere
An error has occurred: LoggedInElsewhere
Logging back in.
[debug] Sending message: 706

Logging off, then back in with the login key after a few seconds delay seems to work? I'll post a refined version of the script when I'm done, if anyone is interested.

Edited by ktfo
Posted

relog is only meant to be used when you're connected. When error is emitted, that means you got disconnected and SteamUser won't attempt to automatically reconnect. You should be able to just log back on immediately with your login key.

 

Also, if you want some more descriptive debug output (actual message names and not just numbers), add "debug": true to your constructor options.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...