Fix Telegram proxy transport via CONNECT tunnel

This commit is contained in:
mirivlad 2026-04-09 03:03:26 +08:00
parent 4a9b2e27fa
commit b0e4249078
1 changed files with 40 additions and 16 deletions

View File

@ -1,33 +1,57 @@
from __future__ import annotations from __future__ import annotations
import http.client
import json import json
from typing import Any from typing import Any
from urllib import parse, request as urllib_request from urllib import parse
class TelegramAPI: class TelegramAPI:
def __init__(self, token: str, proxy_url: str | None = None) -> None: def __init__(self, token: str, proxy_url: str | None = None) -> None:
self.base_url = f"https://api.telegram.org/bot{token}" self.host = "api.telegram.org"
handlers = [] self.base_path = f"/bot{token}"
if proxy_url: self.proxy = parse.urlparse(proxy_url) if proxy_url else None
handlers.append(urllib_request.ProxyHandler({"https": proxy_url, "http": proxy_url}))
self.opener = urllib_request.build_opener(*handlers) if handlers else urllib_request.build_opener() def _connection(self) -> http.client.HTTPSConnection:
if self.proxy:
conn = http.client.HTTPSConnection(
self.proxy.hostname,
self.proxy.port or 443,
timeout=90,
)
conn.set_tunnel(self.host, 443)
return conn
return http.client.HTTPSConnection(self.host, 443, timeout=90)
def _post(self, method: str, payload: dict[str, Any]) -> dict[str, Any]: def _post(self, method: str, payload: dict[str, Any]) -> dict[str, Any]:
data = json.dumps(payload).encode("utf-8") data = json.dumps(payload).encode("utf-8")
req = urllib_request.Request( conn = self._connection()
f"{self.base_url}/{method}", try:
data=data, conn.request(
headers={"Content-Type": "application/json"}, "POST",
method="POST", f"{self.base_path}/{method}",
) body=data,
with self.opener.open(req, timeout=90) as response: headers={"Content-Type": "application/json"},
return json.loads(response.read().decode("utf-8")) )
response = conn.getresponse()
body = response.read().decode("utf-8")
finally:
conn.close()
return json.loads(body)
def _get(self, method: str, params: dict[str, Any]) -> dict[str, Any]: def _get(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
query = parse.urlencode(params) query = parse.urlencode(params)
with self.opener.open(f"{self.base_url}/{method}?{query}", timeout=90) as response: path = f"{self.base_path}/{method}"
return json.loads(response.read().decode("utf-8")) if query:
path = f"{path}?{query}"
conn = self._connection()
try:
conn.request("GET", path)
response = conn.getresponse()
body = response.read().decode("utf-8")
finally:
conn.close()
return json.loads(body)
def get_updates(self, offset: int | None, timeout: int) -> list[dict[str, Any]]: def get_updates(self, offset: int | None, timeout: int) -> list[dict[str, Any]]:
params: dict[str, Any] = {"timeout": timeout} params: dict[str, Any] = {"timeout": timeout}