2026-03-20 17:39:45 +09:00
|
|
|
"""Discord REST API 클라이언트."""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
import httpx
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
DISCORD_API_BASE = "https://discord.com/api/v10"
|
2026-03-20 14:55:41 +09:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class DiscordClient:
|
2026-03-20 17:39:45 +09:00
|
|
|
def __init__(self, token: str):
|
|
|
|
|
self.token = token
|
|
|
|
|
self._client = httpx.AsyncClient(
|
|
|
|
|
base_url=DISCORD_API_BASE,
|
|
|
|
|
headers={"Authorization": f"Bot {self.token}"},
|
|
|
|
|
timeout=15.0,
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-20 14:55:41 +09:00
|
|
|
async def send_message(self, channel_id: str, content: str) -> dict:
|
2026-03-20 17:39:45 +09:00
|
|
|
resp = await self._client.post(
|
|
|
|
|
f"/channels/{channel_id}/messages",
|
|
|
|
|
json={"content": content},
|
|
|
|
|
)
|
|
|
|
|
resp.raise_for_status()
|
|
|
|
|
return resp.json()
|
|
|
|
|
|
|
|
|
|
async def send_thread_reply(self, channel_id: str, thread_id: str, content: str) -> dict:
|
|
|
|
|
return await self.send_message(channel_id=thread_id, content=content)
|
|
|
|
|
|
|
|
|
|
async def close(self):
|
|
|
|
|
await self._client.aclose()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_client: DiscordClient | None = None
|
|
|
|
|
|
2026-03-20 14:55:41 +09:00
|
|
|
|
2026-03-20 17:39:45 +09:00
|
|
|
def get_discord_client() -> DiscordClient:
|
|
|
|
|
global _client
|
|
|
|
|
if _client is None:
|
|
|
|
|
_client = DiscordClient(token=os.environ.get("DISCORD_TOKEN", ""))
|
|
|
|
|
return _client
|