diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 03d76dc7ce..8a332fcd6d 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -36,14 +36,16 @@ from pokemongo_bot.datastore import _init_database, Datastore from worker_result import WorkerResult from tree_config_builder import ConfigException, MismatchTaskApiVersion, TreeConfigBuilder -from inventory import init_inventory +from inventory import init_inventory, player from sys import platform as _platform from pgoapi.protos.POGOProtos.Enums import BadgeType_pb2 import struct + class FileIOException(Exception): pass + class PokemonGoBot(Datastore): @property def position(self): @@ -121,7 +123,9 @@ def start(self): self._setup_event_system() self._setup_logging() self.sleep_schedule = SleepSchedule(self, self.config.sleep_schedule) if self.config.sleep_schedule else None - if self.sleep_schedule: self.sleep_schedule.work() + if self.sleep_schedule: + self.sleep_schedule.work() + self._setup_api() self._load_recent_forts() init_inventory(self) @@ -186,7 +190,7 @@ def _register_events(self): self.event_manager.register_event('set_start_location') self.event_manager.register_event('load_cached_location') self.event_manager.register_event('location_cache_ignored') - + self.event_manager.register_event('debug') # ignore candy above threshold @@ -198,11 +202,6 @@ def _register_events(self): 'threshold' ) ) - - - - - self.event_manager.register_event( 'position_update', parameters=( @@ -227,7 +226,6 @@ def _register_events(self): ) ) - self.event_manager.register_event('location_cache_error') self.event_manager.register_event('bot_start') @@ -601,9 +599,6 @@ def _register_events(self): 'move_to_map_pokemon', parameters=('message') ) - - - # cached recent_forts self.event_manager.register_event('loaded_cached_forts') self.event_manager.register_event('cached_fort') @@ -636,7 +631,8 @@ def tick(self): self.health_record.heartbeat() self.cell = self.get_meta_cell() - if self.sleep_schedule: self.sleep_schedule.work() + if self.sleep_schedule: + self.sleep_schedule.work() now = time.time() * 1000 @@ -774,7 +770,7 @@ def _setup_logging(self): if self.config.logging: logging_format = '%(message)s' logging_format_options = '' - + if ('show_log_level' not in self.config.logging) or self.config.logging['show_log_level']: logging_format = '[%(levelname)s] ' + logging_format if ('show_process_name' not in self.config.logging) or self.config.logging['show_process_name']: @@ -782,7 +778,7 @@ def _setup_logging(self): if ('show_datetime' not in self.config.logging) or self.config.logging['show_datetime']: logging_format = '[%(asctime)s] ' + logging_format logging_format_options = '%Y-%m-%d %H:%M:%S' - + formatter = Formatter(logging_format,logging_format_options) for handler in logging.root.handlers[:]: handler.setFormatter(formatter) @@ -828,7 +824,7 @@ def login(self): formatted="Login procedure started." ) lat, lng = self.position[0:2] - self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero? + self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero? while not self.api.login( self.config.auth_service, @@ -1098,7 +1094,7 @@ def _set_starting_position(self): level='debug', formatted='Loading cached location...' ) - + json_file = os.path.join(_base_dir, 'data', 'last-location-%s.json' % self.config.username) try: @@ -1106,7 +1102,7 @@ def _set_starting_position(self): location_json = json.load(infile) except (IOError, ValueError): # Unable to read json file. - # File may be corrupt. Create a new one. + # File may be corrupt. Create a new one. location_json = [] except: raise FileIOException("Unexpected error reading from {}".web_inventory) @@ -1220,7 +1216,7 @@ def heartbeat(self): responses = request.call() if responses['responses']['GET_PLAYER']['success'] == True: - #we get the player_data anyway, might as well store it + # we get the player_data anyway, might as well store it self._player = responses['responses']['GET_PLAYER']['player_data'] self.event_manager.emit( 'player_data', @@ -1230,7 +1226,7 @@ def heartbeat(self): data={'player_data': self._player} ) if responses['responses']['CHECK_AWARDED_BADGES']['success'] == True: - #store awarded_badges reponse to be used in a task or part of heartbeat + # store awarded_badges reponse to be used in a task or part of heartbeat self._awarded_badges = responses['responses']['CHECK_AWARDED_BADGES'] if self._awarded_badges.has_key('awarded_badges'): @@ -1245,9 +1241,9 @@ def heartbeat(self): level='info', formatted='awarded badge: {badge}, lvl {level}', data={'badge': badgename, - 'level' : badgelevel } + 'level': badgelevel} ) - human_behaviour.action_delay(3,10) + human_behaviour.action_delay(3, 10) try: self.web_update_queue.put_nowait(True) # do this outside of thread every tick @@ -1260,34 +1256,21 @@ def update_web_location_worker(self): self.update_web_location() def display_player_info(self): - inventory_items = self.api.get_inventory() - inventory_items = inventory_items['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] - player_stats = next((x["inventory_item_data"]["player_stats"] - for x in inventory_items - if x.get("inventory_item_data", {}).get("player_stats", {})), - None) + player_stats = player() if player_stats: + nextlvlxp = (int(player_stats.next_level_xp) - int(player_stats.exp)) + self.logger.info( + 'Level: {}'.format(player_stats.level) + + ' (Next Level: {} XP)'.format(nextlvlxp) + + ' (Total: {} XP)' + ''.format(player_stats.exp)) - nextlvlxp = (int(player_stats.get('next_level_xp', 0)) - int(player_stats.get('experience', 0))) - - if 'level' in player_stats and 'experience' in player_stats: - self.logger.info( - 'Level: {level}'.format( - **player_stats) + - ' (Next Level: {} XP)'.format( - nextlvlxp) + - ' (Total: {experience} XP)' - ''.format(**player_stats)) - - if 'pokemons_captured' in player_stats and 'poke_stop_visits' in player_stats: - self.logger.info( - 'Pokemon Captured: ' - '{pokemons_captured}'.format( - **player_stats) + - ' | Pokestops Visited: ' - '{poke_stop_visits}'.format( - **player_stats)) + self.logger.info( + 'Pokemon Captured: ' + '{}'.format(player_stats.pokemons_captured) + + ' | Pokestops Visited: ' + '{}'.format(player_stats.poke_stop_visits)) def get_forts(self, order_by_distance=False): forts = [fort @@ -1322,7 +1305,6 @@ def _load_recent_forts(self): if not self.config.forts_cache_recent_forts: return - cached_forts_path = os.path.join(_base_dir, 'data', 'recent-forts-%s.json' % self.config.username) try: # load the cached recent forts diff --git a/pokemongo_bot/cell_workers/collect_level_up_reward.py b/pokemongo_bot/cell_workers/collect_level_up_reward.py index 4e80d1fb62..ff0c8c5af2 100644 --- a/pokemongo_bot/cell_workers/collect_level_up_reward.py +++ b/pokemongo_bot/cell_workers/collect_level_up_reward.py @@ -12,12 +12,12 @@ class CollectLevelUpReward(BaseTask): def initialize(self): self._process_config() - self.current_level = self._get_current_level() + self.current_level = inventory.player().level self.previous_level = 0 def work(self): if self._should_run(): - self.current_level = self._get_current_level() + self.current_level = inventory.player().level if self.collect_reward: # let's check level reward on bot initialization @@ -70,24 +70,3 @@ def _collect_level_reward(self): 'items': data } ) - - def _get_current_level(self): - level = 0 - response_dict = self.bot.api.get_inventory() - data = (response_dict - .get('responses', {}) - .get('GET_INVENTORY', {}) - .get('inventory_delta', {}) - .get('inventory_items', {})) - - for item in data: - level = (item - .get('inventory_item_data', {}) - .get('player_stats', {}) - .get('level', 0)) - - # we found a level, no need to continue iterate - if level: - break - - return level diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index 0459bd4d34..2a858b6c11 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -1,6 +1,7 @@ import json import logging import os +import time from collections import OrderedDict from pokemongo_bot.base_dir import _base_dir @@ -14,6 +15,7 @@ https://www.reddit.com/r/pokemongodev/comments/4w7mdg/combat_damage_calculation_formula_exactly/ ''' + class FileIOException(Exception): pass @@ -22,8 +24,9 @@ class FileIOException(Exception): # Abstraction class _StaticInventoryComponent(object): - STATIC_DATA_FILE = None # optionally load static data from file, - # dropping the data in a static variable named STATIC_DATA + # optionally load static data from file, + # dropping the data in a static variable named STATIC_DATA + STATIC_DATA_FILE = None STATIC_DATA = None def __init__(self): @@ -80,6 +83,64 @@ def all(self): # # Inventory Components +class Player(_BaseInventoryComponent): + TYPE = 'player_stats' + + def __init__(self, bot, ttl=3): + self.bot = bot + self._exp = None + self._level = None + self.ttl = ttl + self.next_level_xp = None + self.pokemons_captured = None + self.poke_stop_visits = None + self.last_lvl_up_reward = time.time() # ts of last lvl_up_reward api call + super(_BaseInventoryComponent, self).__init__() + + @property + def level(self): + return self._level + + @level.setter + def level(self, value): + if self._level != value: + now = time.time() + if now - self.last_lvl_up_reward > self.ttl: + self.bot.api.level_up_rewards(level=self.level) + + self._level = value + + @property + def exp(self): + return self._exp + + @exp.setter + def exp(self, value): + if self._exp != value: + now = time.time() + if now - self.last_lvl_up_reward > self.ttl: + self.bot.api.level_up_rewards(level=self.level) + + self._exp = value + + def parse(self, item): + self.exp = item['experience'] + self.level = item['level'] + self.next_level_xp = item['next_level_xp'] + self.pokemons_captured = item['pokemons_captured'] + self.poke_stop_visits = item['poke_stop_visits'] + + def retrieve_data(self, inventory): + ret = {} + for item in inventory: + data = item['inventory_item_data'] + if self.TYPE in data: + item = data[self.TYPE] + ret = item + self.parse(item) + + return ret + class Candies(_BaseInventoryComponent): TYPE = 'candy' @@ -626,6 +687,7 @@ class PokemonInfo(object): """ Static information about pokemon kind """ + def __init__(self, data): self._data = data self.id = int(data["Number"]) @@ -1108,19 +1170,21 @@ def __init__(self, bot): self.candy = Candies() self.items = Items() self.pokemons = Pokemons() + self.player = Player(bot=self.bot) # include inventory inside Player? self.refresh() self.item_inventory_size = None self.pokemon_inventory_size = None - def refresh(self): - inventory = self.bot.api.get_inventory() + def refresh(self, inventory=None): + if inventory is None: + inventory = self.bot.api.get_inventory() + inventory = inventory['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] - for i in (self.pokedex, self.candy, self.items, self.pokemons): + for i in (self.pokedex, self.candy, self.items, self.pokemons, self.player): i.refresh(inventory) self.update_web_inventory() - def init_inventory_outfile(self): web_inventory = os.path.join(_base_dir, "web", "inventory-%s.json" % self.bot.config.username) @@ -1128,23 +1192,22 @@ def init_inventory_outfile(self): self.bot.logger.info('No inventory file %s found. Creating a new one' % web_inventory) json_inventory = [] - + with open(web_inventory, "w") as outfile: json.dump(json_inventory, outfile) - def update_web_inventory(self): web_inventory = os.path.join(_base_dir, "web", "inventory-%s.json" % self.bot.config.username) if not os.path.exists(web_inventory): self.init_inventory_outfile() - + try: with open(web_inventory, "r") as infile: json_inventory = json.load(infile) except (IOError, ValueError): # Unable to read json from web inventory - # File may be corrupt. Create a new one. + # File may be corrupt. Create a new one. self.bot.logger.info('[x] Error while opening inventory file for read: %s' % e, 'red') json_inventory = [] except: @@ -1180,9 +1243,9 @@ def jsonify_inventory(self): for pokemon in self.pokemons.all(): json_inventory.append({"inventory_item_data": {"pokemon_data": pokemon._data}}) - + return json_inventory - + def retrieve_inventories_size(self): """ Retrieves the item inventory size @@ -1273,17 +1336,19 @@ def init_inventory(bot): _inventory = Inventory(bot) -def refresh_inventory(): +def refresh_inventory(data=None): """ Refreshes the cached inventory, retrieves data from the server. :return: Nothing. :rtype: None """ - _inventory.refresh() - + _inventory.refresh(data) + + def update_web_inventory(): _inventory.update_web_inventory() + def get_item_inventory_size(): """ Access to the Item inventory size. @@ -1293,6 +1358,7 @@ def get_item_inventory_size(): _inventory.retrieve_inventories_size() return _inventory.item_inventory_size + def get_pokemon_inventory_size(): """ Access to the Item inventory size. @@ -1302,6 +1368,7 @@ def get_pokemon_inventory_size(): _inventory.retrieve_inventories_size() return _inventory.pokemon_inventory_size + def pokedex(): """ @@ -1312,6 +1379,10 @@ def pokedex(): return _inventory.pokedex +def player(): + return _inventory.player + + def candies(): """ diff --git a/pokemongo_bot/metrics.py b/pokemongo_bot/metrics.py index 693a357cfd..147fbc9927 100644 --- a/pokemongo_bot/metrics.py +++ b/pokemongo_bot/metrics.py @@ -1,6 +1,6 @@ import time from datetime import timedelta -from pokemongo_bot.inventory import Pokemons +from pokemongo_bot.inventory import Pokemons, refresh_inventory class Metrics(object): @@ -24,7 +24,7 @@ def __init__(self, bot): self.uniq_pokemons_caught = None self.uniq_pokemons_list = None - + self.player_stats = [] self.inventory_data = [] @@ -107,11 +107,13 @@ def capture_stats(self): request.get_inventory() request.get_player() response_dict = request.call() + refresh_inventory(response_dict) try: uniq_pokemon_list = set() self.dust['latest'] = response_dict['responses']['GET_PLAYER']['player_data']['currencies'][1]['amount'] - if self.dust['start'] < 0: self.dust['start'] = self.dust['latest'] + if self.dust['start'] < 0: + self.dust['start'] = self.dust['latest'] for item in response_dict['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']: if 'inventory_item_data' in item: