~blowry/steamarchiver

3984e8eda035a53dd6feb6bc5adda312ccc55250 — Benjamin Lowry 22 days ago 6aaace3 main
new login system with credential caching

(the command line steam guard code flags are gone, but shouldn't be
needed anymore since sentry files are stored now)
6 files changed, 65 insertions(+), 38 deletions(-)

M .gitignore
M depot_archiver.py
M get_appinfo.py
M get_depot_keys.py
A login.py
M update_appinfo.py
M .gitignore => .gitignore +1 -0
@@ 1,5 1,6 @@
__pycache__
appinfo/
auth/
depots/
venv/
virtualenv/

M depot_archiver.py => depot_archiver.py +4 -7
@@ 21,7 21,6 @@ if __name__ == "__main__": # exit before we import our shit if the args are wron
    parser.add_argument("-i", help="Log into a Steam account interactively.", dest="interactive", action="store_true")
    parser.add_argument("-u", type=str, help="Username for non-interactive login", dest="username", nargs="?")
    parser.add_argument("-p", type=str, help="Password for non-interactive login", dest="password", nargs="?")
    parser.add_argument("-g", type=str, help="Steam Guard code for non-interactive login", dest="code", nargs="?")
    args = parser.parse_args()
    if args.connection_limit < 1:
        print("connection limit must be at least 1")


@@ 45,6 44,7 @@ from steam.exceptions import SteamError
from steam.protobufs.content_manifest_pb2 import ContentManifestPayload
from vdf import loads
from aiohttp import ClientSession
from login import auto_login

def archive_manifest(manifest, c, name="unknown", dry_run=False, server_override=None):
    if not manifest:


@@ 175,14 175,11 @@ if __name__ == "__main__":
    steam_client.connect()
    print("Logging in...")
    if args.interactive:
        steam_client.cli_login()
        auto_login(steam_client, fallback_anonymous=False, relogin=False)
    elif args.username:
        result = steam_client.login(username=args.username, password=args.password, two_factor_code=args.code, auth_code=args.code)
        if result != EResult.OK:
            print("error logging in:", result)
            exit(1)
        auto_login(steam_client, args.username, args.password)
    else:
        steam_client.anonymous_login()
        auto_login(steam_client)
    c = CDNClient(steam_client)
    if args.workshop_id and not args.appid:
        response = steam_client.send_um_and_wait("PublishedFile.GetDetails#1", {'publishedfileids':[args.workshop_id]})

M get_appinfo.py => get_appinfo.py +4 -10
@@ 11,20 11,17 @@ if __name__ == "__main__": # exit before we import our shit if the args are wron
    parser.add_argument("-i", help="Log into a Steam account interactively.", dest="interactive", action="store_true")
    parser.add_argument("-u", type=str, help="Username for non-interactive login", dest="username", nargs="?")
    parser.add_argument("-p", type=str, help="Password for non-interactive login", dest="password", nargs="?")
    parser.add_argument("-g", type=str, help="Steam Guard code for non-interactive login", dest="code", nargs="?")
    parser.add_argument('appids', metavar='appid', type=int, nargs='*', help='Apps '
            'to get appinfo for. If empty, will download appinfo for all '
            'publicly visible apps on Steam (this will take a while)!')
    args = parser.parse_args()
    if args.username and not args.password:
        print("invalid combination of arguments")
        exit(1)

from steam.client import SteamClient
from steam.core.msg import MsgProto
from steam.enums import EResult
from steam.enums.emsg import EMsg
from steam.webapi import WebAPI
from login import auto_login

if __name__ == "__main__":
    # Create directories


@@ 36,14 33,11 @@ if __name__ == "__main__":
    steam_client.connect()
    print("Logging in...")
    if args.interactive:
        steam_client.cli_login()
        auto_login(steam_client, fallback_anonymous=False, relogin=False)
    elif args.username:
        result = steam_client.login(username=args.username, password=args.password, two_factor_code=args.code, auth_code=args.code)
        if result != EResult.OK:
            print("error logging in:", result)
            exit(1)
        auto_login(steam_client, args.username, args.password)
    else:
        steam_client.anonymous_login()
        auto_login(steam_client)

    # Parse arguments
    appids = []

M get_depot_keys.py => get_depot_keys.py +5 -11
@@ 6,6 6,7 @@ from steam.enums.emsg import EMsg
from os.path import exists
from sys import argv
from vdf import loads
from login import auto_login

if __name__ == "__main__":
    steam_client = SteamClient()


