3分钟自定义你的chatGPT聊天微信机器人

编程知识 更新时间:2023-04-25 15:49:57

背景

最近chatGPT实在太火了,不谈下都不好意思说自己在技术圈混了,刚好前段时间公司里面在举办一个企业微信机器人比赛,然后就用注册了openai的一个账号,用python写了一个玩玩,但是想想不过瘾只能公司内部体验,于是乎又花了2个小时写了一个基于微信的聊天机器人,这里和大家交流下。

现实步骤

  1. 首先要能绕开openai 国内的访问限制,这一步骤大家自己想办法;
  2. 需要注册openai账号,这一步骤大家也自己想办法;
  3. 实现模拟登录微信账号,找到一个不错的开源代码,在我的github里面有;
  4. 在接收到微信信息时调用openai的api,这里我使用了go sdk 开源地址也在在我的github里。

相关功能

我的简单demo代码开源拉了github地址点这里
功能介绍:

  • 支持回复微信文本消息
  • 支持文本回复图片消息
  • 支持群消息at回复
  • 支持上下文记忆
  • 支持基于网页链接地址读取总结文件

演示图片

群里时at它

单聊时直接发送消息,支持根据语义来返回图片

实现原理

  1. 调用微信桌面客户端协议登录微信号
  2. 监听微信文本消息进行回复
  3. 在回复消息之前对消息进行语义识别,这里单独写了一个prompt来识别文本的语义是来生成图片还是进行文本回复就行,如果是生成图片的话,就直接回复图片。

源码

package main

import (
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"image/png"
	"os"
	"strings"
	"time"

	"github/eatmoreapple/openwechat"
	openai "github/sashabaranov/go-openai"
)

// OpenaiWechat
type OpenaiWechat struct {
	ChatBot      *openwechat.Bot                           // chat bot
	Openai       *openai.Client                            // openai client
	userDialogue map[string][]openai.ChatCompletionMessage // save user gialogue context
}

var singleton *OpenaiWechat

// firstUserDialogue
var firstUserDialogue = []openai.ChatCompletionMessage{
	{
		Role:    openai.ChatMessageRoleSystem,
		Content: "你是一个非常有帮助的助手",
	},
	{
		Role:    openai.ChatMessageRoleUser,
		Content: "请问我怎么规划我的职业发展呢?",
	},
	{
		Role: openai.ChatMessageRoleAssistant,
		Content: `你好,我很高兴能帮助你。职业发展是一个非常重要的话题,你可以从以下几个方面考虑:
		你的兴趣爱好和技能:你可以考虑你的兴趣爱好和技能,看看哪些职业与你的兴趣爱好和技能相匹配。这样可以让你在工作中感到更有成就感和满足感。
		行业前景:你可以考虑一些行业的前景,看看哪些行业在未来几年内会有更好的发展前景。这样可以让你在职业发展中更有保障。
		学历和培训:你可以考虑你的学历和培训,看看哪些职业需要更高的学历和培训。这样可以让你更好地规划你的职业发展。
		希望这些建议能帮到你。你还有其他问题吗?`,
	},
}

func init() {
	//bot := openwechat.DefaultBot()
	bot := openwechat.DefaultBot(openwechat.Desktop) // deskop mode,you can switch deskop mode if defualt can not login

	// Register message handler function
	bot.MessageHandler = func(msg *openwechat.Message) {
		replyChatMsg(msg)
	}

	// Register login QR code callback
	bot.UUIDCallback = openwechat.PrintlnQrcodeUrl

	// login
	if err := bot.Login(); err != nil {
		fmt.Println(err)
		return
	}

	client := openai.NewClient(os.Getenv("OPENAI_KEY")) // your openai key
	//client := openai.NewClient("")
	singleton = &OpenaiWechat{
		Openai:  client,
		ChatBot: bot,
	}
	singleton.userDialogue = make(map[string][]openai.ChatCompletionMessage)

	// lock goroutine, until an exception occurs or the user actively exits
	bot.Block()
}
func main() {
}

// replyChatMsg
func replyChatMsg(msg *openwechat.Message) error {

	if msg.IsSendByGroup() { // only accept group messages and send them to yourself
		if !msg.IsAt() {
			return nil
		}
	} else if !msg.IsSendByFriend() { // non-group messages only accept messages from your own friends
		return nil
	}

	if msg.IsSendBySelf() { // self sent to self no reply
		return nil
	}
	// only handle text messages
	if msg.IsText() {
		msg.Content = strings.Replace(msg.Content, "@GPT3.5 ", "", 1)
		fmt.Println(msg.Content)
		// simple match processing
		if isImage, _ := isImageContent(msg.Content); isImage {
			return replyImage(msg)
		}
		return replyText(msg)
	}
	return nil

}

// 图片生成校验
var imageMessage = []openai.ChatCompletionMessage{
	{
		Role:    openai.ChatMessageRoleSystem,
		Content: "你现在是一个语义识别助手,用户输入一个文本,你根据文本的内容来判断用户是不是想生成图片,是的话你就回复是,不是的话你就回复否,记住只能回复:是 或者 否",
	},
	{
		Role:    openai.ChatMessageRoleUser,
		Content: "我想生成一张小花猫的图片",
	},
	{
		Role:    openai.ChatMessageRoleAssistant,
		Content: "是",
	},
	{
		Role:    openai.ChatMessageRoleUser,
		Content: "请问冬天下雨我该穿什么衣服",
	},
	{
		Role:    openai.ChatMessageRoleAssistant,
		Content: "否",
	},
}

// isImageConntent
func isImageContent(content string) (bool, error) {
	temp := append(imageMessage, openai.ChatCompletionMessage{
		Role:    openai.ChatMessageRoleUser,
		Content: content,
	})
	result, err := callOpenaiChat(temp)
	if err != nil {
		fmt.Printf("isImageConntent callOpenaiChat error: %v\n", err)
		return false, err
	}
	fmt.Printf("isImageContent result: %s", result)
	if strings.TrimSpace(result) == "是" {
		return true, nil
	}
	return false, nil

}

// replyImage
func replyImage(msg *openwechat.Message) error {
	path, err := generateImage(msg)
	if err != nil {
		fmt.Printf("replyImage generateImage error: %v\n", err)
		return err
	}
	img, err := os.Open(path)
	if err != nil {
		fmt.Printf("replyImage Open error: %v\n", err)
		return err
	}
	defer img.Close()
	msg.ReplyImage(img)
	return nil
}

// replyText
func replyText(msg *openwechat.Message) error {
	messages, err := genMessage(msg)
	if err != nil {
		return err
	}
	result, err := callOpenaiChat(messages)
	if err != nil {
		fmt.Println(err)
		return err
	}
	msg.ReplyText(result)
	addResultToMessage(result, msg)
	return nil
}

// dialogue context max length
const maxLength = 33

// temporary folder to save pictures
const filePath = "./images/"

// genMessage
func genMessage(msg *openwechat.Message) ([]openai.ChatCompletionMessage, error) {
	// TODO
	if _, ok := singleton.userDialogue[msg.FromUserName]; !ok {
		singleton.userDialogue[msg.FromUserName] = firstUserDialogue
	} else if len(singleton.userDialogue[msg.FromUserName]) >= maxLength {
		singleton.userDialogue[msg.FromUserName] = singleton.userDialogue[msg.FromUserName][2:]
	}
	element := openai.ChatCompletionMessage{
		Role:    openai.ChatMessageRoleUser,
		Content: msg.Content,
	}
	singleton.userDialogue[msg.FromUserName] = append(singleton.userDialogue[msg.FromUserName], element)
	return singleton.userDialogue[msg.FromUserName], nil
}

// addResultToMessage
func addResultToMessage(result string, msg *openwechat.Message) error {
	element := openai.ChatCompletionMessage{
		Role:    openai.ChatMessageRoleAssistant,
		Content: result,
	}
	singleton.userDialogue[msg.FromUserName] = append(singleton.userDialogue[msg.FromUserName], element)
	return nil
}

// generateImage
func generateImage(msg *openwechat.Message) (string, error) {
	ctx := context.Background()

	// Sample image by link
	reqUrl := openai.ImageRequest{
		Prompt:         msg.Content,
		Size:           openai.CreateImageSize512x512,
		ResponseFormat: openai.CreateImageResponseFormatB64JSON,
		N:              1,
	}

	respBase64, err := singleton.Openai.CreateImage(ctx, reqUrl)
	if err != nil {
		fmt.Printf("Image creation error: %v\n", err)
		return "", err
	}
	imgBytes, err := base64.StdEncoding.DecodeString(respBase64.Data[0].B64JSON)
	if err != nil {
		fmt.Printf("Base64 decode error: %v\n", err)
		return "", err
	}

	r := bytes.NewReader(imgBytes)
	imgData, err := png.Decode(r)
	if err != nil {
		fmt.Printf("PNG decode error: %v\n", err)
		return "", err
	}
	filePath := fmt.Sprintf("%s%d.png", filePath, time.Now().UnixMicro())
	file, err := os.Create(filePath)
	if err != nil {
		fmt.Printf("File creation error: %v\n", err)
		return "", err
	}
	defer file.Close()

	if err := png.Encode(file, imgData); err != nil {
		fmt.Printf("PNG encode error: %v\n", err)
		return "", err
	}
	return filePath, nil
}

// callOpenaiChat
func callOpenaiChat(messages []openai.ChatCompletionMessage) (string, error) {
	resp, err := singleton.Openai.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model:     openai.GPT3Dot5Turbo,
			Messages:  messages,
			MaxTokens: 512,
		},
	)
	if err != nil {
		fmt.Printf("ChatCompletion error: %v\n", err)
		return "", err
	}
	fmt.Println(resp.Choices[0].Message.Content)
	return resp.Choices[0].Message.Content, nil
}

注意

  1. 微信号最好不要用自己常用的微信号,因为模拟登录微信属于作弊行为很有可能会被封号;
  2. 注册的新的微信号可能无法马上模拟登录成功微信,需要实名认证之后,过一段时间才能成功登录。
  3. 注册的openai账号里面默认有18刀,调用不同的模型话费的tokens不一样,测试的时候注意测试自己的消耗速度,图片比文本更耗钱。

更多推荐

3分钟自定义你的chatGPT聊天微信机器人

本文发布于:2023-04-19 09:59:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/512428b642cbaa01f434df90f5437e50.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自定义   机器人   chatGPT

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 87635文章数
  • 20229阅读数
  • 0评论数