tejado / pgoapi

Pokemon Go API lib
Other
1.4k stars 447 forks source link

Botting #86

Open Nostrademous opened 8 years ago

Nostrademous commented 8 years ago

I cloned a while ago from @tejado and have been working on implementing features... Got to say things are going well and I'm willing to answer questions people have, but not willing to copy/paste source code since it would lead to more questions and at this point I'm divergent from the current code base by several revisions (some of them structural).

I do not claim to have the best or most advanced bot, but it does what I want it to do... it just leveled a brand new test account from lvl 1 -> 20 in under a day (servers were stable).

Here is what I have figured out thus far:

Feel free to ask questions, but like I said, I won't be releasing code or copy/pasting solutions, but can help with proto formats.

ztukaz commented 8 years ago

detection of bad network conditions or session-key changes by server with auto-reconnect after timeout -> miss Tell me more about this, i don't know too much about sessions

dnsBlah commented 8 years ago

I believe your evolution candies are listed in your inventory as well in the dictionary of the pokemon.

{
            "inventory_item_data": {
              "pokemon_family": {
                "family_id": 124, 
                "candy": 20
              }
            }, 
            "modified_timestamp_ms": 1469131340176
          }

Could you help me out how to trigger egg incubation ? In the inventory I can find if I have a available incubator and its id, how can to get my eggs id How do I trigger it ?

Nostrademous commented 8 years ago

@dnsBlah they are listed in dictionary of pokemons but they all are 0 apparently when you walk it... thanks for the pokemon_family data... that will work

Egg Incubation - the proto is slightly wrong as checked (at least time last time I checked). Notice I have "repeated" whereas what's checked in is "optional". @tejado doesn't provide the .proto files here (not sure why) so it won't be an easy fix for you guys - but that fix is only necessary if you want to incubate multiple eggs at the same time.

message EggIncubators {
  repeated EggIncubator egg_incubator = 1;
}

Below should explain how to use incubators (the code is using my structure, but should give you an idea of how to use it).

def useIncubator(api):
    egg_ids = []
    inc_ids = []
    inv = getInventory(api)
    if inv:
        for items in inv.inventory_delta.inventory_items:
            pk = items.inventory_item_data.pokemon
            if pk and pk is not None:
                if pk.is_egg and not pk.egg_km_walked_start:
                    egg_ids.append(pk.id)
            ics = items.inventory_item_data.egg_incubators
            for ic in ics.egg_incubator:  # <-- requires the fixed proto I mentioned, othewise can't loop
                if ic and ic is not None:
                    if ic.item_id:
                        inc_ids.append(ic.item_id)
    log.debug('Available Eggs for Incubation: %s', egg_ids)
    log.debug('Available Incubators: %s', inc_ids)

    for i in range(0, min(len(egg_ids),len(inc_ids))):
        api.use_item_egg_incubator(item_id=inc_ids[i], pokemon_id=egg_ids[i])
        inc_use = api.call(False)
        log.info('Using Egg Incubator "%s" on "%d"...' % (inc_ids[i], egg_ids[i]))
        inc_pay = getPayload(inc_use)
        if inc_pay:
            inc_resp = rsub.UseItemEggIncubatorResponse()
            inc_resp.ParseFromString(inc_pay)
            print(inc_resp)
        else:
            log.error('Bad Payload - Using Egg Incubator')
Nostrademous commented 8 years ago

@ztukaz - regarding sessions, honestly I'm not super familiar with that. @tejado might honestly know better as this would be a API library / networking / connectivity issue. What I know is from here: https://github.com/Mila432/Pokemon_Go_API/blob/master/api.pyy

dnsBlah commented 8 years ago

@Nostrademous Thanks for the hints I don't think the statements are right, as a pokemon['isegg'] == true does not tell me if its active or not. Instead the incubator tells me with start and target_km_walked But can't link a incubator to an egg :-/

Or is that the reason why I need a other proto to request the inventory? or just try all 9 eggs until positive result?

Nostrademous commented 8 years ago

@dnsBlah pokemon['is_egg'] tells you it's an egg pokemon['egg_km_walked_start'] is only present if it is an active egg (and can only be present if it's an egg) <-- this is why I check that it is NOT present to know it's an available egg

With both those checks you can then record pokemon['id'] as a valid egg for incubation. That is the 'id' you pass as the 2nd argument to api.use_item_egg_incubator()

