摘要

ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点,可以探索出更多的应用场景和商业模式。例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智能化、个性化的服务和支持;在ChatGPT中使用元宇宙进行虚拟现实体验,可以为用户提供更加真实、丰富、多样化的交互体验。
下面我将结合元宇宙和ChatGPT的优势,实战开发一个GPT虚拟直播的Demo并推流到抖音平台,

NodeJS接入ChatGPT与即构ZIM

上一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》,主要介绍了如何使用ChatGPT+即构Avatar做虚拟人直播。由于篇幅原因,对代码具体实现部分描述的不够详细。收到不少读者询问代码相关问题,接下来笔者将代码实现部分拆分2部分来详细描述:

  1. NodeJS接入ChatGPT与即构ZIM
  2. ChatGPT与即构Avatar虚拟人对接直播

本文主要讲解如何接入ChatGPT并实现后期能与Avatar对接能力。

在开始讲具体流程之前,我们先来回顾一下整个GPT虚拟直播Demo的实现流程图,本文要分享的内容是下图的右边部分的实现逻辑。

1 基本原理

ChatGPT是纯文本互动,那么如何让它跟Avatar虚拟人联系呢?
首先我们已知一个先验:

  • 即构Avatar有文本驱动能力,即给Avatar输入一段文本,Avatar根据文本渲染口型+播报语音
  • 将观众在直播间发送的弹幕消息抓取后,发送给OpenAI的ChatGPT服务器
  • 得到ChatGPT回复后将回复内容通过Avatar语音播报
    在观众看来,这就是在跟拥有ChatGPT一样智商的虚拟人直播互动了。

2 本文使用的工具

  • GPT虚拟直播弹幕:即构ZIM语聊房群聊弹幕
  • GPT4.0:New bing
  • GPT3.5:ChatGPT

3 对接ChatGPT

这里主要推荐2个库:

  • chatgpt-api
  • chatgpt

chatgpt-api封装了基于bing的chatgpt4.0,chatgpt基于openAI官方的chatgpt3.5。具体如何创建bing账号以及如何获取Cookie值以及如何获取apiKey,可以参考我另一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》。

3.1 chatgpt-api

安装:

npm i @waylaidwanderer/chatgpt-api

bing还没有对中国大陆开放chatgpt,因此需要一个代理,因此需要把代理地址也一起封装。代码如下:


