保存现有功能 增加域名和添加时间关联

This commit is contained in:
huangzhenpc
2025-04-07 15:23:56 +08:00
parent 85c3095e98
commit 92f7c0f3e0

View File

@@ -11,6 +11,8 @@ import json
import signal import signal
import subprocess import subprocess
import time import time
import random
import string
from datetime import datetime from datetime import datetime
from typing import Dict, List, Optional, Tuple, Any from typing import Dict, List, Optional, Tuple, Any
@@ -23,21 +25,36 @@ if sys.platform.startswith("win"):
from core.config import Config from core.config import Config
from core.database import DatabaseManager from core.database import DatabaseManager
from import_emails import import_emails from services.fetch_manager import FetchManager
from services.self_hosted_email import SelfHostedEmail
from services.proxy_pool import ProxyPool
class AutoCursorService: class AutoCursorService:
def __init__(self): def __init__(self):
self.config = Config.from_yaml() self.config = Config.from_yaml()
self.db_manager = DatabaseManager(self.config) self.db_manager = DatabaseManager(self.config)
self.fetch_manager = FetchManager(self.config)
# 添加EmailManager实例用于操作黑名单
self.email_manager = None
# API相关 # API相关
self.api_base_url = "https://cursorapi.nosqli.com/admin/api.AutoCursor" self.api_base_url = "https://cursorapi.nosqli.com/admin/api.AutoCursor"
self.proxy = None self.proxy = None
# 初始化自建邮箱服务
self.self_hosted_email = None
if hasattr(self.config, 'self_hosted_email_config') and self.config.self_hosted_email_config:
self.self_hosted_email = SelfHostedEmail(
self.fetch_manager,
self.config.self_hosted_email_config.api_base_url,
self.config.self_hosted_email_config.api_key
)
logger.info("自建邮箱服务已初始化")
else:
logger.warning("未配置自建邮箱服务,部分功能可能不可用")
# 初始化代理池
self.proxy_pool = ProxyPool(self.config, self.fetch_manager)
# 获取hostname用于API请求参数 # 获取hostname用于API请求参数
self.hostname = getattr(self.config, "hostname", None) self.hostname = getattr(self.config, "hostname", None)
if not self.hostname: if not self.hostname:
@@ -59,12 +76,12 @@ class AutoCursorService:
self.check_interval = getattr(auto_service_config, "check_interval", 60) self.check_interval = getattr(auto_service_config, "check_interval", 60)
self.upload_interval = getattr(auto_service_config, "upload_interval", 300) self.upload_interval = getattr(auto_service_config, "upload_interval", 300)
self.email_check_threshold = getattr(auto_service_config, "email_check_threshold", 30) self.email_check_threshold = getattr(auto_service_config, "email_check_threshold", 30)
self.email_fetch_count = getattr(auto_service_config, "email_fetch_count", 2) self.email_batch_size = getattr(auto_service_config, "email_batch_size", 10)
else: else:
self.check_interval = 60 # 检查API状态的间隔 self.check_interval = 60 # 检查API状态的间隔
self.upload_interval = 300 # 上传账号间隔(秒) self.upload_interval = 300 # 上传账号间隔(秒)
self.email_check_threshold = 30 # 当可用邮箱少于这个数时获取新邮箱 self.email_check_threshold = 30 # 当可用邮箱少于这个数时获取新邮箱
self.email_fetch_count = 2 # 获取邮箱的次数每次15个 self.email_batch_size = 10 # 每次获取邮箱的数量
# 处理邮箱的最小数量阈值,只要有这么多邮箱就会立即处理 # 处理邮箱的最小数量阈值,只要有这么多邮箱就会立即处理
self.min_email_to_process = 1 self.min_email_to_process = 1
@@ -97,11 +114,6 @@ class AutoCursorService:
logger.info("初始化自动化服务") logger.info("初始化自动化服务")
await self.db_manager.initialize() await self.db_manager.initialize()
# 初始化EmailManager
from services.email_manager import EmailManager
self.email_manager = EmailManager(self.config, self.db_manager)
await self.email_manager.initialize()
# 检查并设置代理 # 检查并设置代理
if hasattr(self.config, "proxy_config") and self.config.proxy_config: if hasattr(self.config, "proxy_config") and self.config.proxy_config:
if hasattr(self.config.proxy_config, "api_proxy") and self.config.proxy_config.api_proxy: if hasattr(self.config.proxy_config, "api_proxy") and self.config.proxy_config.api_proxy:
@@ -153,171 +165,63 @@ class AutoCursorService:
logger.error(f"检查注册状态时出错: {e}") logger.error(f"检查注册状态时出错: {e}")
return self.reg_enabled # 出错时保持当前状态 return self.reg_enabled # 出错时保持当前状态
async def fetch_email_accounts(self) -> List[Dict[str, str]]: async def fetch_email_accounts(self, count: int = 10) -> List[Dict[str, str]]:
"""从API获取邮箱账号 """使用自建邮箱服务获取邮箱账号
Returns:
List[Dict[str, str]]: 邮箱账号列表
"""
url = f"{self.api_base_url}/getemailaccount"
params = {"hostname": self.hostname}
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, params=params, proxy=self.proxy, ssl=False) as response:
if response.status != 200:
logger.error(f"获取邮箱API请求失败状态码: {response.status}")
return []
data = await response.json()
if data.get("code") != 0:
logger.error(f"获取邮箱API返回错误: {data.get('msg', 'Unknown error')}")
return []
accounts = data.get("data", {}).get("accounts", [])
logger.info(f"成功获取 {len(accounts)} 个邮箱账号")
return accounts
except Exception as e:
logger.error(f"获取邮箱账号时出错: {e}")
return []
async def import_email_accounts(self, accounts: List[Dict[str, str]]) -> int:
"""导入邮箱账号到数据库
Args: Args:
accounts: 邮箱账号列表 count: 需要获取的邮箱数量
Returns: Returns:
int: 成功导入的账号数量 List[Dict[str, str]]: 邮箱账号列表每个账号包含email和password字段
""" """
if not accounts: if not self.self_hosted_email:
logger.warning("没有邮箱账号可导入") logger.error("自建邮箱服务未初始化,无法获取邮箱")
return 0 return []
count = 0 result = []
blacklist_added = 0 for _ in range(count):
# 如果有EmailManager确保黑名单初始化
if self.email_manager and self.email_manager.use_redis:
await self.email_manager._ensure_blacklist_initialized()
for account in accounts:
try: try:
email = account.get("email", "") # 获取一个邮箱地址
password = account.get("password", "") email = await self.self_hosted_email.get_email()
client_id = account.get("client_id", "") if not email:
refresh_token = account.get("refresh_token", "") logger.warning("获取邮箱失败,跳过")
if not (email and password and client_id and refresh_token):
logger.warning(f"账号数据不完整: {account}")
continue continue
# 检查邮箱是否已在黑名单中 # 生成随机密码
if self.email_manager and await self.email_manager.is_email_blacklisted(email): password = ''.join(random.choices(string.ascii_letters + string.digits, k=12))
logger.warning(f"跳过黑名单中的邮箱: {email}")
blacklist_added += 1
continue
# 插入数据库 # 添加到结果列表
insert_query = ''' account = {
INSERT INTO email_accounts "email": email,
(email, password, client_id, refresh_token, status) "password": password,
VALUES (%s, %s, %s, %s, 'pending') "client_id": "", # 如果有需要可以生成
ON DUPLICATE KEY UPDATE "refresh_token": "" # 如果有需要可以生成
password = VALUES(password), }
client_id = VALUES(client_id), result.append(account)
refresh_token = VALUES(refresh_token), logger.info(f"已获取邮箱: {email}")
status = 'pending',
updated_at = CURRENT_TIMESTAMP
'''
await self.db_manager.execute( # 每次获取后等待一小段时间,避免请求过快
insert_query, await asyncio.sleep(0.5)
(email, password, client_id, refresh_token)
)
count += 1
except Exception as e: except Exception as e:
logger.error(f"导入邮箱账号时出错: {e}") logger.error(f"获取邮箱时出错: {e}")
await asyncio.sleep(1) # 出错后等待较长时间
if blacklist_added > 0: logger.info(f"本次共获取 {len(result)} 个邮箱账号")
logger.warning(f"已跳过 {blacklist_added} 个黑名单中的邮箱") return result
logger.success(f"成功导入 {count} 个邮箱账号") async def import_self_hosted_emails(self, count: int = 10) -> List[Dict[str, str]]:
return count """获取并导入自建邮箱
async def count_pending_accounts(self) -> int: Args:
"""统计可用的pending状态账号数量""" count: 要获取的邮箱数量
if self.email_manager:
# 使用EmailManager的方法确保黑名单一致性
return await self.email_manager.count_pending_accounts()
# 兼容旧代码,直接查询
query = """
SELECT COUNT(*)
FROM email_accounts
WHERE status = 'pending' AND in_use = 0 AND sold = 0
"""
result = await self.db_manager.fetch_one(query)
if result:
return result.get("COUNT(*)", 0)
return 0
async def check_and_fetch_emails(self) -> int:
"""检查并获取邮箱账号(如果需要)
Returns: Returns:
int: 获取的邮箱账号数量 List[Dict[str, str]]: 获取的邮箱列表
""" """
# 检查当前可用邮箱数量 # 直接获取邮箱列表,不再需要导入到数据库
pending_count = await self.count_pending_accounts() emails = await self.fetch_email_accounts(count)
logger.info(f"当前可用邮箱数量: {pending_count}") return emails
if pending_count >= self.email_check_threshold:
logger.info(f"可用邮箱数量充足 ({pending_count} >= {self.email_check_threshold})")
# 确保注册进程已启动
if pending_count >= self.min_email_to_process and self.reg_enabled:
if not self.registration_process or self.registration_process.poll() is not None:
logger.info("有足够邮箱但注册进程未运行,启动注册进程")
await self.start_registration_process()
return 0
# 需要获取新邮箱
logger.info(f"可用邮箱不足 ({pending_count} < {self.email_check_threshold}),准备获取新邮箱")
total_imported = 0
for i in range(self.email_fetch_count):
accounts = await self.fetch_email_accounts()
if not accounts:
logger.warning(f"{i+1} 次获取邮箱失败或无可用邮箱")
break
imported = await self.import_email_accounts(accounts)
total_imported += imported
# 导入后等待一秒,确保数据库状态完全更新
logger.debug("等待数据库状态更新...")
await asyncio.sleep(2)
# 每次获取到新邮箱并成功导入后,立即确保注册进程在运行
if imported > 0 and self.reg_enabled:
# 重新查询一次确保数据是最新的
pending_count = await self.count_pending_accounts()
if pending_count >= self.min_email_to_process:
logger.info(f"已获取到 {imported} 个新邮箱,总可用邮箱数 {pending_count},确保注册进程运行")
if not self.registration_process or self.registration_process.poll() is not None:
await self.start_registration_process()
if imported < 15: # 每次API应返回15个账号
logger.warning(f"获取到的邮箱少于预期 ({imported} < 15),可能没有更多邮箱可用")
break
if i < self.email_fetch_count - 1:
# 在多次请求之间添加延迟
await asyncio.sleep(2)
return total_imported
async def start_registration_process(self): async def start_registration_process(self):
"""启动注册进程""" """启动注册进程"""
@@ -326,16 +230,7 @@ class AutoCursorService:
logger.info("注册进程已在运行中") logger.info("注册进程已在运行中")
return return
# 启动前等待1秒确保数据库状态已更新 logger.info("启动注册进程")
await asyncio.sleep(1)
# 再次检查可用邮箱数量,确保使用最新状态
pending_count = await self.count_pending_accounts()
if pending_count < self.min_email_to_process:
logger.warning(f"可用邮箱数量不足 ({pending_count} < {self.min_email_to_process}),暂不启动注册进程")
return
logger.info(f"{pending_count} 个可用邮箱,启动注册进程")
try: try:
# 获取配置中的batch_size确保并发注册 # 获取配置中的batch_size确保并发注册
batch_size = 1 # 默认值 batch_size = 1 # 默认值
@@ -447,55 +342,29 @@ class AutoCursorService:
"""运行服务主循环""" """运行服务主循环"""
logger.info("启动Cursor自动化服务") logger.info("启动Cursor自动化服务")
# 设置永久开启注册
self.reg_enabled = True
logger.info("注册功能已永久开启")
last_upload_time = 0 last_upload_time = 0
while self.running: while self.running:
try: try:
# 1. 检查注册API状态 # 1. 获取邮箱
current_status = await self.check_registration_status() logger.info("准备获取自建邮箱")
emails = await self.import_self_hosted_emails(self.email_batch_size)
if emails:
logger.info(f"成功获取 {len(emails)} 个自建邮箱")
# 状态变化处理 # 2. 确保注册进程正在运行
if current_status != self.reg_enabled: if not self.registration_process or self.registration_process.poll() is not None:
self.reg_enabled = current_status if emails:
logger.info(f"注册状态变化为: {'开启' if self.reg_enabled else '关闭'}") logger.info(f"{len(emails)} 个可用邮箱,启动注册进程")
# 状态变化后等待2秒确保数据库状态稳定
await asyncio.sleep(2)
if self.reg_enabled:
# 开启注册时,先检查并获取邮箱
await self.check_and_fetch_emails()
# 检查是否有可用邮箱,有则启动注册进程
pending_count = await self.count_pending_accounts()
if pending_count >= self.min_email_to_process:
logger.info(f"{pending_count} 个可用邮箱,启动注册进程")
await self.start_registration_process() await self.start_registration_process()
else: else:
# 关闭注册时,停止注册进程 logger.warning("没有可用邮箱,暂不启动注册进程")
self.stop_registration_process()
# 等待一会,确保上一步操作完全完成 # 3. 等待下一次检查
await asyncio.sleep(1)
# 2. 如果注册已开启,检查并获取邮箱
if self.reg_enabled:
await self.check_and_fetch_emails()
# 确保注册进程正在运行
if not self.registration_process or self.registration_process.poll() is not None:
pending_count = await self.count_pending_accounts()
if pending_count >= self.min_email_to_process:
logger.info(f"发现 {pending_count} 个未处理的邮箱,重新启动注册进程")
await self.start_registration_process()
# 3. 定期上传账号
current_time = time.time()
if current_time - last_upload_time >= self.upload_interval:
await self.upload_accounts()
last_upload_time = current_time
# 上传后等待,确保数据库状态更新
await asyncio.sleep(2)
# 4. 等待下一次检查
logger.debug(f"等待 {self.check_interval} 秒后进行下一次检查") logger.debug(f"等待 {self.check_interval} 秒后进行下一次检查")
for _ in range(self.check_interval): for _ in range(self.check_interval):
if not self.running: if not self.running:
@@ -526,7 +395,7 @@ async def main():
logger.remove() logger.remove()
logger.add(sys.stderr, level="INFO") logger.add(sys.stderr, level="INFO")
logger.add( logger.add(
"auto_cursor_service.log", "logs/auto_cursor_service.log",
rotation="50 MB", rotation="50 MB",
retention="10 days", retention="10 days",
level="DEBUG", level="DEBUG",