Repository: RocketMap/RocketMap

Issue:
Captcha count on status page not updated when using -w parameter
<!--- Provide a general summary of the issue in the Title above -->

## Expected Behavior

When an account gets captcha'd, the captcha count on the status page is supposed to increase by 1

## Current Behavior

- When an account gets captcha'd while using the -w parameter, the status page will not update the captcha count for this account:

```
2017-01-24 17:33:23,136 [ search-worker-2][        search][    INFO] Scanning step 2 for a spawn.
2017-01-24 17:33:24,327 [ search-worker-2][        search][    INFO] Account 4ixxxxxxxxxxxxxx has encountered a captcha, putting away account for now.
2017-01-24 17:33:24,328 [ search-worker-2][        search][    INFO] Waiting to get new account from the queue...
2017-01-24 17:33:24,377 [ search-worker-2][        search][    INFO] Switching to account <xyz>
2017-01-24 17:33:38,593 [account-recycler][        search][    INFO] Account recycler running. Checking status of 1 accounts.
2017-01-24 17:33:38,593 [account-recycler][        search][    INFO] Account 4ixxxxxxxxxxxxxx needs to cool off for 119.0 minutes due to captcha found.
```
![no_captcha](https://cloud.githubusercontent.com/assets/20759246/22257368/1ed77ad4-e25e-11e6-8732-5d82d3932f76.png)


- When I do the same scan without the -w parameter the captcha count is updated correctly:
![captcha](https://cloud.githubusercontent.com/assets/20759246/22257412/4a5bc44e-e25e-11e6-829f-604c7907e98a.png)

## 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. Run a scan with the -w parameter 
2. Wait for a captcha
3. Check the captcha count on the status page

## Context
I solve captchas manually and use the captcha count on the status page to see which accounts are captcha'd. Without the captcha count I need to look at the 'Last Modified' column to figure out which accounts are currently put on hold due to captchas.

## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->
* Version used: up-to-date except for latest commit #1746
* Environment name and version (e.g. Python 2.7): Python 2.7.12
* parameters used:  -ns -speed -gi -w 4 -st 6



Discussion:

Resolving patch:
diff --git a/pogom/captcha.py b/pogom/captcha.py
--- a/pogom/captcha.py
+++ b/pogom/captcha.py
@@ -74,10 +74,15 @@ def captcha_overseer_thread(args, account_queue, account_captchas,
                 # Safety guard
                 tokens_remaining = min(tokens_remaining, 5)
                 for i in range(0, tokens_remaining):
-                    last_scan = account_captchas[0][0]['last_scan_date']
-                    hold_time = (datetime.utcnow() - last_scan).total_seconds()
+                    account = account_captchas[0][1]
+                    last_active = account['last_active']
+                    hold_time = (datetime.utcnow() -
+                                 last_active).total_seconds()
                     if hold_time > args.manual_captcha_timeout:
-
+                        log.debug('Account %s waited %ds for captcha token ' +
+                                  'and reached the %ds timeout.',
+                                  account['username'], hold_time,
+                                  args.manual_captcha_timeout)
                         if args.hash_key:
                             hash_key = key_scheduler.next()
 
@@ -125,20 +130,19 @@ def captcha_solver_thread(args, account_queue, account_captchas, hash_key,
             log.debug('Using proxy %s', proxy_url)
             api.set_proxy({'http': proxy_url, 'https': proxy_url})
 
-    location = [status['latitude'], status['longitude'], 0]
+    location = account['last_location']
 
     if not args.no_jitter:
         # Jitter location before uncaptcha attempt
         location = jitter_location(location)
 
     api.set_position(*location)
-    status['message'] = 'Logging in...'
     check_login(args, account, api, location, proxy_url)
 
     wh_message = {'status_name': args.status_name,
                   'status': 'error',
                   'method': 'manual',
-                  'account': status['username'],
+                  'account': account['username'],
                   'captcha': status['captcha'],
                   'time': 0}
     if not token:
@@ -147,8 +151,8 @@ def captcha_solver_thread(args, account_queue, account_captchas, hash_key,
 
     response = api.verify_challenge(token=token)
 
-    last_scan = status['last_scan_date']
-    hold_time = (datetime.utcnow() - last_scan).total_seconds()
+    last_active = account['last_active']
+    hold_time = (datetime.utcnow() - last_active).total_seconds()
     wh_message['time'] = int(hold_time)
 
     if 'success' in response['responses']['VERIFY_CHALLENGE']:
@@ -168,24 +172,23 @@ def captcha_solver_thread(args, account_queue, account_captchas, hash_key,
 
     if args.webhooks:
         wh_queue.put(('captcha', wh_message))
+    # Make sure status is updated
+    time.sleep(1)
 
 
 def handle_captcha(args, status, api, account, account_failures,
-                   account_captchas, whq, response_dict):
+                   account_captchas, whq, response_dict, step_location):
     try:
         captcha_url = response_dict['responses'][
             'CHECK_CHALLENGE']['challenge_url']
         if len(captcha_url) > 1:
             status['captcha'] += 1
-
             if not args.captcha_solving:
                 status['message'] = ('Account {} has encountered a captcha. ' +
                                      'Putting account away.').format(
-                                        status['username'])
+                                        account['username'])
                 log.warning(status['message'])
-                status['on_hold'] = True
                 account_failures.append({
-                    'status': status,
                     'account': account,
                     'last_fail_time': now(),
                     'reason': 'captcha found'})
@@ -193,7 +196,7 @@ def handle_captcha(args, status, api, account, account_failures,
                     wh_message = {'status_name': args.status_name,
                                   'status': 'encounter',
                                   'mode': 'disabled',
-                                  'account': status['username'],
+                                  'account': account['username'],
                                   'captcha': status['captcha'],
                                   'time': 0}
                     whq.put(('captcha', wh_message))
@@ -204,9 +207,7 @@ def handle_captcha(args, status, api, account, account_failures,
                                            account, whq):
                     return True
                 else:
-                    status['on_hold'] = True
                     account_failures.append({
-                       'status': status,
                        'account': account,
                        'last_fail_time': now(),
                        'reason': 'captcha failed to verify'})
@@ -214,14 +215,16 @@ def handle_captcha(args, status, api, account, account_failures,
             else:
                 status['message'] = ('Account {} has encountered a captcha. ' +
                                      'Waiting for token.').format(
-                                        status['username'])
+                                        account['username'])
                 log.warning(status['message'])
+                account['last_active'] = datetime.utcnow()
+                account['last_location'] = step_location
                 account_captchas.append((status, account, captcha_url))
                 if args.webhooks:
                     wh_message = {'status_name': args.status_name,
                                   'status': 'encounter',
                                   'mode': 'manual',
-                                  'account': status['username'],
+                                  'account': account['username'],
                                   'captcha': status['captcha'],
                                   'time': args.manual_captcha_timeout}
                     whq.put(('captcha', wh_message))
@@ -243,7 +246,7 @@ def automatic_captcha_solve(args, status, api, captcha_url, account, wh_queue):
         wh_message = {'status_name': args.status_name,
                       'status': 'encounter',
                       'mode': '2captcha',
-                      'account': status['username'],
+                      'account': account['username'],
                       'captcha': status['captcha'],
                       'time': 0}
         wh_queue.put(('captcha', wh_message))
diff --git a/pogom/search.py b/pogom/search.py
--- a/pogom/search.py
+++ b/pogom/search.py
@@ -299,7 +299,6 @@ def account_recycler(args, accounts_queue, account_failures):
             if a['last_fail_time'] <= ok_time:
                 # Remove the account from the real list, and add to the account
                 # queue.
-                a['status']['on_hold'] = False
                 log.info('Account {} returning to active duty.'.format(
                     a['account']['username']))
                 account_failures.remove(a)
@@ -455,7 +454,6 @@ def search_overseer_thread(args, new_location_queue, pause_bit, heartb,
             'username': '',
             'proxy_display': proxy_display,
             'proxy_url': proxy_url,
-            'on_hold': False
         }
 
         t = Thread(target=search_worker_thread,
@@ -722,6 +720,10 @@ def search_worker_thread(args, account_queue, account_failures,
     # This reinitializes the API and grabs a new account from the queue.
     while True:
         try:
+            # Force storing of previous worker info to keep consistency
+            if 'starttime' in status:
+                dbq.put((WorkerStatus, {0: WorkerStatus.db_format(status)}))
+
             status['starttime'] = now()
 
             # Track per loop.
@@ -731,10 +733,9 @@ def search_worker_thread(args, account_queue, account_failures,
             while not scheduler.ready:
                 time.sleep(1)
 
-            if not status['on_hold']:
-                status['message'] = ('Waiting to get new account from the ' +
-                                     'queue...')
-                log.info(status['message'])
+            status['message'] = ('Waiting to get new account from the ' +
+                                 'queue...')
+            log.info(status['message'])
 
             # Get an account.
             account = account_queue.get()
@@ -804,9 +805,7 @@ def search_worker_thread(args, account_queue, account_failures,
                             account['username'],
                             args.max_failures)
                     log.warning(status['message'])
-                    status['on_hold'] = True
-                    account_failures.append({'status': status,
-                                             'account': account,
+                    account_failures.append({'account': account,
                                              'last_fail_time': now(),
                                              'reason': 'failures'})
                     # Exit this loop to get a new account and have the API
@@ -823,9 +822,7 @@ def search_worker_thread(args, account_queue, account_failures,
                         'accounts...').format(account['username'],
                                               args.max_empty)
                     log.warning(status['message'])
-                    status['on_hold'] = True
-                    account_failures.append({'status': status,
-                                             'account': account,
+                    account_failures.append({'account': account,
                                              'last_fail_time': now(),
                                              'reason': 'empty scans'})
                     # Exit this loop to get a new account and have the API
@@ -855,9 +852,7 @@ def search_worker_thread(args, account_queue, account_failures,
                             'Account {} is being rotated out to rest.'.format(
                                 account['username']))
                         log.info(status['message'])
-                        status['on_hold'] = True
-                        account_failures.append({'status': status,
-                                                 'account': account,
+                        account_failures.append({'account': account,
                                                  'last_fail_time': now(),
                                                  'reason': 'rest interval'})
                         break
@@ -967,7 +962,7 @@ def search_worker_thread(args, account_queue, account_failures,
                     captcha = handle_captcha(args, status, api, account,
                                              account_failures,
                                              account_captchas, whq,
-                                             response_dict)
+                                             response_dict, step_location)
                     if captcha is not None and captcha:
                         # Make another request for the same location
                         # since the previous one was captcha'd.
@@ -975,7 +970,6 @@ def search_worker_thread(args, account_queue, account_failures,
                         response_dict = map_request(api, step_location,
                                                     args.no_jitter)
                     elif captcha is not None:
-                        status['on_hold'] = True
                         account_queue.task_done()
                         time.sleep(3)
                         break
@@ -1124,9 +1118,7 @@ def search_worker_thread(args, account_queue, account_failures,
                 'with fresh account. See logs for details.').format(
                     account['username'])
             traceback.print_exc(file=sys.stdout)
-            status['on_hold'] = True
-            account_failures.append({'status': status,
-                                     'account': account,
+            account_failures.append({'account': account,
                                      'last_fail_time': now(),
                                      'reason': 'exception'})
             time.sleep(args.scan_delay)


Test patch: