Repository: RocketMap/RocketMap

Issue:
Don't unpause scanner via API watchdog if search control paused the scanner.
<!--

  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!
  ISSUES ARE NOT FOR SUPPORT!!!

  Join discord (channel: #help) for support at this link: https://discord.gg/rocketmap
  
  Submit feature requests @ http://vote.devkat.org
  
-->

<!--- Provide a general summary of the issue in the Title above -->

## Expected Behavior
keep searching paused after api watchdoge check

## Current Behavior
api watchdoge unpauses searching

## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->

## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1. pause searching (from front end search control)
2. wait...
3. look at status

## Context
I want to scan gyms on demand and i can not give acces to my server/rdp to my other users
to start instances manualy.

## Your Environment

C:\Users\RocketMap\RocketMap>git status
On branch develop
Your branch is up-to-date with 'origin/develop'.
nothing to commit, working tree clean

C:\Users\RocketMap\RocketMap>git log -1
commit 8cb7af3c67128928aea287ec26dd1a9625a6aafe
Author: neskk <neskk@oninet.pt>
Date:   Tue Jun 13 09:06:36 2017 +0100

    Bugfix: Shifted api_version to args. #2077 (#2087)

    * Shifted api_version to args.

    * Bug Fix, Gym Info requests should work now :| Left behind code from a different branch, sorry guys :|

    Thanks to user @333 at Discord for bringing this issue to my attention.



Discussion:

Resolving patch:
diff --git a/pogom/search.py b/pogom/search.py
--- a/pogom/search.py
+++ b/pogom/search.py
@@ -340,7 +340,7 @@ def worker_status_db_thread(threads_status, name, db_updates_queue):
 
 
 # The main search loop that keeps an eye on the over all process.
-def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
+def search_overseer_thread(args, new_location_queue, control_flags, heartb,
                            db_updates_queue, wh_queue):
 
     log.info('Search overseer starting...')
@@ -475,7 +475,7 @@ def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
                    name='search-worker-{}'.format(i),
                    args=(args, account_queue, account_sets,
                          account_failures, account_captchas,
-                         search_items_queue, pause_bit,
+                         search_items_queue, control_flags,
                          threadStatus[workerId], db_updates_queue,
                          wh_queue, scheduler, key_scheduler))
         t.daemon = True
@@ -493,7 +493,7 @@ def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
 
     stats_timer = 0
 
-    # The real work starts here but will halt on pause_bit.set().
+    # The real work starts here but will halt when any control flag is set.
     while True:
         if (args.hash_key is not None and
                 (hashkeys_last_upsert + hashkeys_upsert_min_delay)
@@ -504,17 +504,17 @@ def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
         odt_triggered = (args.on_demand_timeout > 0 and
                          (now() - args.on_demand_timeout) > heartb[0])
         if odt_triggered:
-            pause_bit.set()
+            control_flags['on_demand'].set()
             log.info('Searching paused due to inactivity...')
 
         # Wait here while scanning is paused.
-        while pause_bit.is_set():
+        while is_paused(control_flags):
             for i in range(0, len(scheduler_array)):
                 scheduler_array[i].scanning_paused()
             # API Watchdog - Continue to check API version.
             if not args.no_version_check and not odt_triggered:
                 api_check_time = check_forced_version(
-                    args, api_check_time, pause_bit)
+                    args, api_check_time, control_flags['api_watchdog'])
             time.sleep(1)
 
         # If a new location has been passed to us, get the most recent one.
@@ -590,7 +590,7 @@ def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
         # API Watchdog - Check if Niantic forces a new API.
         if not args.no_version_check and not odt_triggered:
             api_check_time = check_forced_version(
-                args, api_check_time, pause_bit)
+                args, api_check_time, control_flags['api_watchdog'])
 
         # Now we just give a little pause here.
         time.sleep(1)