Also, looks liek POGO fixed the "repeated" 20 hrs ago.... heh, didnt' realize. https://github.com/AeonLucid/POGOProtos/blob/master/src/POGOProtos/Inventory/EggIncubators.proto

dnsBlah commented 8 years ago

Hi @Nostrademous Like I mentioned, the egg does not show anything about start...

{
            "inventory_item_data": {
              "pokemon": {
                "creation_time_ms": 1469191037546, 
                "captured_cell_id": 5171996602586365952, 
                "is_egg": true, 
                "id": 11891607393952159418
              }
            }, 
            "modified_timestamp_ms": 1469191037546
          }

(I have 9 eggs *max ofcourse) All 9 look like this, but I'm walking 1.

{
            "inventory_item_data": {
              "egg_incubators": {
                "egg_incubator": {
                  "target_km_walked": 153.98680114746094, 
                  "start_km_walked": 148.98680114746094, 
                  "pokemon_id": -4500287810622364247, 
                  "item_type": 901, 
                  "incubator_type": 1, 
                  "item_id": "EggIncubatorProto-1928901772253991081"
                }
              }
            }, 
            "modified_timestamp_ms": 1469219038432
          }, 

See what I mean :) I understand the call thanks of your hint above. Seems like almost all calls are just 2 parameters?

But, yet, I don't see a link between the incubator and the egg's I tried partials of the incibator item_id, of the pokemon_id None relates to any of the eggs in my inventory

Nostrademous commented 8 years ago

@dnsBlah I just verified and I guess it changed, you are right, 'km' stuff no longer listed for active eggs. I guess you loop over the eggs and try each one.

If you try to incubate an egg already in use it will return a RESPONSE with: ERROR_POKEMON_ALREADY_INCUBATING = 6;

If you get that, just try a different egg: Results are:

    enum Result {
        UNSET = 0;
        SUCCESS = 1;
        ERROR_INCUBATOR_NOT_FOUND = 2;
        ERROR_POKEMON_EGG_NOT_FOUND = 3;
        ERROR_POKEMON_ID_NOT_EGG = 4;
        ERROR_INCUBATOR_ALREADY_IN_USE = 5;
        ERROR_POKEMON_ALREADY_INCUBATING = 6;
        ERROR_INCUBATOR_NO_USES_REMAINING = 7;
    }
dnsBlah commented 8 years ago

<3 thank you!

dnsBlah commented 8 years ago

It also seems like that the incubators are not shown in the inventory list... Just 1 (the unlimited one), and I have 3

Nostrademous commented 8 years ago

@dnsBlah are you using ITEM_ID=902 for the "extra incubators"? Will have to check if they show up or not, I don't really care actually as when I use incubators I enumerate them differently.

dnsBlah commented 8 years ago

I'm guessing i have it working now.. However I don't get unique id's from my item: 902 But will see tomorrow..

Nostrademous commented 8 years ago

@dnsBlah

def useIncubator(api):
    egg_ids = []
    inc_ids = []
    inv = getInventory(api)
    if inv:
        for items in inv.inventory_delta.inventory_items:
            pk = items.inventory_item_data.pokemon
            if pk and pk is not None:
                if pk.is_egg:
                    egg_ids.append(pk.id)
        for items in inv.inventory_delta.inventory_items:
            ics = items.inventory_item_data.egg_incubators
            for ic in ics.egg_incubator:
                if ic and ic is not None:
                    if ic.item_id: # and not ic.target_km_walked:
                        inc_ids.append(ic.item_id)

    log.debug('All Eggs: %s', egg_ids)
    log.debug('All Incubators: %s', inc_ids)

    for inc in inc_ids:
        for egg in egg_ids:
            api.use_item_egg_incubator(item_id=inc, pokemon_id=egg)
            inc_use = api.call(False)
            inc_pay = getPayload(inc_use)
            if inc_pay:
                inc_resp = rsub.UseItemEggIncubatorResponse()
                inc_resp.ParseFromString(inc_pay)
                if inc_resp.result == 1:
                    log.info('Using Egg Incubator "%s" on "%d"...' % (inc, egg))
                    log.info(inc_resp)
                elif inc_resp.result == 6:
                    continue
                elif inc_resp.result == 5:
                    log.info('Incubator "%s" already in use' % inc)
                    break
            else:
                log.error('Bad Payload - Using Egg Incubator')
