class API(object):
    def __init__(self):
        self.s = requests.Session()
        self.s.headers.update({
            'Accept-Encoding': 'gzip, deflate',
            'Content-Type': 'application/json',
            'Expect': '100-continue',
            'User-Agent': None
        })
        self.s.verify = False
        if 'win' in sys.platform:
            self.s.proxies.update({
                'http': 'http://127.0.0.1:8888',
                'https': 'https://127.0.0.1:8888',
            })
        self.game_api = 'https://gl-game.tsubasa-dreamteam.com/ep73/'
        self.language_code = 2
        self.platform_type = 0
        self.auth_count = 1
        self.locale_identifer = 'en_DE'
        self.publicKey = '''-----BEGIN PUBLIC KEY-----
						MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0fjPgtnWaWq2LGfLzR9HraEX
						D9M76SXhJH2ld1oE/U6kVfggpfwXI42SEVmEQytOPn6RjVdBATYaBgKsMbPee1pR
						8Tk1sQD6bA+8IBPoSogqZYSNdRPnAaASCNEVOd+29hjS0mMCLUu7XezctkAjkW8a
						WsRwn+8fvXuU2pSg9wIDAQAB
						-----END PUBLIC KEY-----'''
        self.db = Database()

    def getUnitName(self, id):
        id = str(id)
        if id in units.data:
            return units.data[id]['name']
        return None

    def getUnitRarity(self, id):
        id = str(id)
        if id in units.data:
            return int(units.data[id]['rarity'])
        return None

    def setproxy(self, prox=None):
        self.s.proxies.update({
            'http': 'http://127.0.0.1:8888',
            'https': 'https://127.0.0.1:8888',
        })

    def rndHex(self, n):
        res = ''.join([random.choice('0123456789ABCDEF')
                       for x in range(n)]).lower()
        if n == 32:
            self.log('rndHex called %s' % (res))
        return res

    def rndBytes(self, n):
        return os.urandom(n)

    def generateMaskData(self):
        key = RSA.importKey(self.publicKey)
        cipher = PKCS1_OAEP.new(key)
        self.rndkey = self.rndBytes(32)
        self.log('rndkey:%s' % (self.rndkey.encode('hex')))
        return base64.encodestring(cipher.encrypt(self.rndkey)).replace(
            '\n', '')

    def xor(self, v1, v2):
        #self.log('xor v1:%s v2:%s'%(len(v1),len(v2)))
        return ''.join([
            chr(ord(a) ^ ord(b)).encode('hex') for (a, b) in zip(v1, v2)
        ]).decode('hex')

    def md5(self, s):
        m = hashlib.md5()
        m.update(s)
        return m.hexdigest()

    def resemara_id(self):
        return self.md5('?%s-%s-%s-%s-%s com.klab.captain283.global' %
                        (self.rndHex(8).upper(), self.rndHex(4).upper(),
                         self.rndHex(4).upper(), self.rndHex(4).upper(),
                         self.rndHex(12).upper())).upper()

    def calcDigest(self, raw, key=None):
        if not key:
            key = self.key
        raw = raw.replace('https://gl-game.tsubasa-dreamteam.com/ep68', '')
        hashed = hmac.new(key, raw, sha1)
        return hashed.digest().encode("hex").rstrip('\n')

    def log(self, msg):
        print '[%s]%s' % (time.strftime('%H:%M:%S'), msg.encode('utf-8'))

    def callAPI(self, url, data, key=None):
        uris = []
        uris.append('p=i')
        if hasattr(self, 'mv'):
            uris.append('mv=%s' % (self.mv))
        uris.append('id=%s' % self.rndHex(17))
        if hasattr(self, 'user_id'):
            uris.append('u=%s' % (self.user_id))
            uris.append('t=%s' % (int(time.time())))
            uris.append('lang=En')
        finurl = self.game_api + url + '?' + '&'.join(uris)
        data = '[%s,"%s"]' % (
            data,
            self.calcDigest('%s %s' %
                            (re.sub('.*ep[0-9]*', '', finurl), data), key))
        r = self.s.post(finurl, data=data)
        if r.status_code <> 200:
            self.log('bad status code:%s' % (r.status_code))
            return None
        jdata = json.loads(r.content)
        if 'update_info' in jdata[3] and 'user' in jdata[3]['update_info']:
            self.userid = jdata[3]['update_info']['user']['id']
            self.log('welcome %s:%s' %
                     (jdata[3]['update_info']['user']['id'],
                      jdata[3]['update_info']['user']['name']))
        if 'authorization_count' in r.content:
            return self.callAPI(
                url,
                '{"user_id":%s,"auth_count":%s,"mask":"%s","asset_state":"","resemara_id":"%s"}'
                % (self.user_id,
                   jdata[3]['invalid_auth_count']['authorization_count'] + 1,
                   self.generateMaskData(), self.resemara_id()))
        if 'user_id' in jdata[3]:
            self.user_id = jdata[3]['user_id']
        if 'authorization_key' in r.content:
            self.log('authorization_key:%s' % (base64.b64decode(
                jdata[3]['authorization_key']).encode('hex')))
            self.key = self.xor(
                self.rndkey, base64.b64decode(jdata[3]['authorization_key']))
            self.pw = self.key
        if 'session_key' in jdata[3]:
            self.log('session_key:%s' %
                     (base64.b64decode(jdata[3]['session_key']).encode('hex')))
            self.key = self.xor(self.rndkey,
                                base64.b64decode(jdata[3]['session_key']))
        if 'master_version' in jdata[3]:
            self.mv = jdata[3]['master_version']['version']
        return jdata

    def login_startup(self):
        return self.callAPI(
            'login/startup',
            '{"language_code":%s,"platform_type":%s,"mask":"%s","locale_identifer":"en_DE"}'
            %
            (self.language_code, self.platform_type, self.generateMaskData()),
            'Sqm+kQWVo679raYK')

    def login_login(self):
        res = self.callAPI(
            'login/login',
            '{"user_id":%s,"auth_count":%s,"mask":"%s","asset_state":"","resemara_id":"%s"}'
            % (self.user_id, self.auth_count, self.generateMaskData(),
               self.resemara_id()))
        self.playable_card_by_id = res[3]['update_info']['playable_card_by_id']
        return res

    def dataLink_getDataLinkStatusList(self):
        return self.callAPI('dataLink/getDataLinkStatusList', 'null')

    def user_updateGdprConsents(self):
        return self.callAPI(
            'user/updateGdprConsents',
            '{"consents":[{"consent_type":2,"has_consented":true},{"consent_type":3,"has_consented":true},{"consent_type":4,"has_consented":true}]}'
        )

    def tutorial_download(self):
        return self.callAPI('tutorial/download', 'null')

    def user_checkAdIdentifier(self):
        return self.callAPI(
            'user/checkAdIdentifier',
            '{"consents":[{"consent_type":2,"has_consented":false,"ad_identifier":"00000000-0000-0000-0000-000000000000"}]}'
        )

    def tutorial_kickoff(self):
        return self.callAPI('tutorial/kickoff', 'null')

    def tutorial_matchResult(self):
        return self.callAPI('tutorial/matchResult', 'null')

    def user_fetchHomeInfo(self):
        res = self.callAPI('user/fetchHomeInfo', 'null')
        userinfo = res
        self.userinfo = userinfo
        self.db.addAccount(
            self.user_id,
            base64.b64encode(self.pw)
            if hasattr(self, 'pw') else base64.b64encode(self.key),
            userinfo[3]['update_info']['user']['coin'],
            userinfo[3]['update_info']['user_billing_info']['free_stone'],
            self.total_login_days if hasattr(self, 'total_login_days') else 1)
        return res

    def notification_updateNotificationSetting(self):
        return self.callAPI(
            'notification/updateNotificationSetting',
            '{"device_token":"eM06C_hzOi8:APA91bGVb4RdFnOZcQ7ai64PS1J9uhLnlB33HTg8GshI7pUO-mclrh8IPbsjgZCyE6F7mN58HAQEozxLu-v2O-5_oME_iNJOLnKveZYstXg_tPQRj25Yp97ZyD2Q5_054cMwQSSwBmxb","is_admin_notice":true,"is_ap_max":true,"is_league_result":true,"is_event_reservation":true,"is_coop_recruitment":true}'
        )

    def gacha_fetch(self):
        return self.callAPI('gacha/fetch', 'null')

    def gacha_play(self, gacha_product_info_id=99, play_count=1):
        return self.callAPI(
            'gacha/play',
            '{"gacha_product_info_id":%s,"play_count":%s,"selected_category":1,"mixer_materials":[]}'
            % (gacha_product_info_id, play_count))

    def gacha_fixRetry(self):
        return self.callAPI('gacha/fixRetry', 'null')

    def formation_updateDeckList(self, card_ids, captain_card_id):
        return self.callAPI(
            'formation/updateDeckList',
            '{"deck_list":[{"id":1,"user_id":%s,"formation_id":1,"name":"Deck 1","is_gvg_deck":false,"expiration_date":0,"card_ids":[%s],"team_effect_card_ids":[0,0,0],"captain_card_id":%s,"uniform_numbers":[1,3,4,5,2,7,6,8,10,9,11,12,13,14,15,16]}]}'
            % (self.user_id, ','.join(str(x)
                                      for x in card_ids), captain_card_id))

    def user_setProfile(self, name='Aguilero', team_name='FC.Aguilo'):
        return self.callAPI(
            'user/setProfile',
            '{"name":"%s","team_name":"%s","comment":"Nice to meet ya!"}' %
            (name, team_name))

    def tutorial_end(self):
        return self.callAPI('tutorial/end', 'null')

    def loginBonus_fetchBonus(self):
        return self.callAPI('loginBonus/fetchBonus', 'null')

    def present_fetch(self):
        return self.callAPI('present/fetch', 'null')

    def present_receiveMultiple(self, present_ids):
        return self.callAPI(
            'present/receiveMultiple',
            '{"present_ids":[%s]}' % (','.join(str(x) for x in present_ids)))

    def mission_fetch(self):
        return self.callAPI('mission/fetch', 'null')

    def mission_receiveMultiple(self, mission_ids):
        return self.callAPI(
            'mission/receiveMultiple',
            '{"mission_ids":[%s]}' % (','.join(str(x) for x in mission_ids)))

    def profile_getOne(self):
        res = self.callAPI('profile/getOne', '{"user_id":%s}' % (self.user_id))
        self.total_login_days = res[3]['profile']['total_login_days']
        return res

    def reroll(self):
        self.login_startup()
        team = self.login_login()
        card_ids = []
        for c in team[3]['update_info']['playable_card_by_id']:
            if type(c) == long:
                if len(card_ids) == 15: break
                card_ids.append(c)
        time.sleep(6)
        self.dataLink_getDataLinkStatusList()
        self.user_updateGdprConsents()
        self.tutorial_download()
        self.user_checkAdIdentifier()
        self.tutorial_kickoff()
        self.tutorial_matchResult()
        self.user_fetchHomeInfo()
        self.gacha_fetch()
        self.gacha_play()
        card = self.gacha_fixRetry()
        self.gacha_fetch()
        self.user_fetchHomeInfo()
        captain_card_id = card[3]['update_info']['playable_card_by_id'][0]
        card_ids.append(captain_card_id)
        self.formation_updateDeckList(card_ids, captain_card_id)
        self.user_fetchHomeInfo()
        self.user_setProfile()
        self.tutorial_end()
        self.user_fetchHomeInfo()
        self.loginBonus_fetchBonus()
        self.getAllGifts()
        self.user_fetchHomeInfo()

    def getAllGifts(self):
        present_list = self.present_fetch(
        )[3]['present_response']['present_list']
        presents = []
        for p in present_list:
            presents.append(p['id'])
        if len(presents) >= 1:
            self.present_receiveMultiple(presents)

    def getAllGacha(self):
        elements = self.gacha_fetch()[3]['elements']
        for p in elements:
            if 'product_info_list' not in p: continue
            for q in p['product_info_list']:
                for t in q:
                    for e in q[t]:
                        if e['cost'] <= 0:
                            self.gacha_play(e['id'], e['play_count'])

    def getAllMissions(self):
        present_list = self.mission_fetch()[3]['mission_list']
        missions = []
        for p in present_list:
            if p['is_complete'] and p['is_reward']:
                missions.append(p['mission_id'])
        if len(missions) >= 1:
            self.mission_receiveMultiple(missions)

    def setUserId(self, id):
        self.user_id = id

    def setPassword(self, pw):
        self.key = pw

    def save(self, d, f):
        with io.open(f, 'a', encoding='utf8') as the_file:
            the_file.write('%s\n' % (unicode(d)))

    def exportPlayers(self):
        cards = []
        cards.append('player id:%s' % (self.userid))
        good = [13100131, 4200181, 13700181, 40300081]
        for p in self.playable_card_by_id:
            if type(p) != long:
                cardid = p['master_id']
                if cardid not in good: continue
                rarity = self.getUnitRarity(cardid)
                if rarity >= 40:
                    self.log('card id:%s name:%s rarity:%s' %
                             (cardid, self.getUnitName(cardid), rarity))
                    cards.append(str(cardid))
        if len(cards) > 1:
            self.save(','.join(cards), 'accounts_0_01.txt')

    def dailylogin(self):
        self.login_login()
        time.sleep(1)
        self.dataLink_getDataLinkStatusList()
        self.loginBonus_fetchBonus()
        self.getAllGifts()
        self.getAllMissions()
        self.getAllGacha()
        self.profile_getOne()
        self.user_fetchHomeInfo()
