This commit is contained in:
huangzhenpc
2025-03-27 10:20:06 +08:00
parent 6bcfedeb04
commit cc2a3a34e3
21 changed files with 1791 additions and 0 deletions

14
core/__init__.py Normal file
View File

@@ -0,0 +1,14 @@
from .config import Config
from .exceptions import (CursorRegisterException, EmailError, ProxyFetchError,
RegisterError, TokenGenerationError)
__version__ = "1.0.0"
__all__ = [
'Config',
'CursorRegisterException',
'TokenGenerationError',
'ProxyFetchError',
'RegisterError',
'EmailError'
]

89
core/config.py Normal file
View File

@@ -0,0 +1,89 @@
from dataclasses import dataclass
from typing import Tuple
import yaml
@dataclass
class GlobalConfig:
max_concurrency: int
timeout: int
retry_times: int
@dataclass
class DatabaseConfig:
path: str
pool_size: int
@dataclass
class ProxyConfig:
api_url: str
batch_size: int
check_interval: int
@dataclass
class RegisterConfig:
delay_range: Tuple[int, int]
batch_size: int
@dataclass
class EmailConfig:
file_path: str
@dataclass
class CapsolverConfig:
api_key: str
website_url: str
website_key: str
@dataclass
class YesCaptchaConfig:
client_key: str
website_url: str
website_key: str
use_cn_server: bool
@dataclass
class CaptchaConfig:
provider: str
capsolver: CapsolverConfig
yescaptcha: YesCaptchaConfig
@dataclass
class Config:
global_config: GlobalConfig
database_config: DatabaseConfig
proxy_config: ProxyConfig
register_config: RegisterConfig
email_config: EmailConfig
captcha_config: CaptchaConfig
@classmethod
def from_yaml(cls, path: str = "config.yaml"):
with open(path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
# 创建 captcha 配置对象
captcha_data = data['captcha']
captcha_config = CaptchaConfig(
provider=captcha_data['provider'],
capsolver=CapsolverConfig(**captcha_data['capsolver']),
yescaptcha=YesCaptchaConfig(**captcha_data['yescaptcha'])
)
return cls(
global_config=GlobalConfig(**data['global']),
database_config=DatabaseConfig(**data['database']),
proxy_config=ProxyConfig(**data['proxy']),
register_config=RegisterConfig(**data['register']),
email_config=EmailConfig(**data['email']),
captcha_config=captcha_config
)

86
core/database.py Normal file
View File

@@ -0,0 +1,86 @@
import asyncio
from contextlib import asynccontextmanager
from typing import Any, List, Optional
import aiosqlite
from loguru import logger
from core.config import Config
class DatabaseManager:
def __init__(self, config: Config):
self.db_path = config.database_config.path
self._pool_size = config.database_config.pool_size
self._pool: List[aiosqlite.Connection] = []
self._pool_lock = asyncio.Lock()
async def initialize(self):
"""初始化数据库连接池"""
logger.info("初始化数据库连接池")
async with aiosqlite.connect(self.db_path) as db:
await db.execute('''
CREATE TABLE IF NOT EXISTS email_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
client_id TEXT NOT NULL,
refresh_token TEXT NOT NULL,
in_use BOOLEAN DEFAULT 0,
cursor_password TEXT,
cursor_cookie TEXT,
sold BOOLEAN DEFAULT 0,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
await db.commit()
# 初始化连接池
for i in range(self._pool_size):
conn = await aiosqlite.connect(self.db_path)
self._pool.append(conn)
logger.info(f"数据库连接池初始化完成,大小: {self._pool_size}")
async def cleanup(self):
"""清理数据库连接"""
for conn in self._pool:
await conn.close()
self._pool.clear()
@asynccontextmanager
async def get_connection(self):
"""获取数据库连接"""
async with self._pool_lock:
if not self._pool:
conn = await aiosqlite.connect(self.db_path)
else:
conn = self._pool.pop()
try:
yield conn
finally:
if len(self._pool) < self._pool_size:
self._pool.append(conn)
else:
await conn.close()
async def execute(self, query: str, params: tuple = ()) -> Any:
"""执行SQL语句"""
async with self.get_connection() as conn:
cursor = await conn.execute(query, params)
await conn.commit()
return cursor.lastrowid
async def fetch_one(self, query: str, params: tuple = ()) -> Optional[tuple]:
"""查询单条记录"""
async with self.get_connection() as conn:
cursor = await conn.execute(query, params)
return await cursor.fetchone()
async def fetch_all(self, query: str, params: tuple = ()) -> List[tuple]:
"""查询多条记录"""
async with self.get_connection() as conn:
cursor = await conn.execute(query, params)
return await cursor.fetchall()

23
core/exceptions.py Normal file
View File

@@ -0,0 +1,23 @@
class CursorRegisterException(Exception):
"""基础异常类"""
pass
class TokenGenerationError(CursorRegisterException):
"""Token生成失败"""
pass
class ProxyFetchError(CursorRegisterException):
"""代理获取失败"""
pass
class RegisterError(CursorRegisterException):
"""注册失败"""
pass
class EmailError(CursorRegisterException):
"""邮件处理错误"""
pass

42
core/logger.py Normal file
View File

@@ -0,0 +1,42 @@
import sys
from pathlib import Path
from loguru import logger
from core.config import Config
def setup_logger(config: Config):
"""配置日志系统"""
# 移除默认的处理器
logger.remove()
# 添加控制台处理器,改为 DEBUG 级别
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
level="DEBUG" # 修改为 DEBUG
)
# 创建日志目录
log_dir = Path("logs")
log_dir.mkdir(exist_ok=True)
# 文件处理器保持 DEBUG 级别
logger.add(
"logs/cursor_{time:YYYY-MM-DD}.log",
rotation="00:00", # 每天轮换
retention="7 days", # 保留7天
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
level="DEBUG",
encoding="utf-8"
)
# 设置一些常用的日志格式
logger.level("DEBUG", color="<blue>")
logger.level("INFO", color="<white>")
logger.level("SUCCESS", color="<green>")
logger.level("WARNING", color="<yellow>")
logger.level("ERROR", color="<red>")
return logger