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
|
||||||
PROXY_URL = os.getenv("PROXY_URL", "http://192.168.123.10:11055")
|
PROXY_URL = os.getenv("PROXY_URL", "http://192.168.123.10:11055")
|
||||||
TG_PROXY_URL = os.getenv("TG_PROXY_URL", PROXY_URL)
|
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
|
||||||
WEB_PORT = int(os.getenv("WEB_PORT", "8766"))
|
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))
|
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):
|
def parse_draw_at(value):
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
@@ -307,9 +348,6 @@ async def api_actions(limit: int = Query(default=100, ge=1, le=500)):
|
|||||||
@app.get("/api/groups")
|
@app.get("/api/groups")
|
||||||
async def api_groups():
|
async def api_groups():
|
||||||
import config
|
import config
|
||||||
from telegram import Bot
|
|
||||||
from telegram.request import HTTPXRequest
|
|
||||||
|
|
||||||
sources = []
|
sources = []
|
||||||
for model in (Message, Ban, Action, GameScore, LotteryEvent, ShopItem):
|
for model in (Message, Ban, Action, GameScore, LotteryEvent, ShopItem):
|
||||||
try:
|
try:
|
||||||
@@ -320,12 +358,11 @@ async def api_groups():
|
|||||||
if config.TG_CHAT_ID and config.TG_CHAT_ID not in ids:
|
if config.TG_CHAT_ID and config.TG_CHAT_ID not in ids:
|
||||||
ids.insert(0, config.TG_CHAT_ID)
|
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 = []
|
result = []
|
||||||
for chat_id in ids:
|
for chat_id in ids:
|
||||||
name = str(chat_id)
|
name = str(chat_id)
|
||||||
try:
|
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)
|
name = chat.title or chat.full_name or chat.username or str(chat_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -486,9 +523,6 @@ async def api_restart():
|
|||||||
@app.post("/api/send")
|
@app.post("/api/send")
|
||||||
async def api_send(request: Request):
|
async def api_send(request: Request):
|
||||||
import config
|
import config
|
||||||
from telegram import Bot
|
|
||||||
from telegram.request import HTTPXRequest
|
|
||||||
|
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
text = str(data.get("text", "")).strip()
|
text = str(data.get("text", "")).strip()
|
||||||
if not text:
|
if not text:
|
||||||
@@ -497,8 +531,7 @@ async def api_send(request: Request):
|
|||||||
return JSONResponse({"ok": False, "error": "消息太长"}, status_code=400)
|
return JSONResponse({"ok": False, "error": "消息太长"}, status_code=400)
|
||||||
|
|
||||||
chat_id = int(data.get("chat_id") or config.TG_CHAT_ID)
|
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 run_tg_request(lambda bot: bot.send_message(chat_id=chat_id, text=text), label="send_message")
|
||||||
msg = await bot.send_message(chat_id=chat_id, text=text)
|
|
||||||
rec = await Message.create(
|
rec = await Message.create(
|
||||||
msg_id=msg.message_id,
|
msg_id=msg.message_id,
|
||||||
chat_id=chat_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):
|
async def api_delete_message(message_id: int, request: Request):
|
||||||
"""手动删除聊天记录:先删 Telegram 消息,再把数据库记录标记为已删除。"""
|
"""手动删除聊天记录:先删 Telegram 消息,再把数据库记录标记为已删除。"""
|
||||||
import config
|
import config
|
||||||
from telegram import Bot
|
|
||||||
from telegram.error import BadRequest
|
from telegram.error import BadRequest
|
||||||
from telegram.request import HTTPXRequest
|
|
||||||
|
|
||||||
data = await request.json() if request.headers.get("content-type", "").startswith("application/json") else {}
|
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"))
|
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:
|
if not rec.chat_id or not rec.msg_id:
|
||||||
return JSONResponse({"ok": False, "error": "缺少 TG chat_id/msg_id,无法删除 TG 消息"}, status_code=400)
|
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_deleted = True
|
||||||
tg_error = None
|
tg_error = None
|
||||||
try:
|
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:
|
except BadRequest as e:
|
||||||
# 消息已不存在时,允许继续标记数据库;其他 TG 删除失败不标记。
|
# 消息已不存在时,允许继续标记数据库;其他 TG 删除失败不标记。
|
||||||
if "message to delete not found" in str(e).lower():
|
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:
|
try:
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
unban_at = now_tz() + timedelta(minutes=config.BAN_DURATION_MINUTES)
|
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(
|
ban = await Ban.create(
|
||||||
user_id=rec.user_id,
|
user_id=rec.user_id,
|
||||||
username=rec.username,
|
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):
|
async def api_unban(user_id: int):
|
||||||
import config
|
import config
|
||||||
try:
|
try:
|
||||||
from telegram import Bot
|
await run_tg_request(lambda bot: bot.unban_chat_member(chat_id=config.TG_CHAT_ID, user_id=user_id), label="unban_chat_member")
|
||||||
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 Ban.filter(user_id=user_id).update(auto_unban=False, unban_at=now_tz().replace(tzinfo=None))
|
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 Action.create(action="UNBAN", user_id=user_id, details={"method": "web"})
|
||||||
await broadcast_sse("unban", {"user_id": user_id})
|
await broadcast_sse("unban", {"user_id": user_id})
|
||||||
|
|||||||
Reference in New Issue
Block a user