2020-10-30 13:15:52 +08:00
#!/usr/bin/env python3
2020-10-28 16:17:21 +08:00
import requests
import json
2020-10-29 18:59:10 +08:00
import uuid
2020-10-30 13:15:52 +08:00
import logging
2020-11-18 11:13:11 +08:00
import time
import random
import hashlib
import string
2020-10-30 13:15:52 +08:00
from requests . exceptions import *
logging . basicConfig (
2020-10-30 16:30:42 +08:00
level = logging . INFO ,
format = ' %(asctime)s %(levelname)s %(message)s ' ,
datefmt = ' % Y- % m- %d T % H: % M: % S ' )
2020-10-30 13:15:52 +08:00
class ConfMeta ( type ) :
2020-10-30 16:30:42 +08:00
@property
2020-10-30 18:49:27 +08:00
def index_url ( self ) :
2020-10-30 16:30:42 +08:00
return ' https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html '
2020-10-30 13:15:52 +08:00
2020-11-18 11:13:11 +08:00
@property
def app_version ( self ) :
return ' 2.1.0 '
2020-10-30 16:30:42 +08:00
@property
2020-10-30 18:49:27 +08:00
def ua ( self ) :
2020-11-02 14:29:00 +08:00
return ' Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) ' \
2020-11-18 11:13:11 +08:00
' AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/ %s ' % ( self . app_version )
2020-10-30 13:15:52 +08:00
2020-10-30 18:49:27 +08:00
class Conf ( metaclass = ConfMeta ) :
2020-10-30 16:30:42 +08:00
pass
2020-10-30 13:15:52 +08:00
2020-11-02 14:29:00 +08:00
class Roles ( object ) :
2020-10-30 18:49:27 +08:00
def __init__ ( self , cookie : str = None ) :
2020-10-30 16:30:42 +08:00
if type ( cookie ) is not str :
raise TypeError ( " %s want a %s but got %s " % (
2020-11-19 09:43:05 +08:00
self . __class__ , type ( __name__ ) , type ( cookie ) ) )
2020-10-30 16:30:42 +08:00
self . _cookie = cookie
2020-11-02 14:29:00 +08:00
self . _url = " https://api-takumi.mihoyo.com/binding/api/ " \
2020-10-30 16:30:42 +08:00
" getUserGameRolesByCookie?game_biz= %s " % ( ' hk4e_cn ' )
def get_header ( self ) :
actid = ' e202009291139501 '
ref = " %s ?bbs_auth_required= %s &act_id= %s &utm_source= %s " \
" &utm_medium= %s &utm_campaign= %s " % (
Conf . index_url , ' true ' , actid , ' bbs ' , ' mys ' , ' icon ' )
return {
2020-11-19 09:43:05 +08:00
' User-Agent ' : Conf . ua ,
' Referer ' : ref ,
' Accept-Encoding ' : ' gzip, deflate, br ' ,
' Cookie ' : self . _cookie
2020-10-30 16:30:42 +08:00
}
2020-11-02 14:29:00 +08:00
def get_roles ( self ) :
2020-10-30 16:30:42 +08:00
try :
jdict = json . loads (
requests . Session ( ) . get (
2020-11-19 09:43:05 +08:00
self . _url , headers = self . get_header ( ) ) . text )
2020-10-30 16:30:42 +08:00
except Exception as e :
logging . error ( e )
raise HTTPError
2020-11-02 14:29:00 +08:00
return jdict
2020-10-30 13:15:52 +08:00
class Sign ( object ) :
2020-10-30 18:49:27 +08:00
def __init__ ( self , cookie : str = None ) :
2020-10-30 16:30:42 +08:00
if type ( cookie ) is not str :
raise TypeError ( " %s want a %s but got %s " % (
2020-11-19 09:43:05 +08:00
self . __class__ , type ( __name__ ) , type ( cookie ) ) )
2020-10-30 16:30:42 +08:00
2020-12-26 14:42:59 +08:00
self . _cookie = cookie
2020-10-30 16:30:42 +08:00
self . _url = ' https://api-takumi.mihoyo.com/event/bbs_sign_reward/sign '
2020-11-02 14:29:00 +08:00
roles = Roles ( cookie )
2020-10-30 18:49:27 +08:00
errstr = None
for i in range ( 1 , 4 ) :
try :
2020-11-02 14:29:00 +08:00
self . _roles = roles . get_roles ( )
2020-10-30 18:49:27 +08:00
except HTTPError as e :
2020-12-26 14:42:59 +08:00
logging . error ( " HTTP error when get user game roles, retry %s time(s) ... " % ( i ) )
2020-10-30 18:49:27 +08:00
logging . error ( " error is %s " % ( e ) )
errstr = str ( e )
continue
except KeyError as e :
2020-12-26 14:42:59 +08:00
logging . error ( " Wrong response to get user game roles, retry %s time(s) ... " % ( i ) )
2020-10-30 18:49:27 +08:00
logging . error ( " response is %s " % ( e ) )
errstr = str ( e )
continue
except Exception as e :
logging . error ( " Unknown error %s , die " % ( e ) )
errstr = str ( e )
raise
else :
break
2020-10-30 16:30:42 +08:00
try :
2020-12-26 14:42:59 +08:00
self . _bindList = self . _roles [ ' data ' ] [ ' list ' ]
2020-10-30 18:49:27 +08:00
except AttributeError :
raise Exception ( errstr )
2020-10-30 16:30:42 +08:00
2020-11-18 11:13:11 +08:00
# Provided by Steesha
def md5 ( self , text ) :
md5 = hashlib . md5 ( )
md5 . update ( text . encode ( ) )
return ( md5 . hexdigest ( ) )
def get_DS ( self ) :
n = self . md5 ( Conf . app_version )
i = str ( int ( time . time ( ) ) )
r = ' ' . join ( random . sample ( string . ascii_lowercase + string . digits , 6 ) )
c = self . md5 ( " salt= " + n + " &t= " + i + " &r= " + r )
return i + " , " + r + " , " + c
2020-10-30 16:30:42 +08:00
def get_header ( self ) :
actid = ' e202009291139501 '
ref = " %s ?bbs_auth_required= %s &act_id= %s &utm_source= %s " \
" &utm_medium= %s &utm_campaign= %s " % (
Conf . index_url , ' true ' , actid , ' bbs ' , ' mys ' , ' icon ' )
2020-12-26 14:42:59 +08:00
# x-rpc-client_type
# 1: ios
# 2: android
# 4: pc web
# 5: mobile web
2020-10-30 16:30:42 +08:00
return {
2020-11-19 09:43:05 +08:00
' x-rpc-device_id ' : str ( uuid . uuid3 (
uuid . NAMESPACE_URL , self . _cookie ) ) . replace ( ' - ' , ' ' ) . upper ( ) ,
' x-rpc-client_type ' : ' 5 ' ,
' Accept-Encoding ' : ' gzip, deflate, br ' ,
' User-Agent ' : Conf . ua ,
' Referer ' : ref ,
' x-rpc-app_version ' : Conf . app_version ,
' DS ' : self . get_DS ( ) ,
' Cookie ' : self . _cookie
2020-10-30 16:30:42 +08:00
}
def run ( self ) :
2020-12-26 14:42:59 +08:00
for i in range ( len ( self . _bindList ) ) :
# region
# cn_gf01: 天空岛
# cn_qd01: 世界树
try :
self . _region = self . _bindList [ i ] [ ' region ' ]
except :
raise KeyError ( str ( self . _roles ) )
try :
self . _region_name = self . _bindList [ i ] [ ' region_name ' ]
except :
raise KeyError ( str ( self . _roles ) )
try :
self . _uid = self . _bindList [ i ] [ ' game_uid ' ]
except :
raise KeyError ( str ( self . _roles ) )
logging . info ( ' Your account has been bound %s role(s), UID %s is %s in %s ' % ( len ( self . _bindList ) , i + 1 , str ( self . _uid ) . replace ( str ( self . _uid ) [ 3 : 6 ] , ' *** ' , 1 ) , self . _region_name ) )
data = {
' act_id ' : ' e202009291139501 ' ,
' region ' : self . _region ,
' uid ' : self . _uid
}
try :
jdict = json . loads ( requests . Session ( ) . post (
self . _url , headers = self . get_header ( ) ,
data = json . dumps ( data , ensure_ascii = False ) ) . text )
except Exception as e :
raise
return jdict
2020-10-30 13:15:52 +08:00
2020-10-30 18:49:27 +08:00
def makeResult ( result : str , data = None ) :
2020-10-30 16:30:42 +08:00
return json . dumps (
2020-10-30 18:49:27 +08:00
{
' result ' : result ,
' message ' : data
} ,
sort_keys = False , indent = 2 , ensure_ascii = False
2020-10-30 16:30:42 +08:00
)
2020-10-28 16:17:21 +08:00
if __name__ == " __main__ " :
2020-11-18 11:13:11 +08:00
seconds = random . randint ( 10 , 300 )
2020-11-19 09:43:05 +08:00
ret = - 1
2020-10-30 13:15:52 +08:00
2020-11-19 09:43:05 +08:00
logging . info ( ' Sleep for %s seconds ... ' % ( seconds ) )
2020-11-18 11:13:11 +08:00
time . sleep ( seconds )
2020-10-30 13:15:52 +08:00
2020-10-30 16:30:42 +08:00
try :
jdict = Sign ( input ( ) . strip ( ) ) . run ( )
2020-10-30 18:49:27 +08:00
jstr = json . dumps ( jdict , ensure_ascii = False )
2020-10-30 16:30:42 +08:00
code = jdict [ ' retcode ' ]
except Exception as e :
jstr = str ( e )
2020-10-30 13:15:52 +08:00
2020-10-30 16:30:42 +08:00
result = makeResult ( ' Failed ' , jstr )
2020-10-30 13:15:52 +08:00
2020-10-30 16:30:42 +08:00
try :
code
except NameError :
code = - 1
2020-10-30 13:15:52 +08:00
2020-10-30 16:30:42 +08:00
# 0: success
# -5003: already signed in
if code in [ 0 , - 5003 ] :
result = makeResult ( ' Success ' , jstr )
2020-11-19 09:43:05 +08:00
ret = 0
logging . info ( result )
exit ( ret )