Files
ai_mip/scheduler.py
2026-01-13 18:59:26 +08:00

216 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)