Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt code to new PGoApi Code #2357

Merged
merged 8 commits into from
Aug 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions pokemongo_bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class PokemonGoBot(object):
def position(self):
return self.api._position_lat, self.api._position_lng, 0

@position.setter
def position(self, position_tuple):
self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple

def __init__(self, config):
self.config = config
self.fort_timeouts = dict()
Expand Down Expand Up @@ -103,7 +107,7 @@ def get_meta_cell(self):
wild_pokemons += cell["wild_pokemons"]
if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]):
catchable_pokemons += cell["catchable_pokemons"]

# If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved
if len(forts) > 1 or not self.cell:
return {
Expand Down Expand Up @@ -138,14 +142,13 @@ def update_web_location(self, cells=[], lat=None, lng=None, alt=None):
if 'forts' in cell:
for fort in cell['forts']:
if fort.get('type') != 1:
self.api.get_gym_details(
response_gym_details = self.api.get_gym_details(
gym_id=fort.get('id'),
player_latitude=lng,
player_longitude=lat,
gym_latitude=fort.get('latitude'),
gym_longitude=fort.get('longitude')
)
response_gym_details = self.api.call()
fort['gym_details'] = response_gym_details.get(
'responses', {}
).get('GET_GYM_DETAILS', None)
Expand Down Expand Up @@ -239,6 +242,9 @@ def check_session(self, position):

if remaining_time < 60:
logger.log("Session stale, re-logging in", 'yellow')
position = self.position
self.api = ApiWrapper()
self.position = position
self.login()

@staticmethod
Expand All @@ -251,13 +257,13 @@ def is_numeric(s):

def login(self):
logger.log('Attempting login to Pokemon Go.', 'white')
self.api.reset_auth()
lat, lng = self.position[0:2]
self.api.set_position(lat, lng, 0)

while not self.api.login(self.config.auth_service,
str(self.config.username),
str(self.config.password)):
while not self.api.login(
self.config.auth_service,
str(self.config.username),
str(self.config.password)):

logger.log('[X] Login Error, server busy', 'red')
logger.log('[X] Waiting 10 seconds to try again', 'red')
Expand All @@ -267,7 +273,7 @@ def login(self):

def _setup_api(self):
# instantiate pgoapi
self.api = ApiWrapper(PGoApi())
self.api = ApiWrapper()

# provide player position on the earth
self._set_starting_position()
Expand All @@ -286,8 +292,7 @@ def _setup_api(self):
def _print_character_info(self):
# get player profile call
# ----------------------
self.api.get_player()
response_dict = self.api.call()
response_dict = self.api.get_player()
# print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2)))
currency_1 = "0"
currency_2 = "0"
Expand Down Expand Up @@ -368,15 +373,11 @@ def _print_character_info(self):
logger.log('')

def use_lucky_egg(self):
self.api.use_item_xp_boost(item_id=301)
inventory_req = self.api.call()
return inventory_req
return self.api.use_item_xp_boost(item_id=301)

def get_inventory(self):
if self.latest_inventory is None:
self.api.get_inventory()
response = self.api.call()
self.latest_inventory = response
self.latest_inventory = self.api.get_inventory()
return self.latest_inventory

def update_inventory(self):
Expand Down Expand Up @@ -538,9 +539,10 @@ def heartbeat(self):
self.fort_timeouts = {id: timeout for id, timeout
in self.fort_timeouts.iteritems()
if timeout >= time.time() * 1000}
self.api.get_player()
self.api.check_awarded_badges()
self.api.call()
request = self.api.create_request()
request.get_player()
request.check_awarded_badges()
request.call()
self.update_web_location() # updates every tick

def get_inventory_count(self, what):
Expand Down Expand Up @@ -620,14 +622,12 @@ def get_map_objects(self, lat, lng, timestamp, cellid):
if time.time() - self.last_time_map_object < self.config.map_object_cache_time:
return self.last_map_object

self.api.get_map_objects(
self.last_map_object = self.api.get_map_objects(
latitude=f2i(lat),
longitude=f2i(lng),
since_timestamp_ms=timestamp,
cell_id=cellid
)

self.last_map_object = self.api.call()
self.last_time_map_object = time.time()

return self.last_map_object
108 changes: 73 additions & 35 deletions pokemongo_bot/api_wrapper.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,70 @@
import time

from pgoapi.exceptions import (NotLoggedInException,
ServerBusyOrOfflineException)
from pgoapi.exceptions import ServerSideRequestThrottlingException, NotLoggedInException, ServerBusyOrOfflineException, NoPlayerPositionSetException, EmptySubrequestChainException
from pgoapi.pgoapi import PGoApi, PGoApiRequest, RpcApi
from pgoapi.protos.POGOProtos.Networking.Requests_pb2 import RequestType

import logger
import pokemongo_bot.logger as logger
from human_behaviour import sleep


class ApiWrapper(object):
def __init__(self, api):
self._api = api
class ApiWrapper(PGoApi):
def __init__(self):
PGoApi.__init__(self)
self.useVanillaRequest = False

def create_request(self):
RequestClass = ApiRequest
if self.useVanillaRequest:
RequestClass = PGoApiRequest

return RequestClass(
self._api_endpoint,
self._auth_provider,
self._position_lat,
self._position_lng,
self._position_alt
)

def login(self, *args):
# login needs base class "create_request"
self.useVanillaRequest = True
try:
ret_value = PGoApi.login(self, *args)
finally:
# cleanup code
self.useVanillaRequest = False
return ret_value


class ApiRequest(PGoApiRequest):
def __init__(self, *args):
PGoApiRequest.__init__(self, *args)
self.request_callers = []
self.reset_auth()
self.last_api_request_time = None
self.requests_per_seconds = 2

def reset_auth(self):
self._api._auth_token = None
self._api._auth_provider = None
self._api._api_endpoint = None

# wrap api methods
def _can_call(self):
if not self._api._req_method_list or len(self._api._req_method_list) == 0:
raise RuntimeError('Trying to call the api without setting any request')
if self._api._auth_provider is None or not self._api._auth_provider.is_login():
logger.log('Not logged in!', 'red')
def can_call(self):
if not self._req_method_list:
raise EmptySubrequestChainException()

if (self._position_lat is None) or (self._position_lng is None) or (self._position_alt is None):
raise NoPlayerPositionSetException()

if self._auth_provider is None or not self._auth_provider.is_login():
self.log.info('Not logged in')
raise NotLoggedInException()

return True

def _call(self):
return PGoApiRequest.call(self)

def _pop_request_callers(self):
r = self.request_callers
self.request_callers = []
return [i.upper() for i in r]

def _is_response_valid(self, result, request_callers):
def is_response_valid(self, result, request_callers):
if not result or result is None or not isinstance(result, dict):
return False

Expand All @@ -54,22 +84,35 @@ def _is_response_valid(self, result, request_callers):

def call(self, max_retry=5):
request_callers = self._pop_request_callers()
if not self._can_call():
if not self.can_call():
return False # currently this is never ran, exceptions are raised before

request_timestamp = None

api_req_method_list = self._api._req_method_list
api_req_method_list = self._req_method_list
result = None
try_cnt = 0
throttling_retry = 0
while True:
request_timestamp = self.throttle_sleep()
self._api._req_method_list = [req_method for req_method in api_req_method_list] # api internally clear this field after a call
result = self._api.call()
if not self._is_response_valid(result, request_callers):
# self._call internally clear this field, so save it
self._req_method_list = [req_method for req_method in api_req_method_list]
try:
result = self._call()
should_retry = False
except ServerSideRequestThrottlingException:
should_retry = True

if should_retry:
throttling_retry += 1
logger.log("Server is throttling, let's slow down a bit")
if throttling_retry >= max_retry:
raise ServerSideRequestThrottlingException('Server throttled too many times')
sleep(1) # huge sleep ?
continue # skip response checking

if not self.is_response_valid(result, request_callers):
try_cnt += 1
logger.log('Server seems to be busy or offline - try again - {}/{}'.format(try_cnt, max_retry), 'red')

if try_cnt >= max_retry:
raise ServerBusyOrOfflineException()
sleep(1)
Expand All @@ -79,23 +122,18 @@ def call(self, max_retry=5):
self.last_api_request_time = request_timestamp
return result

def login(self, provider, username, password):
return self._api.login(provider, username, password)

# fallback
def __getattr__(self, func):
DEFAULT_ATTRS = ['_position_lat', '_position_lng', '_auth_provider', '_api_endpoint', 'set_position', 'get_position']
if func not in DEFAULT_ATTRS:
if func.upper() in RequestType.keys():
self.request_callers.append(func)
return getattr(self._api, func)
return PGoApiRequest.__getattr__(self, func)

def throttle_sleep(self):
now_milliseconds = time.time() * 1000
required_delay_between_requests = 1000 / self.requests_per_seconds

difference = now_milliseconds - (self.last_api_request_time if self.last_api_request_time else 0)

if (self.last_api_request_time != None and difference < required_delay_between_requests):
if self.last_api_request_time != None and difference < required_delay_between_requests:
sleep_time = required_delay_between_requests - difference
time.sleep(sleep_time / 1000)

Expand Down
3 changes: 1 addition & 2 deletions pokemongo_bot/cell_workers/collect_level_up_reward.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ def work(self):
self.previous_level = self.current_level

def _collect_level_reward(self):
self.bot.api.level_up_rewards(level=self.current_level)
response_dict = self.bot.api.call()
response_dict = self.bot.api.level_up_rewards(level=self.current_level)
if 'status_code' in response_dict and response_dict['status_code'] == 1:
data = (response_dict
.get('responses', {})
Expand Down
6 changes: 2 additions & 4 deletions pokemongo_bot/cell_workers/evolve_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ def _execute_pokemon_evolve(self, pokemon, cache):
if pokemon_name in cache:
return

self.bot.api.evolve_pokemon(pokemon_id=pokemon_id)
response_dict = self.bot.api.call()
response_dict = self.bot.api.evolve_pokemon(pokemon_id=pokemon_id)
status = response_dict['responses']['EVOLVE_POKEMON']['result']
if status == 1:
logger.log('[#] Successfully evolved {} with {} CP and {} IV!'.format(
Expand All @@ -173,8 +172,7 @@ def _execute_pokemon_evolve(self, pokemon, cache):

# TODO: move to utils. These methods are shared with other workers.
def transfer_pokemon(self, pid):
self.bot.api.release_pokemon(pokemon_id=pid)
response_dict = self.bot.api.call()
response_dict = self.bot.api.release_pokemon(pokemon_id=pid)

def count_pokemon_inventory(self):
response_dict = self.bot.get_inventory()
Expand Down
1 change: 0 additions & 1 deletion pokemongo_bot/cell_workers/handle_soft_ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def spin_fort(self, fort):
player_latitude=f2i(self.bot.position[0]),
player_longitude=f2i(self.bot.position[1])
)
self.bot.api.call()

def should_run(self):
return self.bot.softban
9 changes: 5 additions & 4 deletions pokemongo_bot/cell_workers/incubate_eggs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ def _apply_incubators(self):
continue
if self.bot.config.debug:
logger.log('[x] Attempting to apply incubator {} to egg {}'.format(incubator['id'], egg['id']))
self.bot.api.use_item_egg_incubator(item_id=incubator["id"], pokemon_id=egg["id"])
ret = self.bot.api.call()
ret = self.bot.api.use_item_egg_incubator(
item_id=incubator["id"],
pokemon_id=egg["id"]
)
if ret:
code = ret.get("responses", {}).get("USE_ITEM_EGG_INCUBATOR", {}).get("result", 0)
if code == 1:
Expand Down Expand Up @@ -125,8 +127,7 @@ def _check_inventory(self, lookup_ids=[]):
return matched_pokemon

def _hatch_eggs(self):
self.bot.api.get_hatched_eggs()
response_dict = self.bot.api.call()
response_dict = self.bot.api.get_hatched_eggs()
log_color = 'green'
try:
result = reduce(dict.__getitem__, ["responses", "GET_HATCHED_EGGS"], response_dict)
Expand Down
13 changes: 6 additions & 7 deletions pokemongo_bot/cell_workers/nickname_pokemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def initialize(self):
self.template = self.config.get('nickname_template','').lower().strip()
if self.template == "{name}":
self.template = ""

def work(self):
try:
inventory = reduce(dict.__getitem__, ["responses", "GET_INVENTORY", "inventory_delta", "inventory_items"], self.bot.get_inventory())
Expand All @@ -16,8 +16,8 @@ def work(self):
else:
pokemon_data = self._get_inventory_pokemon(inventory)
for pokemon in pokemon_data:
self._nickname_pokemon(pokemon)
self._nickname_pokemon(pokemon)

def _get_inventory_pokemon(self,inventory_dict):
pokemon_data = []
for inv_data in inventory_dict:
Expand All @@ -29,7 +29,7 @@ def _get_inventory_pokemon(self,inventory_dict):
if not pokemon.get('is_egg',False):
pokemon_data.append(pokemon)
return pokemon_data

def _nickname_pokemon(self,pokemon):
"""This requies a pokemon object containing all the standard fields: id, ivs, cp, etc"""
new_name = ""
Expand Down Expand Up @@ -62,10 +62,9 @@ def _nickname_pokemon(self,pokemon):
logger.log("Unable to nickname {} due to bad template ({})".format(name,bad_key),log_color)
if pokemon.get('nickname', "") == new_name:
return
self.bot.api.nickname_pokemon(pokemon_id=instance_id,nickname=new_name)
response = self.bot.api.call()
response = self.bot.api.nickname_pokemon(pokemon_id=instance_id,nickname=new_name)
sleep(1.2)
try:
try:
result = reduce(dict.__getitem__, ["responses", "NICKNAME_POKEMON"], response)
except KeyError:
logger.log("Attempt to nickname received bad response from server.",log_color)
Expand Down
Loading