import { BingAIClient } from '@waylaidwanderer/chatgpt-api';export class BingGPT {/** http_proxy, apiKey**/constructor(http_proxy, userCookie) {this.api = this.init(http_proxy, userCookie);this.conversationSignature = "";this.conversationId = "";this.clientId = "";this.invocationId = "";}init(http_proxy, userCookie) {console.log(http_proxy, userCookie)const options = { host: 'https://www.bing.com', userToken: userCookie,// If the above doesn't work, provide all your cookies as a string insteadcookies: '',// A proxy string like "http://<ip>:<port>"proxy: http_proxy,// (Optional) Set to true to enable `console.debug()` loggingdebug: false,};return new BingAIClient(options);}////此处省略chat函数......//
}

上面代码完成了VPN和BingAIClient的封装,还缺少聊天接口,因此添加chat函数完成聊天功能:

//调用chatpgt
chat(text, cb) {var res=""var that = this;console.log("正在向bing发送提问", text ) this.api.sendMessage(text, { toneStyle: 'balanced',onProgress: (token) => { if(token.length==2 && token.charCodeAt(0)==55357&&token.charCodeAt(1)==56842){cb(true, res);} res+=token;}}).then(function(response){ that.conversationSignature = response.conversationSignature;that.conversationId = response.conversationId;that.clientId = response.clientId;that.invocationId = response.invocationId;}) ;  }

在使用的时候只需如下调用:

var bing = new BingGPT(HTTP_PROXY, BING_USER_COOKIE);
bing.chat("这里传入提问内容XXXX?", function(succ, response){if(succ)console.log("回复内容:", response)
})

需要注意的是,基于bing的chatgpt4.0主要是通过模拟浏览器方式封住。在浏览器端有很多防机器人检测,因此容易被卡断。这里笔者建议仅限自己体验,不适合作为产品接口使用。如果需要封装成产品,建议使用下一节2.2内容。

3.2 chatgpt

安装:

npm install chatgpt

跟上一小节2.1类似,基于openAI的chatgpt3.5依旧需要梯子才能使用。chatgpt库没有内置代理能力,因此我们可以自己安装代理库:

npm install https-proxy-agent node-fetch

接下来将代理和chatgpt库一起集成封装成一个类:

import { ChatGPTAPI } from "chatgpt";
import proxy from "https-proxy-agent";
import nodeFetch from "node-fetch";export class ChatGPT {constructor(http_proxy, apiKey) {this.api = this.init(http_proxy, apiKey);this.conversationId = null;this.ParentMessageId = null;}init(http_proxy, apiKey) {console.log(http_proxy, apiKey)return new ChatGPTAPI({apiKey: apiKey,fetch: (url, options = {}) => {const defaultOptions = {agent: proxy(http_proxy),};const mergedOptions = {...defaultOptions,...options,};return nodeFetch(url, mergedOptions);},});}//...//此处省略chat函数//...
}

完成ChatGPTAPI的封装后,接下来添加聊天接口:

//调用chatpgt
chat(text, cb) {let that = thisconsole.log("正在向ChatGPT发送提问:", text)that.api.sendMessage(text, {conversationId: that.ConversationId,parentMessageId: that.ParentMessageId}).then(function (res) {that.ConversationId = res.conversationIdthat.ParentMessageId = res.idcb && cb(true, res.text)}).catch(function (err) {console.log(err)cb && cb(false, err);});
}

使用时就非常简单:

var chatgpt =  new ChatGPT(HTTP_PROXY, API_KEY);
chatgpt.chat("这里传入提问内容XXXX?", function(succ, response){if(succ)console.log("回复内容:", response)
})

chatgpt库主要基于openAI的官方接口,相对来说比较稳定,推荐这种方式使用。

3.3 两库一起封装

为了更加灵活方便使用,随意切换chatgpt3.5和chatgpt4.0。将以上两个库封装到一个接口中。

首先创建一个文件保存各种配置, KeyCenter.js:

const HTTP_PROXY = "http://127.0.0.1:xxxx";//本地vpn代理端口
//openAI的key,
const API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxx";
//bing cookie
const BING_USER_COOKIE = 'xxxxxxxxxxxxxxxxxxxxxxxx--BA';module.exports = { HTTP_PROXY: HTTP_PROXY,API_KEY: API_KEY,BING_USER_COOKIE:BING_USER_COOKIE
}

注意,以上相关配置内容需要读者替换。

接下来封装两个不同版本的chatGPT:

const KEY_CENTER = require("../KeyCenter.js");
var ChatGPTObj = null, BingGPTObj = null;
//初始化chatgpt
function getChatGPT(onInitedCb) {if (ChatGPTObj != null) {onInitedCb(true, ChatGPTObj);return;}(async () => {let { ChatGPT } = await import("./chatgpt.mjs");return new ChatGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.API_KEY);})().then(function (obj) {ChatGPTObj = obj;onInitedCb(true, obj);}).catch(function (err) {onInitedCb(false, err);});
}function getBingGPT(onInitedCb){if(BingGPTObj!=null) {onInitedCb(true, BingGPTObj);return;}(async () => {let { BingGPT } = await import("./binggpt.mjs");return new BingGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.BING_USER_COOKIE);})().then(function (obj) {BingGPTObj = obj;onInitedCb(true, obj);}).catch(function (err) {console.log(err)onInitedCb(false, err);});
}

上面两个函数getBingGPTgetChatGPT分别对应2.1节2.2节封装的版本。在切换版本的时候直接调用对应的函数即可,但笔者认为,还不够优雅!使用起来还是不够舒服,因为需要维护不同的对象。最好能进一步封装,调用的时候一行代码来使用是最好的。那进一步封装,补充以下代码:

//调用chatgpt聊天
function chatGPT(text, cb) {getChatGPT(function (succ, obj) {if (succ) {obj.chat(text, cb);} else {cb && cb(false, "chatgpt not inited!!!");}})
}function chatBing(text, cb){getBingGPT(function (succ, obj) {if (succ) {obj.chat(text, cb);} else {cb && cb(false, "chatgpt not inited!!!");}})}module.exports = {chatGPT: chatGPT,chatBing:chatBing
}

