import httpx from app.config import settings class WecomClient: """Minimal WeChat Work API client for sending app messages.""" def __init__(self): self.corp_id = settings.WECOM_CORP_ID self.agent_id = settings.WECOM_AGENT_ID self.secret = settings.WECOM_SECRET self._access_token: str | None = None async def _get_token(self) -> str: if self._access_token: return self._access_token url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={self.corp_id}&corpsecret={self.secret}" async with httpx.AsyncClient() as client: resp = await client.get(url, timeout=10) data = resp.json() if data.get("errcode") == 0: self._access_token = data["access_token"] return self._access_token raise Exception(f"Failed to get wecom token: {data}") async def get_userinfo_by_code(self, code: str) -> dict | None: """Exchange OAuth2 code for userid (used in silent login).""" token = await self._get_token() url = f"https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token={token}&code={code}" async with httpx.AsyncClient() as client: resp = await client.get(url, timeout=10) data = resp.json() if data.get("errcode") == 0: return data return None async def send_text_message(self, user_ids: list[str], content: str) -> bool: """Send a text app message to specified users.""" if not settings.WECOM_AGENT_ID: return False # Not configured, skip silently in dev token = await self._get_token() url = f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}" body = { "touser": "|".join(user_ids), "msgtype": "text", "agentid": int(settings.WECOM_AGENT_ID), "text": {"content": content}, } async with httpx.AsyncClient() as client: resp = await client.post(url, json=body, timeout=10) data = resp.json() return data.get("errcode") == 0 wecom_client = WecomClient()