@@ 13,20 14,13 @@ if __name__ == "__main__":
    steam_client.connect()
    print("Logging in...")
    if len(argv) == 1:
        steam_client.cli_login()
        auto_login(steam_client, fallback_anonymous=False) # probably don't want to default to anonymous for a depot key dumper...
    elif len(argv) == 2:
        if argv[1] == "anonymous":
            steam_client.anonymous_login()
            print("Logged in anonymously")
        else:
            steam_client.cli_login(username=argv[1])
        auto_login(steam_client, argv[1], fallback_anonymous=False)
    elif len(argv) == 3:
        steam_client.cli_login(username=argv[1], password=argv[2])
        print("Logged in as", argv[1])
    elif len(argv) == 4:
        steam_client.login(username=argv[1], password=argv[2], two_factor_code=argv[3], auth_code=argv[3])
        auto_login(steam_client, argv[1], argv[2])
    else:
        print("usage:", argv[0], "[username password steam_guard_code]")
        print("usage:", argv[0], "[username password]")
        exit(1)
    licensed_packages = []
    licensed_apps = []

A login.py => login.py +47 -0
@@ 0,0 1,47 @@
#!/usr/bin/env python3
from steam.client import SteamClient
from steam.enums import EResult
from os import makedirs
from os.path import exists

def auto_login(client, username="", password="", fallback_anonymous=True, relogin=True):
    assert(type(client) == SteamClient)
    client.set_credential_location("./auth")
    if username == "anonymous":
        client.anonymous_login()
        return
    if username == "" and exists("./auth/lastuser.txt") and relogin:
        with open("./auth/lastuser.txt", "r") as f: username = f.read()
    if username != "":
        keypath = "./auth/" + username + ".txt"
        if exists(keypath):
            with open(keypath, "r") as f: login_key = f.read()
            print("Logging in as", username, "using saved login key")
            result = client.login(username, login_key=login_key)
            while result in (EResult.AccountLoginDeniedNeedTwoFactor, EResult.TwoFactorCodeMismatch):
                result = client.login(username, login_key=login_key, two_factor_code=input("Enter 2FA code: "))
            while result in (EResult.AccountLogonDenied, EResult.InvalidLoginAuthCode):
                result = client.login(username, login_key=login_key, auth_code=input("Enter email code: "))
            if result == EResult.OK: return post_login(client, used_login_key=True)
        client.cli_login(username, password) # fallback to CLI prompts if the above didn't work but we still have a specific username
        return post_login(client)
    # if no username, fall back to either anonymous or CLI login based on fallback_anonymous
    if fallback_anonymous:
        client.anonymous_login()
        return
    else:
        client.cli_login()
        return post_login(client)

def post_login(client, used_login_key=False):
    assert(type(client) == SteamClient)
    makedirs("./auth/", exist_ok=True)
    if not used_login_key:
        if not client.login_key:
            print("Waiting for login key...")
            client.wait_event(SteamClient.EVENT_NEW_LOGIN_KEY)
        print("Writing login key...")
        with open("./auth/" + client.username + ".txt", "w") as f:
            f.write(client.login_key)
    with open("./auth/lastuser.txt", "w") as f:
        f.write(client.username)

M update_appinfo.py => update_appinfo.py +4 -10
@@ 11,17 11,14 @@ if __name__ == "__main__": # exit before we import our shit if the args are wron
    parser.add_argument("-t", type=int, help="Number of seconds to sleep between requests in daemon mode (default 5)", dest="time", default=5)
    parser.add_argument("-u", type=str, help="Username for non-interactive login", dest="username", nargs="?")
    parser.add_argument("-p", type=str, help="Password for non-interactive login", dest="password", nargs="?")
    parser.add_argument("-g", type=str, help="Steam Guard code for non-interactive login", dest="code", nargs="?")
    args = parser.parse_args()
    if args.username and not args.password:
        print("invalid combination of arguments")
        exit(1)

from steam.client import SteamClient
from steam.core.msg import MsgProto
from steam.enums import EResult
from steam.enums.emsg import EMsg
from steam.webapi import WebAPI
from login import auto_login

if __name__ == "__main__":
    # Create directories


@@ 33,14 30,11 @@ if __name__ == "__main__":
    steam_client.connect()
    print("Logging in...")
    if args.interactive:
        steam_client.cli_login()
        auto_login(steam_client, fallback_anonymous=False)
    elif args.username:
        result = steam_client.login(username=args.username, password=args.password, two_factor_code=args.code, auth_code=args.code)
        if result != EResult.OK:
            print("error logging in:", result)
            exit(1)
        auto_login(steam_client, args.username, args.password)
    else:
        steam_client.anonymous_login()
        auto_login(steam_client)

    highest_changenumber = 0
    if path.exists("./last_change.txt"):