加了以上代码后,就舒服多了:想要使用bing的chatgpt4.0,那就调用chatBing函数好了;想要使用openAI官方的chatgpt3.5,那就调用chatGPT函数就好!

4 对接Avatar

4.1 基本思路

好了,第2节介绍了对chatgpt的封装,不同的版本只需调用不同函数即可实现与chatgpt对话。接下来怎么将chatGPT的文本对话内容传递给Avatar呢?即构Avatar是即构推出的一款虚拟形象产品,它可以跟即构内的其他产品对接,比如即时通讯ZIM和音视频通话RTC。这就好办了,我们只需利用ZIM或RTC即可。

这里我们主要利用即构ZIM实现,因为即构ZIM非常方便实时文本内容。即构ZIM群聊消息稳定可靠,延迟低,全球任何一个地区都有接入服务的节点保障到达。

尤其是ZIM群聊有弹幕功能,相比发送聊天消息,发送弹幕消息不会被存储,更适合直播间评论功能。

4.2 代码实现

即构官方提供的js版本库主要是基于浏览器,需要使用到浏览器的特性如DOM、localStorage等。而这里我们主要基于NodeJS,没有浏览器环境。因此我们需要安装一些必要的库, 相关库已经在package.json有记录,直接执行如下命令即可:

npm install

4.2.1 创建模拟浏览器环境

首先执行浏览器环境模拟,通过fake-indexeddb、jsdom、node-localstorage库模拟浏览器环境以及本地存储环境。创建WebSocket、XMLHttpRequest等全局对象。

var fs = require('fs');
//先清理缓存
fs.readdirSync('./local_storage').forEach(function (fileName) {fs.unlinkSync('./local_storage/' + fileName);
});const KEY_CENTER = require("../KeyCenter.js");
const APPID = KEY_CENTER.APPID, SERVER_SECRET = KEY_CENTER.SERVER_SECRET;
const generateToken04 = require('./TokenUtils.js').generateToken04;
var LocalStorage = require('node-localstorage').LocalStorage;
localStorage = new LocalStorage('./local_storage');
var indexedDB = require("fake-indexeddb/auto").indexedDB;
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(``, {url: "http://localhost/",referrer: "http://localhost/",contentType: "text/html",includeNodeLocations: true,storageQuota: 10000000
});
window = dom.window;
document = window.document;
navigator = window.navigator;
location = window.location;
WebSocket = window.WebSocket;
XMLHttpRequest = window.XMLHttpRequest;

4.2.2 创建ZIM对象

将即构官方下载的index.js引入,获取ZIM类并实例化,这个过程封装到createZIM函数中。需要注意的是登录需要Token,为了安全考虑,Token建议在服务器端生成。接下来把整个初始化过程封装到initZego函数中,包含注册监听接收消息,监控Token过期并重置。

