Python脚本15分钟计时结束后未执行点击问题求助
问题
我编写了一个Python自动化脚本,预期功能如下:
- 点击Spirit Attraction Potion 1 (Dose 1)
- 设置15分钟后的结束时间
- 在15分钟内随机点击Seren Stones,时间到后停止点击并切换至下一剂药水
- 重复直至用完所有药剂,再切换至下一瓶药水循环执行
但运行日志显示:设置的结束时间为08:14:32,实际却在08:17:03才触发15分钟周期结束的操作,未按预期时间执行后续步骤。
运行日志
07:59:19 - Initial delay of 10 seconds before first click. 07:59:29 - Clicked on Spirit Attraction Potion 1 (Dose 1). Total clicks: 1 07:59:32 - Current time: 07:59:32, End time set to 08:14:32 07:59:32 - Clicked on Seren stone. Total clicks: 2 08:05:37 - Clicked on Seren stone. Total clicks: 3 08:11:52 - Clicked on Seren stone. Total clicks: 4 08:17:03 - 15 minutes interval completed for Potion 1, Dose 1. 08:17:04 - Clicked on Spirit Attraction Potion 1 (Dose 2). Total clicks: 5
脚本代码
import time import random import logging import sys import threading import pyautogui from pynput.keyboard import Listener, KeyCode # Setup logging to output to the console logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%H:%M:%S', stream=sys.stdout ) # Activation keys START_STOP_KEY = KeyCode(char='-') EXIT_KEY = KeyCode(char='+') # Configuration INITIAL_DELAY = 10 # Initial delay before the first click (seconds) SEREN_CLICK_INTERVAL_MIN = 300 # Minimum interval between Seren stone clicks (seconds) SEREN_CLICK_INTERVAL_MAX = 600 # Maximum interval between Seren stone clicks (seconds) POST_CLICK_DELAY = 3 # Delay after each click (seconds) # Global state running = False click_count = 0 click_thread = None lock = threading.Lock() def on_press(key): global running, click_thread try: if key == START_STOP_KEY: with lock: running = not running if running: logging.info("Script started.") click_thread = threading.Thread(target=click_loop) click_thread.start() else: logging.info("Script stopped.") elif key == EXIT_KEY: logging.info(f"Exiting script. Total clicks made: {click_count}") with lock: running = False if click_thread: click_thread.join() listener.stop() except Exception as e: logging.error(f"Error in on_press: {e}") def click_loop(): global click_count, running try: logging.info(f"Initial delay of {INITIAL_DELAY} seconds before first click.") time.sleep(INITIAL_DELAY) while running: try: perform_click_sequence() except Exception as e: logging.error(f"Error during click loop iteration: {e}") with lock: running = False except Exception as e: logging.error(f"Error in click_loop: {e}") with lock: running = False def perform_click_sequence(): global click_count try: # Define coordinate ranges for each potion (example ranges) potion_coordinate_ranges = [ ((1560, 1576), (838, 860)), # Potion 1 (Row 3) ((1602, 1622), (838, 860)), # Potion 2 (Row 3) ((1645, 1664), (838, 860)), # Potion 3 (Row 3) ((1686, 1708), (838, 860)), # Potion 4 (Row 3) ((1560, 1576), (874, 896)), # Potion 1 (Row 4) ((1602, 1622), (874, 896)), # Potion 2 (Row 4) ((1645, 1664), (874, 896)), # Potion 3 (Row 4) ((1686, 1708), (874, 896)), # Potion 4 (Row 4) ((1560, 1576), (908, 930)), # Potion 1 (Row 5) ((1602, 1622), (908, 930)), # Potion 2 (Row 5) ((1645, 1664), (908, 930)), # Potion 3 (Row 5) ((1686, 1708), (908, 930)), # Potion 4 (Row 5) ((1560, 1576), (946, 968)), # Potion 1 (Row 6) ((1602, 1622), (946, 968)), # Potion 2 (Row 6) ((1645, 1664), (946, 968)), # Potion 3 (Row 6) ((1686, 1708), (946, 968)), # Potion 4 (Row 6) # ... add coordinate ranges for all 26 potions ] for potion_number in range(1, 16): # Loop through 16 potions for dose in range(4): # Each potion has 4 doses if not running: logging.info("Script is not running. Exiting click sequence.") return # Get the coordinate range for the current potion x_range, y_range = potion_coordinate_ranges[potion_number - 1] # Generate random coordinates within the specified range x = random.randint(x_range[0], x_range[1]) y = random.randint(y_range[0], y_range[1]) # Click on the current potion click(f"Clicked on Spirit Attraction Potion {potion_number} (Dose {dose + 1})", x, y) time.sleep(POST_CLICK_DELAY) # Calculate the end time for the 15-minute interval end_time = time.time() + 15 * 60 logging.info(f"Current time: {time.strftime('%H:%M:%S', time.localtime(time.time()))}, End time set to {time.strftime('%H:%M:%S', time.localtime(end_time))}") # Click Seren stones in a random sequence within the 15 minutes while time.time() < end_time: if not running: logging.info("Script is not running. Exiting click sequence.") return click("Clicked on Seren stone", random.randint(780, 965), random.randint(335, 380)) # Sleep for a random interval between clicks on Seren stone time.sleep(random.uniform(SEREN_CLICK_INTERVAL_MIN, SEREN_CLICK_INTERVAL_MAX)) time.sleep(POST_CLICK_DELAY) logging.info(f"15 minutes interval completed for Potion {potion_number}, Dose {dose + 1}.") except Exception as e: logging.error(f"Error in perform_click_sequence: {e}") def click(description, x, y): global click_count pyautogui.moveTo(x, y) pyautogui.click() click_count += 1 logging.info(f"{description}. Total clicks: {click_count}") try: with Listener(on_press=on_press) as listener: # Start the keyboard listener logging.info("Listener started. Press '-' to start/stop and '+' to exit.") listener.join() # Wait for the listener to stop except Exception as e: logging.error(f"Error starting listener: {e}")
问题分析与修复方案
问题原因
核心问题在于Seren Stones的循环逻辑:每次点击后会进入一段300-600秒的长睡眠,再加3秒点击后延迟,但睡眠期间不会检查是否到达结束时间。比如最后一次点击Seren Stones是08:11:52,之后睡眠了约5分11秒+3秒延迟,醒来时已经远超设定的08:14:32结束时间,导致周期结束操作延迟触发。
修复方案
将长睡眠拆分为多次短睡眠(比如每次1秒),每次睡眠后立即检查是否到达结束时间或脚本是否停止,一旦满足条件就退出循环,确保15分钟到点时能及时停止操作。
修复后的代码
import time import random import logging import sys import threading import pyautogui from pynput.keyboard import Listener, KeyCode # Setup logging to output to the console logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%H:%M:%S', stream=sys.stdout ) # Activation keys START_STOP_KEY = KeyCode(char='-') EXIT_KEY = KeyCode(char='+') # Configuration INITIAL_DELAY = 10 # Initial delay before the first click (seconds) SEREN_CLICK_INTERVAL_MIN = 300 # Minimum interval between Seren stone clicks (seconds) SEREN_CLICK_INTERVAL_MAX = 600 # Maximum interval between Seren stone clicks (seconds) POST_CLICK_DELAY = 3 # Delay after each click (seconds) CHECK_INTERVAL = 1 # Interval to check end time during sleep (seconds) # Global state running = False click_count = 0 click_thread = None lock = threading.Lock() def on_press(key): global running, click_thread try: if key == START_STOP_KEY: with lock: running = not running if running: logging.info("Script started.") click_thread = threading.Thread(target=click_loop) click_thread.start() else: logging.info("Script stopped.") elif key == EXIT_KEY: logging.info(f"Exiting script. Total clicks made: {click_count}") with lock: running = False if click_thread: click_thread.join() listener.stop() except Exception as e: logging.error(f"Error in on_press: {e}") def click_loop(): global click_count, running try: logging.info(f"Initial delay of {INITIAL_DELAY} seconds before first click.") time.sleep(INITIAL_DELAY) while running: try: perform_click_sequence() except Exception as e: logging.error(f"Error during click loop iteration: {e}") with lock: running = False except Exception as e: logging.error(f"Error in click_loop: {e}") with lock: running = False def perform_click_sequence(): global click_count try: # Define coordinate ranges for each potion (example ranges) potion_coordinate_ranges = [ ((1560, 1576), (838, 860)), # Potion 1 (Row 3) ((1602, 1622), (838, 860)), # Potion 2 (Row 3) ((1645, 1664), (838, 860)), # Potion 3 (Row 3) ((1686, 1708), (838, 860)), # Potion 4 (Row 3) ((1560, 1576), (874, 896)), # Potion 1 (Row 4) ((1602, 1622), (874, 896)), # Potion 2 (Row 4) ((1645, 1664), (874, 896)), # Potion 3 (Row 4) ((1686, 1708), (874, 896)), # Potion 4 (Row 4) ((1560, 1576), (908, 930)), # Potion 1 (Row 5) ((1602, 1622), (908, 930)), # Potion 2 (Row 5) ((1645, 1664), (908, 930)), # Potion 3 (Row 5) ((1686, 1708), (908, 930)), # Potion 4 (Row 5) ((1560, 1576), (946, 968)), # Potion 1 (Row 6) ((1602, 1622), (946, 968)), # Potion 2 (Row 6) ((1645, 1664), (946, 968)), # Potion 3 (Row 6) ((1686, 1708), (946, 968)), # Potion 4 (Row 6) # ... add coordinate ranges for all 26 potions ] for potion_number in range(1, 16): # Loop through 16 potions for dose in range(4): # Each potion has 4 doses if not running: logging.info("Script is not running. Exiting click sequence.") return # Get the coordinate range for the current potion x_range, y_range = potion_coordinate_ranges[potion_number - 1] # Generate random coordinates within the specified range x = random.randint(x_range[0], x_range[1]) y = random.randint(y_range[0], y_range[1]) # Click on the current potion click(f"Clicked on Spirit Attraction Potion {potion_number} (Dose {dose + 1})", x, y) time.sleep(POST_CLICK_DELAY) # Calculate the end time for the 15-minute interval end_time = time.time() + 15 * 60 logging.info(f"Current time: {time.strftime('%H:%M:%S', time.localtime(time.time()))}, End time set to {time.strftime('%H:%M:%S', time.localtime(end_time))}") # Click Seren stones in a random sequence within the 15 minutes while time.time() < end_time: if not running: logging.info("Script is not running. Exiting click sequence.") return click("Clicked on Seren stone", random.randint(780, 965), random.randint(335, 380)) # Calculate random sleep duration and split into small chunks sleep_duration = random.uniform(SEREN_CLICK_INTERVAL_MIN, SEREN_CLICK_INTERVAL_MAX) slept = 0 while slept < sleep_duration and time.time() < end_time and running: time.sleep(CHECK_INTERVAL) slept += CHECK_INTERVAL # Only apply post-click delay if we haven't reached end time yet if time.time() < end_time and running: time.sleep(POST_CLICK_DELAY) logging.info(f"15 minutes interval completed for Potion {potion_number}, Dose {dose + 1}.") except Exception as e: logging.error(f"Error in perform_click_sequence: {e}") def click(description, x, y): global click_count pyautogui.moveTo(x, y) pyautogui.click() click_count += 1 logging.info(f"{description}. Total clicks: {click_count}") try: with Listener(on_press=on_press) as listener: # Start the keyboard listener logging.info("Listener started. Press '-' to start/stop and '+' to exit.") listener.join() # Wait for the listener to stop
相关产品推荐
相关产品推荐

