diff --git a/configs/config.json.example b/configs/config.json.example index 826fe1a4a5..901bae608d 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -211,6 +211,14 @@ "recycle_force_max": "00:05:00" } }, + { + "type": "CatchLimiter", + "config": { + "enabled": false, + "min_balls": 20, + "duration": 15 + } + }, { "type": "Sniper", "config": { diff --git a/configs/config.json.map.example b/configs/config.json.map.example index 239519c366..af64d62a77 100644 --- a/configs/config.json.map.example +++ b/configs/config.json.map.example @@ -209,6 +209,14 @@ "enabled": true } }, + { + "type": "CatchLimiter", + "config": { + "enabled": false, + "min_balls": 20, + "duration": 15 + } + }, { "type": "MoveToMapPokemon", "config": { diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 7c4450f2f0..77b57afc16 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -28,6 +28,7 @@ - [Settings Description](#settings-description) - [`flee_count` and `flee_duration`](#flee_count-and-flee_duration) - [Previous `catch_simulation` Behaviour](#previous-catch_simulation-behaviour) +- [CatchLimiter Settings](#catchlimiter-settings) - [Sniping _(MoveToLocation)_](#sniping-_-movetolocation-_) - [Description](#description) - [Options](#options) @@ -190,6 +191,10 @@ The behaviors of the bot are configured via the `tasks` key in the `config.json` * `changeball_wait_max`: 5 | Maximum time to wait when changing balls * `newtodex_wait_min`: 20 | Minimum time to wait if we caught a new type of pokemon * `newtodex_wait_max`: 39 | Maximum time to wait if we caught a new type of pokemon +* Catch Limiter + * `enabled`: Default false | Enable/disable the task + * `min_balls`: Default 20 | Minimum balls on hand before catch tasks enabled + * `duration`: Default 15 | Length of time to disable catch tasks * EvolvePokemon * `enable`: Disable or enable this task. * `evolve_all`: Default `NONE` | Depreciated. Please use evolve_list and donot_evolve_list @@ -652,6 +657,30 @@ If you want to make your bot behave as it did prior to the catch_simulation upda } ``` +## CatchLimiter Settings +[[back to top](#table-of-contents)] + +These settings define thresholds and duration to disable all catching tasks for a specified duration when balls are low. This allows your bot to spend time moving/looting and recovering balls spent catching. + +## Default Settings + +``` +"enabled": false, +"min_balls": 20, +"duration": 15 +``` + +### Settings Description +[[back to top](#table-of-contents)] + +Setting | Description +---- | ---- +`enabled` | Specify whether this task should run or not +`min_balls` | Determine minimum ball level required for catching tasks to be enabled +`duration` | How long to disable catching + +Catching will be disabled when balls on hand reaches/is below "min_balls" and will be re-enabled when "duration" is reached, or when balls on hand > min_balls (whichever is later) + ## Sniping _(MoveToLocation)_ [[back to top](#table-of-contents)] diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index dddff7da51..259be9b3cd 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -122,6 +122,9 @@ def __init__(self, db, config): self.inventory_refresh_threshold = 10 self.inventory_refresh_counter = 0 self.last_inventory_refresh = time.time() + + # Catch on/off + self.catch_disabled = False self.capture_locked = False # lock catching while moving to VIP pokemon @@ -717,6 +720,11 @@ def _register_events(self): self.event_manager.register_event('sniper_log', parameters=('message', 'message')) self.event_manager.register_event('sniper_error', parameters=('message', 'message')) self.event_manager.register_event('sniper_teleporting', parameters=('latitude', 'longitude', 'name')) + + # Catch-limiter + self.event_manager.register_event('catch_limit_on') + self.event_manager.register_event('catch_limit_off') + def tick(self): self.health_record.heartbeat() diff --git a/pokemongo_bot/cell_workers/__init__.py b/pokemongo_bot/cell_workers/__init__.py index b6e3d4133b..87249763bc 100644 --- a/pokemongo_bot/cell_workers/__init__.py +++ b/pokemongo_bot/cell_workers/__init__.py @@ -31,3 +31,4 @@ from .camp_fort import CampFort from .discord_task import DiscordTask from .buddy_pokemon import BuddyPokemon +from .catch_limiter import CatchLimiter diff --git a/pokemongo_bot/cell_workers/catch_limiter.py b/pokemongo_bot/cell_workers/catch_limiter.py new file mode 100644 index 0000000000..ee79bda8a5 --- /dev/null +++ b/pokemongo_bot/cell_workers/catch_limiter.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from __future__ import absolute_import + +from datetime import datetime, timedelta +from pokemongo_bot.base_task import BaseTask +from pokemongo_bot.worker_result import WorkerResult +from pokemongo_bot import inventory +from pokemongo_bot.item_list import Item + +class CatchLimiter(BaseTask): + SUPPORTED_TASK_API_VERSION = 1 + + def __init__(self, bot, config): + super(CatchLimiter, self).__init__(bot, config) + self.bot = bot + self.config = config + self.enabled = self.config.get("enabled",False) + self.min_balls = self.config.get("min_balls",20) + self.duration = self.config.get("duration",15) + if not hasattr(self.bot, "catch_resume_at"): self.bot.catch_resume_at = None + + def work(self): + if not self.enabled: + return WorkerResult.SUCCESS + + now = datetime.now() + balls_on_hand = self.get_pokeball_count() + + # If resume time has passed, resume catching tasks + if self.bot.catch_disabled and now >= self.bot.catch_resume_at: + if balls_on_hand > self.min_balls: + self.emit_event( + 'catch_limit_off', + formatted="Resume time has passed and balls on hand ({}) exceeds threshold {}. Re-enabling catch tasks.". + format(balls_on_hand,self.min_balls) + ) + self.bot.catch_disabled = False + + # If balls_on_hand less than threshold, pause catching tasks for duration minutes + if not self.bot.catch_disabled and balls_on_hand <= self.min_balls: + self.bot.catch_resume_at = now + timedelta(minutes = self.duration) + self.bot.catch_disabled = True + self.emit_event( + 'catch_limit_on', + formatted="Balls on hand ({}) has reached threshold {}. Disabling catch tasks until {} or balls on hand > threshold (whichever is later).". + format(balls_on_hand, self.min_balls, self.bot.catch_resume_at.strftime("%H:%M:%S")) + ) + + return WorkerResult.SUCCESS + + def get_pokeball_count(self): + return sum([inventory.items().get(ball.value).count for ball in [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL]]) diff --git a/pokemongo_bot/cell_workers/move_to_map_pokemon.py b/pokemongo_bot/cell_workers/move_to_map_pokemon.py index abfdd472ce..aea9158d41 100644 --- a/pokemongo_bot/cell_workers/move_to_map_pokemon.py +++ b/pokemongo_bot/cell_workers/move_to_map_pokemon.py @@ -289,6 +289,15 @@ def work(self): self._emit_log("Not enough balls to start sniping (have {}, {} needed)".format( pokeballs_quantity + superballs_quantity + ultraballs_quantity, self.min_ball)) return WorkerResult.SUCCESS + + if self.bot.catch_disabled: + if not hasattr(self.bot,"mtmp_disabled_global_warning") or \ + (hasattr(self.bot,"mtmp_disabled_global_warning") and not self.bot.mtmp_disabled_global_warning): + self._emit_log("All catching tasks are currently disabled. Sniping will resume when catching tasks are re-enabled") + self.bot.mtmp_disabled_global_warning = True + return WorkerResult.SUCCESS + else: + self.bot.mtmp_disabled_global_warning = False # Retrieve pokemos self.dump_caught_pokemon() diff --git a/pokemongo_bot/cell_workers/pokemon_catch_worker.py b/pokemongo_bot/cell_workers/pokemon_catch_worker.py index 2480eac531..91603020c7 100644 --- a/pokemongo_bot/cell_workers/pokemon_catch_worker.py +++ b/pokemongo_bot/cell_workers/pokemon_catch_worker.py @@ -125,7 +125,7 @@ def work(self, response_dict=None): is_vip = self._is_vip_pokemon(pokemon) # skip ignored pokemon - if not self._should_catch_pokemon(pokemon) and not is_vip: + if (not self._should_catch_pokemon(pokemon) and not is_vip) or self.bot.catch_disabled: if not hasattr(self.bot,'skipped_pokemon'): self.bot.skipped_pokemon = [] @@ -136,6 +136,9 @@ def work(self, response_dict=None): pokemon.ivcp == skipped_pokemon.ivcp: return WorkerResult.SUCCESS + if self.bot.catch_disabled: + self.logger.info("Not catching {}. All catching tasks are currently disabled.".format(pokemon)) + self.bot.skipped_pokemon.append(pokemon) self.emit_event( 'pokemon_appeared', diff --git a/pokemongo_bot/cell_workers/sniper.py b/pokemongo_bot/cell_workers/sniper.py index 93a7f0fe92..6e30b0d913 100644 --- a/pokemongo_bot/cell_workers/sniper.py +++ b/pokemongo_bot/cell_workers/sniper.py @@ -372,7 +372,15 @@ def work(self): # Do nothing if this task was invalidated if self.disabled: self._error("Sniper was disabled for some reason. Scroll up to find out.") + + elif self.bot.catch_disabled: + if not hasattr(self.bot,"sniper_disabled_global_warning") or \ + (hasattr(self.bot,"sniper_disabled_global_warning") and not self.bot.sniper_disabled_global_warning): + self._log("All catching tasks are currently disabled. Sniper will resume when catching tasks are re-enabled") + self.bot.sniper_disabled_global_warning = True + else: + self.bot.sniper_disabled_global_warning = False targets = [] # Retrieve the targets diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index b433b7e4b6..dacbf6e2e0 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -122,7 +122,9 @@ class LoggingHandler(EventHandler): 'spun_fort': 'none', 'threw_berry': 'none', 'threw_pokeball': 'none', - 'used_lucky_egg': 'none' + 'used_lucky_egg': 'none', + 'catch_limit_on': 'yellow', + 'catch_limit_off': 'green' } COLOR_CODE = { 'gray': '\033[90m',