ztukaz commented 8 years ago

@Nostrademous

@tejado doesn't provide the .proto files here (not sure why) so it won't be an easy fix for you guys - but that fix is only necessary if you want to incubate multiple eggs at the same time.

Did you imported in this project manually?

dnsBlah commented 8 years ago

@Nostrademous With an other proto class its working fine now :) Also finding other incubators :-)

Nostrademous commented 8 years ago

@dnsBlah glad you got it working :)

I just recently added mass evolving capability to my bot with the use of a Lucky Egg (if I can do more than 125 evolutions at once). This required tracking proper candy and candy requirements (which I do now).

Also added level-up detection and level-up-prize gathering.

Leveled a brand new account to level 22 in under 12 hours - played by bot from level 1 on (without the use of any evolution or lucky-egg).

ztukaz commented 8 years ago

@Nostrademous How did you approach gym ? It should be something like, StartGymBattle, AttackGym and then Put pokemon right?

With StartGymBattle I get always status = 3 and empty results

gym_id = id of the gym (fort)
attacking_pokemon_ids = [idpok1,idpok2]
defending_pokemon_id=id_first_defender
player_latitude=lat
player_longitude=lng

{'responses': {'START_GYM_BATTLE': {}}, 'status_code': 3, 'auth_ticket': {'expire_timestamp_ms': 1469664544675L, 'start': 'tbXTF4N9/wntFc4f0PV6KlBm7kfwsflM2jW/PTtO2tS8jTG9YYAlBBcFTag9PbRo1TFX9NwYDOAiAmuIEpg6bygUFhoHklL52T4i2OWkOgU=', 'end': 'n5kcn21Ojw+YUJZDUBPwwg=='}, 'request_id': 8145806132888207460L}

dnsBlah commented 8 years ago

@Nostrademous Please share the level-up-prize gathering. :-D However I'm sure that the items are gathered anyways when you reach the next level tho.. Still I think it would be good to simulate as much as possible. Because lots of times I end up with more items than my bag can handle ;-)

Nostrademous commented 8 years ago

@ztukaz tackling gyms next - haven't started yet

@dnsBlah - for leveling use the LEVEL_UP_REWARDS call and you must supply your current level (which you can obtain from Inventory ItemData PlayerStats. The response message will have all the info you need, and unlocked new items which you can now start getting at pokestops.

Nostrademous commented 8 years ago

@ztukaz isn't status_code 3 stating that it is a NEUTRAL GYM? Meaning you can't attack it as no one owns it?

dnsBlah commented 8 years ago

Hi @Nostrademous

That's so cool! I now get AWARED_ALREADY Guess I will have to monitor before I level up, to see what the response might be :)

Any idea when this is called in the real game? In the heartbeat ? Btw you also know how the heartbeat actually looks like? :D

Thanks!

Nostrademous commented 8 years ago

@ztukaz @tejado Working on gyms... but apparently I keep failing my request... no idea why: any thoughts?

def battleGym(fort):
    log.info("<>________________________________________________________________________<>")
    log.info("%s GYM NEARBY", TeamColor.DESCRIPTOR.values[fort.owned_by_team].name)
    # get gym details
    gym_proto = getGymDetails(fort)
    if not gym_proto: return 0
    log.info(gym_proto)
    log.info("<>________________________________________________________________________<>")

    ##############################################
    # gather necessary details to start a battle #
    ##############################################

    # check if gym is neutral/unowned - if so deploy to it
    if fort.owned_by_team == 0: # Neutral
        deployPokemonToGym(gym_proto)
        return 0

    # save a boolean checking if we are on the same team
    sameTeam = gl.my_team == fort.owned_by_team

    # find out who is defending
    defending_pokemon = []
    for defender in gym_proto.gym_state.memberships:
        defending_pokemon.append(defender.pokemon_data)

    # determine who we will attack with
    num = 6
    if sameTeam: num = 1
    my_attackers = pickAttackers(num) # this will be a list []

    #log.error('Function not finished, returning out...')
    #return 0

    # start the battle
    log.info('Gym ID: %s' % gym_proto.gym_state.fort_data.id)
    log.info('Attacker list: %s' % str(my_attackers))
    log.info('Defender: %d' % defending_pokemon[0].id)
    gl.api.start_gym_battle(gym_id=gym_proto.gym_state.fort_data.id, attacking_pokemon_ids=my_attackers, defending_pokemon_id=defending_pokemon[0].id, player_latitude=gl.p_lat, player_longitude=gl.p_lng)

    start_reply = gl.api.call(False)
    start_payload = getPayload(start_reply)

Printing the request I see:

 [23:45:59] [ INFO] Gym ID: 555ba115a93546aca7851fed86d3ef6c.16
 [23:45:59] [ INFO] Attacker list: [17xxxxxx list of IDs xxxx 2L]
 [23:45:59] [ INFO] Defender: 568051736162843098
 [23:46:00] [ERROR] startGymBattle() request failed to get payload

and printing the gym_proto result:

 [23:45:59] [ INFO] gym_state {
  fort_data {
    id: "555ba115a93546aca7851fed86d3ef6c.16"
    last_modified_timestamp_ms: 1469762856688
    latitude: 38.854506
    longitude: -77.349488
    owned_by_team: RED
    guard_pokemon_id: FLAREON
    enabled: true
    gym_points: 2000
  }
  memberships {
    pokemon_data {
      id: 568051736162843098
      pokemon_id: FLAREON
      cp: 1002
      stamina: 130
      stamina_max: 130
      move_1: EMBER_FAST
      move_2: FLAMETHROWER
      owner_name: "Firehawk04xx"
      height_m: 0.884167253971
      weight_kg: 9.47041511536
      individual_attack: 15
      individual_defense: 14
      individual_stamina: 1
      cp_multiplier: 0.481684952974
      pokeball: ITEM_GREAT_BALL
      battles_attacked: 10
      battles_defended: 1
      creation_time_ms: 1469753995514
      num_upgrades: 2
      additional_cp_multiplier: 0.0181734859943
      nickname: "Flareon"
    }
    trainer_public_profile {
      name: "Firehawk04xx"
      level: 13
      avatar {
        skin: 2
        hair: 2
        shirt: 1
        hat: 2
        shoes: 1
        backpack: 2
      }
    }
  }
}
ztukaz commented 8 years ago

@Nostrademous I was waiting for you for the same problem, but still didn't get out

Nostrademous commented 8 years ago

@tejado @ztukaz so I get it to work sometimes. The fix is modifying the StartGymBattleMessage() protobuf. the defender_id needs to be fixed64.

However it fails at time when the defender_id happens to be larger than int64 but still smaller than uint64. Python by default treats everything as signed.. but I checked your rpc_api and you use "setattr()" in the _build_sub_requests() function which is pass by reference.. so I am not sure what the issue is. However, there is a pattern for me. For IDs larger than int64 but still 8-bytes I get no reponse payload from server. This also happens for me in Incense-based Pokemon encounters.

dnsBlah commented 8 years ago

I'm just getting responses like: Session.py - line 407 - startGymBattle()

status_code: 3
request_id: 951201794807
returns: ""

Which are not good I guess status 3? Or I will need the request_id to perform AttackGymMessage <-> Response for the actual battle I guess Then still I think status 3 is not good

According to the responses it should be, ERROR_GYM_NEUTRAL ? The gym is not neutral! :-/

@Nostradamous where exactly do you change what in the request message ?

Nostrademous commented 8 years ago

@dnsBlah if you go into the /pgoapi/protos/POGOProtos_src/Networking/Requests/Messages/StartGymBattleMessage.proto

you need to change the defending_id from uint64 to fixed64

then you need to recompile the proto using "protoc" which you need to have installed of version 3.0.0b4

dnsBlah commented 8 years ago

Ahhh I need to do that before compiling it!? Thanks will have a look :)


awesome ;-) but its not always working, however using the same defender, so that part has nothing to do with the fixed64, it's fine I guess I am experiencing some server issues throughout the whole day already tho.

Guess its a matter of a loop now and send back the actions battle_log.battle_actions to the BattleAction proto, together with the battle_id and some BattleAction's... Not sure how to create a BattleAction fo... send an fast-attack for special

joshk6656 commented 8 years ago

@Nostrademous can you show some examples of running / reading forts from map_objects (i keep getting a weird error, at work now so can't supply it at this time)?

dnsBlah commented 7 years ago

You need bigger delay's... You can only call map_objects per 5 seconds. Besides that there are hardly any pokemon shown anymore. Only within a range of 50-70 meters. Dispite the fact your scanning a bigger area

Nostrademous commented 7 years ago

@joshk6656 did @dnsBlah comments help you? It's hard to tell what your errors were. If you are not getting good fort data from map_objects it's probably b/c you are soft-banned for teleporting too much. Otherwise it is fairly straight forward.

Nostrademous commented 7 years ago

Fixed Incense Encounters (catch them all now - reference for fix: https://github.com/AeonLucid/POGOProtos/pull/124

Added support for detecting and catching Lured Pokemon at Lured Pokestops (just check the fort details -> modifiers -> item_id == 501 and check fort.lure_info.lure_expires_timestamp_ms to know if it is lured and for how long). Then use the DiskEncounterMessage and pass the fort.lure_info.fort_id as the spawnpoint_id to the CatchPokemonMessage.

ztukaz commented 7 years ago

in the field attack_actions used in api.attack_gym ( gym_id , battle_id, attack_actions, last_retrieved_actions, player_latitude, player_longitude) Did you find out what the ??? stands for?

attack_actions = {
         "action_start_ms": int(round(time.time() * 1000)),  #time when the attack start
         "target_index": -1,                                                   #??? index of the array of defensor?
         "damage_windows_end_timestamp_mss":,            #time when the attack end
         "active_pokemon_id":                                             #??? id of attacker
         "attacker_index": -1,                                               #??? index of the array of attacker?
         "duration_ms": 750,                                                #??? duration of the attack
         "energy_delta": 7,                                                   #???
         "Type": ,                                                                  #???
         "damage_windows_start_timestamp_mss":            #???
}
Nihisil commented 7 years ago

@Nostrademous if you can tell how to build actions to api.attack_gym it would be really nice :)

I'm using:

        for x in range(1, times):
            start = server_ms + (x * pokemon_move['duration'])
            window_end = start + pokemon_move['duration']
            window_start = window_end - 200
            actions.append({
                'Type': BattleActionType.ACTION_ATTACK,
                'action_start_ms': server_ms + (x * pokemon_move['duration']),
                'duration_ms': pokemon_move['duration'],
                'damage_windows_start_timestamp_mss': window_start,
                'damage_windows_end_timestamp_mss': window_end,
                'active_pokemon_id': pokemon_id,
                'energy_delta': pokemon_move['damage'] - 1,
                'target_index': -1,
                'attacker_index': -1,
            })

and as result:

[
    {
        "damage_windows_end_timestamp_mss": 1470193970167,
        "active_pokemon_id": 14676247372908233687,
        "action_start_ms": 1470193969627,
        "attacker_index": -1,
        "duration_ms": 540,
        "energy_delta": 6,
        "type": 1,
        "target_index": -1,
        "damage_windows_start_timestamp_mss": 1470193969967
    }
    ...
]

But server didn't accept them, so they are not appear in battle_actions section, so probably I missed some required fields, or I'm sending not right values.

Nostrademous commented 7 years ago

@ztukaz and @Nihisil Haven't gotten there yet... I can start gym battle's now, but haven't written code to do the actual fight yet.

Instead I spent my time fixing lures/incense encounters, and writing code to auto-deploy pokemon to gyms that belong to my team but don't have full membership saturation (low hanging fruit for getting into a gym). Also track IV stats of pokemon now.

I will work on battles next, although any bugs I find trump that. Also, had a friend implement a webserver to track my bot and all info I want (example below). web

Nihisil commented 7 years ago

@Nostrademous got it, I'm looking forward to hear update from you about gym battles. If I will be able to fix it, I will let you know what I did.

Also, maybe do you have a dump of official app traffic?

Nostrademous commented 7 years ago

@Nihisil would be much appreciated if you share what you did if you get it working

Regarding web-server - i don't dump any official traffic... the webserver runs from my own bot and uses only bot data. I just download a package with all the pokemon icons and use google's map api to draw pokestops and pokemon on them. All blue circles are pokestops, those that are connected are the path I plan on taking (calculated using some advanced traveling salesman algorithm), the pokemon appear where I find/catch them.

Nihisil commented 7 years ago

@Nostrademous got it. I was interested in official traffic, because of required attributes to attack gym :)

Your webversion looking great, btw.

dnsBlah commented 7 years ago

That web integration is crazy! wow

