from __future__ import annotations import json import logging import httpx logger = logging.getLogger(__name__) KAKAO_API_BASE = "https://kapi.kakao.com" class KakaoTalkBotClient: def __init__(self, admin_key: str): self._admin_key = admin_key self._http = httpx.AsyncClient( base_url=KAKAO_API_BASE, headers={ "Authorization": f"KakaoAK {admin_key}", "Content-Type": "application/x-www-form-urlencoded", }, timeout=httpx.Timeout(10.0), ) async def probe(self) -> bool: try: resp = await self._http.get("/v2/user/me") return resp.status_code == 200 except Exception: return False async def send_text(self, user_key: str, text: str) -> dict: template = { "object_type": "text", "text": text, } resp = await self._http.post( "/v1/api/talk/friends/message/send", data={ "receiver_uuids": json.dumps([user_key]), "template_object": json.dumps(template), }, ) resp.raise_for_status() return resp.json() async def send_photo(self, user_key: str, image_url: str) -> dict: template = { "object_type": "feed", "content": { "title": "", "image_url": image_url, "image_width": 800, "image_height": 800, "link": {"web_url": image_url}, }, } resp = await self._http.post( "/v1/api/talk/friends/message/send", data={ "receiver_uuids": json.dumps([user_key]), "template_object": json.dumps(template), }, ) resp.raise_for_status() return resp.json() async def send_list( self, user_key: str, header_title: str, items: list[dict], buttons: list[dict] | None = None, ) -> dict: template = { "object_type": "list", "header_title": header_title, "header_link": {"web_url": ""}, "contents": [ { "title": item.get("title", ""), "description": item.get("description", ""), "image_url": item.get("image_url", ""), "link": item.get("link", {"web_url": ""}), } for item in items[:5] ], } if buttons: template["buttons"] = buttons[:2] return await self.send_template(user_key, template) async def send_commerce( self, user_key: str, title: str, price: int, currency: str = "won", product_url: str = "", image_url: str = "", ) -> dict: template = { "object_type": "commerce", "content": { "title": title, "image_url": image_url, "link": {"web_url": product_url}, "regular_price": price, "currency_unit": currency, "currency_unit_position": 0, }, } return await self.send_template(user_key, template) async def send_location( self, user_key: str, address: str, latitude: float, longitude: float, content_title: str = "", ) -> dict: template = { "object_type": "location", "content": { "title": content_title or address, "address": address, }, "address": address, "address_title": address, "social": {}, } return await self.send_template(user_key, template) async def send_template(self, user_key: str, template: dict) -> dict: return await self.send_template_multi([user_key], template) async def send_template_multi(self, user_keys: list[str], template: dict) -> dict: resp = await self._http.post( "/v1/api/talk/friends/message/send", data={ "receiver_uuids": json.dumps(user_keys[:5]), "template_object": json.dumps(template), }, ) resp.raise_for_status() return resp.json() async def close(self) -> None: await self._http.aclose()