def test_decrypt(self): ciphertext = b'EFEgY5bexGnwjGSUQKK35TPD7fAjG66REq5m9N1eyFHrZQwzv+aLc7bVmJ9FzCyxbCnbyUnzDKiY505br' + \ b'oEb+KO41XKW668xJzh/JvOK0Cu/+bc4/zSFHZM6JsTYEVDIXgR39ZlypeB34jDVI2544w1ey+DmTWbe8n' + \ b'UbagjnmRkok6kOAq8Avsf9BVJMw3BnSn/4cCC+gOxOJY5fp4DecNDQnp0HyyUz2VMMh/JUYILS5+67fXq' + \ b'29CbIQ1DOTqDfqRPA62nkRVPY83cKIe/UXw==' self.assertEqual(0, len(b64decode(ciphertext)) % 16) password = "******" crypter = Crypter(Crypter.createIvKey(password.encode('utf-8'), "pepper".encode('utf-8'), iterations=3)) self.assertEqual(b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(ciphertext)))
def test_decrypt(self): ciphertext = b'EFEgY5bexGnwjGSUQKK35TPD7fAjG66REq5m9N1eyFHrZQwzv+aLc7bVmJ9FzCyxbCnbyUnzDKiY505br' + \ b'oEb+KO41XKW668xJzh/JvOK0Cu/+bc4/zSFHZM6JsTYEVDIXgR39ZlypeB34jDVI2544w1ey+DmTWbe8n' + \ b'UbagjnmRkok6kOAq8Avsf9BVJMw3BnSn/4cCC+gOxOJY5fp4DecNDQnp0HyyUz2VMMh/JUYILS5+67fXq' + \ b'29CbIQ1DOTqDfqRPA62nkRVPY83cKIe/UXw==' self.assertEqual(0, len(b64decode(ciphertext)) % 16) password = "******" crypter = Crypter( Crypter.createIvKey(password.encode('utf-8'), "pepper".encode('utf-8'), iterations=3)) self.assertEqual( b'Important information with quite some length. ' + b'This message is as long as this because otherwise only one cipher block would ' + b'be encrypted. This long message insures that more than one block is needed.', crypter.decrypt(b64decode(ciphertext)))
class API(object): def __init__(self): self.crypter = Crypter() self.base = 'https://g-api.grandsummoners.com/app/' self.s = requests.Session() self.s.headers.update({ 'Content-Type': 'application/x-www-form-urlencoded', 'Connection': 'keep-alive', 'User-Agent': 'Grand%20Summoners/145 CFNetwork/808.2.16 Darwin/16.3.0', 'Accept-Language': 'en-gb', 'Accept-Encoding': 'gzip, deflate' }) self.s.verify = False self.app_hash = 'c8fb64652fcdfb325012c09f7efea208' self.app_version = 33 self.master_version = 438 self.resource_version = 191 self.scenario_master_version = 150 self.key = 'ZFlyUU0ycFJaTVlFRkVoaA==' self.useNewKey = False self.device_id = None self.session_id = None def setDevice(self, id): self.platform_type = id def setProxy(self, ip, port): part = '%s:%s' % (ip, port) self.log('using proxy %s' % (part)) self.s.proxies.update({ 'http': 'http://%s' % part, 'https': 'https://%s' % part, }) def getUnitName(self, id): id = str(id) if id in unit_master.data: return unit_master.data[id]['name'] self.log('%s missing' % (id)) return None def getUnitRarity(self, id): id = str(id) if id in unit_master.data: return unit_master.data[id]['rarity'] self.log('%s missing' % (id)) return None def getEquipName(self, id): id = str(id) if id in item_master.data: return item_master.data[id]['name'] self.log('%s missing' % (id)) return None def getEquipRarity(self, id): id = str(id) if id in item_master.data: return item_master.data[id]['rarity'] self.log('%s missing' % (id)) return None def setNextKey(self, key): self.key = base64.b64encode(key) def challenge_end(self, challenge_id): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "challenge_id": challenge_id, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def challenge_top(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def eventshop_page(self, event_id): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "event_id": event_id, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def gacha_do(self, button_no, gacha_id): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "button_no": button_no, "gacha_id": gacha_id, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def gacha_top(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def game_init(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "encryption_key": "dYrQM2pRZMYEFEhh\\ZgAEi{I[?ZToT@G", "master_version": 0, "platform_type": self.platform_type, "resource_version": 0, "scenario_master_version": 0 }) def home(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def login(self): res = self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "country_code": "", "device_id": "", "device_token": "", "is_agree": 1, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) self.useNewKey = True return res def setSessionId(self, id): self.session_id = id def setDeviceId(self, id): self.device_id = id def doLogin(self, id1, id2, force=False): if force: self.useNewKey = False self.setSessionId(id2) self.setDeviceId(id1) return self.login() else: self.game_init() self.version_get() self.setSessionId(id2) self.setDeviceId(id1) return self.login() def message_list_get(self, page): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "get_type": 0, "master_version": self.master_version, "page": page, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def message_open(self, message_id_list): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "message_id_list": message_id_list, "page": 1, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def party_update(self, user_party_list): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "party": { "main_no": 0, "user_party_list": user_party_list }, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def quest_end(self, quest_end, tutorial_is_end=None, tutorial_step=None, tutorial_type=None): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "is_ranking_view": False, "master_version": self.master_version, "platform_type": self.platform_type, "quest_end": quest_end, "resource_version": self.resource_version, "tutorial_is_end": tutorial_is_end, "tutorial_step": tutorial_step, "tutorial_type": tutorial_type, "user_unique_id": "" }) def quest_start(self, quest_id, dungeon_id=10100, map_id=10000): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "dungeon_id": dungeon_id, "food_id": 0, "map_id": map_id, "master_version": self.master_version, "party": {}, "platform_type": self.platform_type, "quest_id": quest_id, "resource_version": self.resource_version, "supporter_id": "0", "user_tool_id": 0, "user_unique_id": "" }) def register_email(self, mail, password): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "email": mail, "master_version": self.master_version, "password": password, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def scenario_end(self, played_scenario_id, tutorial_is_end=0, tutorial_step=0, tutorial_type=0): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "platform_type": self.platform_type, "played_scenario_id": played_scenario_id, "resource_version": self.resource_version, "tutorial_is_end": tutorial_is_end, "tutorial_step": tutorial_step, "tutorial_type": tutorial_type, "user_unique_id": "" }) def tutorial_update(self, tutorial_is_end, tutorial_step, tutorial_type): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "tutorial_is_end": tutorial_is_end, "tutorial_step": tutorial_step, "tutorial_type": tutorial_type, "user_unique_id": "" }) def user_create(self, nickname): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "country_code": "", "master_version": self.master_version, "nickname": nickname, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def tutorial_skip(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "country_code": "", "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "user_unique_id": "" }) def tutorial_unit_add(self, tutorial_step, tutorial_type, unit_no=1): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "country_code": "", "master_version": self.master_version, "platform_type": self.platform_type, "resource_version": self.resource_version, "tutorial_step": tutorial_step, "tutorial_type": tutorial_type, "unit_no": unit_no, "user_unique_id": "" }) def version_get(self): return self.callAPI({ "app_hash": self.app_hash, "app_version": self.app_version, "master_version": 0, "platform_type": self.platform_type, "resource_version": 0 }) def decrypt(self, input): return self.crypter.decrypt( input, 'ZFlyUU0ycFJaTVlFRkVoaA==' if not self.useNewKey else self.key) def encrypt(self, input): return self.crypter.encrypt( input, 'ZFlyUU0ycFJaTVlFRkVoaA==' if not self.useNewKey else self.key) def genRandomIP(self): return socket.inet_ntoa( struct.pack('>I', random.randint(1, 0xffffffff))) def callAPI(self, idata): _caller = sys._getframe(1).f_code.co_name if 'scenario_master_version' not in idata: idata['scenario_master_version'] = self.scenario_master_version self.log('..%s()' % (_caller)) r = self.s.post(self.base + _caller, data=json.dumps({ 'body': self.encrypt(idata), 'device_id': '' if not self.device_id else self.device_id, 'session_id': '' if not self.session_id else self.session_id })) if 'error_message' in r.content: self.log('error_message:%s()' % (_caller)) return None body = json.loads(r.content)['body'] if len(body) <= 100: return body data = json.loads(self.decrypt(body)) if 'STATUS' in data and data['STATUS'] != 100: self.log('%s() status:%s' % (_caller, data['STATUS'])) return None if 'encryption_key' in data: self.setNextKey(data['encryption_key']) if 'session_id' in data: self.session_id = data['session_id'] if 'player_update' in data['result']: if 'public_id' in data['result']['player_update']: self.log('hello %s:%s' % (data['result']['player_update']['public_id'], data['result']['player_update']['nickname'])) if 'device_id' in data['result']['player_update']: self.device_id = data['result']['player_update'][ 'device_id'] if 'master_version' in data['result']: self.master_version = data['result']['master_version'] self.resource_version = data['result']['resource_version'] self.scenario_master_version = data['result'][ 'scenario_master_version'] return data def log(self, msg): print '[%s]%s' % (time.strftime('%H:%M:%S'), msg.encode('utf-8')) def sappend(self, d, f): with io.open(f, 'a', encoding='utf8') as the_file: the_file.write('%s\n' % (unicode(d))) def genRandomHex(self, n): return ''.join([random.choice('0123456789ABCDEF') for x in range(n)]).lower() def doMission(self, quest_id, tutorial_is_end=None, tutorial_step=None, tutorial_type=None): start = self.quest_start(quest_id) defeated_enemies = [] for i in start['result']['quest']['battle_list']: for j in i['enemy_list']: if j == 0: continue defeated_enemies.append(j['enemy_no']) quest_end = {} quest_end['boss_id'] = -1 quest_end['break_count'] = 0 quest_end['break_max_damage'] = 0 quest_end['clear_time'] = 13.312557220458984 quest_end['combo_max'] = 0 quest_end['continue_count'] = 0 quest_end['crystal_count'] = 11 quest_end['dead_count'] = 0 quest_end['defeated_enemies'] = defeated_enemies quest_end['dungeon_id'] = 1 quest_end['equipped_item_id_list'] = [300018000, 300001050] quest_end['fullcharge_kill'] = True quest_end['is_state_abnormal'] = False quest_end['live_unit_count'] = 2 quest_end['map_id'] = 10000 quest_end['max_damage'] = 3528 quest_end['multi_user_id_list'] = [] quest_end['quest_id'] = quest_id quest_end['reached_battle_no'] = len( start['result']['quest']['battle_list']) quest_end['rest_boss_hp'] = 0 quest_end['result'] = 1 quest_end['used_item_count'] = {} return self.quest_end(quest_end, tutorial_is_end, tutorial_step, tutorial_type) def getAllGifts(self): message_list = self.message_list_get(1)['result']['message_list'] message_id_list = [] for i in message_list: message_id_list.append(i['id']) while (True): res = self.message_open( message_id_list)['result']['message']['player_message_list'] if len(res) == 0: break message_id_list = [] for i in res: message_id_list.append(i['id']) def getAllChallenges(self): challenge = self.challenge_top( )['result']['player_update']['challenge'] for i in challenge: if i['clear_value'] == i['current_value']: self.challenge_end(i['user_challenge_id']) def exportUnits(self): units = {} equip = {} device_id = self.device_id session_id = self.session_id self.device_id = None self.session_id = None res = self.doLogin(device_id, session_id, True) player_unit_list = res['result']['player']['unit']['player_unit_list'] player_item_list = res['result']['player']['item']['player_item_list'] fourstar = 0 for u in player_unit_list: name = self.getUnitName(u['unit_id']) if not name: continue rare = self.getUnitRarity(u['unit_id']) if not rare: continue if rare < 4: self.log('unit:%s rare:%s' % (name, rare)) continue if rare >= 4 and 'Chicken' not in name: fourstar += 1 if u['unit_id'] not in units: units[u['unit_id']] = {} units[u['unit_id']]['name'] = name units[u['unit_id']]['count'] = 1 else: units[u['unit_id']]['count'] += 1 units = ';'.join( ['%s x%s' % (units[x]['name'], units[x]['count']) for x in units]) fourstare = 0 for u in player_item_list: name = self.getEquipName(u['item_id']) if not name: continue rare = self.getEquipRarity(u['item_id']) if not rare: continue if rare < 4: self.log('equip:%s rare:%s' % (name, rare)) continue if rare >= 4: fourstare += 1 if u['item_id'] not in equip: equip[u['item_id']] = {} equip[u['item_id']]['name'] = name equip[u['item_id']]['count'] = 1 else: equip[u['item_id']]['count'] += 1 if True: _password = self.genRandomHex(10) _mail = '*****@*****.**' % (self.genRandomHex(8)) #self.getAllGifts() print _mail, _password self.register_email(_mail, _password) equip = ';'.join([ '%s x%s' % (equip[x]['name'], equip[x]['count']) for x in equip ]) self.sappend( '%s,%s,%s,%s,%s,%s:%s,%s,%s' % ('ios' if self.platform_type == 2 else 'android', fourstar, units, fourstare, equip, _mail, _password, device_id, session_id), 'reroll.csv') else: print fourstar def reroll(self, skipTut=False): if skipTut: self.game_init() self.version_get() self.user_create(self.genRandomHex(6)) if not self.login(): return self.tutorial_skip() self.tutorial_unit_add(12, 1, 2) self.scenario_end(1010399, 1, 13, 1) self.home() self.getAllGifts() self.exportUnits() else: self.game_init() self.version_get() self.user_create(self.genRandomHex(6)) if not self.login(): return self.scenario_end(1000000, 0, 2, 1) res = self.doMission(10101, 0, 4, 1) self.scenario_end(1010190) self.tutorial_update(0, 5, 1) self.scenario_end(10020, 0, 6, 1) self.tutorial_update(0, 7, 1) self.home() player_unit_list = res['result']['player_update']['unit'][ 'player_unit_list'] user_party_list = [{ "leader_unit_id": player_unit_list[1]['id'], "no": 0, "unit": [{ "id": player_unit_list[1]['id'], "no": 0 }, { "id": player_unit_list[0]['id'], "no": 1 }, { "id": -1, "no": 2 }, { "id": -1, "no": 3 }] }] self.party_update(user_party_list) self.scenario_end(1010201, 0, 7, 1) self.scenario_end(10041) self.doMission(10102) self.scenario_end(1010290) #17 self.scenario_end(10050, 0, 9, 1) self.scenario_end(10060, 0, 10, 1) self.scenario_end(10070) #20 self.doMission(10103) self.scenario_end(1010301) self.scenario_end(1010390) #25 self.tutorial_unit_add(12, 1, 2) self.scenario_end(1010399, 1, 13, 1) self.gacha_top() self.home() self.gacha_do(3, 10001) self.gacha_do(3, 20001) self.getAllGifts() self.eventshop_page(2008000) self.scenario_end(201805301) self.getAllChallenges() #self.gacha_do(2,10017) #self.gacha_do(2,10018) #self.gacha_do(2,20006) #self.gacha_do(2,20007) self.home() self.exportUnits()