wechat-chatgpt 源码阅读 | GitHub涨星狂魔,短短几天拥有3K star 的代码该怎么写?

lxf2023-02-16 15:49:45

wechat-chatgpt 是什么?

它是一个可以让一个微信账号变成一个 ChatGPT 机器人的工具。
例如:你可以向它问一些关于编程的问题

wechat-chatgpt 源码阅读 | GitHub涨星狂魔,短短几天拥有3K star 的代码该怎么写?

wechat-chatgpt 实现步骤:

  1. 通过 wechaty 库实现获取二维码编号
  2. 通过 qrcode 库将二维码编号转换成终端二维码
  3. 扫码二维码监听 login 登录事件获取微信用户信息
  4. 监听 message 事件获取微信号收到的所有消息
  5. 将消息内容交给 chatGPTBot 发送 (监测消息类型 → 通过 chatgpt库和token 请求ChatGPT得到回复 → 将回复通过wechaty发到微信)
// main.ts

import { WechatyBuilder } from "wechaty";
import QRCode from "qrcode";
import { ChatGPTBot } from "./chatgpt.js";
const chatGPTBot = new ChatGPTBot();

// 获取Wechaty实例
const bot = WechatyBuilder.build({
  name: "wechat-assistant", // generate xxxx.memory-card.json and save login data for the next login
});

