给指定用户发消息,听起来就是调 sendText 传个 to。实际做下来坑不少:用户 ID 从哪来、发错人怎么防、发了怎么确认送达。这篇把精准发送的完整链路讲清楚,从用户查找、消息发送到状态确认。
底层用的是Eyun 平台开放的企微 API,凭证在Eyun 平台首页开通,核心接口是 contact 模块查用户 + message 模块发消息。
第一步:查用户拿 uin
发消息前必须知道用户的 uin(账号 ID)。有三种查法:
按手机号查:客户留了手机号,调 contact/phoneNumberSearch 查 uin。
def search_by_phone(appid, phone): resp = requests.post( f"{BASE}/wx-api/api/contact/phoneNumberSearch", headers=HEADERS, json={"appid": appid, "phone": phone} ) data = resp.json()["data"] return data.get("uin") if data else None按昵称查:contact/search 按关键词搜,返回匹配的联系人列表。昵称可能重名,要让用户确认身份。
从业务系统拿:如果业务系统存了客户 uin,直接用,不用再查。
接口字段在开发文档里有完整说明,phoneNumberSearch 和 search 的返回结构不同,用的时候注意区分。
第二步:发消息带 msgId 追踪
查到 uin 后调 sendText,返回的 msgId 存起来,后续查送达状态用。
def send_to_user(appid, uin, content): resp = requests.post( f"{BASE}/wx-api/api/message/sendText", headers=HEADERS, json={"appid": appid, "to": uin, "content": content} ) body = resp.json() if body["code"] == 0: return body["data"]["msgId"] raise Exception(body["message"])发之前做校验:uin 不为空、内容不为空、内容长度不超限制。校验不过直接报错,别让平台返回错误。
第三步:确认送达
发出去不是结束,要知道客户收没收到。用 getMessageReceipt 查已读状态,或者等 Webhook 推已读事件。
已读事件是异步的,客户读了之后平台推回调,回调体里带 msgId 和 readTime。没收到已读事件的,定时调 getMessageReceipt 补查。
防错:发错人比没发更严重
精准发送最怕发错人。我们踩过坑——按昵称查返回了多个结果,取了第一个发,结果发给了同名的另一个客户。
防错措施:
手机号查返回唯一结果才发,多个结果让用户选
昵称查返回多个结果,全部展示让用户确认
发完之后把接收人昵称和消息内容一起存流水,方便追溯
精准发送的关键在用户定位
精准发送的难点不在调 sendText,在怎么准确找到目标用户。手机号查最准,昵称查要防重名,业务系统映射最稳定。定位准了,发出去再用 msgId 追踪送达,整个链路就闭环了。