diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c307c84537..6d6d77f426 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -72,3 +72,4 @@ * net8q * SyncX * umbreon222 + * DeXtroTip diff --git a/configs/config.json.cluster.example b/configs/config.json.cluster.example index 7bec9873cc..3449cc2e14 100644 --- a/configs/config.json.cluster.example +++ b/configs/config.json.cluster.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "TransferPokemon", "config": { diff --git a/configs/config.json.example b/configs/config.json.example index 55d99e85cf..86cbde071c 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "TransferPokemon", "config": { diff --git a/configs/config.json.map.example b/configs/config.json.map.example index 74022566a8..a68cb1fae2 100644 --- a/configs/config.json.map.example +++ b/configs/config.json.map.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "TransferPokemon", "config": { diff --git a/configs/config.json.optimizer.example b/configs/config.json.optimizer.example index c06719ad27..b5e394b24d 100644 --- a/configs/config.json.optimizer.example +++ b/configs/config.json.optimizer.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "PokemonOptimizer", "config": { diff --git a/configs/config.json.path.example b/configs/config.json.path.example index 6593166664..d58612da5f 100644 --- a/configs/config.json.path.example +++ b/configs/config.json.path.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "TransferPokemon", "config": { diff --git a/configs/config.json.pokemon.example b/configs/config.json.pokemon.example index 35e44127f7..986b6985b0 100644 --- a/configs/config.json.pokemon.example +++ b/configs/config.json.pokemon.example @@ -48,6 +48,15 @@ "terminal_title": true } }, + { + "type": "UpdateLiveInventory", + "config": { + "enabled": false, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + }, { "type": "TransferPokemon", "config": { diff --git a/pokemongo_bot/cell_workers/__init__.py b/pokemongo_bot/cell_workers/__init__.py index b8cb0fe0ab..75eec6625c 100644 --- a/pokemongo_bot/cell_workers/__init__.py +++ b/pokemongo_bot/cell_workers/__init__.py @@ -19,5 +19,6 @@ from follow_cluster import FollowCluster from sleep_schedule import SleepSchedule from update_live_stats import UpdateLiveStats +from update_live_inventory import UpdateLiveInventory from catch_pokemon import CatchPokemon from complete_tutorial import CompleteTutorial diff --git a/pokemongo_bot/cell_workers/update_live_inventory.py b/pokemongo_bot/cell_workers/update_live_inventory.py new file mode 100644 index 0000000000..af4e160036 --- /dev/null +++ b/pokemongo_bot/cell_workers/update_live_inventory.py @@ -0,0 +1,260 @@ +import ctypes +import logging +from datetime import datetime, timedelta + +from pokemongo_bot import inventory +from pokemongo_bot.base_task import BaseTask +from pokemongo_bot.worker_result import WorkerResult +from pokemongo_bot.tree_config_builder import ConfigException + + +class UpdateLiveInventory(BaseTask): + """ + Periodically displays the user inventory in the terminal. + + Example config : + { + "type": "UpdateLiveInventory", + "config": { + "enabled": true, + "min_interval": 120, + "show_all_multiple_lines": false, + "items": ["space_info", "pokeballs", "greatballs", "ultraballs", "razzberries", "luckyegg"] + } + } + + min_interval : The minimum interval at which the stats are displayed, + in seconds (defaults to 120 seconds). + The update interval cannot be accurate as workers run synchronously. + show_all_multiple_lines : Logs all items on inventory using multiple lines. + Ignores configuration of 'items' + items : An array of items to display and their display order (implicitly), + see available items below (defaults to []). + + Available items : + 'space_info': not an item but shows inventory bag space (i.e. 'Items: 140/350') + 'pokeballs' + 'greatballs' + 'ultraballs' + 'masterballs' + 'razzberries' + 'blukberries' + 'nanabberries' + 'luckyegg' + 'incubator' + 'troydisk' + 'potion' + 'superpotion' + 'hyperpotion' + 'maxpotion' + 'incense' + 'incensespicy' + 'incensecool' + 'revive' + 'maxrevive' + """ + + SUPPORTED_TASK_API_VERSION = 1 + + def initialize(self): + self.next_update = None + self.min_interval = self.config.get('min_interval', 120) + self.show_all_multiple_lines = self.config.get('show_all_multiple_lines', False) + self.displayed_items = self.config.get('items', []) + self.logger = logging.getLogger(type(self).__name__) + + if self.show_all_multiple_lines: + self.bot.event_manager.register_event('show_inventory') + else: + self.bot.event_manager.register_event('show_inventory', parameters=('items',)) + + + def work(self): + """ + Displays the items if necessary. + :return: Always returns WorkerResult.SUCCESS. + :rtype: WorkerResult + """ + if not self.should_print(): + return WorkerResult.SUCCESS + + self.inventory = inventory.items() + if self.show_all_multiple_lines: + self.print_all() + self.print_inv(self.get_inventory_line(True), True) + return WorkerResult.SUCCESS + + line = self.get_inventory_line() + if not line: + return WorkerResult.SUCCESS + + self.print_inv(line) + return WorkerResult.SUCCESS + + def should_print(self): + """ + Returns a value indicating whether the items should be displayed. + :return: True if the stats should be displayed; otherwise, False. + :rtype: bool + """ + return self.next_update is None or datetime.now() >= self.next_update + + def compute_next_update(self): + """ + Computes the next update datetime based on the minimum update interval. + :return: Nothing. + :rtype: None + """ + self.next_update = datetime.now() + timedelta(seconds=self.min_interval) + + def print_inv(self, items, is_debug=False): + """ + Logs the items into the terminal using an event. + :param items: The items to display. + :type items: string + :param is_debug: If True emits event at debug level. + :type is_debug: boolean + :return: Nothing. + :rtype: None + """ + if not is_debug: + self.emit_event( + 'show_inventory', + formatted="{items}", + data={ + 'items': items + } + ) + else: + self.emit_event( + 'show_inventory', + sender=self, + level='debug', + formatted="{items}", + data={ + 'items': items + } + ) + + self.compute_next_update() + + + def get_inventory_line(self, is_debug=False): + """ + Generates a items string according to the configuration. + :param is_debug: If True returns a string with all items. + :type is_debug: boolean + :return: A string containing items and their count, ready to be displayed. + :rtype: string + """ + available_items = { + 'space_info': 'Items: {:,}/{:,}'.format(self.inventory.get_space_used(), + self.inventory.get_space_used() + self.inventory.get_space_left()), + 'pokeballs': 'Pokeballs: {:,}'.format(self.inventory.get(1).count), + 'greatballs': 'GreatBalls: {:,}'.format(self.inventory.get(2).count), + 'ultraballs': 'UltraBalls: {:,}'.format(self.inventory.get(3).count), + 'masterballs': 'MasterBalls: {:,}'.format(self.inventory.get(4).count), + 'razzberries': 'RazzBerries: {:,}'.format(self.inventory.get(701).count), + 'blukberries': 'BlukBerries: {:,}'.format(self.inventory.get(702).count), + 'nanabberries': 'NanabBerries: {:,}'.format(self.inventory.get(703).count), + 'luckyegg': 'LuckyEgg: {:,}'.format(self.inventory.get(301).count), + 'incubator': 'Incubator: {:,}'.format(self.inventory.get(902).count), + 'troydisk': 'TroyDisk: {:,}'.format(self.inventory.get(501).count), + 'potion': 'Potion: {:,}'.format(self.inventory.get(101).count), + 'superpotion': 'SuperPotion: {:,}'.format(self.inventory.get(102).count), + 'hyperpotion': 'HyperPotion: {:,}'.format(self.inventory.get(103).count), + 'maxpotion': 'MaxPotion: {:,}'.format(self.inventory.get(104).count), + 'incense': 'Incense: {:,}'.format(self.inventory.get(401).count), + 'incensespicy': 'IncenseSpicy: {:,}'.format(self.inventory.get(402).count), + 'incensecool': 'IncenseCool: {:,}'.format(self.inventory.get(403).count), + 'revive': 'Revive: {:,}'.format(self.inventory.get(201).count), + 'maxrevive': 'MaxRevive: {:,}'.format(self.inventory.get(202).count) + } + + def get_item(item): + """ + Fetches a item string from the available items dictionary. + :param item: The item name. + :type item: string + :return: The generated item string. + :rtype: string + :raise: ConfigException: When the provided item string isn't in the available items + dictionary. + """ + if item not in available_items: + raise ConfigException("item '{}' isn't available for displaying".format(item)) + return available_items[item] + + if is_debug: + temp = [] + for key, value in available_items.iteritems(): + temp.append(value) + return ' | '.join(temp) + + line = ' | '.join(map(get_item, self.displayed_items)) + return line + + def print_all(self): + """ + Logs the items into the terminal using self.logger. + It logs using multiple lines and logs all items. + :return: Nothing. + :rtype: None + """ + self.logger.info( + 'Items: {}/{}'.format( + self.inventory.get_space_used(), + self.inventory.get_space_used() + self.inventory.get_space_left() + ) + ) + + self.logger.info( + 'PokeBalls: {} | GreatBalls: {} | UltraBalls: {} | MasterBalls: {}'.format( + self.inventory.get(1).count, + self.inventory.get(2).count, + self.inventory.get(3).count, + self.inventory.get(4).count + ) + ) + + self.logger.info( + 'RazzBerries: {} | BlukBerries: {} | NanabBerries: {}'.format( + self.inventory.get(701).count, + self.inventory.get(702).count, + self.inventory.get(703).count + ) + ) + + self.logger.info( + 'LuckyEgg: {} | Incubator: {} | TroyDisk: {}'.format( + self.inventory.get(301).count, + self.inventory.get(902).count, + self.inventory.get(501).count + ) + ) + + self.logger.info( + 'Potion: {} | SuperPotion: {} | HyperPotion: {} | MaxPotion: {}'.format( + self.inventory.get(101).count, + self.inventory.get(102).count, + self.inventory.get(103).count, + self.inventory.get(104).count + ) + ) + + self.logger.info( + 'Incense: {} | IncenseSpicy: {} | IncenseCool: {}'.format( + self.inventory.get(401).count, + self.inventory.get(402).count, + self.inventory.get(403).count + ) + ) + + self.logger.info( + 'Revive: {} | MaxRevive: {}'.format( + self.inventory.get(201).count, + self.inventory.get(202).count + ) + ) + + self.compute_next_update()