async function main() {
  bot
    .on("scan", async (qrcode, status) => {
      const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`;
      console.log(`Scan QR Code to login: ${status}\n${url}`);
      console.log(
        await QRCode.toString(qrcode, { type: "terminal", small: true })  // 显示二维码
      );
    })
    .on("login", async (user) => {     // 监听登录成功
      console.log(`User ${user} logged in`);
      chatGPTBot.setBotName(user.name());
      await chatGPTBot.startGPTBot();
    })
    .on("message", async (message) => { // 监听微信收到的所有消息
      if (message.text().startsWith("/ping ")) {
        await message.say("pong");
        return;
      }
      try {
        console.log(`Message: ${message}`);
        await chatGPTBot.onMessage(message);
      } catch (e) {
        console.error(e);
      }
    });
  try {
    await bot.start(); // 开始运行
  } catch (e) {
    console.error(
      `⚠️ Bot start failed, can you log in through wechat on the web?: ${e}`
    );
  }
}
main();

ChatGPTBot 和 ChatGPTPoole 两个类各自的作用:

import { ChatGPTAPI } from "chatgpt";

// 总结: 操作 ChatGPT 获取 token,发送消息,接收ChatGPT消息,创建会话池关联会话等
class ChatGPTPoole {
	getSessionToken, // 获取 ChatGPT 账户 token 用到 python 来获取 (token在Cookies内需要借助python获取)
	resetAccount,    // 重置 chatGPTPools 内的不可用账号,删除对于 conversationsPool 的发信息对象
	startPools,      // new ChatGPTAPI 实例化所有ChatGPT账户
	getConversation, // 通过 conversationsPool 获取会话id 对于的发信息实例 (或者负载均衡获取一个发信息实例)
	sendMessage,     // 像 ChatGPT 发送消息
	error2msg,       // 分析返回的错误消息,进行归类
}

// 总结: 接收微信消息,判断消息是否回复,交给ChatGPTPoole去发送ChatGPT消息,根据消息类型特殊处理对于的回复到微信
class ChatGPTBot {
	setBotName,    // 设置称呼
	chatGroupTiggerKeyword, // 获取群聊天关键字(称呼)
	startGPTBot,   // 调用 ChatGPTPoole.startPools 内部 new ChatGPTAPI 实例化所有ChatGPT账户
	cleanMessage,  // 清理用户传递过来的消息(删除里面的对自己的称呼)
	getGPTMessage, // 调用 ChatGPTPoole.sendMessage 向ChatGPT发送消息
	trySay,        // 将ChatGPT返回的消息发送到微信 (字数过长500分段发)
	tiggerGPTMessage, //  判断消息是否需要回复
	isNonsense,       // 过滤不需要回复的系统消息
	onPrivateMessage, // 发送私聊消息
	onGroupMessage,   // 发送群聊消息
	onMessage,        // 监听微信发来的消息
}

聊天池概念:

config.yamlchatGPTAccountPool 中所有的ChatGPT账户存储到 chatGPTPools 数组中,便于后续对聊天请求进行负载均衡,分发到各个ChatGPT账户,减少每条消息的响应时间

import { ChatGPTAPI, ChatGPTConversation } from "chatgpt";

// ChatGPTAPI池
chatGPTPools: Array<IChatGPTItem> | [] = [];
// 对话池
conversationsPool: Map<string, IConversationItem> = new Map();
// 聊天池-负载均衡
get chatGPTAPI(): IChatGPTItem {
  return this.chatGPTPools[
    Math.floor(Math.random() * this.chatGPTPools.length)
  ];
}
// 随机获取会话项表单池
getConversation(talkid: string): IConversationItem {
  // 去对话池找, 找不到就负载均衡随机分配一个
  if (this.conversationsPool.has(talkid)) {
    return this.conversationsPool.get(talkid) as IConversationItem;
  }
  const chatGPT = this.chatGPTAPI;
  if (!chatGPT) {
    throw new Error("⚠️ No chatgpt item in pool");
  }

  const conversation = chatGPT.chatGpt.getConversation(); // 获取chatGPTAPI的对话
  const conversationItem = {
    conversation, // conversation.sendMessage("发信息给 chatGPT")
    account: chatGPT.account,
  };
  this.conversationsPool.set(talkid, conversationItem);   // 存储会话
  return conversationItem;
}

/* 
chatGPTPools: 聊天池存储:talkid 对应对方聊天人id,存池的目的是后续这个发言依旧是和这个 ChatGPT实例对应
{
  chatGpt: new ChatGPTAPI({
    sessionToken: account.session_token, // ChatGPT sdk通过 token 就能实例化
  }),
  account,
}
*/

/*
conversationsPool: 存储处理该会话id对应的ChatGPT实例
{
  talkid: { 
    conversation: chatGPT.chatGpt.getConversation(),
    account: chatGPT.account,
  }
}
*/

node使用本地json文件作为缓存:

缓存 config.yamlchatGPTAccountPool 中所有的ChatGPT邮箱密码账户通过 generate_session.py 获取到的

import fs from "fs";
export class Cache {
  private _cache: Record<string, any>;
  constructor(private _file: string) {
    if (fs.existsSync(_file)) {
      this._cache = JSON.parse(fs.readFileSync(_file, "utf-8"));
    } else {
      this._cache = {};
    }
  }
  get(key: string) {
    return this._cache[key];
  }
  async set(key: string, value: any) {
    this._cache[key] = value;
    fs.writeFileSync(this._file, JSON.stringify(this._cache));
  }
  async delete(key: string) {
    delete this._cache[key];
    fs.writeFileSync(this._file, JSON.stringify(this._cache));
  }
}

// 使用
const cache = new Cache("cache.json");

cache.set(email, "需要存储的字符串")
cache.get(email) // 需要存储的字符串
cache.delete(email)

如何实现一个简单的 wechat-chatgpt ?下面就是一个简单的例子

wechat-chatgpt 简写版本

import { WechatyBuilder } from "wechaty";
import { ChatGPTAPI } from "chatgpt";
import QRCode from "qrcode";

// 通过 session_token 登录 ChatGPTAPI
const chatGpt = new ChatGPTAPI({
        sessionToken: "session_token", // 去 https://chat.openai.com/chat 打开看控制台 -> Application -> Cookies -> __Secure-next-auth.session-token
});

const bot = WechatyBuilder.build({
	name: "wechat-assistant", // generate xxxx.memory-card.json and save login data for the next login
});

bot.on("scan", async (qrcode, status) => {
	const url = `https://wechaty.js.org/qrcode/${encodeURIComponent(qrcode)}`;
	console.log(`Scan QR Code to login: ${status}\n${url}`);
	console.log(
		await QRCode.toString(qrcode, { type: "terminal", small: true }), // 显示二维码
	);
})
	.on("login", async user => {
		console.log(`User ${user} logged in`);
	})
	.on("message", async message => {
		const talker = message.talker();
		const room = message.room();
		const rawText = message.text();

		// 获取chatGTP返回的消息
		const gptMessage = await chatGpt.getConversation().sendMessage(rawText); // 发信息 rawText 给 chatGPT

                // 回复到微信
		if (!!room) {
			// 群聊
			room.say(gptMessage);
		} else {
			// 私聊
			talker.say(gptMessage);
		}
	});

bot.start(); // 开始