企业微信智能机器人 Python 插件获取回调和发送消息支持文字图片语音视频

企业微信智能机器人 Python 插件

企业微信智能机器人的 Python SDK,基于 WebSocket 长连接实现,功能完整、易于使用。

📋 目录

✨ 特性

核心能力

  • WebSocket 长连接 - 稳定的双向通信,自动心跳保活
  • 消息收发 - 支持文本、图片、视频、文件等多种消息类型
  • 分片上传 - 支持大文件上传(最大 100MB)
  • 媒体下载 - 自动下载并解密媒体文件
  • 访问控制 - 灵活的 DM 和群聊权限管理
  • 自动重连 - 网络断开自动重连(最多 100 次)
  • 消息历史 - 内置消息历史存储
  • 类型安全 - 完整的类型注解

技术特点

  • 🔒 线程安全 - 使用锁保护共享资源
  • 🎯 响应等待 - 同步等待服务器响应,确保可靠性
  • 📝 详细日志 - 完整的调试和错误日志
  • 🔄 兼容性 - 与 Node.js 版本协议完全兼容
  • 🚀 高性能 - 异步处理,支持高并发

🚀 快速开始

安装依赖

bash 复制代码
pip install -r requirements.txt

依赖包:

  • websocket-client>=1.6.0 - WebSocket 客户端
  • requests>=2.28.0 - HTTP 请求
  • pyyaml>=6.0 - YAML 配置解析
  • pycryptodome>=3.15.0 - AES 加密解密

基础使用

1. 创建配置文件

复制 config.yaml.exampleconfig.yaml

yaml 复制代码
channels:
  wecom:
    botId: "你的机器人ID"
    secret: "你的机器人密钥"
    dmPolicy: "open"
    groupPolicy: "open"
2. 运行示例程序
bash 复制代码
# 启动主程序(接收消息并自动下载媒体)
python src/main.py --config config.yaml

# 发送文本消息
python test_upload.py --bot-id YOUR_BOT_ID --secret YOUR_SECRET --chat-id CHAT_ID --text "你好"

# 发送图片
python test_upload.py --bot-id YOUR_BOT_ID --secret YOUR_SECRET --chat-id CHAT_ID --file-path image.jpg
3. 代码示例
python 复制代码
from src import create_plugin, WeComMessage

# 创建插件实例
plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret"
)

# 设置消息回调
def on_message(msg: WeComMessage):
    print(f"收到消息: {msg.content}")
    # 回复消息
    plugin.reply_message(msg, "收到你的消息了")

plugin.on_message = on_message

# 启动插件
plugin.start()

🏗️ 项目架构

目录结构

复制代码
wecom-python-plugin/
├── src/                         # 核心源代码
│   ├── __init__.py              # 主插件实现
│   ├── main.py                  # 应用入口
│   └── handlers.py              # 消息历史存储
├── sender_api.py                # API 发送接口
│   ├── text                     # 文本消息发送
│   ├── image                    # 图片消息发送
│   ├── vedio                    # 视频消息发送
│   └── file                     # 文件消息发送
├── downloader.py                # 媒体下载器(图片/视频/文件)
├── config.yaml                  # 配置文件
├── requirements.txt             # Python 依赖
└── download/                    # 媒体文件下载目录

核心模块

WeComClient - WebSocket 客户端

负责与企业微信服务器的 WebSocket 通信:

  • 连接管理 - 建立连接、认证、心跳、重连
  • 消息收发 - 接收推送、发送消息
  • 媒体上传 - 分片上传大文件
  • 响应等待 - 同步等待服务器响应
MessageParser - 消息解析器

解析企业微信推送的消息:

  • 提取消息字段(ID、类型、内容等)
  • 处理不同消息类型(文本、图片、视频、文件)
  • 创建 WeComMessage 对象
AccessController - 访问控制器

管理消息访问权限:

MediaDownloader - 媒体下载器

下载和解密媒体文件:

  • HTTP 下载
  • AES-256-CBC 解密
  • 文件类型检测(Magic Bytes)
  • 自动保存

🔧 核心功能

1. 消息接收

支持接收以下类型的消息:

消息类型 说明 字段
text 文本消息 content, mentioned_list
image 图片消息 url, aeskey, media_id
video 视频消息 url, aeskey, media_id
voice 语音消息 url, aeskey, media_id
file 文件消息 url, aeskey, media_id, filename

消息对象结构:

python 复制代码
@dataclass
class WeComMessage:
    msg_id: str                    # 消息ID
    msg_type: str                  # 消息类型
    chat_type: str                 # 会话类型(single/group)
    chat_id: str                   # 会话ID
    from_user_id: str              # 发送者ID
    from_user_name: str            # 发送者名称
    content: str                   # 消息内容
    media_url: str                 # 媒体URL
    mentioned_list: List[str]      # @成员列表
    timestamp: int                 # 时间戳
    raw_body: Dict[str, Any]       # 原始消息体

2. 消息发送

发送文本消息
python 复制代码
# 单聊
plugin.send_message("user_id", "你好")

# 群聊(@成员)
plugin.send_message("group_id", "大家好", mentioned_list=["user1", "user2"])

# 回复消息
plugin.reply_message(msg, "收到你的消息了")
发送图片
python 复制代码
plugin.client.send_image("chat_id", "path/to/image.jpg")

支持格式: JPG, PNG, GIF, BMP
大小限制: ≤ 10MB

发送视频
python 复制代码
plugin.client.send_video(
    "chat_id",
    "path/to/video.mp4",
    title="视频标题",
    description="视频描述"
)

支持格式: MP4
大小限制: ≤ 100MB
分辨率建议: ≤ 1920×1080
编码要求: H.264 + AAC

发送文件
python 复制代码
plugin.client.send_file("chat_id", "path/to/document.pdf")

支持格式: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, ZIP 等
大小限制: ≤ 100MB

3. 媒体上传机制

分片上传流程
复制代码
1. 初始化上传
   ├─ 发送文件信息(大小、MD5、分片数)
   └─ 获取 upload_id

2. 分片上传
   ├─ 每片 512KB
   ├─ Base64 编码
   ├─ 最多 200 片(约 100MB)
   └─ 等待每片确认

3. 完成上传
   ├─ 发送完成请求
   └─ 获取 media_id

4. 发送消息
   └─ 使用 media_id 发送
上传参数
python 复制代码
CHUNK_SIZE = 512 * 1024    # 分片大小:512KB
MAX_CHUNKS = 200           # 最大分片数:200(约100MB)

修改大小限制:

如需支持更大文件,修改 src/__init__.py 中的 MAX_CHUNKS 参数:

python 复制代码
MAX_CHUNKS = 400  # 支持约 200MB

4. 媒体下载机制

自动下载

主程序会自动下载收到的媒体文件:

python 复制代码
# 在 src/main.py 中已实现
def _on_message(self, msg: WeComMessage):
    if msg.msg_type in ["image", "video", "file"]:
        result = self.downloader.download_from_callback(
            msg.raw_body,
            self.download_dir
        )
        if result:
            print(f"文件已保存: {result['file_path']}")
手动下载
python 复制代码
from downloader import MediaDownloader

downloader = MediaDownloader()

# 从回调消息下载
result = downloader.download_from_callback(
    callback_data=msg.raw_body,
    save_dir="./download"
)

# 从URL下载
data = downloader.download_from_url(
    url="https://...",
    aeskey="base64_encoded_key",
    save_path="output.jpg"
)
AES 解密

企业微信的媒体文件使用 AES-256-CBC 加密:

  • 算法: AES-256-CBC
  • IV: 密钥的前 16 字节
  • 密钥格式: URL 安全的 Base64 编码

下载器会自动处理解密过程。

文件类型检测

通过 Magic Bytes 自动识别文件类型:

格式 Magic Bytes 扩展名
JPEG FF D8 FF .jpg
PNG 89 50 4E 47 .png
MP4 00 00 00 ?? 66 74 79 70 .mp4
PDF 25 50 44 46 .pdf
ZIP 50 4B 03 04 .zip

支持 30+ 种文件格式的自动识别。

5. 访问控制

DM(私聊)策略
yaml 复制代码
dmPolicy: "open"              # 策略类型
allowFrom: ["user1", "user2"] # 白名单(allowlist模式)

策略类型:

  • pairing - 配对模式(需要用户首次发送消息批准)
  • open - 开放模式(所有用户可发送)
  • allowlist - 白名单模式(只允许白名单用户)
  • disabled - 禁用模式(不接收任何私聊)
群聊策略
yaml 复制代码
groupPolicy: "open"                    # 策略类型
groupAllowFrom: ["group1", "group2"]   # 白名单

策略类型:

  • open - 开放模式(所有群聊可用)
  • allowlist - 白名单模式(只允许白名单群聊)
  • disabled - 禁用模式(不接收任何群消息)

6. 连接管理

心跳保活
python 复制代码
HEARTBEAT_INTERVAL_MS = 30000  # 30秒发送一次心跳

自动发送 ping 帧保持连接活跃。

自动重连
python 复制代码
MAX_RECONNECT_ATTEMPTS = 100   # 最多重连100次

连接断开时自动重连,使用指数退避策略。

连接状态回调
python 复制代码
def on_connected():
    print("已连接")

def on_disconnected(reason):
    print(f"已断开: {reason}")

def on_error(error):
    print(f"错误: {error}")

plugin.on_connected = on_connected
plugin.on_disconnected = on_disconnected
plugin.on_error = on_error

📚 API 文档

插件初始化

方式1:使用工厂函数(推荐)
python 复制代码
from src import create_plugin

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret",
    dm_policy="open",
    group_policy="open"
)
方式2:手动创建
python 复制代码
from src import WeComPlugin, WeComConfig

config = WeComConfig(
    bot_id="your_bot_id",
    secret="your_secret",
    dm_policy="open",
    allow_from=["user1", "user2"],
    group_policy="allowlist",
    group_allow_from=["group1"],
    websocket_url="wss://openws.work.weixin.qq.com"
)

plugin = WeComPlugin(config)

WeComPlugin 类

方法
方法 说明 参数
start() 启动插件
stop() 停止插件
send_message(chat_id, content, mentioned_list) 发送文本消息 chat_id, content, mentioned_list=[]
reply_message(msg, content) 回复消息 msg, content
回调函数
回调 说明 参数
on_message 收到消息 msg: WeComMessage
on_connected 连接成功
on_disconnected 连接断开 reason: str
on_error 发生错误 error: Exception

WeComClient 类

方法
方法 说明 返回值
send_text(chat_id, content, mentioned_list) 发送文本 bool
send_image(chat_id, image_path) 发送图片 bool
send_video(chat_id, video_path, title, description) 发送视频 bool
send_file(chat_id, file_path) 发送文件 bool
upload_media(file_path, media_type) 上传媒体 media_id: str

MediaDownloader 类

方法
方法 说明 返回值
download_from_callback(callback_data, save_dir) 从回调下载 Dict
download_from_url(url, aeskey, save_path) 从URL下载 bytes
download_image(image_data, save_path) 下载图片 bytes
download_video(video_data, save_path) 下载视频 bytes
download_file(file_data, save_path) 下载文件 bytes

⚙️ 配置说明

完整配置示例

yaml 复制代码
channels:
  wecom:
    # 必填:机器人凭证
    botId: "aibhpZhzMDvtrBcmnCq0N0FEbCx78CIQOhQ"
    secret: "kOtU0A4wKCGfViGin1zjcp6ClwBdlyae1AOmUY2Yjmm"

    # 可选:WebSocket地址(默认值)
    websocketUrl: "wss://openws.work.weixin.qq.com"

    # DM访问策略
    dmPolicy: "open"              # pairing | open | allowlist | disabled
    allowFrom: []                 # DM白名单

    # 群聊访问策略
    groupPolicy: "open"           # open | allowlist | disabled
    groupAllowFrom: []            # 群聊白名单

# 日志配置
logging:
  level: INFO                     # DEBUG | INFO | WARNING | ERROR
  file: "wecom.log"

# 消息存储配置
storage:
  history_days: 180               # 历史消息保留天数
  type: memory                    # memory | mysql | postgres

# AI配置(可选)
ai:
  enabled: false
  api_url: ""
  api_key: ""

配置加载优先级

  1. 命令行参数(最高优先级)

    bash 复制代码
    python src/main.py --bot-id XXX --secret XXX
  2. 配置文件

    bash 复制代码
    python src/main.py --config config.yaml
  3. OpenClaw配置 (自动读取 ~/.openclaw/config.json

💡 使用示例

示例1:简单的回声机器人

python 复制代码
from src import create_plugin, WeComMessage

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret"
)

def on_message(msg: WeComMessage):
    # 回复相同的内容
    plugin.reply_message(msg, f"你说: {msg.content}")

plugin.on_message = on_message
plugin.start()

示例2:自动回复机器人

python 复制代码
from src import create_plugin, WeComMessage

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret"
)

# 关键词回复字典
REPLIES = {
    "你好": "你好!有什么可以帮助你的吗?",
    "帮助": "我可以回答你的问题,发送图片、视频等。",
    "再见": "再见!祝你有美好的一天!"
}

def on_message(msg: WeComMessage):
    content = msg.content.strip()

    # 查找匹配的关键词
    for keyword, reply in REPLIES.items():
        if keyword in content:
            plugin.reply_message(msg, reply)
            return

    # 默认回复
    plugin.reply_message(msg, "抱歉,我不太明白你的意思。")

plugin.on_message = on_message
plugin.start()

示例3:图片处理机器人

python 复制代码
from src import create_plugin, WeComMessage
from downloader import MediaDownloader
from PIL import Image

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret"
)

downloader = MediaDownloader()

def on_message(msg: WeComMessage):
    if msg.msg_type == "image":
        # 下载图片
        result = downloader.download_from_callback(
            msg.raw_body,
            "./download"
        )

        if result:
            # 处理图片(例如:转换为灰度)
            img = Image.open(result['file_path'])
            gray_img = img.convert('L')
            gray_path = result['file_path'].replace('.jpg', '_gray.jpg')
            gray_img.save(gray_path)

            # 发送处理后的图片
            plugin.client.send_image(msg.chat_id, gray_path)
            plugin.reply_message(msg, "已将图片转换为灰度")

plugin.on_message = on_message
plugin.start()

示例4:群聊管理机器人

python 复制代码
from src import create_plugin, WeComMessage

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret",
    group_policy="open"
)

def on_message(msg: WeComMessage):
    # 只处理群聊消息
    if msg.chat_type != "group":
        return

    content = msg.content.strip()

    # @所有人
    if content == "/all":
        plugin.send_message(
            msg.chat_id,
            "大家好!",
            mentioned_list=["@all"]
        )

    # 欢迎新成员
    elif content.startswith("/welcome"):
        user_id = content.split()[1] if len(content.split()) > 1 else None
        if user_id:
            plugin.send_message(
                msg.chat_id,
                f"欢迎新成员!",
                mentioned_list=[user_id]
            )

plugin.on_message = on_message
plugin.start()

示例5:文件管理机器人

python 复制代码
from src import create_plugin, WeComMessage
from downloader import MediaDownloader
import os

plugin = create_plugin(
    bot_id="your_bot_id",
    secret="your_secret"
)

downloader = MediaDownloader()

def on_message(msg: WeComMessage):
    if msg.msg_type == "file":
        # 下载文件
        result = downloader.download_from_callback(
            msg.raw_body,
            "./files"
        )

        if result:
            file_path = result['file_path']
            file_size = os.path.getsize(file_path)
            file_ext = result['file_ext']

            # 发送文件信息
            info = f"文件已保存\n"
            info += f"文件名: {os.path.basename(file_path)}\n"
            info += f"大小: {file_size / 1024:.2f} KB\n"
            info += f"类型: {file_ext}"

            plugin.reply_message(msg, info)

plugin.on_message = on_message
plugin.start()

🔍 常见问题

Q1: 如何获取 bot_id 和 secret?

  1. 登录企业微信管理后台
  2. 进入"应用管理" → "智能机器人"
  3. 创建或选择一个机器人
  4. 查看机器人详情获取 bot_id 和 secret

Q2: 视频上传失败,错误码 40011?

原因: 视频不符合企业微信要求

解决方案:

  • 检查视频分辨率(建议 ≤ 1920×1080)

  • 检查视频编码(使用 H.264 + AAC)

  • 检查视频大小(≤ 50MB)

  • 使用 FFmpeg 转换:

    bash 复制代码
    ffmpeg -i input.mp4 -vf scale=1280:720 -c:v libx264 -crf 23 -c:a aac -b:a 128k output.mp4

Q3: 如何支持更大的文件?

修改 src/__init__.py 中的 MAX_CHUNKS 参数:

python 复制代码
MAX_CHUNKS = 200  # 默认100MB
MAX_CHUNKS = 400  # 支持200MB

Q4: 连接频繁断开怎么办?

检查项:

  1. 网络连接是否稳定
  2. 防火墙是否阻止 WebSocket 连接
  3. bot_id 和 secret 是否正确
  4. 查看日志中的错误信息

解决方案:

  • 启用自动重连(默认已启用)
  • 增加心跳间隔
  • 检查服务器时间是否同步

Q5: 如何调试?

启用 DEBUG 日志:

python 复制代码
import logging
logging.basicConfig(level=logging.DEBUG)

或在配置文件中:

yaml 复制代码
logging:
  level: DEBUG

查看详细日志:

  • WebSocket 连接状态
  • 消息收发详情
  • 上传下载进度
  • 错误堆栈信息

Q6: 支持哪些消息类型?

类型 接收 发送 说明
文本 支持 @成员
图片 JPG, PNG, GIF
视频 MP4
语音 AMR, MP3
文件 各类文档
卡片 暂不支持

Q7: 如何处理群聊 @ 消息?

python 复制代码
def on_message(msg: WeComMessage):
    # 检查是否被@
    if msg.from_user_id in msg.mentioned_list or "@all" in msg.mentioned_list:
        plugin.reply_message(msg, "收到你的@")

Q8: 消息历史如何存储?

默认: 内存存储(重启后丢失)

持久化存储: 可扩展为数据库存储

python 复制代码
# 在 handlers.py 中实现
class DatabaseMessageStore(MessageHistoryStore):
    def add_message(self, chat_id, msg):
        # 保存到数据库
        pass

Q9: 如何与 AI 集成?

python 复制代码
import openai

def on_message(msg: WeComMessage):
    # 调用 AI API
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": msg.content}]
    )

    ai_reply = response.choices[0].message.content
    plugin.reply_message(msg, ai_reply)

Q10: 性能优化建议?

  1. 异步处理 - 使用线程池处理耗时操作
  2. 消息队列 - 使用队列缓冲消息
  3. 连接池 - 复用 HTTP 连接
  4. 缓存 - 缓存常用数据
  5. 日志级别 - 生产环境使用 INFO 或 WARNING
相关推荐
紫丁香2 小时前
Dify源码深度剖析3
后端·python·ai·flask·fastapi
七夜zippoe2 小时前
消息队列选型:Kafka vs RabbitMQ vs Redis 深度对比
redis·python·kafka·消息队列·rabbitmq
赵谨言2 小时前
基于YOLOv5的海棠花花朵检测识别:文献综述与研究展望
大数据·开发语言·经验分享·python
-Excalibur-2 小时前
IP数据包在计算机网络传输的全过程
java·网络·c++·笔记·python·网络协议·智能路由器
weixin199701080162 小时前
“迷你京东”全栈架构设计与实现
java·大数据·python·数据库架构
SysMax2 小时前
从“能动“到“稳定“:OpenArm 开源机械臂 CAN 通信链路整改指南
机器人·机械臂·can通讯·关节电机·openarm
Mountain and sea2 小时前
发那科(FANUC)机器人指令体系深度解析与高阶应用实践
机器人
虚幻如影2 小时前
Tesseract-OCR 引擎安装
python·ocr