Files
ai_mip/scheduler.py

216 lines
6.3 KiB
Python
Raw Normal View History

2026-01-13 18:59:26 +08:00
import random
import time
from datetime import datetime, timedelta
from typing import List, Dict
from threading import Thread, Lock
from loguru import logger
from adspower_client import AdsPowerClient
from ad_automation import MIPAdAutomation
from data_manager import DataManager
from config import Config
class ClickScheduler:
"""点击任务调度器"""
def __init__(self):
self.adspower_client = AdsPowerClient()
self.data_manager = DataManager()
self.running = False
self.lock = Lock()
def add_url(self, url: str) -> bool:
"""
添加待点击的URL
Args:
url: MIP页面链接
Returns:
是否添加成功
"""
return self.data_manager.add_url(url)
def add_urls(self, urls: List[str]) -> int:
"""
批量添加URL
Args:
urls: URL列表
Returns:
成功添加的数量
"""
count = 0
for url in urls:
if self.add_url(url):
count += 1
return count
def start_scheduler(self):
"""启动调度器"""
if self.running:
logger.warning("调度器已在运行中")
return
self.running = True
logger.info("启动点击调度器")
# 启动调度线程
thread = Thread(target=self._schedule_loop, daemon=True)
thread.start()
def stop_scheduler(self):
"""停止调度器"""
self.running = False
logger.info("停止点击调度器")
def _schedule_loop(self):
"""调度循环"""
while self.running:
try:
# 检查当前时间是否在工作时间内
if not self._is_work_time():
logger.debug("当前不在工作时间内,等待...")
time.sleep(60)
continue
# 获取待处理的URL
url = self._get_next_url()
if url:
logger.info(f"开始处理URL: {url}")
self._process_url(url)
else:
logger.debug("暂无待处理的URL等待...")
time.sleep(30)
except Exception as e:
logger.error(f"调度循环异常: {str(e)}")
time.sleep(10)
def _is_work_time(self) -> bool:
"""
检查当前是否在工作时间内
Returns:
是否在工作时间
"""
now = datetime.now()
current_hour = now.hour
return Config.WORK_START_HOUR <= current_hour < Config.WORK_END_HOUR
def _get_next_url(self) -> str:
"""
获取下一个需要处理的URL
Returns:
URL或None
"""
with self.lock:
# 获取所有活跃的URL
urls = self.data_manager.get_active_urls()
for url_data in urls:
url = url_data['url']
# 检查是否已达到随机点击次数上限
click_count = url_data.get('click_count', 0)
target_clicks = url_data.get('target_clicks', 0)
if click_count >= target_clicks:
# 标记为已完成
self.data_manager.mark_url_completed(url)
continue
# 检查距离上次点击是否超过间隔时间
last_click_time = url_data.get('last_click_time')
if last_click_time:
last_click = datetime.fromisoformat(last_click_time)
time_diff = datetime.now() - last_click
if time_diff.total_seconds() < Config.CLICK_INTERVAL_MINUTES * 60:
continue
return url
return None
def _process_url(self, url: str):
"""
处理单个URL的点击任务
Args:
url: 待处理的URL
"""
page = None
try:
# 启动 AdsPower 浏览器
browser_info = self.adspower_client.start_browser()
if not browser_info:
logger.error("启动 AdsPower 浏览器失败")
return
# 通过 CDP 连接到浏览器
browser = self.adspower_client.connect_browser(browser_info)
if not browser:
logger.error("连接浏览器失败")
return
# 获取页面
page = self.adspower_client.get_page(browser)
if not page:
logger.error("获取页面失败")
return
# 执行广告点击操作
automation = MIPAdAutomation(page)
click_success, has_reply = automation.check_and_click_ad(url)
# 更新数据统计
with self.lock:
if click_success:
self.data_manager.record_click(url, has_reply)
logger.info(f"URL点击成功获得回复: {has_reply}")
else:
logger.warning(f"URL点击失败: {url}")
# 随机延迟
delay = random.randint(10, 30)
time.sleep(delay)
except Exception as e:
logger.error(f"处理URL异常: {str(e)}")
finally:
# 停止浏览器(会自动清理 Playwright 资源)
try:
self.adspower_client.stop_browser()
except Exception as e:
logger.error(f"停止浏览器异常: {str(e)}")
def get_statistics(self) -> Dict:
"""
获取统计数据
Returns:
统计数据
"""
return self.data_manager.get_statistics()
def get_url_detail(self, url: str) -> Dict:
"""
获取URL详细信息
Args:
url: URL
Returns:
URL详细信息
"""
return self.data_manager.get_url_info(url)