const ZIM = require('./index.js').ZIM; function newToken(userId) {const token = generateToken04(APPID, userId, SERVER_SECRET, 60 * 60 * 24, '');return token;
}
/*** 创建ZIM对象
*/
function createZIM(onError, onRcvMsg, onTokenWillExpire) {var zim = ZIM.create(APPID);zim.on('error', onError);zim.on('receivePeerMessage', function (zim, msgObj) {console.log("收到P2P消息")onRcvMsg(false, zim, msgObj)});// 收到群组消息的回调zim.on('receiveRoomMessage', function (zim, msgObj) {console.log("收到群组消息")onRcvMsg(true, zim, msgObj)});zim.on('tokenWillExpire', onTokenWillExpire);return zim;
}
/*
*初始化即构ZIM
*/
function initZego(onError, onRcvMsg, myUID) {var token = newToken(myUID);var startTimestamp = new Date().getTime();function _onError(zim, err) {onError(err);}function _onRcvMsg(isFromGroup, zim, msgObj) {var msgList = msgObj.messageList;var fromConversationID = msgObj.fromConversationID;msgList.forEach(function (msg) {if (msg.timestamp - startTimestamp >= 0) { //过滤掉离线消息var out = parseMsg(zim, isFromGroup, msg.message, fromConversationID)if (out)onRcvMsg(out); }})}function onTokenWillExpire(zim, second) {token = newToken(userId);zim.renewToken(token);}var zim = createZIM(_onError, _onRcvMsg, onTokenWillExpire);login(zim, myUID, token, function (succ, data) {if (succ) {console.log("登录成功!")} else {console.log("登录失败!", data)}})return zim;
}

4.2.3 登录、创建房间、加入房间、离开房间

调用zim对象的login函数完成登录,封装到login函数中;调用zim对象的joinRoom完成加入房间,封装到joinRoom函数中;调用zim的leaveRoom函数完成退出房间,封装到leaveRoom函数中。

/*** 登录即构ZIM
*/
function login(zim, userId, token, cb) {var userInfo = { userID: userId, userName: userId };zim.login(userInfo, token).then(function () {cb(true, null);}).catch(function (err) {cb(false, err);});
}
/*** 加入房间
*/
function joinRoom(zim, roomId, cb = null) {zim.joinRoom(roomId).then(function ({ roomInfo }) {cb && cb(true, roomInfo);}).catch(function (err) {cb && cb(false, err);});
}
/*** 离开房间
*/
function leaveRoom(zim, roomId) {zim.leaveRoom(roomId).then(function ({ roomID }) {// 操作成功console.log("已离开房间", roomID)}).catch(function (err) {// 操作失败console.log("离开房间失败", err)});
}

4.2.4 发送消息、解析消息

发送消息分为一对一发送和发送到房间,这里通过isGroup参数来控制,如下sendMsg函数所示。将接收消息UID和发送内容作为sendMsg参数,最终封装并调用ZIM的sendMessage函数完成消息发送。

接收到消息后,在我们的应用中设置了发送的消息内容是个json对象,因此需要对内容进行解析,具体的json格式可以参考完整源码,这里不做详细讲解。

/*** 发送消息
*/
function sendMsg(zim, isGroup, msg, toUID, cb) { var type = isGroup ? 1 : 0; // 会话类型,取值为 单聊:0,房间:1,群组:2var config = {priority: 1, // 设置消息优先级,取值为 低:1(默认),中:2,高:3}; var messageTextObj = { type: 20, message: msg, extendedData: '' };var notification = {onMessageAttached: function (message) { console.log("已发送", message)}} zim.sendMessage(messageTextObj, toUID, type, config, notification).then(function ({ message }) {// 发送成功cb(true, null);}).catch(function (err) {// 发送失败cb(false, err)});
}
/*** 解析收到的消息
*/
function parseMsg(zim, isFromGroup, msg, fromUid) {//具体实现略
}

4.2.5 导出接口

有了以上的实现后,把关键函数导出暴露给其他业务调用:

module.exports = {initZego: initZego,sendMsg: sendMsg,joinRoom: joinRoom
}

以上代码主要封装:

  1. 即构ZIM初始化
  2. 发送消息
  3. 加入房间

至此,我们就具备了将chatgpt消息群发到一个房间的能力、加入房间、接收到房间的弹幕消息能力。

更多关于即构ZIM接口与官方Demo可以点击参考这里,对即构ZIM了解更多可以点击这里

关于Avatar如何播报chatgpt内容,我们在下一篇文章实现。

5 相关代码

  1. nodejs接入chatgpt与即构zim

GPT虚拟直播Demo系列(一)|GPT接入直播间实现主播与观众互动相关推荐

  1. GPT虚拟直播Demo系列(二)|无人直播间实现虚拟人回复粉丝

    摘要 虚拟人和数字人是人工智能技术在现实生活中的具体应用,它们可以为人们的生活和工作带来便利和创新.在直播间场景里,虚拟人和数字人可用于直播主播.智能客服.营销推广等.接入GPT的虚拟人像是加了超强b ...

  2. 抖音电商直播间SOP主播工作计划脚本话术模板方案

    抖音电商直播间SOP主播工作计划脚本话术模板方案 网盘文档下载地址https://pan.baidu.com/s/1EeI_qPZBKH-hmwiG2bX-ZQ?pwd=rrh4 一家公司十几万的人是 ...

  3. 全民直播时代,如何才能杀出重围成为一个优质主播?

    1月6日友望数据线上大咖分享会,我们邀请到人气形体主播-孙天舒前来分享她的关于视频号直播变现的经验. 以下内容是直播当天分享的删减版,错过分享会的朋友请往下阅读. 大家好,我是孙天舒,孙天舒优雅形体的 ...

  4. python刷直播人气_python3爬取斗鱼某些版块的主播人气

    python 3爬取斗鱼某些版块的主播人气 1.爬虫版块 import Test3 import urllib.request from bs4 import BeautifulSoup import ...

  5. java 框架 直播_java如何实现秀场直播功能?基于即构SDK开发

    目前,直播市场一片火热,直播已经广泛应用于娱乐.会议.培训.商演等活动中,给各行业带来了不少经济效益.不过当下很多商家平台并没有直播经验,也不知道如何搭建直播间,比如最近有一位朋友咨询我,如何用jav ...

  6. 用 ZEGO Avatar 做一个虚拟人|虚拟主播直播解决方案

    虚拟直播既可以实现单人视频直播,也可以邀请观众上麦.与虚拟主播进行多人连麦互动. 虚拟直播场景架构设计 虚拟直播场景的主要架构如下图所示(以多人连麦直播互动为例): 虚拟人直播体验 App 源码 ZE ...

  7. 音视频开发(2)---red5+java打造直播平台系列 初级

    red5+java打造直播平台系列 初级 red5+java打造直播平台系列 (搭建调测) 前言:如今各式各样的直播平台,像雨后春笋般冒出来.本系列重零开始,基于RED5为服务器,通过JAVA编写后台 ...

  8. 什么是虚拟主播?虚拟数字人直播,不用出镜,不用露脸的直播方式

    "虚拟主播"(Virtual Youtuber,简称Vtuber)是于2016年在Youtube等视频平台新出现的一类非真实视频主播.此类主播无需真人露脸,视频主体角色多为3D建模 ...

  9. 虚拟主播也带货?直播电商的变与不变

    5月6日晚,海外虚拟主播vox在B站开启了中国直播首秀.从最终数据来看,直播1.7小时,营收111万人民币,当晚还登上平台热门首位,这样的直播吸金能力,让不少明星都望尘莫及. 更值得关注的是,直播间当 ...

最新文章

  1. 如何度过二十多岁这段又穷又迷茫的岁月?
  2. 火狐浏览器 firebug调试不能载入javascript
  3. ISIS和OSPF的比较
  4. LeetCode 螺旋矩阵(Spiral Matrix)
  5. JavaFX技巧7:使用CSS颜色常量/派生颜色
  6. PMP读书笔记(第7章)
  7. ElasticSearch(二十四)基于scoll技术滚动搜索大量数据
  8. 数学建模、统计学之方差分析
  9. alter 用法集合(持续更新)
  10. 谷歌研究员称 CVE-2020-1509 的补丁不完整,详情和 PoC 已发布
  11. abb工业机器人电压不稳_ABB工业机器人应用常见故障九问九答
  12. VisualAssistX中文注释提示错误 解决办法
  13. 大数据分析的误区有哪些
  14. 程序设计基础(c语言)(第3版)课后习题答案
  15. FFmpegFFplay常用命令汇总
  16. 智慧城市顶层设计策略方案(ppt)
  17. 王之泰201771010131《面向对象程序设计(java)》第十三周学习总结
  18. ECSHOP漏洞集:http://sebug.net/appdir/ECSHOP
  19. 永恒之蓝(勒索病毒)
  20. autoCAD绘制简单三维立体图形

热门文章

  1. 威联通(QNAP)docker jellyfin开启硬解
  2. 微信小程序-点击按钮,图片重新加载
  3. el-tree 改变接口中的名字不是el-tree所需的label和children
  4. 编写程序将quot;hello-NOworldquot;变成quot;hello-worldquot;
  5. Python爬虫基础教程,详细讲解(含爬取文字为乱码解决办法、反爬虫机制解决办法)
  6. css实现文章首字下沉显示
  7. 在win7上安装驱动程序时报错“your systme requires a security update from microsoft before this driver software “
  8. 如何成为CNNIC域名注册服务机构及域名注册服务机构申请书
  9. 再见了,昔日的英雄们!
  10. OPPO A53刷机包下载 OPPO A53T刷机包下载_oppo密码忘记怎么办?来这里搞定