可用
This commit is contained in:
14
core/__init__.py
Normal file
14
core/__init__.py
Normal 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
89
core/config.py
Normal 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
86
core/database.py
Normal 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
23
core/exceptions.py
Normal 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
42
core/logger.py
Normal 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
|
||||
Reference in New Issue
Block a user