import logging import httpx logger = logging.getLogger(__name__) MENU_CREATE_URL = "https://qyapi.weixin.qq.com/cgi-bin/menu/create" MENU_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/menu/get" MENU_DELETE_URL = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete" class WeComMenu: def __init__(self, gateway): self._gateway = gateway self._http: httpx.AsyncClient | None = None @property def _token(self) -> str | None: return self._gateway.access_token if self._gateway else None async def create(self, agent_id: int, buttons: list[dict]) -> bool: token = self._token if not token: return False url = f"{MENU_CREATE_URL}?access_token={token}&agentid={agent_id}" try: resp = await self._get_http().post(url, json={"button": buttons}) data = resp.json() if data.get("errcode") == 0: logger.info("WeCom menu created for agent %s", agent_id) return True logger.warning("WeCom menu create failed: %s", data) return False except Exception: logger.exception("WeCom menu create error") return False async def get(self, agent_id: int) -> dict | None: token = self._token if not token: return None url = f"{MENU_GET_URL}?access_token={token}&agentid={agent_id}" try: resp = await self._get_http().get(url) return resp.json() except Exception: logger.exception("WeCom menu get error") return None async def delete(self, agent_id: int) -> bool: token = self._token if not token: return False url = f"{MENU_DELETE_URL}?access_token={token}&agentid={agent_id}" try: resp = await self._get_http().get(url) data = resp.json() if data.get("errcode") == 0: logger.info("WeCom menu deleted for agent %s", agent_id) return True logger.warning("WeCom menu delete failed: %s", data) return False except Exception: logger.exception("WeCom menu delete error") return False def _get_http(self) -> httpx.AsyncClient: if self._http is None: self._http = httpx.AsyncClient(timeout=10.0) return self._http async def close(self): if self._http: await self._http.aclose() self._http = None