first commit

This commit is contained in:
2026-03-04 17:23:52 +08:00
parent 26a0b3507d
commit cfabc52026
41 changed files with 0 additions and 0 deletions

0
utils/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

34
utils/logger.py Normal file
View File

@@ -0,0 +1,34 @@
import logging
import sys
from pathlib import Path
from wechat_auto.config import settings
def setup_logger(name: str = "wechat_auto") -> logging.Logger:
logger = logging.getLogger(name)
if logger.handlers:
return logger
logger.setLevel(getattr(logging, settings.log_level.upper()))
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
try:
file_handler = logging.FileHandler(settings.log_file)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
except Exception as e:
logger.warning(f"无法创建日志文件: {e}")
return logger
logger = setup_logger()

88
utils/retry.py Normal file
View File

@@ -0,0 +1,88 @@
import asyncio
import functools
from typing import Callable, Any, TypeVar, Coroutine
from wechat_auto.utils.logger import logger
from wechat_auto.config import settings
T = TypeVar('T')
def async_retry(
max_retries: int = None,
base_delay: float = None,
exponential: bool = True,
exceptions: tuple = (Exception,)
):
max_retries = max_retries or settings.max_retries
base_delay = base_delay or settings.retry_base_delay
def decorator(func: Callable[..., Coroutine[Any, Any, T]]):
@functools.wraps(func)
async def wrapper(*args, **kwargs) -> T:
last_exception = None
for attempt in range(max_retries):
try:
return await func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt < max_retries - 1:
if exponential:
delay = base_delay * (2 ** attempt)
else:
delay = base_delay
logger.warning(
f"{func.__name__} 失败,{attempt + 1}/{max_retries}"
f"{delay:.1f}秒后重试: {e}"
)
await asyncio.sleep(delay)
else:
logger.error(
f"{func.__name__} 失败,已达到最大重试次数 {max_retries}: {e}"
)
raise last_exception
return wrapper
return decorator
def sync_retry(
max_retries: int = None,
base_delay: float = None,
exponential: bool = True,
exceptions: tuple = (Exception,)
):
max_retries = max_retries or settings.max_retries
base_delay = base_delay or settings.retry_base_delay
def decorator(func: Callable[..., T]):
@functools.wraps(func)
def wrapper(*args, **kwargs) -> T:
last_exception = None
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except exceptions as e:
last_exception = e
if attempt < max_retries - 1:
if exponential:
delay = base_delay * (2 ** attempt)
else:
delay = base_delay
logger.warning(
f"{func.__name__} 失败,{attempt + 1}/{max_retries}"
f"{delay:.1f}秒后重试: {e}"
)
import time
time.sleep(delay)
else:
logger.error(
f"{func.__name__} 失败,已达到最大重试次数 {max_retries}: {e}"
)
raise last_exception
return wrapper
return decorator