最近做的企微二开,要在客户群里放个机器人——客户在群里说话机器人能收到,机器人发消息客户也能看到,双向通信是基础。和之前聊的群智能助手不同,那篇重点是响应策略,这篇重点是双向消息通信本身怎么打通——群消息怎么收、怎么发、@机器人怎么识别、群成员怎么管。把调了哪些接口记下来。

底层用的是 Eyun 平台开放的企微 API,统一 POST+JSON,鉴权用 App Token 加 appid(Authorization: Bearer eyk_xxxx),路径 {BASE_URL}/wx-api/api/<模块>/<动作>,响应封套 {code, data, detail, message, time},code 为 0 成功。

群消息接收:Webhook 收群消息

客户在群里发消息,Webhook 回调进来,消息体里有 conversationId(群聊是 roomId)、fromUin(发言者)、content、msgType:

from flask import Flask, request

app = Flask(__name__)

@app.route("/wx-api/webhook/", methods=["POST"])
def webhook():
    event = request.headers.get("X-Eyun-Event")
    if event != "message":
        return "ok"
    payload = request.json["data"]
    appid = payload["appid"]
    conv_id = payload["conversationId"]  # 群聊是roomId
    from_uin = payload["fromUin"]
    content = payload["content"]
    msg_type = payload["msgType"]
    
    # 群消息处理
    if is_group_conversation(conv_id, appid):
        handle_group_message(appid, conv_id, from_uin, content, msg_type)
    
    return "ok"

conversationId 在群聊里是 roomId(number 类型,不能传字符串),单聊是客户 uin。要先判断是不是群消息——调群接口查 roomId 是否存在。回调机制和事件类型在Eyun 开发文档里有完整说明。

@机器人识别

群里消息很多,机器人不该每条都处理。只处理 @机器人 的消息。@ 信息在消息内容里,要解析出来:

import re

def is_at_robot(content, robot_uin):
    # 企微群里@格式:@机器人昵称 后面跟内容
    # 回调里可能有atUserList字段
    at_pattern = re.compile(rf"@.*?\s")
    return bool(at_pattern.search(content))

更准的方式是看回调里的 atUserList 字段,里面是被 @ 的用户 uin 列表,机器人的 uin 在里面就是要处理。不解析 @ 就处理所有群消息,机器人刷屏,客户烦。

群消息发送:conversationId 用 roomId

机器人往群里发消息,调 message/sendText,conversationId 用 roomId:

import requests

BASE = "https://api.eyun.com"
HEADERS = {"Authorization": "Bearer eyk_xxxx", "Content-Type": "application/json"}

def send_group_text(appid, room_id, content):
    resp = requests.post(
        f"{BASE}/wx-api/api/message/sendText",
        headers=HEADERS,
        json={
            "appid": appid,
            "conversationId": room_id,  # 群消息用roomId
            "content": content
        }
    )
    return resp.json()["code"] == 0

conversationId 必须是 number 类型,传字符串会报 -3004 参数错误。这个坑踩过——回调里拿到的 roomId 有时是字符串,要 int(room_id) 转一下。

群成员管理:拉人踢人

机器人要能管群成员——拉新人进群、移除违规成员。这两类操作都走群模块接口,但调用前需要先确认机器人有群管理权限,否则接口会返回权限不足的错误。下面先看拉人和踢人的核心接口:

def add_group_member(appid, room_id, uins):
    """拉人进群,uins 是客户 uin 列表,一次最多拉 20 人"""
    resp = requests.post(
        f"{BASE}/wx-api/api/group/addGroupMember",
        headers=HEADERS,
        json={"appid": appid, "roomId": room_id, "members": uins}
    )
    return resp.json()["code"] == 0

def remove_group_member(appid, room_id, uin):
    """踢人出群,一次只能移除一个成员"""
    resp = requests.post(
        f"{BASE}/wx-api/api/group/removeGroupMember",
        headers=HEADERS,
        json={"appid": appid, "roomId": room_id, "member": uin}
    )
    return resp.json()["code"] == 0

def get_group_members(appid, room_id):
    """查群成员列表,返回成员 uin 列表"""
    resp = requests.post(
        f"{BASE}/wx-api/api/group/getGroupMemberList",
        headers=HEADERS,
        json={"appid": appid, "roomId": room_id}
    )
    return resp.json()["data"]["members"]

addGroupMember 拉人进群,removeGroupMember 踢人出群,getGroupMemberList 查群成员列表。机器人拉人前要先查成员列表,避免重复拉——虽然已在群里的 uin 接口会自动跳过,但查一下更稳,也能提前过滤掉无效 uin。踢人时要注意:一次只能移除一个成员,批量踢人要循环调用;而且踢人操作不可逆,建议先做二次确认再执行。另外,群管理接口在Eyun 企业微信 API 平台开通后可用,未开通时调用会返回权限错误。

实际开发中,拉人和踢人往往不是单独调一个接口就完事,而是要走一套完整的流程。以「拉新人进群」为例,建议按下面几步来:

  1. 先查群成员列表:调 getGroupMemberList 拿到当前群成员 uin 集合,把要拉的人过滤一遍,去掉已经在群里的 uin,避免无效调用。
  2. 校验 uin 合法性:拉人前先确认这些 uin 是有效的客户 uin,不是空值或格式错误的字符串,否则接口会返回参数错误。
  3. 分批调用 addGroupMember:一次最多拉 20 人,如果名单超过 20 人,要按 20 人一组分批循环调用,每批之间可以稍微加一点延时,避免触发频率限制。
  4. 检查返回结果:每次调用后判断 code 是否为 0,不为 0 时把失败的 uin 记录下来,方便后续重试或人工处理。

踢人的流程类似,但更强调「谨慎」:先查列表确认目标成员确实在群里,再二次确认是否真的要移除,最后循环调用 removeGroupMember。因为踢人不可逆,一旦执行,对方需要重新扫码或被重新拉入才能回到群里,所以建议在管理后台加一个确认弹窗,或者要求操作者输入二次确认口令,避免误操作。

另外还有一个容易被忽略的点:群管理接口的权限是跟着机器人账号走的。如果机器人不是群主或群管理员,即使接口开通了,调用 addGroupMember 或 removeGroupMember 也会返回权限不足的错误。所以在做群管理功能之前,先确认机器人在目标群里具备管理权限,否则功能做了也白做。

双向通信的闭环

群机器人双向通信的完整闭环:

客户在群里发消息 → Webhook回调 → 机器人收到
                                ↓
                          识别是否@机器人
                                ↓
                          处理消息内容
                                ↓
                          sendText发群消息 → 客户看到

每个环节都是接口调用。收消息靠 Webhook 回调,发消息靠 sendText,管群靠 group 模块。把接口串对了,双向通信就通了。

群消息的几个坑

  • roomId 必须是 number,字符串会报错

  • 群消息频率有限制,1 分钟内发太多会被限流(返回 rate_limit)

  • 机器人不在群里不能发群消息,要先拉进群

  • 群解散了再发消息会报 -3020 会话错误

  • @机器人时回调里 atUserList 字段才有机器人 uin,普通群消息没有

写在最后

外部群机器人双向通信这套东西,本质是把企微的消息接口和群接口串起来——Webhook 收群消息、解析 @机器人、sendText 发群消息、group 模块管成员。里。把接口串对、@识别准、roomId 类型对,群机器人双向通信就通了。开通。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