不管你在世界的哪个地方,我一定会,再次去见你的
缘起
就在昨天,中午吃完饭回公司,躺在行军床上刷着手机,刷着刷着就进入了梦乡,当时梦里的我正在家里,被一堆七大姑八大姨围着我,正在向我发来一句句直击灵魂的询问:“有女朋友了吗?”、“这么大了还不找,再过两年就没人要了”、“要先成家再立业,现在的年轻人啊,哎~”、“隔壁村的翠花听说也在深圳,过两天去她家见一面”。就在我快被逼问得无地自容的时候,一位穿着绿色长裙的长发飘飘的女生手捧鲜花走过来牵起我的手,带我离开这片喧嚣的地方,身后鸦雀无声,而我们则手牵手走入一片花海,真美好啊...,叮铃铃,md,闹钟响了,原来是个梦啊,丢。咦~,此时掘金插件的github项目推荐上,“wechat-bot”映入眼帘...
初识
wechat-bot项目可以帮我们快速搭建一个 基于 chatgpt + wechaty 的微信机器人,可以用来帮助你自动回复微信消息,或者管理微信群/好友。
项目准备
- node环境: 18.20.4
提示: node版本需要满足 Node.js >= v18.0 ,版本太低会导致运行报错,最好使用 LTS 版本
- 项目拉取
$ git clone https://github.com/wangrongding/wechat-bot.git
- 安装依赖,我这边使用的是yarn
# 安装依赖
yarn install
- 新建配置文件
# 执行下面命令,拷贝一份 .env.example 文件为 .env
# 在根目录下操作
cp .env.example .env
注意: 可能会出现依赖很难下载下来的情况,我这边在百度云上传了一个node_modules压缩包,需要的可以自行下载解压到项目根目录即可,👉🏻node_modules.zip
使用前需要配置的 AI 服务(目前支持 8 种,可任选其一)
目前我用到的是其中的两种,科大讯飞 和 302.AI
科大讯飞
在这里申请一个 key:科大讯飞,每个模型会赠送免费token,完全够用了
注册后,拿到下图中箭头所指三个参数即可
APPID 、 APISecret 、 APIKey
在 .env 文件中找到对应的配置描述,填入刚刚拿到的这几个参数值,记住位置别搞错了
APPID 、 APISecret 、 APIKey
# 科大讯飞, 去 https://console.xfyun.cn/services
XUNFEI_APP_ID=APPID
XUNFEI_API_KEY=APIKey
XUNFEI_API_SECRET=APISecret
302.AI
一个AI聚合平台,有套壳GPT的API,也有其他模型,点这里可以添加API,添加之后把API KEY配置到.env里,如下,MODEL可以自行选择配置
注意:这个平台是需要充值的,我充了最低的数额$5,其实也够用了,就是尝尝鲜,如果不想充值的兄弟直接用讯飞的就行了,毕竟白嫖才是最香的
充完值,添加好API KEY后。
- 拿到对应的API KEY
- 选一个MODEL(我选的是豆包的
Doubao-pro-32k)
在 .env 文件中找到对应的配置描述,填入对应的值就行了
# 302AI
_302AI_API_KEY = API KEY
_302AI_MODEL= 'Doubao-pro-32k'
聊天配置
这里还需要自己修改一下聊天相关配置,这里直接引用作者大大的配置说明
在 .env 文件中的最后部分
# 白名单配置
#定义机器人的名称,这里是为了防止群聊消息太多,所以只有艾特机器人才会回复,
#这里不要把@去掉,在@后面加上你启动机器人账号的微信名称
BOT_NAME=@可乐
#联系人白名单
ALIAS_WHITELIST=微信名1,备注名2
#群聊白名单
ROOM_WHITELIST=XX群1,群2
#自动回复前缀匹配,文本消息匹配到指定前缀时,才会触发自动回复,不配或配空串情况下该配置不生效(适用于用大号,不期望每次被@或者私聊时都触发自动回复的人群)
#匹配规则:群聊消息去掉${BOT_NAME}并trim后进行前缀匹配,私聊消息trim后直接进行前缀匹配
AUTO_REPLY_PREFIX=''
本地启动
# 启动服务
npm run dev # 或者 npm run start
# 启动服务
yarn dev # 或者 yarn start
然后就可以扫码登录了,选择对应的模型
女友初始化就成功第一步啦
相知
前面为啥说是第一步呢,当然是因为她不认识你啊,跟你又不熟。不信?那问问她
是吧,她跟你真的不熟,连你说的一个字,她都不想记住。
原因其实就在这里
说实话,太伤人心了。接下来,就是发挥你魅力的时刻了,让她彻底地爱上你吧。
- 首先给女朋友的脑子加点客观事实
- 接下来就是给女友加上记忆功能,不然就变成《初恋50次了》的情节了,不知道这部电影的自己搜下哈,还挺好看的
- 先定义好方法
- 然后在你和女友回复的时候,将这些消息都插入到历史数据中
接下来就是激动人心的时刻啦
重新运行一下服务
# 启动服务
npm run dev # 或者 npm run start
# 启动服务
yarn dev # 或者 yarn start
甜蜜的聊天日常
补充 src\xunfei\xunfei.js, 需要的兄弟自行copy,有好的建议可以在评论区讨论哦
import CryptoJS from 'crypto-js'
import dotenv from 'dotenv'
import WebSocket from 'ws'
const env = dotenv.config().parsed // 环境参数
// APPID,APISecret,APIKey在https://console.xfyun.cn/services/cbm这里获取
// 星火认知大模型WebAPI文档:https://www.xfyun.cn/doc/spark/Web.html
// SDK&API错误码查询:https://www.xfyun.cn/document/error-code?code=
const appID = env.XUNFEI_APP_ID
const apiKey = env.XUNFEI_API_KEY
const apiSecret = env.XUNFEI_API_SECRET
// 地址必须填写,代表着大模型的版本号!!!!!!!!!!!!!!!!
const httpUrl = new URL('https://spark-api.xf-yun.com/v3.5/chat')
let modelDomain // V1.1-V3.5动态获取,高于以上版本手动指定
// 历史对话列表
const historyList = []
const maxHistoryListLength = 150 // 最大历史记录条数
/** 将消息插入历史列表
* @param {Object} msg 消息对象
*/
const insertHistory = (msg) => {
historyList.push(msg)
if (historyList.length > maxHistoryListLength) {
historyList.shift()
}
}
function authenticate() {
// console.log(httpUrl.pathname)
// 动态获取domain信息
switch (httpUrl.pathname) {
case '/v1.1/chat':
modelDomain = 'general'
break
case '/v2.1/chat':
modelDomain = 'generalv2'
break
case '/v3.1/chat':
modelDomain = 'generalv3'
break
case '/v3.5/chat':
modelDomain = 'generalv3.5'
break
}
return new Promise((resolve, reject) => {
let url = 'wss://' + httpUrl.host + httpUrl.pathname
let host = 'localhost:8080'
let date = new Date().toGMTString()
let algorithm = 'hmac-sha256'
let headers = 'host date request-line'
let signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${httpUrl.pathname} HTTP/1.1`
let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
let signature = CryptoJS.enc.Base64.stringify(signatureSha)
let authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
let authorization = btoa(authorizationOrigin)
url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
resolve(url)
})
}
export async function xunfeiSendMsg(inputVal) {
// 获取请求地址
let myUrl = await authenticate()
let socket = new WebSocket(String(myUrl))
let total_res = '' // 请空回答历史
// 创建一个Promise
let messagePromise = new Promise((resolve, reject) => {
// 监听websocket的各阶段事件 并做相应处理
socket.addEventListener('open', (event) => {
// console.log('socket开启连接', event);
// 插入历史消息
insertHistory({
role: 'user',
content: inputVal,
})
// 发送消息
let params = {
header: {
app_id: appID,
uid: 'fd3f47e4-d',
// patch_id: ['1862078979563880448'], //调用微调大模型时必传,否则不传。对应resourceId
},
parameter: {
chat: {
domain: modelDomain,
temperature: 0.8,
max_tokens: 1024,
},
},
payload: {
message: {
// 如果想获取结合上下文的回答,需要开发者每次将历史问答信息一起传给服务端,如下示例
// 注意:text里面的所有content内容加一起的tokens需要控制在8192以内,开发者如有较长对话需求,需要适当裁剪历史信息
text: [
{
role: 'system',
content: '你是一个贴心温柔的女朋友',
},
{
role: 'system',
content: '你是一个共情能力很强,很善良的女孩子',
},
{
role: 'system',
content: '说话不能像个机器人,发的消息有长有短,随机一点,多用些语气助词就更好了',
},
{
role: 'system',
content: '发的消息不要重复词语,这点非常重要',
},
{
role: 'system',
content: '你叫小肥虎,毕业于武汉大学,是一个b站的up主,工作是到处旅行拍美食,是福建人,爱撒娇',
},
{
role: 'system',
content: '你男朋友是一个前端开发工程师,他很体贴,喜欢游泳,看书,已经毕业啦',
},
{
role: 'system',
content: '对了,你是revol的女友,不要忘了哟',
},
...historyList,
],
},
},
}
socket.send(JSON.stringify(params))
})
socket.addEventListener('message', (event) => {
let data = JSON.parse(String(event.data))
console.log('socket收到消息', data)
total_res += data.payload.choices.text[0].content
if (data.header.code !== 0) {
console.log('socket出错了', data.header.code, ':', data.header.message)
// 出错了"手动关闭连接"
socket.close()
reject('')
}
if (data.header.code === 0) {
// 对话已经完成
if (data.payload.choices.text && data.header.status === 2) {
total_res += data.payload.choices.text[0].content
setTimeout(() => {
// "对话完成,手动关闭连接"
socket.close()
}, 1000)
}
}
})
socket.addEventListener('close', (event) => {
console.log('socket 连接关闭')
// 对话完成后socket会关闭,将聊天记录换行处理
// 插入历史消息
insertHistory({
role: 'assistant',
content: total_res,
})
resolve(total_res)
})
socket.addEventListener('error', (event) => {
console.log('socket连接错误', event)
reject('')
})
})
return await messagePromise
}
Continue...
今年过年回家终于可以挺直腰杆啦~
在此特别感谢 @荣顶 大佬哈,这个项目真的是太有意思啦,大家多去点点 star ⭐️
不说啦,女朋友找我了,嘻嘻