ForcePilot/backend/package/yuxi/channel/extensions/wecom/externalcontact.py
Kris 7215418610 feat(channel): 添加企业微信、微博、WhatsApp 和 Workplace 渠道扩展
新增企业微信、微博、WhatsApp、Workplace 四个渠道扩展。

企业微信渠道扩展主要模块:config, gateway, webhook, webhook_bot, outbound, streaming, pairing, security, crypto, dedupe, persistent_dedupe, card, directory, events, externalcontact, media, mentions, menu, message, oauth, status

微博渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, passive_reply, broadcast, message, menu, media, subscription, template, status

WhatsApp 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, monitor, status

Workplace 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, dedupe, actions, challenge, groups, media, mentions, menu, monitor, persona, quick_reply, signature, subscriptions, template, threading, users, status
2026-05-21 12:01:56 +08:00

141 lines
5.0 KiB
Python

import logging
import httpx
logger = logging.getLogger(__name__)
LIST_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/list"
GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get"
BATCH_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/batch/get_by_user"
GET_TAGS_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list"
GROUPCHAT_LIST_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/list"
GROUPCHAT_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/groupchat/get"
class WeComExternalContact:
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 list_customers(self, user_id: str) -> list[str]:
token = self._token
if not token:
return []
url = f"{LIST_URL}?access_token={token}&userid={user_id}"
try:
resp = await self._get_http().get(url)
data = resp.json()
if data.get("errcode") == 0:
return data.get("external_userid", [])
logger.warning("WeCom list_customers failed: %s", data)
return []
except Exception:
logger.exception("WeCom list_customers error")
return []
async def get_customer(self, external_userid: str) -> dict | None:
token = self._token
if not token:
return None
url = f"{GET_URL}?access_token={token}&external_userid={external_userid}"
try:
resp = await self._get_http().get(url)
data = resp.json()
if data.get("errcode") == 0:
return data.get("external_contact")
logger.warning("WeCom get_customer failed: %s", data)
return None
except Exception:
logger.exception("WeCom get_customer error")
return None
async def batch_get_customers(self, user_id: str, cursor: str = "") -> dict:
token = self._token
if not token:
return {}
url = f"{BATCH_GET_URL}?access_token={token}"
try:
resp = await self._get_http().post(url, json={"userid": user_id, "cursor": cursor})
data = resp.json()
if data.get("errcode") == 0:
return data
logger.warning("WeCom batch_get_customers failed: %s", data)
return {}
except Exception:
logger.exception("WeCom batch_get_customers error")
return {}
async def get_corp_tags(self, tag_ids: list[str] | None = None) -> list[dict]:
token = self._token
if not token:
return []
url = f"{GET_TAGS_URL}?access_token={token}"
body: dict = {}
if tag_ids:
body["tag_id"] = tag_ids
try:
resp = await self._get_http().post(url, json=body)
data = resp.json()
if data.get("errcode") == 0:
return data.get("tag_group", [])
logger.warning("WeCom get_corp_tags failed: %s", data)
return []
except Exception:
logger.exception("WeCom get_corp_tags error")
return []
async def list_groupchats(
self,
status_filter: int = 0,
owner_filter: dict | None = None,
cursor: str = "",
limit: int = 100,
) -> dict:
token = self._token
if not token:
return {}
url = f"{GROUPCHAT_LIST_URL}?access_token={token}"
body: dict = {"status_filter": status_filter, "cursor": cursor, "limit": limit}
if owner_filter:
body["owner_filter"] = owner_filter
try:
resp = await self._get_http().post(url, json=body)
data = resp.json()
if data.get("errcode") == 0:
return data
logger.warning("WeCom list_groupchats failed: %s", data)
return {}
except Exception:
logger.exception("WeCom list_groupchats error")
return {}
async def get_groupchat(self, chat_id: str) -> dict | None:
token = self._token
if not token:
return None
url = f"{GROUPCHAT_GET_URL}?access_token={token}"
try:
resp = await self._get_http().post(url, json={"chat_id": chat_id})
data = resp.json()
if data.get("errcode") == 0:
return data.get("group_chat")
logger.warning("WeCom get_groupchat failed: %s", data)
return None
except Exception:
logger.exception("WeCom get_groupchat error")
return None
def _get_http(self) -> httpx.AsyncClient:
if self._http is None:
self._http = httpx.AsyncClient(timeout=15.0)
return self._http
async def close(self):
if self._http:
await self._http.aclose()
self._http = None