diff --git a/.env.example b/.env.example
index a2a8ec7..609a546 100644
--- a/.env.example
+++ b/.env.example
@@ -19,3 +19,7 @@ WEB_PANEL_SESSION_SECRET=
TG_API_ID=
TG_API_HASH=
TG_API_SESSION=
+
+# Optional: proxy for Telethon (MTProto) connections. Required for servers in China.
+# Examples: socks5://127.0.0.1:1080, http://127.0.0.1:7890
+TG_PROXY=
diff --git a/app.py b/app.py
index fd47871..7a7753a 100644
--- a/app.py
+++ b/app.py
@@ -643,7 +643,24 @@ def clear_telegram_login_session() -> None:
conn.commit()
-async def telegram_login_prepare_qr() -> dict[str, Any]:
+def _build_telethon_proxy(proxy: str) -> Any:
+ """Build Telethon proxy argument from a proxy string."""
+ if not proxy:
+ return None
+ p = proxy.strip()
+ if p.startswith("socks5://") or p.startswith("socks4://"):
+ try:
+ import socks
+ parts = p.replace("socks5://", "").replace("socks4://", "").split(":")
+ return (socks.SOCKS5 if "socks5" in p else socks.SOCKS4,
+ parts[0], int(parts[1]) if len(parts) > 1 else 1080)
+ except ImportError:
+ logger.warning("pysocks not installed, cannot use socks proxy")
+ return None
+ return p
+
+
+async def telegram_login_prepare_qr(proxy: str = "") -> dict[str, Any]:
if TelegramClient is None or StringSession is None:
return {"ok": False, "error": "telethon not installed"}
api_id = os.getenv("TG_API_ID", "").strip()
@@ -654,7 +671,8 @@ async def telegram_login_prepare_qr() -> dict[str, Any]:
api_id_int = int(api_id)
except Exception:
return {"ok": False, "error": "TG_API_ID must be int"}
- client = TelegramClient(StringSession(), api_id_int, api_hash)
+ proxy_arg = _build_telethon_proxy(proxy or os.getenv("TG_PROXY", "").strip())
+ client = TelegramClient(StringSession(), api_id_int, api_hash, proxy=proxy_arg)
try:
await client.connect()
qr_login = await client.qr_login()
@@ -2639,6 +2657,7 @@ def env_values() -> dict[str, str]:
"TG_API_ID": os.getenv("TG_API_ID", ""),
"TG_API_HASH": os.getenv("TG_API_HASH", ""),
"TG_API_SESSION": os.getenv("TG_API_SESSION", ""),
+ "TG_PROXY": os.getenv("TG_PROXY", ""),
}
@@ -2666,6 +2685,7 @@ def write_env_values(values: dict[str, str]) -> None:
f"TG_API_ID={values.get('TG_API_ID','')}",
f"TG_API_HASH={values.get('TG_API_HASH','')}",
f"TG_API_SESSION={values.get('TG_API_SESSION','')}",
+ f"TG_PROXY={values.get('TG_PROXY','')}",
"",
]
ENV_PATH.write_text("\n".join(lines), encoding="utf-8")
@@ -3516,6 +3536,7 @@ HostLoc|https://hostloc.com|VPS,补货,优惠"""
用于频道媒体转发。先填 TG_API_ID / TG_API_HASH 并保存,再点二维码登录。
+
登录状态:{html_escape(login_status)} · 账号:{html_escape(login_user)}
QR扫码登录 Telegram
正在生成二维码...
@@ -3573,7 +3594,7 @@ async function logoutTgSession() {{
cfg_save(cfg)
@app.post("/settings", response_class=HTMLResponse)
- async def settings_save(_: str = Depends(panel_auth), TELEGRAM_BOT_TOKEN: str = Form(""), ADMIN_CHAT_ID: str = Form(""), TG_API_ID: str = Form(""), TG_API_HASH: str = Form(""), TG_API_SESSION: str = Form(""), LOG_LEVEL: str = Form("INFO"), WEB_PANEL_ENABLED: str = Form("true"), WEB_PANEL_HOST: str = Form("127.0.0.1"), WEB_PANEL_PORT: str = Form("8765"), WEB_PANEL_USER: str = Form("admin"), WEB_PANEL_PASSWORD: str = Form("admin"), CLEANUP_INTERVAL_MINUTES: int = Form(60), CLEANUP_MESSAGE_DELETE_AFTER_MINUTES: int = Form(60), CLEANUP_RETENTION_MINUTES: int = Form(1440)) -> str:
+ async def settings_save(_: str = Depends(panel_auth), TELEGRAM_BOT_TOKEN: str = Form(""), ADMIN_CHAT_ID: str = Form(""), TG_API_ID: str = Form(""), TG_API_HASH: str = Form(""), TG_API_SESSION: str = Form(""), TG_PROXY: str = Form(""), LOG_LEVEL: str = Form("INFO"), WEB_PANEL_ENABLED: str = Form("true"), WEB_PANEL_HOST: str = Form("127.0.0.1"), WEB_PANEL_PORT: str = Form("8765"), WEB_PANEL_USER: str = Form("admin"), WEB_PANEL_PASSWORD: str = Form("admin"), CLEANUP_INTERVAL_MINUTES: int = Form(60), CLEANUP_MESSAGE_DELETE_AFTER_MINUTES: int = Form(60), CLEANUP_RETENTION_MINUTES: int = Form(1440)) -> str:
save_panel_settings(locals() | {"WEB_PANEL_ENABLED": WEB_PANEL_ENABLED}, CLEANUP_INTERVAL_MINUTES, CLEANUP_MESSAGE_DELETE_AFTER_MINUTES, CLEANUP_RETENTION_MINUTES)
return layout("已保存", "已保存,不会自动重启;修改 Token/管理员 ID 后请重启。
返回 重启机器人
")
@@ -3704,7 +3725,7 @@ async function logoutTgSession() {{
return layout("用户管理", body)
@app.post("/users/settings", response_class=HTMLResponse)
- async def users_settings_save(_: str = Depends(panel_auth), TELEGRAM_BOT_TOKEN: str = Form(""), ADMIN_CHAT_ID: str = Form(""), TG_API_ID: str = Form(""), TG_API_HASH: str = Form(""), TG_API_SESSION: str = Form(""), LOG_LEVEL: str = Form("INFO"), WEB_PANEL_ENABLED: str = Form("true"), WEB_PANEL_HOST: str = Form("127.0.0.1"), WEB_PANEL_PORT: str = Form("8765"), WEB_PANEL_USER: str = Form("admin"), WEB_PANEL_PASSWORD: str = Form("admin")) -> str:
+ async def users_settings_save(_: str = Depends(panel_auth), TELEGRAM_BOT_TOKEN: str = Form(""), ADMIN_CHAT_ID: str = Form(""), TG_API_ID: str = Form(""), TG_API_HASH: str = Form(""), TG_API_SESSION: str = Form(""), TG_PROXY: str = Form(""), LOG_LEVEL: str = Form("INFO"), WEB_PANEL_ENABLED: str = Form("true"), WEB_PANEL_HOST: str = Form("127.0.0.1"), WEB_PANEL_PORT: str = Form("8765"), WEB_PANEL_USER: str = Form("admin"), WEB_PANEL_PASSWORD: str = Form("admin")) -> str:
cleanup = (cfg_load_fresh().get("cleanup") or {})
save_panel_settings(
locals() | {"WEB_PANEL_ENABLED": WEB_PANEL_ENABLED},
@@ -3716,7 +3737,7 @@ async function logoutTgSession() {{
@app.post("/api/tg-login/qr")
async def api_tg_login_qr(_: str = Depends(panel_auth)) -> dict[str, Any]:
- result = await telegram_login_prepare_qr()
+ result = await telegram_login_prepare_qr(proxy=os.getenv("TG_PROXY", "").strip())
if not result.get("ok"):
return {"ok": False, "error": result.get("error", "failed")}
login_id = secrets.token_urlsafe(8)