mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
64 lines
2.3 KiB
Python
64 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from datetime import date, datetime, timedelta, timezone
|
|
from typing import Any
|
|
|
|
import httpx
|
|
|
|
|
|
class MqttGatewayError(RuntimeError):
|
|
def __init__(self, message: str, status_code: int | None = None):
|
|
super().__init__(message)
|
|
self.status_code = status_code
|
|
|
|
|
|
def daily_authorization_tokens(signature_key: str, now: datetime | None = None) -> list[str]:
|
|
if not signature_key.strip() or signature_key.strip().lower() == "null":
|
|
raise MqttGatewayError("MQTT Gateway signature key is empty")
|
|
instant = now or datetime.now(tz=timezone.utc)
|
|
utc_date = instant.astimezone(timezone.utc).date()
|
|
dates: tuple[date, date, date] = (utc_date, utc_date - timedelta(days=1), utc_date + timedelta(days=1))
|
|
return [hashlib.sha256(f"{value.isoformat()}{signature_key}".encode()).hexdigest() for value in dates]
|
|
|
|
|
|
async def post_json(
|
|
url: str,
|
|
body: Any,
|
|
signature_key: str,
|
|
*,
|
|
timeout_seconds: float,
|
|
now: datetime | None = None,
|
|
client: httpx.AsyncClient | None = None,
|
|
) -> str:
|
|
encoded = json.dumps(body, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
owns_client = client is None
|
|
selected = client or httpx.AsyncClient()
|
|
last_unauthorized: int | None = None
|
|
try:
|
|
for token in daily_authorization_tokens(signature_key, now):
|
|
response = await selected.post(
|
|
url,
|
|
content=encoded,
|
|
headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}"},
|
|
timeout=timeout_seconds,
|
|
)
|
|
if response.status_code == 401:
|
|
last_unauthorized = response.status_code
|
|
continue
|
|
if not 200 <= response.status_code < 300:
|
|
raise MqttGatewayError(
|
|
f"MQTT Gateway request failed with HTTP status {response.status_code}",
|
|
response.status_code,
|
|
)
|
|
return response.text
|
|
finally:
|
|
if owns_client:
|
|
await selected.aclose()
|
|
raise MqttGatewayError(
|
|
"MQTT Gateway rejected all daily authorization tokens"
|
|
+ ("" if last_unauthorized is None else f" (HTTP {last_unauthorized})"),
|
|
last_unauthorized,
|
|
)
|