这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
import aiohttp
|
|
|
|
from yuxi.channels.models import DeliveryResult
|
|
from yuxi.utils.logging_config import logger
|
|
|
|
|
|
async def send_with_retry(
|
|
http_client: aiohttp.ClientSession,
|
|
token: str,
|
|
api_base: str,
|
|
payload: dict,
|
|
config: dict | None = None,
|
|
) -> DeliveryResult:
|
|
cfg = config or {}
|
|
retry_cfg = cfg.get("retry", {})
|
|
max_retries = retry_cfg.get("attempts", 3)
|
|
min_delay = retry_cfg.get("min_delay_ms", 400) / 1000
|
|
max_delay = retry_cfg.get("max_delay_ms", 30000) / 1000
|
|
send_timeout = cfg.get("send_timeout", 30)
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
url = f"{api_base}/api/v1/bot/send/message"
|
|
last_error = None
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
async with http_client.post(
|
|
url,
|
|
json=payload,
|
|
headers=headers,
|
|
timeout=aiohttp.ClientTimeout(total=send_timeout),
|
|
) as resp:
|
|
if resp.status == 200:
|
|
data = await resp.json()
|
|
return DeliveryResult(
|
|
success=True,
|
|
message_id=data.get("msg_id"),
|
|
)
|
|
elif resp.status == 401:
|
|
return DeliveryResult(
|
|
success=False,
|
|
error="Auth failed (401): token expired",
|
|
auth_expired=True,
|
|
)
|
|
elif resp.status == 429:
|
|
try:
|
|
retry_after = int(resp.headers.get("Retry-After", "30"))
|
|
except (ValueError, TypeError):
|
|
retry_after = 30
|
|
retry_after = max(retry_after, 1)
|
|
logger.warning(f"[Yuanbao] Rate limited, retry after {retry_after}s")
|
|
await asyncio.sleep(retry_after)
|
|
continue
|
|
else:
|
|
error_body = await resp.text()
|
|
last_error = f"Send failed ({resp.status}): {error_body}"
|
|
except TimeoutError:
|
|
last_error = "Request timeout"
|
|
except aiohttp.ClientError as e:
|
|
last_error = f"Network error: {e}"
|
|
logger.debug(f"[Yuanbao] Send attempt {attempt + 1} failed: {e}")
|
|
|
|
if attempt < max_retries - 1:
|
|
delay = min(min_delay * (2**attempt), max_delay)
|
|
await asyncio.sleep(delay)
|
|
|
|
return DeliveryResult(
|
|
success=False,
|
|
error=str(last_error) if last_error else "Max retries exceeded",
|
|
)
|