@ -17,6 +17,8 @@ import sys
import json
import json
import base64
import base64
import re
import re
import atexit
from base64 import b64decode
from cryptography . fernet import Fernet
from cryptography . fernet import Fernet
from cryptography . hazmat . primitives import hashes
from cryptography . hazmat . primitives import hashes
@ -43,6 +45,7 @@ class SteamAccount(object):
web_api = None
web_api = None
authenticator = None
authenticator = None
_credentials = { }
_path = None
_path = None
_file = None
_file = None
_fernet_key = None
_fernet_key = None
@ -52,12 +55,29 @@ class SteamAccount(object):
def __init__ ( self , username , password ) :
def __init__ ( self , username , password ) :
atexit . register ( self . __cleanup__ )
self . username = username
self . username = username
self . password = password
self . password = password
if not self . _setup ( ) :
if not self . _setup ( ) :
raise SteamAccountException ( ' Could not access account. ' )
raise SteamAccountException ( ' Could not access account. ' )
def __getattr__ ( self , key ) :
if key not in self . _credentials . keys ( ) :
raise AttributeError ( " No %s attribute " % repr ( key ) )
return self . _credentials . get ( key )
def __del__ ( self ) :
def __del__ ( self ) :
self . __save_account_credentials__ ( )
def __cleanup__ ( self ) :
self . __save_account_credentials__ ( )
def __save_account_credentials__ ( self ) :
"""
Make sure it doesnt write the file if the memory has been cleared
"""
if self . _count_account_credentials ( ) < = 2 :
return
try :
try :
self . _update_credential_file ( )
self . _update_credential_file ( )
except TypeError :
except TypeError :
@ -65,29 +85,33 @@ class SteamAccount(object):
Ignore TypeError exception when destructor gets called after the memory has been cleared
Ignore TypeError exception when destructor gets called after the memory has been cleared
"""
"""
pass
pass
self . _file . close ( )
except ValueError :
"""
Ignore ValueError exception when the file could not be written
"""
pass
def set_account_property ( self , property , value ) :
def set_account_property ( self , property , value ) :
setattr ( self , property , value )
self . _credentials [ property ] = value
self . _update_credential_file ( )
self . _update_credential_file ( )
def del_account_property ( self , property ) :
def del_account_property ( self , property ) :
delattr ( self , property )
del self . _credentials [ property ]
self . _update_credential_file ( )
self . _update_credential_file ( )
def check_account_property ( self , property ) :
def check_account_property ( self , property ) :
return hasattr ( self , property )
return property in self . _credentials
@property
@property
def login_code ( self ) :
def login_code ( self ) :
if self . authenticator :
if self . authenticator and hasattr ( self . authenticator , ' shared_secret ' ) :
return self . authenticator . get_code ( )
return self . authenticator . get_code ( )
elif hasattr ( self , ' shared_secret ' ) :
elif hasattr ( self , ' shared_secret ' ) :
return steam . guard . generate_twofactor_code ( self . shared_secret )
return steam . guard . generate_twofactor_code ( b64decode ( self . shared_secret ) )
else :
else :
raise SharedSecretNotSet ( ' Add shared_secret to this instance to generate login codes ' )
raise SharedSecretNotSet ( ' Add shared_secret to this instance to generate login codes ' )
def get_api_key ( self , retrieve_if_missing = True ) :
def get_api_key ( self , retrieve_if_missing = False , hostname_for_retrieving = ' localhost.com ' ) :
if self . check_account_property ( ' apikey ' ) :
if self . check_account_property ( ' apikey ' ) :
return self . apikey
return self . apikey
@ -95,14 +119,14 @@ class SteamAccount(object):
if not self . _has_web_session ( ) :
if not self . _has_web_session ( ) :
self . _spawn_web_session ( )
self . _spawn_web_session ( )
api_key = self . retrieve_api_key ( )
api_key = self . retrieve_api_key ( hostname_for_retrieving )
self . set_account_property ( ' apikey ' , api_key )
self . set_account_property ( ' apikey ' , api_key )
self . _spawn_web_api ( )
self . _spawn_web_api ( )
return api_key
return api_key
else :
else :
raise APIKeyException ( ' Could not return the apikey. The apikey is not set as account property and retrieve_if_missing is not allowed. ' )
raise APIKeyException ( ' Could not return the apikey. The apikey is not set as account property and retrieve_if_missing is not allowed. ' )
def retrieve_api_key ( self ) :
def retrieve_api_key ( self , hostname_for_retrieving = ' localhost.com ' ) :
if not self . _has_web_session ( ) :
if not self . _has_web_session ( ) :
raise APIKeyException ( ' A web session is required to retrieve the api key. ' )
raise APIKeyException ( ' A web session is required to retrieve the api key. ' )
@ -117,7 +141,7 @@ class SteamAccount(object):
session_id = regex_result . group ( 1 )
session_id = regex_result . group ( 1 )
data = {
data = {
' domain ' : ' localhost.com ' ,
' domain ' : hostname_for_retrieving ,
' agreeToTerms ' : ' agreed ' ,
' agreeToTerms ' : ' agreed ' ,
' submit ' : ' Register ' ,
' submit ' : ' Register ' ,
' sessionid ' : session_id
' sessionid ' : session_id
@ -138,16 +162,16 @@ class SteamAccount(object):
self . _generate_fernet_key ( )
self . _generate_fernet_key ( )
self . _spawn_fernet_suite ( )
self . _spawn_fernet_suite ( )
self . _path = ' %s / %s ' % ( BASE_LOCATION , self . username )
self . _path = ' %s / %s ' % ( BASE_LOCATION , self . username )
if not os . path . isfile ( self . _path ) :
if not os . path . exists ( self . _path ) or not os . path . isfile ( self . _path ) :
self . _create_credential_file ( )
self . _create_credential_file ( )
else :
else :
self . _file = open ( self . _path , ' r+ ' , 0 )
credentials = self . _parse_credential_file ( )
credentials = self . _parse_credential_file ( )
for key , value in credentials . iteritems ( ) :
for key , value in credentials . iteritems ( ) :
setattr ( self , key , value )
self . _credentials [ key ] = value
if self . check_account_property ( ' shared_secret ' ) :
if self . check_account_property ( ' shared_secret ' ) :
self . _spawn_authenticator ( )
self . _spawn_authenticator ( )
else :
else :
self . authenticator = steam . guard . SteamAuthenticator ( )
self . authenticator = steam . guard . SteamAuthenticator ( )
@ -156,37 +180,52 @@ class SteamAccount(object):
return True
return True
def _create_credential_file ( self ) :
def _create_credential_file ( self ) :
open ( self . _path , ' a ' ) . close ( )
open ( self . _path , ' w+ ' ) . close ( )
self . _file = open ( self . _path , ' r+ ' , 0 )
self . _spawn_file_pointer ( )
data = json . dumps ( {
data = json . dumps ( {
' username ' : self . username ,
' username ' : self . username ,
' password ' : self . password
' password ' : self . password
} )
} )
token = self . _fernet_suite . encrypt ( data )
token = self . _fernet_suite . encrypt ( data )
self . _file . write ( token )
self . _file . write ( token )
self . _file . close ( )
def _parse_credential_file ( self ) :
def _parse_credential_file ( self ) :
self . _file . seek ( 0 )
self . _spawn_file_pointer ( )
token = self . _file . read ( )
token = self . _file . read ( )
self . _file . seek ( 0 )
data = json . loads ( self . _fernet_suite . decrypt ( token ) )
data = json . loads ( self . _fernet_suite . decrypt ( token ) )
self . _file . close ( )
return data
return data
def _update_credential_file ( self ) :
def _update_credential_file ( self ) :
self . _spawn_file_pointer ( )
credentials = self . _gather_credentials ( )
credentials = self . _gather_credentials ( )
data = json . dumps ( credentials )
data = json . dumps ( credentials )
token = self . _fernet_suite . encrypt ( data )
token = self . _fernet_suite . encrypt ( data )
self . _file . truncate ( )
self . _file . truncate ( )
self . _file . write ( token )
self . _file . write ( token )
self . _file . close ( )
def _gather_credentials ( self ) :
def _gather_credentials ( self ) :
data = { }
data = { }
names = dir ( self )
names = self . _credentials
for name in names :
for name in names :
if name in ACCOUNT_ATTRIBUTES :
if name in ACCOUNT_ATTRIBUTES :
data . __setitem__ ( name , getattr ( self , name ) )
data . __setitem__ ( name , self . _credentials [ name ] )
return data
return data
def _count_account_credentials ( self ) :
count = 0
for attr in ACCOUNT_ATTRIBUTES :
if self . check_account_property ( attr ) :
count + = 1
return count
def _generate_fernet_key ( self ) :
def _generate_fernet_key ( self ) :
digest = hashes . Hash ( hashes . SHA256 ( ) , backend = default_backend ( ) )
digest = hashes . Hash ( hashes . SHA256 ( ) , backend = default_backend ( ) )
digest . update ( bytes ( self . password ) )
digest . update ( bytes ( self . password ) )
@ -195,75 +234,59 @@ class SteamAccount(object):
def _spawn_fernet_suite ( self ) :
def _spawn_fernet_suite ( self ) :
self . _fernet_suite = Fernet ( self . _fernet_key )
self . _fernet_suite = Fernet ( self . _fernet_key )
def _spawn_file_pointer ( self ) :
self . _file = open ( self . _path , ' r+ ' , 0 )
def _spawn_web_api ( self ) :
def _spawn_web_api ( self ) :
self . web_api = steam . webapi . WebAPI ( self . apikey )
self . web_api = steam . webapi . WebAPI ( self . apikey )
def _spawn_authenticator ( self ) :
def _spawn_authenticator ( self ) :
secrets = {
secrets = {
' identity_secret ' : getattr ( self , ' identity_secret ' ) ,
' identity_secret ' : self . _credentials . get ( ' identity_secret ' ) ,
' shared_secret ' : getattr ( self , ' shared_secret ' ) ,
' shared_secret ' : self . _credentials . get ( ' shared_secret ' ) ,
' secret_1 ' : getattr ( self , ' secret_1 ' ) ,
' secret_1 ' : self . _credentials . get ( ' secret_1 ' ) ,
' revocation_code ' : getattr ( self , ' revocation_code ' ) ,
' revocation_code ' : self . _credentials . get ( ' revocation_code ' ) ,
}
' deviceid ' : self . _credentials . get ( ' deviceid ' )
}
self . authenticator = steam . guard . SteamAuthenticator ( secrets )
self . authenticator = steam . guard . SteamAuthenticator ( secrets )
self . _spawn_mobile_session ( )
self . _spawn_mobile_session ( )
self . authenticator . medium = self . _mobile_auth
self . authenticator . medium = self . _mobile_auth
def _has_session ( self ) :
def _has_session ( self ) :
if self . _has_web_session ( ) or self . _has_mobile_session ( ) :
return True if self . _has_web_session ( ) or self . _has_mobile_session ( ) else False
return True
return False
def _has_web_session ( self ) :
def _has_web_session ( self ) :
if isinstance ( self . _web_auth , steam . webauth . WebAuth ) :
return isinstance ( self . _web_auth , steam . webauth . WebAuth )
return True
return False
def _has_mobile_session ( self ) :
def _has_mobile_session ( self ) :
if isinstance ( self . _mobile_auth , steam . webauth . MobileWebAuth ) :
return isinstance ( self . _mobile_auth , steam . webauth . MobileWebAuth )
return True
return False
def _spawn_web_session ( self , captcha = ' ' , email_code = ' ' , twofactor_code = ' ' ) :
def _spawn_web_session ( self ) :
self . _web_auth = steam . webauth . WebAuth ( self . username , self . password )
self . _web_auth = steam . webauth . WebAuth ( self . username , self . password )
self . _login_web_session ( self . _web_auth , captcha , email_code , twofactor_code )
self . _login_web_session ( self . _web_auth )
self . web_session = self . _web_auth . session
self . web_session = self . _web_auth . session
def _spawn_mobile_session ( self , captcha = ' ' , email_code = ' ' , twofactor_code = ' ' ) :
def _spawn_mobile_session ( self ) :
self . _mobile_auth = steam . webauth . MobileWebAuth ( self . username , self . password )
self . _mobile_auth = steam . webauth . MobileWebAuth ( self . username , self . password )
self . _login_web_session ( self . _mobile_auth , captcha , email_code , twofactor_code )
self . _login_web_session ( self . _mobile_auth )
self . mobile_session = self . _mobile_auth . session
self . mobile_session = self . _mobile_auth . session
def _login_web_session ( self , web_auth , captcha = ' ' , email_code = ' ' , twofactor_code = ' ' ) :
def _login_web_session ( self , web_auth ) :
if not isinstance ( web_auth , steam . webauth . WebAuth ) and not isinstance ( web_auth , steam . webauth . MobileWebAuth ) :
if not isinstance ( web_auth , steam . webauth . WebAuth ) and not isinstance ( web_auth , steam . webauth . MobileWebAuth ) :
raise WebAuthNotComplete ( ' Please supply a valid WebAuth or MobileWebAuth session ' )
raise WebAuthNotComplete ( ' Please supply a valid WebAuth or MobileWebAuth session ' )
try :
try :
web_auth . login ( )
twofactor_code = self . login_code
except SharedSecretNotSet :
except steam . webauth . CaptchaRequired :
twofactor_code = ' '
if not captcha :
raise CaptchaNotProvided ( ' The steam login captcha is required for logging in, but was not provided. ' )
web_auth . login ( twofactor_code = twofactor_code )
web_auth . login ( captcha = captcha )
except steam . webauth . EmailCodeRequired :
if not email_code :
raise EMailCodeNotProvided ( ' The email code is required for logging in, but was not provided. ' )
web_auth . login ( email_code = email_code )
except steam . webauth . TwoFactorCodeRequired :
if not twofactor_code :
try :
twofactor_code = self . login_code
except SharedSecretNotSet :
raise TwoFACodeNotProvided ( ' The twofactor code is required for logging in, but was not provided. ' )
web_auth . login ( twofactor_code = twofactor_code )
if web_auth . complete :
if web_auth . complete :
if not hasattr ( self , ' steamid ' ) :
if not hasattr ( self , ' steamid ' ) :
self . set_account_property ( ' steamid ' , web_auth . steam_id )
self . set_account_property ( ' steamid ' , web_auth . steam_id )
if isinstance ( web_auth , steam . webauth . MobileWebAuth ) and not hasattr ( self , ' oauth_token ' ) :
if isinstance ( web_auth , steam . webauth . MobileWebAuth ) :
self . set_account_property ( ' oauth_token ' , web_auth . oauth_token )
self . set_account_property ( ' oauth_token ' , web_auth . oauth_token )
else :
else :
raise WebAuthNotComplete ( ' The web authentication could not be completed. ' )
raise WebAuthNotComplete ( ' The web authentication could not be completed. ' )
@ -286,20 +309,5 @@ class WebException(SteamAccountException):
class APIKeyException ( SteamAccountException ) :
class APIKeyException ( SteamAccountException ) :
pass
pass
class MobileAuthenticatorException ( SteamAccountException ) :
pass
class ParameterNotProvidedException ( SteamAccountException ) :
class ParameterNotProvidedException ( SteamAccountException ) :
pass
class CaptchaNotProvided ( ParameterNotProvidedException ) :
pass
class EMailCodeNotProvided ( ParameterNotProvidedException ) :
pass
class TwoFACodeNotProvided ( ParameterNotProvidedException ) :
pass
class SMSCodeNotProvided ( ParameterNotProvidedException ) :
pass
pass