Beispiel #2
0
class API(object):
    def __init__(self):
        self.s = requests.Session()
        self.s.headers.update({
            'Content-Type': 'text/json',
            'User-Agent': 'global/434 CFNetwork/808.2.16 Darwin/16.3.0',
            'Accept-Language': 'en-gb',
            'X-Unity-Version': '2017.4.17f1'
        })
        self.s.verify = False
        if 'win' in sys.platform:
            self.s.proxies.update({
                'http': 'http://127.0.0.1:8888',
                'https': 'https://127.0.0.1:8888',
            })
        self.crypter = RijndaelEncryptor()
        self.key = 'abcdefghijkrstuv024680wxyzlmnopq'
        self.seq = 2
        self.version = '1.43.12'
        self.db = Database()

    def reroll(self):
        n = NEONAPI()
        n.auto_login()
        token = n.login()
        if 'access_token' in token['value']:
            self.setAccess_token(token['value']['access_token'])
            self.log(token['value']['access_token'])

    def setAccess_token(self, access_token):
        self.access_token = access_token

    def setUdid(self, udid):
        n = NEONAPI()
        n.setUdid(udid)
        self.udid = udid
        token = n.login()
        if 'access_token' in token['value']:
            self.setAccess_token(token['value']['access_token'])
            self.log(token['value']['access_token'])

    def setRegion(self, region):
        self.region = {
            'global': 600,
            'sg': 400,
            'jp': 300,
            'tw': 500,
            'eu': 700
        }[region.lower()]

    def decrypt(self, data):
        return self.crypter.decrypt(data, self.key)

    def encrypt(self, data):
        return self.crypter.encrypt(data, self.key)

    def getUnitStars(self, id):
        id = str(id)
        return int(units.data[id]['stars'])

    def getUnitName(self, id):
        id = str(id)
        return units.data[id]['name']

    def getUnitType(self, id):
        id = str(id)
        return int(units.data[id]['type'])

    def callAPI(self, data=None, url=None):
        jdata = json.loads(data, object_pairs_hook=OrderedDict)
        if 'version' in jdata: jdata['version'] = self.version
        if 'access_token' in jdata: jdata['access_token'] = self.access_token
        if 'seq' in jdata: jdata['seq'] = str(self.seq)
        if 'pmang_usn' in jdata:
            jdata['pmang_usn'] = self.access_token.split('|')[0]
        data = json.dumps(jdata, separators=(',', ':'))
        if url:
            r = self.s.post('%s/%s' % (url, inspect.stack()[1][3]),
                            data=self.encrypt(data))
        else:
            r = self.s.post('%s/%s' % (self.game_url, inspect.stack()[1][3]),
                            data=self.encrypt(data))
        self.seq += 1
        decoded_data = self.decrypt(r.content)
        res = json.loads(decoded_data)
        if 'maintenance' in res:
            self.log('Server Maintenance..')
            self.isMaintenance = True
            exit(1)
        if 'result' in res and res['result'] <> 0:
            self.log('%s(): %s' % (inspect.stack()[1][3], res['result']))
        if 'user_id' in res:
            self.log('hello %s:%s' % (res['user_id'], res['owner_index']))
        return res

    def log(self, msg):
        try:
            print '[%s] %s' % (time.strftime('%H:%M:%S'),
                               msg.decode().encode('utf-8'))
        except:
            print '[%s] %s' % (time.strftime('%H:%M:%S'),
                               msg.encode('utf-8', 'ignore'))

    def ownerGuildInfo(self, data):
        return self.callAPI(data)

    def mailInvenAllRecv(self, data):
        return self.callAPI(data)

    def itemInvenRandomBoxUse(self, data):
        return self.callAPI(data)

    def userCampaignSeason2List(self, data):
        return self.callAPI(data)

    def userDuelData(self, data):
        return self.callAPI(data)

    def selectFriendList(self, data):
        return self.callAPI(data)

    def connectEventCheck(self, data):
        return self.callAPI(data)

    def selectFriendRequestList(self, data):
        return self.callAPI(data)

    def selectActiveItem(self, data):
        return self.callAPI(data)

    def newStarDuelSwordCheck(self, data):
        return self.callAPI(data)

    def selectBillingDataBasic(self, data):
        return self.callAPI(data)

    def selectPartyRaidHistoryDataList(self, data):
        return self.callAPI(data)

    def userCampaignData(self, data):
        return self.callAPI(data)

    def requestGuildwarDateInfo(self, data):
        return self.callAPI(data)

    def updateCharInvenState(self, data):
        return self.callAPI(data)

    def userCampaignSeason2Data(self, data):
        return self.callAPI(data)

    def loginUser(self, data):
        res = self.callAPI(data)
        if not self.db.getAccount(self.udid):
            self.db.addAccount(self.udid, res['gold'], res['jewelry'],
                               res['level'], res['owner_index'])
        else:
            self.db.updateAccount(self.udid, res['gold'], res['jewelry'],
                                  res['level'], res['owner_index'])
        return res

    def userCampaignList(self, data):
        return self.callAPI(data)

    def arenaPhaseInfo(self, data):
        return self.callAPI(data)

    def selectGuildInvitation(self, data):
        return self.callAPI(data)

    def selectHotdeal(self, data):
        return self.callAPI(data)

    def globalDate(self, data):
        return self.callAPI(data)

    def userCampaignStageClear(self, data):
        return self.callAPI(data)

    def userNewStarDuelData(self, data):
        return self.callAPI(data)

    def enterChannel(self, data):
        return self.callAPI(data)

    def missionReward(self, data):
        return self.callAPI(data)

    def userArenaData(self, data):
        return self.callAPI(data)

    def selectItemInven(self, data):
        return self.callAPI(data)

    def checkAttendanceReward(self, data):
        return self.callAPI(data)

    def selectTerritoryUserInfo(self, data):
        return self.callAPI(data)

    def mailInvenRecv(self, data):
        return self.callAPI(data)

    def announce(self, data):
        return self.callAPI(data)

    def webViewEventCheck(self, data):
        return self.callAPI(data)

    def eventDate(self, data):
        return self.callAPI(data)

    def joinUser(self, data):
        return self.callAPI(data)

    def selectBuyLimit(self, data):
        return self.callAPI(data)

    def selectCharInven(self, data, makedeck=False):
        res = self.callAPI(data)
        self.deck_list = set([])
        used = set([])
        forbidden = set([4])
        for unit in res:
            if len(unit) <= 2:
                if makedeck:
                    if res[unit]['code'] not in used:
                        if self.getUnitType(res[unit]['code']) in forbidden:
                            continue
                        if 'Slime Girl' in self.getUnitName(res[unit]['code']):
                            continue
                        print self.getUnitStars(
                            res[unit]['code']), self.getUnitName(
                                res[unit]['code']), self.getUnitType(
                                    res[unit]['code']), res[unit]['code']
                        self.deck_list.add(res[unit]['inven_index'])
                        used.add(res[unit]['code'])
                if self.getUnitStars(res[unit]['code']) > 4:
                    self.log('%s with: %s*' %
                             (self.getUnitName(res[unit]['code']),
                              self.getUnitStars(res[unit]['code'])))
                    if self.getUnitStars(res[unit]['code']) > 4:
                        if not hasattr(self, 'five_stars'): self.five_stars = 0
                        self.five_stars += 1
        return res

    def movementCheck(self, data):
        return self.callAPI(data)

    def worldbossUserInfo(self, data):
        return self.callAPI(data)

    def selectFirstPackage(self, data):
        return self.callAPI(data)

    def changeDeviceCertificateCancel(self, data):
        return self.callAPI(data)

    def selectMailInven(self, data):
        return self.callAPI(data)

    def selectModeBeforeSeasonTopInfo(self, data):
        return self.callAPI(data)

    def userCampaignStageStart(self, data):
        return self.callAPI(data)

    def scoutShopBuy(self, data):
        return self.callAPI(data)

    def missionInvenUpdate(self, data):
        return self.callAPI(data)

    def requestSelectImportantNotice(self, data):
        return self.callAPI(data)

    def userRuneModeData(self, data):
        return self.callAPI(data)

    def missionInvenInsert(self, data):
        return self.callAPI(data)

    def changeDeviceCertificate(self, data):
        return self.callAPI(data)

    def selectMissionInven(self, data):
        return self.callAPI(data)

    def selectEquipInven(self, data):
        return self.callAPI(data)

    def selectSoulItemDictionary(self, data):
        return self.callAPI(data)

    def selectSoulItemInven(self, data):
        return self.callAPI(data)

    def selectTerritoryInven(self, data):
        return self.callAPI(data)

    def selectDailyData(self, data):
        return self.callAPI(data)

    def selectDictionary(self, data):
        return self.callAPI(data)

    def selectSavedUserDeckList(self, data):
        return self.callAPI(data)

    def arenaUsableCharSelect(self, data):
        return self.callAPI(data)

    def arenaTournamentInfo(self, data):
        return self.callAPI(data)

    def arenaTournamentCheerInfo(self, data):
        return self.callAPI(data)

    def selectCastleList(self, data):
        return self.callAPI(data)

    def selectLevelUpPackage(self, data):
        return self.callAPI(data)

    def selectReservation(self, data):
        return self.callAPI(data)

    def selectRepeatBuyList(self, data):
        return self.callAPI(data)

    def requestCostumeInvenInfo(self, data):
        return self.callAPI(data)

    def selectShopDiscount(self, data):
        return self.callAPI(data)

    def selectModeTurnCountRecord(self, data):
        return self.callAPI(data)

    def backgroundBattleAllStop(self, data):
        return self.callAPI(data)

    def selectRecoveryLimitList(self, data):
        return self.callAPI(data)

    def checkPmangUsn(self, data):
        return self.callAPI(data)

    def selectVersion(self, data):
        return self.callAPI(data,
                            url='http://mbrowng-glb-maintenance.pmang.cloud')

    def selectServerInfoRequest(self, data):
        res = self.callAPI(data,
                           url='http://mbrowng-glb-maintenance.pmang.cloud')
        for s in res:
            if self.region == res[s]['region']:
                self.game_url = res[s]['game_server_info'][:-1]
                self.log('self.game_url:%s' % (self.game_url))
                break
        return res

    def login(self):
        self.selectVersion('{"seq":"2","market_type":"1"}')
        self.selectServerInfoRequest('{"seq":"3"}')
        self.loginUser(
            '{"seq":"4","market_type":"1","version":"1.38.10","access_token":"251737673|653|IPAD|KR|23a2e656f121dee33353f5d7ec0eb66b729291d6|1554649259248"}'
        )
        self.userCampaignData('{"seq":"5"}')
        self.userCampaignSeason2Data('{"seq":"6"}')
        self.userRuneModeData('{"seq":"7"}')
        self.userCampaignList('{"seq":"8"}')
        self.userCampaignSeason2List('{"seq":"9"}')
        self.selectMissionInven('{"seq":"10"}')
        self.userArenaData('{"seq":"11"}')
        self.userDuelData('{"seq":"12"}')
        self.userNewStarDuelData('{"seq":"13"}')
        self.selectItemInven('{"seq":"14"}')
        self.selectActiveItem('{"seq":"15"}')
        self.selectEquipInven('{"seq":"16"}')
        self.selectSoulItemDictionary('{"seq":"17"}')
        self.selectSoulItemInven('{"seq":"18"}')
        self.selectTerritoryInven('{"seq":"19"}')
        self.selectTerritoryUserInfo('{"seq":"20"}')
        self.selectDailyData('{"seq":"21"}')
        self.enterChannel('{"seq":"22","channel":"0"}')
        self.selectBuyLimit('{"seq":"23"}')
        self.selectDictionary('{"seq":"24"}')
        self.selectSavedUserDeckList('{"seq":"25"}')
        self.arenaPhaseInfo('{"seq":"26"}')
        self.arenaUsableCharSelect('{"seq":"27"}')
        self.arenaTournamentInfo('{"seq":"28"}')
        self.arenaTournamentCheerInfo('{"seq":"29"}')
        self.selectCastleList('{"seq":"30"}')
        self.selectLevelUpPackage('{"seq":"31"}')
        self.selectReservation('{"seq":"32"}')
        self.selectFirstPackage('{"seq":"33"}')
        self.selectRepeatBuyList('{"seq":"34"}')
        self.selectBillingDataBasic('{"seq":"35"}')
        self.requestCostumeInvenInfo('{"seq":"36"}')
        self.selectPartyRaidHistoryDataList('{"seq":"37"}')
        self.selectShopDiscount('{"seq":"38"}')
        self.selectModeTurnCountRecord('{"seq":"39"}')
        self.backgroundBattleAllStop('{"seq":"40"}')
        self.selectRecoveryLimitList('{"seq":"41"}')
        self.selectCharInven('{"seq":"42"}')
        self.globalDate('{"seq":"43"}')
        self.eventDate('{"seq":"44"}')
        self.ownerGuildInfo('{"seq":"45"}')
        self.selectFriendRequestList('{"seq":"46"}')
        self.selectFriendList('{"seq":"47"}')
        self.requestSelectImportantNotice(
            '{"seq":"48","last_index":"0","language_type":"1"}')
        self.selectPartyRaidHistoryDataList('{"seq":"49"}')
        self.worldbossUserInfo('{"seq":"50"}')
        self.checkAttendanceReward('{"seq":"51"}')
        self.selectGuildInvitation('{"seq":"52"}')
        self.requestGuildwarDateInfo('{"seq":"53"}')
        self.selectModeBeforeSeasonTopInfo('{"seq":"54"}')
        self.selectMailInven(
            '{"seq":"55","last_index":"0","request_count":"100"}')
        self.connectEventCheck('{"seq":"56","lang_type":"1"}')
        self.selectMailInven(
            '{"seq":"57","last_index":"0","request_count":"100"}')
        self.webViewEventCheck('{"seq":"58"}')
        self.announce('{"seq":"59"}')
        self.selectBillingDataBasic('{"seq":"60"}')
        self.selectHotdeal('{"seq":"61","hotdeal_index":"8"}')

    def generateUsername(self):
        return 'Rain%s' % (random.randint(10000, 90000))

    def finishTutorial(self):
        self.loginUser(
            '{"seq":"4","market_type":"1","version":"1.38.10","access_token":"251702245|653|IPAD|KR|4805c07b21284817069e4016a76ed8783cc7ab57|1554637238658"}'
        )
        self.checkPmangUsn('{"seq":"5","pmang_usn":"251702245"}')
        self.joinUser(
            '{"seq":"6","market_type":"1","user_id":"Rain","access_token":"251702245|653|IPAD|KR|4805c07b21284817069e4016a76ed8783cc7ab57|1554637238658"}'
        )
        self.joinUser(
            '{"seq":"7","market_type":"1","user_id":"%s","access_token":"%s"}'
            % (self.generateUsername(), self.access_token))
        self.selectItemInven('{"seq":"8"}')
        self.selectCharInven('{"seq":"9"}')
        self.enterChannel('{"seq":"10","channel":"0"}')
        self.selectBuyLimit('{"seq":"11"}')
        self.selectBillingDataBasic('{"seq":"12"}')
        self.userCampaignData('{"seq":"13"}')
        self.userCampaignSeason2Data('{"seq":"14"}')
        self.userCampaignList('{"seq":"15"}')
        self.userCampaignSeason2List('{"seq":"16"}')
        self.userRuneModeData('{"seq":"17"}')
        self.selectFirstPackage('{"seq":"18"}')
        self.selectTerritoryUserInfo('{"seq":"19"}')
        self.arenaPhaseInfo('{"seq":"20"}')
        self.userArenaData('{"seq":"21"}')
        self.userDuelData('{"seq":"22"}')
        self.userNewStarDuelData('{"seq":"23"}')
        self.newStarDuelSwordCheck('{"seq":"24"}')
        self.globalDate('{"seq":"25"}')
        self.eventDate('{"seq":"26"}')
        self.ownerGuildInfo('{"seq":"27"}')
        self.selectFriendRequestList('{"seq":"28"}')
        self.selectFriendList('{"seq":"29"}')
        self.requestSelectImportantNotice(
            '{"seq":"30","last_index":"0","language_type":"1"}')
        self.selectPartyRaidHistoryDataList('{"seq":"31"}')
        self.worldbossUserInfo('{"seq":"32"}')
        self.checkAttendanceReward('{"seq":"33"}')
        self.selectGuildInvitation('{"seq":"34"}')
        self.requestGuildwarDateInfo('{"seq":"35"}')
        self.selectModeBeforeSeasonTopInfo('{"seq":"36"}')
        self.missionInvenInsert(
            '{"seq":"37","0":{"code":"9001","value":"1"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"38","0":{"code":"9301","value":"1"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"39","0":{"code":"9308","value":"1"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"40","0":{"code":"9701","value":"0"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"41","0":{"code":"1001","value":"0"},"1":{"code":"1002","value":"0"},"2":{"code":"1003","value":"0"},"3":{"code":"1004","value":"0"},"4":{"code":"1005","value":"0"},"5":{"code":"1006","value":"0"},"6":{"code":"1007","value":"0"},"7":{"code":"1008","value":"0"},"8":{"code":"1009","value":"0"},"9":{"code":"1010","value":"0"},"10":{"code":"1018","value":"0"},"11":{"code":"1019","value":"0"},"12":{"code":"1020","value":"0"},"13":{"code":"1021","value":"0"},"14":{"code":"1022","value":"0"},"15":{"code":"1023","value":"0"},"16":{"code":"1024","value":"0"},"17":{"code":"1017","value":"0"},"18":{"code":"1516","value":"0"},"19":{"code":"1517","value":"0"},"20":{"code":"1518","value":"0"},"21":{"code":"1519","value":"0"},"22":{"code":"1520","value":"0"},"23":{"code":"1831","value":"0"},"list_count":"24"}'
        )
        self.missionInvenUpdate('{"seq":"42","code":"1520","value":"50000"}')
        self.selectBuyLimit('{"seq":"43"}')
        self.connectEventCheck('{"seq":"44","lang_type":"1"}')
        mails = self.selectMailInven(
            '{"seq":"45","last_index":"0","request_count":"100"}')
        last_index = mails['5']['inven_index']
        self.webViewEventCheck('{"seq":"46"}')
        self.announce('{"seq":"47"}')
        self.selectBillingDataBasic('{"seq":"48"}')
        self.selectHotdeal('{"seq":"49","hotdeal_index":"8"}')
        self.selectActiveItem('{"seq":"50"}')
        self.selectMailInven(
            '{"seq":"51","last_index":"%s","request_count":"94"}' %
            (last_index))
        self.webViewEventCheck('{"seq":"52"}')
        self.mailInvenAllRecv(
            '{"seq":"53","inven_index_list":{"0":{"index":"52142457"},"1":{"index":"52142458"},"2":{"index":"52142460"},"3":{"index":"52142461"},"list_count":"4"}}'
        )
        self.missionInvenUpdate('{"seq":"54","code":"1520","value":"75000"}')
        self.mailInvenRecv('{"seq":"55","inven_index":"52142456"}')
        self.missionInvenInsert(
            '{"seq":"56","0":{"code":"3046","value":"1"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"57","0":{"code":"3066","value":"1"},"list_count":"1"}')
        self.updateCharInvenState(
            '{"seq":"58","inven_index":"133211225","inven_state":"1"}')
        self.webViewEventCheck('{"seq":"59"}')
        self.mailInvenRecv('{"seq":"60","inven_index":"52142459"}')
        self.missionInvenUpdate('{"seq":"61","code":"3046","value":"2"}')
        self.missionInvenUpdate('{"seq":"62","code":"3066","value":"2"}')
        self.updateCharInvenState(
            '{"seq":"63","inven_index":"133211268","inven_state":"1"}')
        self.webViewEventCheck('{"seq":"64"}')
        self.itemInvenRandomBoxUse('{"seq":"65","inven_index":"125745425"}')
        self.missionInvenInsert(
            '{"seq":"66","0":{"code":"3041","value":"1"},"list_count":"1"}')
        self.missionInvenInsert(
            '{"seq":"67","0":{"code":"3061","value":"1"},"list_count":"1"}')
        self.webViewEventCheck('{"seq":"68"}')
        self.itemInvenRandomBoxUse('{"seq":"69","inven_index":"125745426"}')
        self.missionInvenUpdate('{"seq":"70","code":"3041","value":"2"}')
        self.missionInvenInsert(
            '{"seq":"71","0":{"code":"3056","value":"1"},"list_count":"1"}')

    def getMail(self):
        mails = self.selectMailInven(
            '{"seq":"57","last_index":"0","request_count":"100"}')
        collectmail = {}
        i = 0
        if len(mails) > 1:
            for m in mails:
                if len(m) > 2: continue
                if mails[m]['item_type'] == 5:
                    self.mailInvenRecv('{"seq":"62","inven_index":"%s"}' %
                                       (str(mails[m]['inven_index'])))
                    continue
                collectmail[str(i)] = {}
                collectmail[str(i)] = {"index": str(mails[m]['inven_index'])}
                i += 1
            collectmail['list_count'] = len(collectmail)
            collectmail = json.dumps(collectmail, separators=(',', ':'))
            self.mailInvenAllRecv('{"seq":"60","inven_index_list":%s}' %
                                  (collectmail))

    def getPermanent(self):
        mails = self.selectItemInven('{"seq":"14"}')
        if len(mails) > 1:
            for m in mails:
                if len(m) > 2: continue
                if mails[m]['code'] < 1000: continue
                self.webViewEventCheck('{"seq":"70"}')
                self.itemInvenRandomBoxUse('{"seq":"67","inven_index":"%s"}' %
                                           (str(mails[m]['inven_index'])))

    def useScroll(self,
                  goods_index=29,
                  create_index=338,
                  count=10,
                  free_scout=0):
        res = self.scoutShopBuy(
            '{"seq":"84","goods_index":"%s","create_index":"%s","count":"%s","free_scout":"%s"}'
            % (goods_index, create_index, count, free_scout))
        for unit in res:
            if len(unit) == 1:
                self.log('%s with: %s*' % (self.getUnitName(
                    res[unit]['code']), self.getUnitName(res[unit]['code'])))
        return res

    def getReward(self, rwd):
        for r in rwd:
            if len(r) <= 2:
                self.missionReward('{"seq":"72","inven_index":"%s"}' %
                                   (rwd[r]['inven_index']))

    def getRandomBox(self, rwd):
        for r in rwd:
            if len(r) <= 2:
                self.log('getRandomBox() reward:%s type:%s' %
                         (r, rwd[r]['type']))
                self.itemInvenRandomBoxUse('{"seq":"61","inven_index":"%s"}' %
                                           (rwd[r]['index']))

    def fakerewards(self, id):
        if id == 1:
            pass
        else:
            pass

    def finishQuest(self, campaign_level, campaign_number):
        self.log('doing campaign_level:%s campaign_number:%s' %
                 (campaign_level, campaign_number))
        #self.selectCharInven('{"seq":"42"}',makedeck=True)
        deck = list(self.deck_list)
        if campaign_number <= 5:
            data = '{"seq":"71","campaign_level":"%s","campaign_number":"%s","dummy_code":"0","friend_index":"0","friend_table_index":"0","deck_list":{"0":{"index":"%s","position":"0","sequence":"1"},"1":{"index":"%s","position":"1","sequence":"2"},"2":{"index":"%s","position":"2","sequence":"3"},"3":{"index":"%s","position":"3","sequence":"4"},"4":{"index":"%s","position":"4","sequence":"5"},"list_count":"5"}}' % (
                campaign_level, campaign_number, deck[0], deck[1], deck[2],
                deck[3], deck[4])
        else:
            data = '{"seq":"61","campaign_level":"%s","campaign_number":"%s","dummy_code":"0","friend_index":"0","friend_table_index":"0","deck_list":{"0":{"index":"%s","position":"1","sequence":"7"},"1":{"index":"%s","position":"3","sequence":"8"},"2":{"index":"%s","position":"5","sequence":"9"},"3":{"index":"%s","position":"7","sequence":"4"},"4":{"index":"%s","position":"9","sequence":"5"},"5":{"index":"%s","position":"10","sequence":"2"},"6":{"index":"%s","position":"11","sequence":"6"},"7":{"index":"%s","position":"13","sequence":"3"},"8":{"index":"%s","position":"15","sequence":"1"},"list_count":"9"}}' % (
                campaign_level, campaign_number, deck[0], deck[1], deck[2],
                deck[3], deck[4], deck[5], deck[6], deck[7], deck[8])
        self.movementCheck('{"seq":"60"}')
        if self.userCampaignStageStart(data)['result'] <> 0:
            self.log('quest not started')
            return
        reward = self.userCampaignStageClear(
            '{"seq":"63","battle_result":"1","expert_booster":"0","event_check":"1","campaign_stars":"3","turn_count":"%s","die_count":"0"}'
            % (random.randint(10, 20)))
        if reward['result'] == 0:
            #self.missionInvenUpdate('{"seq":"64","code":"1004","value":"1"}') # Clear 5 Campaign stages
            #self.missionInvenInsert('{"seq":"65","0":{"code":"4001","value":"1"},"list_count":"1"}') # Win in Campaign 20 times
            #self.missionInvenInsert('{"seq":"66","0":{"code":"4011","value":"1"},"list_count":"1"}') # Keep all your Mercenaries alive 5 times
            self.getRandomBox(reward)
            self.fakerewards(campaign_number)