杰瑞科技汇

Flask与wxpy如何实现微信机器人开发?

这是一个非常经典和实用的组合,可以让你:

Flask与wxpy如何实现微信机器人开发?-图1
(图片来源网络,侵删)
  • 通过 Flask 搭建一个网站或 API 接口。
  • 通过 WxPy 运行一个微信机器人,实现自动回复、好友管理、群管理等功能。
  • 让这两者能够互相通信,通过 Web 网页发送指令给微信机器人,或者让微信机器人将收到的消息推送到 Web 页面上显示。

核心概念与工作流程

  1. Python: 作为基础编程语言,连接所有组件。
  2. Flask: 一个轻量级的 Web 框架,它会启动一个 Web 服务器,监听来自浏览器的 HTTP 请求(比如用户点击按钮、提交表单),并根据请求返回响应(HTML 页面或 JSON 数据)。
  3. WxPy: 一个强大的微信个人号接口库,它会登录你的微信,并持续监听来自微信的消息(好友消息、群消息、好友请求等)。

它们如何协同工作?

最核心的挑战在于,Flask 的 Web 服务器是事件驱动、阻塞式的(在处理一个请求时,不能处理其他请求),而 WxPy 的机器人是长期运行、轮询式的(需要一直在线等待消息),如果直接在同一个主线程中运行,Flask 将无法响应任何 Web 请求,因为线程会被 WxPy 的 embed() 方法阻塞。

我们必须使用多线程来解决这个问题。

标准工作流程:

Flask与wxpy如何实现微信机器人开发?-图2
(图片来源网络,侵删)
  1. 主线程:启动 Flask 应用。
  2. 子线程 1:运行 WxPy 机器人的 bot.embed(),让它保持登录并监听微信消息。
  3. 子线程 2 (可选):运行一个简单的 Flask 开发服务器,或者由主线程来启动它。

当有微信消息到达时,WxPy 的回调函数(如 @bot.register)会被触发,在这个回调函数中,我们可以执行任何操作,

  • 调用一个 Flask 的 API 端点(通过 requests 库)。
  • 修改一个共享的变量(需要使用线程安全的方式,如 queue.Queue)。
  • 直接操作一个由 Flask 提供的数据库。

当有 Web 请求到达时,Flask 的视图函数会被触发,在这个函数中,我们可以:

  • 调用 WxPy 的 API(如 bot.file_helper.send_file())来发送文件。
  • 读取由 WxPy 线程更新的共享变量,并将数据显示在网页上。

环境准备

确保你已经安装了必要的库。

# 安装 Flask
pip install Flask
# 安装 WxPy (注意:WxPy 已停止维护,但仍然可用)
# 推荐使用 `wxpy`,它是一个较新的 fork
pip install wxpy
# (可选) 如果需要在 Flask 视图函数中调用 WxPy API,确保它们在同一进程
# 如果分开,则需要用 HTTP 请求通信
pip install requests

实战案例:一个简单的 Web 控制台

这个案例将实现以下功能:

Flask与wxpy如何实现微信机器人开发?-图3
(图片来源网络,侵删)
  1. 微信机器人自动回复所有好友消息。
  2. 启动一个 Flask 网站,网站上有两个按钮:
    • "发送文件": 点击后,机器人会通过文件传输助手发送一个指定的文本文件。
    • "获取好友列表": 点击后,页面会显示机器人的前 10 个好友。

第 1 步:编写微信机器人代码 (robot.py)

这个脚本将负责登录微信和定义消息回复逻辑。

# robot.py
from wxpy import Bot, Message
import threading
import time
# 全局变量,用于存储好友列表,以便 Flask 读取
# 注意:直接共享全局变量在多线程中可能不安全,这里为了简化演示。
# 在生产环境中,建议使用 queue.Queue 或数据库。
global_friends_cache = []
def wechat_bot_logic():
    """
    WxPy 机器人的核心逻辑,将在子线程中运行。
    """
    print("正在登录微信...")
    # cache_path=True 可以缓存登录状态,下次不用扫码
    bot = Bot(cache_path=True)
    print("登录成功!")
    # 注册消息回调函数,自动回复所有文本消息
    @bot.register(Message, TEXT)
    def auto_reply(msg):
        print(f"收到来自 {msg.sender} 的消息: {msg.text}")
        # 回复消息
        msg.reply(f"我已经收到你的消息: {msg.text}")
        # 可以在这里调用 Flask API,
        # requests.post('http://127.0.0.1:5000/api/received_message', json={'sender': msg.sender.name, 'content': msg.text})
    # 更新全局好友列表
    global global_friends_cache
    global_friends_cache = bot.friends(update=True)[:10] # 只取前10个
    print("已更新好友列表缓存。")
    # 嵌入微信的终端,保持机器人运行
    print("机器人已启动,等待消息...")
    bot.embed() # 这会阻塞当前线程
if __name__ == '__main__':
    # 启动机器人线程
    bot_thread = threading.Thread(target=wechat_bot_logic, daemon=True)
    bot_thread.start()
    # 主线程可以继续做其他事情,比如启动 Flask
    # 或者就保持运行
    while True:
        time.sleep(1)

第 2 步:编写 Flask Web 应用 (app.py)

这个脚本将创建 Web 服务器和用户界面。

# app.py
from flask import Flask, render_template_string, request
import requests # 用于调用 WxPy 的 API (WxPy 在另一个进程中)
# --- 注意 ---
# 在这个例子中,我们将 WxPy 和 Flask 放在同一个进程中。
# 这样,Flask 的视图函数可以直接访问 `bot` 对象。
# 但请注意,这仍然需要将 WxPy 的 `embed()` 放在单独的线程中。
from wxpy import Bot
import threading
# 创建 Flask 应用
app = Flask(__name__)
# 全局变量,用于存储 bot 实例
# 注意:直接在 Flask 视图中使用全局 bot 对象,前提是它已经在另一个线程中被初始化。
# 这是一种比较简单但不完全线程安全的方式。
global_bot = None
# HTML 模板
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">微信机器人控制台</title>
    <style>
        body { font-family: sans-serif; margin: 40px; }
        h1 { color: #333; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
        button { padding: 10px 15px; margin: 10px 5px; font-size: 16px; cursor: pointer; }
        button:hover { background-color: #f0f0f0; }
        #friend-list { margin-top: 20px; border: 1px solid #eee; padding: 10px; border-radius: 5px; }
        .friend-item { padding: 5px 0; border-bottom: 1px solid #eee; }
    </style>
</head>
<body>
    <div class="container">
        <h1>微信机器人控制台</h1>
        <p>这是一个由 Flask 和 WxPy 驱动的简单控制台。</p>
        <h2>机器人操作</h2>
        <button onclick="sendFile()">发送文件到文件传输助手</button>
        <button onclick="getFriends()">获取好友列表</button>
        <div id="friend-list"></div>
    </div>
    <script>
        function sendFile() {
            alert('正在发送文件...');
            fetch('/send_file')
                .then(response => response.json())
                .then(data => alert(data.message));
        }
        function getFriends() {
            alert('正在获取好友列表...');
            fetch('/get_friends')
                .then(response => response.json())
                .then(data => {
                    const listDiv = document.getElementById('friend-list');
                    if (data.friends.length > 0) {
                        listDiv.innerHTML = '<h3>好友列表:</h3>';
                        data.friends.forEach(friend => {
                            listDiv.innerHTML += `<div class="friend-item">- ${friend.name} (ID: ${friend.id})</div>`;
                        });
                    } else {
                        listDiv.innerHTML = '<p>未能获取到好友列表。</p>';
                    }
                });
        }
    </script>
</body>
</html>
"""
def run_bot():
    """
    在子线程中初始化并运行 WxPy 机器人。
    """
    global global_bot
    print("机器人线程启动...")
    global_bot = Bot(cache_path=True)
    print("机器人登录成功。")
    # 可以在这里添加机器人的其他逻辑,比如自动回复
    from wxpy import Message, TEXT
    @global_bot.register(Message, TEXT)
    def reply(msg):
        msg.reply(f"Web控制台已收到: {msg.text}")
    global_bot.embed() # 阻塞机器人线程
# 启动机器人线程
bot_thread = threading.Thread(target=run_bot, daemon=True)
bot_thread.start()
# 等待 bot 初始化完成
import time
time.sleep(3) # 给 bot 几秒钟时间来登录
# Flask 路由
@app.route('/')
def index():
    return render_template_string(HTML_TEMPLATE)
@app.route('/send_file')
def send_file_route():
    """
    通过 Web 请求,让机器人发送一个文件。
    """
    if global_bot:
        try:
            # 创建一个简单的文本文件并发送
            content = "这是从 Web 控制台发送的文件!\n时间: " + str(time.time())
            with open('test_from_web.txt', 'w', encoding='utf-8') as f:
                f.write(content)
            global_bot.file_helper.send_file('test_from_web.txt')
            return {'message': '文件发送成功!'}
        except Exception as e:
            return {'message': f'发送失败: {str(e)}'}
    return {'message': '机器人未初始化'}
@app.route('/get_friends')
def get_friends_route():
    """
    通过 Web 请求,获取机器人好友列表。
    """
    if global_bot:
        try:
            friends = global_bot.friends()
            # 返回 JSON 格式的好友信息
            return {'friends': [{'name': f.name, 'id': f.id} for f in friends[:10]]}
        except Exception as e:
            return {'message': f'获取好友列表失败: {str(e)}', 'friends': []}
    return {'message': '机器人未初始化', 'friends': []}
if __name__ == '__main__':
    # 运行 Flask 应用
    # host='0.0.0.0' 允许从网络中的其他计算机访问
    app.run(debug=True, port=5000, host='0.0.0.0')

第 3 步:运行应用

  1. 保存文件:将上面的代码分别保存为 robot.pyapp.py
  2. 运行 Flask 应用:在终端中,进入文件所在目录,运行 app.py
    python app.py
  3. 扫码登录:程序会弹出一个二维码,使用你的微信扫描二维码登录。
  4. 访问网站:登录成功后,打开浏览器访问 http://127.0.0.1:5000
  5. 测试功能
    • 在微信上给你的机器人发消息,它会自动回复。
    • 在网页上点击“发送文件”按钮,你的微信文件传输助手会收到一个 test_from_web.txt 文件。
    • 点击“获取好友列表”按钮,页面会显示你的部分好友信息。

高级架构与最佳实践

上面的例子为了简单,将 bot 对象作为全局变量,对于更复杂的应用,这种做法并不健壮,以下是更推荐的架构:

进程间通信 (IPC)

这是最健壮、最灵活的架构,特别适合大型或分布式应用。

  1. Flask 进程:只负责 Web 服务,不包含任何 WxPy 代码,它暴露 API 端点。
  2. WxPy 进程:只负责与微信交互,它监听微信消息,并通过某种方式通知 Flask 进程。
  3. 通信桥梁:两者之间通过一个中间件进行通信。

通信桥梁的选择:

  • 消息队列:如 Redis、RabbitMQ、Celery。

    • WxPy 进程:收到消息后,将消息数据(发送者、内容、时间等)作为任务推送到队列中。
    • Flask 进程:有一个后台任务(如 Celery Worker)从队列中取出任务,进行处理(例如存入数据库、触发 Webhook)。
    • 优点:解耦度高,性能好,支持异步。
    • 缺点:架构更复杂,需要额外的服务。
  • HTTP API

    • WxPy 进程:收到消息后,使用 requests 库向 Flask 的一个特定 API 端点(如 /api/wehook)发送一个 POST 请求。
    • Flask 进程:接收这个请求,处理数据。
    • 优点:实现简单,易于理解。
    • 缺点:耦合度稍高,WxPy 进程需要知道 Flask 的地址。

共享数据库

这是另一种常见的解耦方式。

  1. WxPy 进程:收到消息后,将消息内容写入一个共享的数据库(如 SQLite, MySQL, PostgreSQL)。
  2. Flask 进程:通过查询数据库来获取最新的消息,并展示在 Web 页面上,Flask 也通过写入数据库来向 WxPy 发送指令(创建一个“待发送消息”的表)。
  3. WxPy 进程:可以定期轮询“待发送消息”表,发现有新指令就执行。

优点:数据持久化,架构清晰。 缺点:存在一定的延迟(轮询或查询开销),需要处理数据库并发问题。

特性 简单多线程 (直接调用) 进程间通信 (如 HTTP API) 共享数据库
复杂度
耦合度
性能 一般 取决于数据库和查询
健壮性 低 (一个崩溃可能影响另一个) 高 (进程隔离) 高 (数据持久化)
适用场景 快速原型、小型工具 大多数生产环境、需要解耦的场景 需要数据持久化、有复杂业务逻辑的场景

对于初学者,从简单多线程开始是最好的选择,当你需要构建更稳定、可扩展的应用时,应该转向进程间通信共享数据库的架构。

分享:
扫描分享到社交APP
上一篇
下一篇