mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 21:37:11 +00:00
Use chatgpt-api (#106)
* feat: use the latest api from chatgpt-api * feat: use the latest api from chatgpt-api
This commit is contained in:
parent
09e8909b51
commit
bdafec9a21
3 changed files with 174 additions and 23 deletions
155
apps/chat.js
155
apps/chat.js
|
|
@ -5,7 +5,9 @@ import showdown from 'showdown'
|
|||
import mjAPI from 'mathjax-node'
|
||||
import { ChatGPTPuppeteer } from '../utils/browser.js'
|
||||
import { uuid } from 'oicq/lib/common.js'
|
||||
import delay from "delay";
|
||||
import delay from 'delay'
|
||||
import { ChatGPTAPI } from 'chatgpt'
|
||||
import { getMessageById, upsertMessage } from '../utils/common.js'
|
||||
// import puppeteer from '../utils/browser.js'
|
||||
// import showdownKatex from 'showdown-katex'
|
||||
const blockWords = '屏蔽词1,屏蔽词2,屏蔽词3'
|
||||
|
|
@ -51,7 +53,7 @@ export class chatgpt extends plugin {
|
|||
/** 命令正则匹配 */
|
||||
reg: '^[^#][sS]*',
|
||||
/** 执行方法 */
|
||||
fnc: 'chatgpt'
|
||||
fnc: 'chatgptNew'
|
||||
},
|
||||
{
|
||||
reg: '#chatgpt对话列表',
|
||||
|
|
@ -195,7 +197,7 @@ export class chatgpt extends plugin {
|
|||
// 队列队尾插入,开始排队
|
||||
await redis.rPush('CHATGPT:CHAT_QUEUE', [randomId])
|
||||
if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) {
|
||||
await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 120 })
|
||||
await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 60 })
|
||||
} else {
|
||||
let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1
|
||||
await this.reply(`我正在思考如何回复你,请稍等,当前队列前方还有${length}个问题`, true, { recallMsg: 120 })
|
||||
|
|
@ -318,4 +320,151 @@ export class chatgpt extends plugin {
|
|||
await this.reply(`与OpenAI通信异常,请稍后重试:${e}`, true, { recallMsg: e.isGroup ? 10 : 0 })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #chatgpt
|
||||
* @param e oicq传递的事件参数e
|
||||
*/
|
||||
async chatgptNew (e) {
|
||||
if (!e.msg || e.msg.startsWith('#')) {
|
||||
return
|
||||
}
|
||||
if (e.isGroup && !e.atme) {
|
||||
return
|
||||
}
|
||||
this.chatGPTApi = new ChatGPTAPI({
|
||||
apiKey: Config.apiKey,
|
||||
debug: true,
|
||||
upsertMessage,
|
||||
getMessageById
|
||||
})
|
||||
|
||||
let prompt = e.msg.trimStart()
|
||||
|
||||
let randomId = uuid()
|
||||
// 队列队尾插入,开始排队
|
||||
await redis.rPush('CHATGPT:CHAT_QUEUE', [randomId])
|
||||
if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) {
|
||||
await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 8 })
|
||||
} else {
|
||||
let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1
|
||||
await this.reply(`我正在思考如何回复你,请稍等,当前队列前方还有${length}个问题`, true, { recallMsg: 8 })
|
||||
// 开始排队
|
||||
while (true) {
|
||||
if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) {
|
||||
break
|
||||
} else {
|
||||
await delay(1500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`chatgpt prompt: ${prompt}`)
|
||||
// try {
|
||||
// await this.chatGPTApi.init()
|
||||
// } catch (e) {
|
||||
// await this.reply('chatgpt初始化出错:' + e.msg, true)
|
||||
// }
|
||||
let previousConversation = await redis.get(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`)
|
||||
let conversation = null
|
||||
if (!previousConversation) {
|
||||
let ctime = new Date()
|
||||
previousConversation = {
|
||||
sender: e.sender,
|
||||
ctime,
|
||||
utime: ctime,
|
||||
num: 0
|
||||
}
|
||||
// await redis.set(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`, JSON.stringify(previousConversation), { EX: CONVERSATION_PRESERVE_TIME })
|
||||
} else {
|
||||
previousConversation = JSON.parse(previousConversation)
|
||||
conversation = {
|
||||
conversationId: previousConversation.conversation.conversationId,
|
||||
parentMessageId: previousConversation.conversation.parentMessageId
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let option = {
|
||||
timeoutMs: 120000
|
||||
}
|
||||
if (conversation) {
|
||||
option = Object.assign(option, conversation)
|
||||
}
|
||||
console.log(conversation)
|
||||
let chatMessage = await this.chatGPTApi.sendMessage(prompt, option)
|
||||
previousConversation.conversation = {
|
||||
conversationId: chatMessage.conversationId,
|
||||
parentMessageId: chatMessage.id
|
||||
}
|
||||
let response = chatMessage?.text
|
||||
previousConversation.num = previousConversation.num + 1
|
||||
await redis.set(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`, JSON.stringify(previousConversation), { EX: CONVERSATION_PRESERVE_TIME })
|
||||
// 移除队列首位,释放锁
|
||||
await redis.lPop('CHATGPT:CHAT_QUEUE', 0)
|
||||
// 检索是否有屏蔽词
|
||||
const blockWord = blockWords.split(',').find(word => response.toLowerCase().includes(word.toLowerCase()))
|
||||
if (blockWord) {
|
||||
await this.reply('返回内容存在敏感词,我不想回答你', true)
|
||||
return
|
||||
}
|
||||
let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
|
||||
if (userSetting) {
|
||||
userSetting = JSON.parse(userSetting)
|
||||
} else {
|
||||
userSetting = {
|
||||
usePicture: false
|
||||
}
|
||||
}
|
||||
if (userSetting.usePicture) {
|
||||
let endTokens = ['.', '。', '……', '!', '!', ']', ')', ')', '】', '?', '?', '~', '"', "'"]
|
||||
while (!endTokens.find(token => response.trimEnd().endsWith(token))) {
|
||||
// while (!response.trimEnd().endsWith('.') && !response.trimEnd().endsWith('。') && !response.trimEnd().endsWith('……') &&
|
||||
// !response.trimEnd().endsWith('!') && !response.trimEnd().endsWith('!') && !response.trimEnd().endsWith(']') && !response.trimEnd().endsWith('】')
|
||||
// ) {
|
||||
await this.reply('内容有点多,我正在奋笔疾书,请再等一会', true, { recallMsg: 5 })
|
||||
option = {
|
||||
onConversationResponse: function (c) {
|
||||
previousConversation.conversation = {
|
||||
conversationId: c.conversation_id,
|
||||
parentMessageId: c.message.id
|
||||
}
|
||||
redis.set(`CHATGPT:CONVERSATIONS:${e.sender.user_id}`, JSON.stringify(previousConversation), { EX: CONVERSATION_PRESERVE_TIME }).then(res => {
|
||||
logger.debug('redis set conversation')
|
||||
})
|
||||
}
|
||||
}
|
||||
option = Object.assign(option, previousConversation.conversation)
|
||||
const responseAppend = await this.chatGPTApi.sendMessage('Continue', option)
|
||||
// console.log(responseAppend)
|
||||
// 检索是否有屏蔽词
|
||||
const blockWord = blockWords.split(',').find(word => responseAppend.toLowerCase().includes(word.toLowerCase()))
|
||||
if (blockWord) {
|
||||
await this.reply('返回内容存在敏感词,我不想回答你', true)
|
||||
return
|
||||
}
|
||||
if (responseAppend.indexOf('conversation') > -1 || responseAppend.startsWith("I'm sorry")) {
|
||||
logger.warn('chatgpt might forget what it had said')
|
||||
break
|
||||
}
|
||||
|
||||
response = response + responseAppend
|
||||
}
|
||||
// logger.info(response)
|
||||
// markdown转为html
|
||||
// todo部分数学公式可能还有问题
|
||||
let converted = converter.makeHtml(response)
|
||||
|
||||
/** 最后回复消息 */
|
||||
await e.runtime.render('chatgpt-plugin', 'content/index', { content: converted, prompt, senderName: e.sender.nickname })
|
||||
} else {
|
||||
await this.reply(`${response}`, e.isGroup)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
// 异常了也要腾地方(todo 大概率后面的也会异常,要不要一口气全杀了)
|
||||
await redis.lPop('CHATGPT:CHAT_QUEUE', 0)
|
||||
await this.reply(`与OpenAI通信异常,请稍后重试:${e}`, true, { recallMsg: e.isGroup ? 10 : 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,12 @@
|
|||
// Token,如不需要手动配置不填
|
||||
const SESSION_TOKEN = ''
|
||||
|
||||
// CFtoken,每小时刷新一般不用填
|
||||
const CF_CLEARANCE = ''
|
||||
|
||||
const PROXY = ''
|
||||
const PROXY = 'http://127.0.0.1:7890'
|
||||
const API_KEY = ''
|
||||
|
||||
export const Config = {
|
||||
token: SESSION_TOKEN,
|
||||
cfClearance: CF_CLEARANCE,
|
||||
apiKey: API_KEY,
|
||||
// 暂时不支持proxy
|
||||
proxy: PROXY,
|
||||
username: '',
|
||||
password: '',
|
||||
// 改为true后,全局默认以图片形式回复,并自动发出Continue命令补全回答
|
||||
defaultUsePicture: true,
|
||||
// 每个人发起的对话保留时长。超过这个时长没有进行对话,再进行对话将开启新的对话。单位:秒
|
||||
conversationPreserveTime: 0,
|
||||
// UA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
|
||||
// 服务器无interface的话只能用true,但是可能遇到验证码就一定要配置下面的2captchaToken了
|
||||
// true时使用无头模式,无界面的服务器可以为true,但遇到验证码时可能无法使用。
|
||||
headless: false,
|
||||
// 为空使用默认puppeteer的chromium,也可以传递自己本机安装的Chrome可执行文件地址,提高通过率
|
||||
chromePath: '',
|
||||
// 可注册2captcha实现跳过验证码,收费服务但很便宜。否则需要手点
|
||||
'2captchaToken': ''
|
||||
conversationPreserveTime: 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
import { remark } from 'remark'
|
||||
import stripMarkdown from 'strip-markdown'
|
||||
export function markdownToText (markdown) {
|
||||
return remark()
|
||||
.use(stripMarkdown)
|
||||
.processSync(markdown ?? '')
|
||||
.toString()
|
||||
}
|
||||
|
||||
export async function upsertMessage (message) {
|
||||
await redis.set(`CHATGPT:MESSAGE:${message.id}`, JSON.stringify(message))
|
||||
}
|
||||
|
||||
export async function getMessageById (id) {
|
||||
let messageStr = await redis.get(`CHATGPT:MESSAGE:${id}`)
|
||||
return JSON.parse(messageStr)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue