Build reusable bot framework
Some checks failed
CI / test (push) Has been cancelled
CI / compose-smoke (push) Has been cancelled

This commit is contained in:
Chelsea Lee
2026-07-19 21:53:24 -05:00
parent 7ecc1107b2
commit fbdf33e894
66 changed files with 8428 additions and 0 deletions

99
bot/api_client.py Normal file
View File

@@ -0,0 +1,99 @@
"""Async API client used by the Discord adapter."""
import os
import httpx
class ApiClient:
def __init__(self, discord_id, display_name):
self.discord_id = str(discord_id)
self.display_name = display_name
self.api_url = os.getenv("API_URL", "http://127.0.0.1:5000").rstrip("/")
self.service_key = os.getenv("BOT_API_KEY", "")
self.token = None
self.user_uuid = None
self.timezone = os.getenv("DEFAULT_TIMEZONE", "UTC")
self.http = httpx.AsyncClient(timeout=10.0)
async def authenticate(self):
headers = {"Authorization": f"Bearer {self.service_key}"}
try:
response = await self.http.post(
f"{self.api_url}/api/auth/discord/session",
headers=headers,
json={
"discord_id": self.discord_id,
"display_name": self.display_name,
},
)
except httpx.HTTPError:
return {"error": "API unavailable"}, 503
data = _response_json(response)
if response.status_code == 200:
self.token = data.get("token")
self.user_uuid = data.get("user_uuid")
self.timezone = data.get("timezone") or self.timezone
return data, response.status_code
async def request(self, method, endpoint, data=None, params=None):
if not self.token:
_, status = await self.authenticate()
if status != 200:
return {"error": "authentication failed"}, status
try:
response = await self._request(
method,
endpoint,
self.token,
data=data,
params=params,
)
except httpx.HTTPError:
return {"error": "API unavailable"}, 503
if response.status_code == 401:
authResult, status = await self.authenticate()
if status != 200:
return authResult, status
try:
response = await self._request(
method,
endpoint,
self.token,
data=data,
params=params,
)
except httpx.HTTPError:
return {"error": "API unavailable"}, 503
return _response_json(response), response.status_code
async def service_request(self, method, endpoint, data=None):
try:
response = await self._request(
method, endpoint, self.service_key, data=data
)
except httpx.HTTPError:
return {"error": "API unavailable"}, 503
return _response_json(response), response.status_code
async def _request(self, method, endpoint, token, data=None, params=None):
requestOptions = {
"headers": {"Authorization": f"Bearer {token}"},
"params": params,
}
if data is not None:
requestOptions["json"] = data
return await self.http.request(
method.upper(), f"{self.api_url}{endpoint}", **requestOptions
)
async def close(self):
await self.http.aclose()
def _response_json(response):
try:
return response.json()
except ValueError:
return {}