@nihisil I guess we'll need to use the data/battleaction to setup and retreive actions as we also beed to send last_received actions I think there must be a way to retrieve the last actions through the proto directly as a battleaction type or convert them somehow :-s

Nihisil commented 7 years ago

@dnsBlah yeah, I have sent latest action.

I tried different ways to send attack_actions, but server just doesn't accept them (but it returns OK response) and battle always ends with my defeat.

dnsBlah commented 7 years ago

Currently I'm in the need to resolve my status_code 3 on numerous requests. When I fixed it I'm gonna have a look into the battleAction proto Maybe again something wrong with the proto, and needs to be fixed64 or so, especially the defender id

ztukaz commented 7 years ago

@dnsBlah https://github.com/tejado/pgoapi/pull/161

dnsBlah commented 7 years ago

@ztukaz not 53 102 but 3

STATUS CODE 3!
status_code: 3
request_id: 843962591764
returns: ""

This happend randomly in getMapObjects, useItemCapture, capturePokemon after 10 times, it will re-login from scratch

Sometimes it's fixed, most times it doesnt, also seems location related... Doing getMapObjects max. 10 time with a delay of at least 5 + the loop counter as well as increasing the radius to search with 25 each time.

Nostrademous commented 7 years ago

Making progress here - bunch of things needed to be done first.

First - need to heal my pokemon that I am trying to fight with. If you submit your attacker list but one isn't full health you will get a bad payload return from server. Implementing this now.

Nostrademous commented 7 years ago

Okay, getting far enough to have my first AttackGymMessage accepted... I have realized the first message you send after StartGymBattle must be essentially blank.

I basically send a AttackGym() with only gym_id, battle_id and player lat/lng.

Server replies with a valid "Active Attacker" and "Active Defender" information with health.. as well as Battle Log info with server timestamp (so we can synchronize actions as your time might have skew) and target_index information I can use in following attacks. This info is the same as in StartGymBattle() but at least I know it's somewhat working.

Nihisil commented 7 years ago

@Nostrademous was you able to put your attack message to the server?

So you send message and it appears in the battle_log.

I send first request with empty attack actions list after this I'm trying to send a new attack message in loop, each one second.

So, each second I'm getting response ATTACK_GYM, and I'm trying to use this information to send my next message. But server didn't apply my actions.

If you did it and server accept your actions, can you please send to me example of code with your attack_actions? I wonder what attributes do you use.

Nostrademous commented 7 years ago

@tejado I think the issue currently is that you don't support RPC requests where a "nested list/dictionary" type is passed when building outgoing requests in your API library.

For AttackGym we need to supply a "repeated BattleAction" list and you currently only handle dictionaries, lists and base type but not lists of more complicated types, etc

EDIT: in reality I think we need to pass "repeated class" objects

Nostrademous commented 7 years ago

I believe rpc_api.py needs to be refined to handle a list of BattleActions.

Something like: NOTE - COMPLETELY UNTESTED CODE

                for (key, value) in entry_content.items():
                    if isinstance(value, list):
                        #self.log.error('Found list: %s - trying as repeated', key)
                        for i in value:
                            #self.log.error('Value is %s' % (type(i)))
                            if isinstance(i, RpcSub.BattleAction):
                                self.log.error('RpcSub.BattleAction Class')
                                subsub_ext = get_class('pgoapi.protos.RpcSub_pb2.BattleAction')()
                                try:
                                    setattr(subsub_ext, 'Type', i.Type)
                                    setattr(subsub_ext, 'action_start_ms', i.action_start_ms)
                                    setattr(subsub_ext, 'duration_ms', i.duration_ms)
                                    setattr(subsub_ext, 'target_index', i.target_index) 
                                    r = getattr(subrequest_extension, key)
                                    r.append(subsub_ext)
                                except Exception as e:
                                    self.log.warning('Argument %s with value %s unknown inside %s (Exception: %s)', key, str(value), proto_name, str(e))

                            else:
                                try:
                                    r = getattr(subrequest_extension, key)
                                    r.append(i)
                                except Exception as e:
                                    self.log.warning('Arguement %s with value %s unknown inside %s (Exception %s)', key, i, proto_name, str(e))
Nostrademous commented 7 years ago

Made a separate issue based on the what I believe is preventing this API library from properly attacking gyms.

Nihisil commented 7 years ago

@Nostrademous maybe this will help https://github.com/AeonLucid/POGOProtos/pull/112 when api will start to work again.