@@ -752,7 +752,7 @@ def generate_hive_locations(current_location, step_distance,
 
 def search_worker_thread(args, account_queue, account_sets,
                          account_failures, account_captchas,
-                         search_items_queue, pause_bit, status, dbq, whq,
+                         search_items_queue, control_flags, status, dbq, whq,
                          scheduler, key_scheduler):
 
     log.debug('Search worker thread starting...')
@@ -810,7 +810,7 @@ def search_worker_thread(args, account_queue, account_sets,
             # The forever loop for the searches.
             while True:
 
-                while pause_bit.is_set():
+                while is_paused(control_flags):
                     status['message'] = 'Scanning paused.'
                     time.sleep(2)
 
@@ -894,7 +894,7 @@ def search_worker_thread(args, account_queue, account_sets,
                     first_loop = True
                     paused = False
                     while now() < appears + 10:
-                        if pause_bit.is_set():
+                        if is_paused(control_flags):
                             paused = True
                             break  # Why can't python just have `break 2`...
                         status['message'] = messages['early']
@@ -1288,7 +1288,7 @@ def stat_delta(current_status, last_status, stat_name):
     return current_status.get(stat_name, 0) - last_status.get(stat_name, 0)
 
 
-def check_forced_version(args, api_check_time, pause_bit):
+def check_forced_version(args, api_check_time, api_watchdog_flag):
     if int(time.time()) > api_check_time:
         log.debug("Checking forced API version.")
         api_check_time = int(time.time()) + args.version_check_interval
@@ -1296,7 +1296,7 @@ def check_forced_version(args, api_check_time, pause_bit):
 
         if not forced_api:
             # Couldn't retrieve API version. Pause scanning.
-            pause_bit.set()
+            api_watchdog_flag.set()
             log.warning('Forced API check got no or invalid response. ' +
                         'Possible bad proxy.')
             log.warning('Scanner paused due to failed API check.')
@@ -1306,7 +1306,7 @@ def check_forced_version(args, api_check_time, pause_bit):
         try:
             if StrictVersion(args.api_version) < StrictVersion(forced_api):
                 # Installed API version is lower. Pause scanning.
-                pause_bit.set()
+                api_watchdog_flag.set()
                 log.warning('Started with API: %s, ' +
                             'Niantic forced to API: %s',
                             args.api_version,
@@ -1317,17 +1317,17 @@ def check_forced_version(args, api_check_time, pause_bit):
                 # installed API version is newer or equal forced API.
                 # Continue scanning.
                 log.debug("API check was successful. Continue scanning.")
-                pause_bit.clear()
+                api_watchdog_flag.clear()
 
         except ValueError as e:
             # Unknown version format. Pause scanning as well.
-            pause_bit.set()
+            api_watchdog_flag.set()
             log.warning('Niantic forced unknown API version format: %s.',
                         forced_api)
             log.warning('Scanner paused due to unknown API version format.')
         except Exception as e:
             # Something else happened. Pause scanning as well.
-            pause_bit.set()
+            api_watchdog_flag.set()
             log.warning('Unknown error on API version comparison: %s.',
                         repr(e))
             log.warning('Scanner paused due to unknown API check error.')
@@ -1370,3 +1370,10 @@ def get_api_version(args):
     except Exception as e:
         log.warning('error on API check: %s', repr(e))
         return False
+
+
+def is_paused(control_flags):
+    for flag in control_flags.values():
+        if flag.is_set():
+            return True
+    return False
diff --git a/runserver.py b/runserver.py
--- a/runserver.py
+++ b/runserver.py
@@ -287,10 +287,17 @@ def main():
     app.set_current_location(position)
 
     # Control the search status (running or not) across threads.
-    pause_bit = Event()
-    pause_bit.clear()
+    control_flags = {
+      'on_demand': Event(),
+      'api_watchdog': Event(),
+      'search_control': Event()
+    }
+
+    for flag in control_flags.values():
+        flag.clear()
+
     if args.on_demand_timeout > 0:
-        pause_bit.set()
+        control_flags['on_demand'].set()
 
     heartbeat = [now()]
 
@@ -382,7 +389,7 @@ def main():
                 file.write(json.dumps(spawns))
                 log.info('Finished exporting spawn points')
 
-        argset = (args, new_location_queue, pause_bit,
+        argset = (args, new_location_queue, control_flags,
                   heartbeat, db_updates_queue, wh_updates_queue)
 
         log.debug('Starting a %s search thread', args.scheduler)
@@ -397,7 +404,7 @@ def main():
     # No more stale JS.
     init_cache_busting(app)
 
-    app.set_search_control(pause_bit)
+    app.set_search_control(control_flags['search_control'])
     app.set_heartbeat_control(heartbeat)
     app.set_location_queue(new_location_queue)
 


Test patch: