WeChat Bot!

背景

帮朋友调研微信机器人方案, 发了个 GitHub 仓库 chatgpt-on-wechat 让我部署下 发现这个仓库是使用 Python 的 itchat 库实现的 web 版本的微信机器人, 并且做了一些相关的 patch 处理 参见 lib/itchat

首先微信没有开放官方的 API, 社区上有一些三方库, 主要实现有以下集中

  1. Web 协议, 通过无头浏览器模拟 web 端发出请求和拦截请求
  1. Windows 协议, 通过住
  1. iPad 协议
  1. 直接 Hack Android 手机, 逆向微信

Dokploy

Dokploy 是一个基于 Docker 自动化部署平台, 开源, 免费自行部署

refs: https://www.reddit.com/r/SideProject/comments/1gq6o61/after_6_months_of_work_dokploy_cloud_is_live/

快速搭建一个微信机器人

基于 wechatbot-webhook 部署

部署到 Dokploy

version: '3.8'
services:
  wechat-webhook:
    image: dannicool/docker-wechatbot-webhook
    container_name: wechat-webhook
    volumes:
      - ./wechat-webhook-logs:/app/log
    ports:
      - "3001:3001"
    environment:
      - LOG_LEVEL=info
      # - DISABLE_AUTO_LOGIN=true
      - ACCEPT_RECVD_MSG_MYSELF=true
      - RECVD_MSG_API=https://wechat-callback.hz.yuler.dev
      - LOGIN_API_TOKEN=WEBHOOK_TOKEN # 登录地址Token访问地址: http://localhost:3001/login?token=[LOCAL_LOGIN_API_TOKEN]
    restart: unless-stopped

Callback

我们再创建一个服务接受上面的请求

import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()


app.post('/', async (ctx) => {
  const formData = await ctx.req.formData()
  const type = (formData.get('type') ?? '') as string
  let content = (formData.get('content') ?? '') as any
  let source = (formData.get('source') ?? '') as any
  const isMentioned = (formData.get('isMentioned') ?? '') as string
  const isMsgFromSelf = (formData.get('isMsgFromSelf') ?? '') as string
  const topic = source?.room?.payload?.topic ?? ''
  
  try {
    source = JSON.parse(source)
  } catch {
    console.log('source parse error')
  }

  // 非 `#dev` 群消息不处理
  if (!topic.startsWith('#dev')) {
    return ctx.json({})
  }

  console.log({ type, content, source, isMentioned, isMsgFromSelf })
  
  // 文本消息 'text' & @xxx
  if (type === 'text' && isMentioned === '1') {
    // TODO: 动态化 @xxx
    content = content.slice('@xxx '.length)
    if (content === 'ping')  {
      const mention = `@${source.from.payload.name}`
      return ctx.json({ success: true, data: { type: 'text', content: mention + ' pong' }})
    }
  }

  return ctx.json({})
})

集成 OCR

Umi-OCR 开源 OCR 免费并且离线

Docker 运行

Linux 版本

通过上面的仓库构建 image 并且 push 到 docker hub yule/umi-ocr-paddle

docker run -d --name umi-ocr -e HEADLESS=true -p 1224:1224 yule/umi-ocr-paddle

部署到到 Dokploy

services:
  wechat-ocr:
    image: yule/umi-ocr-paddle
    container_name: wechat-ocr
    # volumes:
    #   - ./wechat-ocr-logs:/app/log
    ports:
      - "1224:1224"
    environment:
      - HEADLESS=true
      - LOG_LEVEL=info 
    restart: unless-stopped

集成 AI

  1. proxy?

测试脚本

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4o",
    "messages": [
      {
        "role": "system",
        "content": "You are a helpful assistant."
      },
      {
        "role": "user",
        "content": "Hello!"
      }
    ]
  }'

由于 OpenAI 禁止大陆 IP 访问, 所以需要使用代理

refs: https://github.com/justjavac/openai-proxy

  1. Node SDK
import OpenAI from 'openai'

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

const completion = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [
    { role: "system", content: 'You are a helpful assistant.' },
    { role: "user", '你好啊' }
  ],
  temperature: 0.8,
});

const answer = completion.choices[0].message.content
console.log({answer})

使用场景