Repository: PokemonGoF/PokemonGo-Bot

Issue:
Catch worker edge bug
With the merger of the catch worker tasks, we have an error that will cause two catch attempts in one call to work if we have only one visible pokemon and at least one lured pokemon.

This could be the cause that folks see fewer pokemon over time (server throttle). 

PR #4523



Discussion:

Resolving patch:
diff --git a/pokemongo_bot/cell_workers/catch_pokemon.py b/pokemongo_bot/cell_workers/catch_pokemon.py
--- a/pokemongo_bot/cell_workers/catch_pokemon.py
+++ b/pokemongo_bot/cell_workers/catch_pokemon.py
@@ -1,28 +1,128 @@
 # -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+import json
+import os
+
 from pokemongo_bot.base_task import BaseTask
+from pokemongo_bot.cell_workers.pokemon_catch_worker import PokemonCatchWorker
 from pokemongo_bot.worker_result import WorkerResult
-from pokemongo_bot.cell_workers import CatchVisiblePokemon, CatchLuredPokemon
 from pokemongo_bot.item_list import Item
 from pokemongo_bot import inventory
+from utils import distance
+from pokemongo_bot.base_dir import _base_dir
+from pokemongo_bot.constants import Constants
 
 
 class CatchPokemon(BaseTask):
     SUPPORTED_TASK_API_VERSION = 1
 
-    def initialize(self):
-        self.catch_workers = []
-        if self.config.get('catch_visible_pokemon', True):
-            self.catch_workers.append(CatchVisiblePokemon(self.bot, self.config))
-        if self.config.get('catch_lured_pokemon', True):
-            self.catch_workers.append(CatchLuredPokemon(self.bot, self.config))
-
     def work(self):
-
         if sum([inventory.items().get(ball.value).count for ball in 
             [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL]]) <= 0:
             return WorkerResult.ERROR
 
-        for cw in self.catch_workers:
-            if cw.work() == WorkerResult.RUNNING:
+        pokemon = []
+        if self.config.get('catch_visible_pokemon', True):
+            pokemon = self.get_visible_pokemon()
+        if self.config.get('catch_lured_pokemon', True):
+            pokemon += self.get_lured_pokemon()
+
+        num_pokemon = len(pokemon)
+        if num_pokemon > 0:
+            pokemon = self.sort_pokemon(pokemon)
+            self.catch_pokemon(pokemon[0])
+            if num_pokemon > 1:
                 return WorkerResult.RUNNING
+
         return WorkerResult.SUCCESS
+
+    def get_visible_pokemon(self):
+        pokemon_to_catch = []
+        if 'catchable_pokemons' in self.bot.cell:
+            pokemon_to_catch = self.bot.cell['catchable_pokemons']
+
+            if len(pokemon_to_catch) > 0:
+    		    # Update web UI
+    		    user_web_catchable = os.path.join(_base_dir, 'web', 'catchable-{}.json'.format(self.bot.config.username))
+    		    for pokemon in pokemon_to_catch:
+    		        with open(user_web_catchable, 'w') as outfile:
+    		            json.dump(pokemon, outfile)
+    		        self.emit_event(
+    		            'catchable_pokemon',
+    		            level='debug',
+    		            data={
+    		                'pokemon_id': pokemon['pokemon_id'],
+    		                'spawn_point_id': pokemon['spawn_point_id'],
+    		                'encounter_id': pokemon['encounter_id'],
+    		                'latitude': pokemon['latitude'],
+    		                'longitude': pokemon['longitude'],
+    		                'expiration_timestamp_ms': pokemon['expiration_timestamp_ms'],
+    		            }
+    		        )
+
+        if 'wild_pokemons' in self.bot.cell:
+            pokemon_to_catch += self.bot.cell['wild_pokemons']
+
+        return pokemon_to_catch
+
+    def get_lured_pokemon(self):
+        forts_in_range = []
+        pokemon_to_catch = []
+        forts = self.bot.get_forts(order_by_distance=True)
+
+        if len(forts) == 0:
+            return []
+
+        for fort in forts:
+            distance_to_fort = distance(
+                self.bot.position[0],
+                self.bot.position[1],
+                fort['latitude'],
+                fort['longitude']
+            )
+
+            encounter_id = fort.get('lure_info', {}).get('encounter_id', None)
+            if distance_to_fort < Constants.MAX_DISTANCE_FORT_IS_REACHABLE and encounter_id:
+                forts_in_range.append(fort)
+
+
+        for fort in forts_in_range:
+            details = fort_details(self.bot, fort_id=fort['id'],
+                                  latitude=fort['latitude'],
+                                  longitude=fort['longitude'])
+            fort_name = details.get('name', 'Unknown')
+            encounter_id = fort['lure_info']['encounter_id']
+
+            result = {
+                'encounter_id': encounter_id,
+                'fort_id': fort['id'],
+                'fort_name': u"{}".format(fort_name),
+                'latitude': fort['latitude'],
+                'longitude': fort['longitude']
+            }
+            pokemon_to_catch.append(result)
+
+            self.emit_event(
+                'lured_pokemon_found',
+                level='info',
+                formatted='Lured pokemon at fort {fort_name} ({fort_id})',
+                data=result
+            )
+        return pokemon_to_catch
+
+    def catch_pokemon(self, pokemon):
+        worker = PokemonCatchWorker(pokemon, self.bot, self.config)
+        return_value = worker.work()
+
+        return return_value
+        
+    def sort_pokemon(self, pokemon_list):
+        # Sort all by distance from current pos- eventually this should
+        # build graph & A* it
+        pokemon_list.sort(
+            key=
+            lambda x: distance(self.bot.position[0], self.bot.position[1], x['latitude'], x['longitude'])
+        )
+        
+        return pokemon_list


Test patch: