fix: use telegram proxy pool in web api
This commit is contained in:
@@ -18,6 +18,7 @@ OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen3:8b")
|
||||
# Proxy
|
||||
PROXY_URL = os.getenv("PROXY_URL", "http://192.168.123.10:11055")
|
||||
TG_PROXY_URL = os.getenv("TG_PROXY_URL", PROXY_URL)
|
||||
TG_PROXY_URLS = [x.strip() for x in os.getenv("TG_PROXY_URLS", TG_PROXY_URL).split(",") if x.strip()]
|
||||
|
||||
# Web
|
||||
WEB_PORT = int(os.getenv("WEB_PORT", "8766"))
|
||||
|
||||
+46
-20
@@ -245,6 +245,47 @@ async def api_stats(day: str = Query(default=None, pattern=r"^\d{4}-\d{2}-\d{2}$
|
||||
return JSONResponse(await get_daily_stats(day, chat_id=chat_id))
|
||||
|
||||
|
||||
|
||||
def tg_proxy_urls(config) -> list[str | None]:
|
||||
urls: list[str | None] = []
|
||||
for value in getattr(config, "TG_PROXY_URLS", []) or []:
|
||||
if value and value not in urls:
|
||||
urls.append(value)
|
||||
for value in [getattr(config, "TG_PROXY_URL", None), getattr(config, "PROXY_URL", None)]:
|
||||
if value and value not in urls:
|
||||
urls.append(value)
|
||||
return urls or [None]
|
||||
|
||||
|
||||
async def run_tg_request(operation, *, connect_timeout: float = 20.0, read_timeout: float = 20.0, label: str = "tg"):
|
||||
import config
|
||||
from telegram import Bot
|
||||
from telegram.error import BadRequest, Forbidden, NetworkError, TimedOut
|
||||
from telegram.request import HTTPXRequest
|
||||
|
||||
last_error: Exception | None = None
|
||||
proxies = tg_proxy_urls(config)
|
||||
for idx, proxy in enumerate(proxies, 1):
|
||||
try:
|
||||
bot = Bot(
|
||||
token=config.TG_BOT_TOKEN,
|
||||
request=HTTPXRequest(proxy=proxy, connect_timeout=connect_timeout, read_timeout=read_timeout),
|
||||
)
|
||||
return await operation(bot)
|
||||
except (BadRequest, Forbidden):
|
||||
raise
|
||||
except (NetworkError, TimedOut, Exception) as e:
|
||||
last_error = e
|
||||
if idx < len(proxies):
|
||||
logging.getLogger("spam_guard").warning(
|
||||
f"🌐 Web TG 请求失败,切换代理重试: {label}: {type(e).__name__}: {e}; next={proxies[idx]}"
|
||||
)
|
||||
continue
|
||||
raise
|
||||
if last_error:
|
||||
raise last_error
|
||||
raise RuntimeError("TG 请求失败")
|
||||
|
||||
def parse_draw_at(value):
|
||||
if not value:
|
||||
return None
|
||||
@@ -307,9 +348,6 @@ async def api_actions(limit: int = Query(default=100, ge=1, le=500)):
|
||||
@app.get("/api/groups")
|
||||
async def api_groups():
|
||||
import config
|
||||
from telegram import Bot
|
||||
from telegram.request import HTTPXRequest
|
||||
|
||||
sources = []
|
||||
for model in (Message, Ban, Action, GameScore, LotteryEvent, ShopItem):
|
||||
try:
|
||||
@@ -320,12 +358,11 @@ async def api_groups():
|
||||
if config.TG_CHAT_ID and config.TG_CHAT_ID not in ids:
|
||||
ids.insert(0, config.TG_CHAT_ID)
|
||||
|
||||
bot = Bot(token=config.TG_BOT_TOKEN, request=HTTPXRequest(proxy=getattr(config, "TG_PROXY_URL", config.PROXY_URL), connect_timeout=10.0, read_timeout=10.0))
|
||||
result = []
|
||||
for chat_id in ids:
|
||||
name = str(chat_id)
|
||||
try:
|
||||
chat = await bot.get_chat(chat_id=chat_id)
|
||||
chat = await run_tg_request(lambda bot, cid=chat_id: bot.get_chat(chat_id=cid), connect_timeout=10.0, read_timeout=10.0, label=f"get_chat:{chat_id}")
|
||||
name = chat.title or chat.full_name or chat.username or str(chat_id)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -486,9 +523,6 @@ async def api_restart():
|
||||
@app.post("/api/send")
|
||||
async def api_send(request: Request):
|
||||
import config
|
||||
from telegram import Bot
|
||||
from telegram.request import HTTPXRequest
|
||||
|
||||
data = await request.json()
|
||||
text = str(data.get("text", "")).strip()
|
||||
if not text:
|
||||
@@ -497,8 +531,7 @@ async def api_send(request: Request):
|
||||
return JSONResponse({"ok": False, "error": "消息太长"}, status_code=400)
|
||||
|
||||
chat_id = int(data.get("chat_id") or config.TG_CHAT_ID)
|
||||
bot = Bot(token=config.TG_BOT_TOKEN, request=HTTPXRequest(proxy=getattr(config, "TG_PROXY_URL", config.PROXY_URL)))
|
||||
msg = await bot.send_message(chat_id=chat_id, text=text)
|
||||
msg = await run_tg_request(lambda bot: bot.send_message(chat_id=chat_id, text=text), label="send_message")
|
||||
rec = await Message.create(
|
||||
msg_id=msg.message_id,
|
||||
chat_id=chat_id,
|
||||
@@ -527,9 +560,7 @@ async def api_send(request: Request):
|
||||
async def api_delete_message(message_id: int, request: Request):
|
||||
"""手动删除聊天记录:先删 Telegram 消息,再把数据库记录标记为已删除。"""
|
||||
import config
|
||||
from telegram import Bot
|
||||
from telegram.error import BadRequest
|
||||
from telegram.request import HTTPXRequest
|
||||
|
||||
data = await request.json() if request.headers.get("content-type", "").startswith("application/json") else {}
|
||||
ban_user = bool(data.get("ban_user") or data.get("ban"))
|
||||
@@ -542,11 +573,10 @@ async def api_delete_message(message_id: int, request: Request):
|
||||
if not rec.chat_id or not rec.msg_id:
|
||||
return JSONResponse({"ok": False, "error": "缺少 TG chat_id/msg_id,无法删除 TG 消息"}, status_code=400)
|
||||
|
||||
bot = Bot(token=config.TG_BOT_TOKEN, request=HTTPXRequest(proxy=getattr(config, "TG_PROXY_URL", config.PROXY_URL), connect_timeout=20.0, read_timeout=20.0))
|
||||
tg_deleted = True
|
||||
tg_error = None
|
||||
try:
|
||||
await bot.delete_message(chat_id=rec.chat_id, message_id=rec.msg_id)
|
||||
await run_tg_request(lambda bot: bot.delete_message(chat_id=rec.chat_id, message_id=rec.msg_id), connect_timeout=20.0, read_timeout=20.0, label="delete_message")
|
||||
except BadRequest as e:
|
||||
# 消息已不存在时,允许继续标记数据库;其他 TG 删除失败不标记。
|
||||
if "message to delete not found" in str(e).lower():
|
||||
@@ -566,7 +596,7 @@ async def api_delete_message(message_id: int, request: Request):
|
||||
try:
|
||||
from datetime import timedelta
|
||||
unban_at = now_tz() + timedelta(minutes=config.BAN_DURATION_MINUTES)
|
||||
await bot.ban_chat_member(chat_id=rec.chat_id, user_id=rec.user_id, until_date=int(unban_at.timestamp()))
|
||||
await run_tg_request(lambda bot: bot.ban_chat_member(chat_id=rec.chat_id, user_id=rec.user_id, until_date=int(unban_at.timestamp())), connect_timeout=20.0, read_timeout=20.0, label="ban_chat_member")
|
||||
ban = await Ban.create(
|
||||
user_id=rec.user_id,
|
||||
username=rec.username,
|
||||
@@ -618,11 +648,7 @@ async def api_scores(limit: int = Query(default=50, ge=1, le=200)):
|
||||
async def api_unban(user_id: int):
|
||||
import config
|
||||
try:
|
||||
from telegram import Bot
|
||||
from telegram.request import HTTPXRequest
|
||||
|
||||
bot = Bot(token=config.TG_BOT_TOKEN, request=HTTPXRequest(proxy=getattr(config, "TG_PROXY_URL", config.PROXY_URL)))
|
||||
await bot.unban_chat_member(chat_id=config.TG_CHAT_ID, user_id=user_id)
|
||||
await run_tg_request(lambda bot: bot.unban_chat_member(chat_id=config.TG_CHAT_ID, user_id=user_id), label="unban_chat_member")
|
||||
await Ban.filter(user_id=user_id).update(auto_unban=False, unban_at=now_tz().replace(tzinfo=None))
|
||||
await Action.create(action="UNBAN", user_id=user_id, details={"method": "web"})
|
||||
await broadcast_sse("unban", {"user_id": user_id})
|
||||
|
||||
Reference in New Issue
Block a user