Merge branch 'ikechan8370:v2' into v2

This commit is contained in:
ifeif 2023-10-03 16:46:12 +08:00 committed by GitHub
commit 5797d4e90f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 95625 additions and 60868 deletions

View file

@ -67,9 +67,10 @@ import { SendDiceTool } from '../utils/tools/SendDiceTool.js'
import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
import { SendMessageToSpecificGroupOrUserTool } from '../utils/tools/SendMessageToSpecificGroupOrUserTool.js'
import { SetTitleTool } from '../utils/tools/SetTitleTool.js'
import { createCaptcha, solveCaptcha, solveCaptchaOneShot } from '../utils/bingCaptcha.js'
import { solveCaptchaOneShot } from '../utils/bingCaptcha.js'
import { ClaudeAIClient } from '../utils/claude.ai/index.js'
import fs from 'fs'
import { getProxy } from '../utils/proxy.js'
try {
await import('@azure/openai')
@ -88,14 +89,7 @@ try {
logger.warn('【ChatGPT-Plugin】依赖keyv未安装可能影响Sydney模式下Bing对话建议执行pnpm install keyv安装')
}
let version = Config.version
let proxy
if (Config.proxy) {
try {
proxy = (await import('https-proxy-agent')).default
} catch (e) {
console.warn('未安装https-proxy-agent请在插件目录下执行pnpm add https-proxy-agent')
}
}
let proxy = getProxy()
/**
* 每个对话保留的时长单个对话内ai是保留上下文的超时后销毁对话再次对话创建新的对话
* 单位
@ -108,8 +102,8 @@ const defaultPropmtPrefix = ', a large language model trained by OpenAI. You ans
const newFetch = (url, options = {}) => {
const defaultOptions = Config.proxy
? {
agent: proxy(Config.proxy)
}
agent: proxy(Config.proxy)
}
: {}
const mergedOptions = {
...defaultOptions,
@ -119,7 +113,7 @@ const newFetch = (url, options = {}) => {
return fetch(url, mergedOptions)
}
export class chatgpt extends plugin {
constructor() {
constructor () {
let toggleMode = Config.toggleMode
super({
/** 功能名称 */
@ -267,7 +261,7 @@ export class chatgpt extends plugin {
* @param e
* @returns {Promise<void>}
*/
async getConversations(e) {
async getConversations (e) {
// todo 根据use返回不同的对话列表
let keys = await redis.keys('CHATGPT:CONVERSATIONS:*')
if (!keys || keys.length === 0) {
@ -290,7 +284,7 @@ export class chatgpt extends plugin {
* @param e
* @returns {Promise<void>}
*/
async destroyConversations(e) {
async destroyConversations (e) {
const userData = await getUserData(e.user_id)
const use = (userData.mode === 'default' ? null : userData.mode) || await redis.get('CHATGPT:USE')
await redis.del(`CHATGPT:WRONG_EMOTION:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}`)
@ -452,7 +446,7 @@ export class chatgpt extends plugin {
}
}
async endAllConversations(e) {
async endAllConversations (e) {
let use = await redis.get('CHATGPT:USE') || 'api'
let deleted = 0
switch (use) {
@ -547,7 +541,7 @@ export class chatgpt extends plugin {
await this.reply(`结束了${deleted}个用户的对话。`, true)
}
async deleteConversation(e) {
async deleteConversation (e) {
let ats = e.message.filter(m => m.type === 'at')
let use = await redis.get('CHATGPT:USE') || 'api'
if (use !== 'api3') {
@ -605,7 +599,7 @@ export class chatgpt extends plugin {
}
}
async switch2Picture(e) {
async switch2Picture (e) {
let userReplySetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`)
if (!userReplySetting) {
userReplySetting = getDefaultReplySetting()
@ -618,7 +612,7 @@ export class chatgpt extends plugin {
await this.reply('ChatGPT回复已转换为图片模式')
}
async switch2Text(e) {
async switch2Text (e) {
let userSetting = await getUserReplySetting(this.e)
userSetting.usePicture = false
userSetting.useTTS = false
@ -626,7 +620,7 @@ export class chatgpt extends plugin {
await this.reply('ChatGPT回复已转换为文字模式')
}
async switch2Audio(e) {
async switch2Audio (e) {
switch (Config.ttsMode) {
case 'vits-uma-genshin-honkai':
if (!Config.ttsSpace) {
@ -654,7 +648,7 @@ export class chatgpt extends plugin {
await this.reply('ChatGPT回复已转换为语音模式')
}
async switchTTSSource(e) {
async switchTTSSource (e) {
let target = e.msg.replace(/^#chatgpt语音换源/, '')
switch (target.trim()) {
case '1': {
@ -677,7 +671,7 @@ export class chatgpt extends plugin {
await e.reply('语音转换源已切换为' + Config.ttsMode)
}
async setDefaultRole(e) {
async setDefaultRole (e) {
if (Config.ttsMode === 'vits-uma-genshin-honkai' && !Config.ttsSpace) {
await this.reply('您没有配置vits-uma-genshin-honkai API请前往后台管理或锅巴面板进行配置')
return
@ -761,7 +755,7 @@ export class chatgpt extends plugin {
/**
* #chatgpt
*/
async chatgpt(e) {
async chatgpt (e) {
let prompt
if (this.toggleMode === 'at') {
if (!e.raw_message || e.msg?.startsWith('#')) {
@ -827,7 +821,7 @@ export class chatgpt extends plugin {
await this.abstractChat(e, prompt, use)
}
async abstractChat(e, prompt, use) {
async abstractChat (e, prompt, use) {
// 关闭私聊通道后不回复
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
return false
@ -1144,9 +1138,9 @@ export class chatgpt extends plugin {
response.length / 2 < endIndex
? [response.substring(startIndex), response.substring(0, startIndex)]
: [
response.substring(0, endIndex + 1),
response.substring(endIndex + 1)
]
response.substring(0, endIndex + 1),
response.substring(endIndex + 1)
]
const match = ttsArr[0].match(emotionReg)
response = ttsArr[1].replace(/\n/, '').trim()
if (match) {
@ -1312,7 +1306,7 @@ export class chatgpt extends plugin {
}
}
async chatgpt1(e) {
async chatgpt1 (e) {
if (!Config.allowOtherMode) {
return false
}
@ -1331,7 +1325,7 @@ export class chatgpt extends plugin {
return true
}
async chatgpt3(e) {
async chatgpt3 (e) {
if (!Config.allowOtherMode) {
return false
}
@ -1350,7 +1344,7 @@ export class chatgpt extends plugin {
return true
}
async chatglm(e) {
async chatglm (e) {
if (!Config.allowOtherMode) {
return false
}
@ -1369,7 +1363,7 @@ export class chatgpt extends plugin {
return true
}
async bing(e) {
async bing (e) {
if (!Config.allowOtherMode) {
return false
}
@ -1426,7 +1420,7 @@ export class chatgpt extends plugin {
return true
}
async xh(e) {
async xh (e) {
if (!Config.allowOtherMode) {
return false
}
@ -1445,7 +1439,7 @@ export class chatgpt extends plugin {
return true
}
async cacheContent(e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
async cacheContent (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
let cacheData = { file: '', status: '' }
cacheData.file = randomString()
const cacheresOption = {
@ -1485,13 +1479,13 @@ export class chatgpt extends plugin {
return cacheData
}
async renderImage(e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
async renderImage (e, use, content, prompt, quote = [], mood = '', suggest = '', imgUrls = []) {
let cacheData = await this.cacheContent(e, use, content, prompt, quote, mood, suggest, imgUrls)
const template = use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index'
if (cacheData.error || cacheData.status != 200) { await this.reply(`出现错误:${cacheData.error || 'server error ' + cacheData.status}`, true) } else { await e.reply(await renderUrl(e, (Config.viewHost ? `${Config.viewHost}/` : `http://127.0.0.1:${Config.serverPort || 3321}/`) + `page/${cacheData.file}?qr=${Config.showQRCode ? 'true' : 'false'}`, { retType: Config.quoteReply ? 'base64' : '', Viewport: { width: parseInt(Config.chatViewWidth), height: parseInt(parseInt(Config.chatViewWidth) * 0.56) }, func: (parseFloat(Config.live2d) && !Config.viewHost) ? 'window.Live2d == true' : '', deviceScaleFactor: parseFloat(Config.cloudDPR) }), e.isGroup && Config.quoteReply) }
}
async sendMessage(prompt, conversation = {}, use, e) {
async sendMessage (prompt, conversation = {}, use, e) {
if (!conversation) {
conversation = {
timeoutMs: Config.defaultTimeoutMs
@ -1709,39 +1703,40 @@ export class chatgpt extends plugin {
}
}
} else
if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
throttledTokens.push(bingToken)
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
const now = new Date()
const hours = now.getHours()
now.setHours(hours + 6)
bingTokens[badBingToken].State = '受限'
bingTokens[badBingToken].DisactivationTime = now
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
// 不减次数
} else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) {
// token过期了
// let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
// const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
// // 可能是微软抽风,给三次机会
// if (bingTokens[badBingToken].exception) {
// if (bingTokens[badBingToken].exception <= 3) {
// bingTokens[badBingToken].exception += 1
// } else {
// bingTokens[badBingToken].exception = 0
// bingTokens[badBingToken].State = '过期'
// }
// } else {
// bingTokens[badBingToken].exception = 1
// }
// await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
logger.warn(`token${bingToken}疑似不存在或已过期,再试试`)
retry = retry - 0.1
if (message && typeof message === 'string' && message.indexOf('限流') > -1) {
throttledTokens.push(bingToken)
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
const now = new Date()
const hours = now.getHours()
now.setHours(hours + 6)
bingTokens[badBingToken].State = '受限'
bingTokens[badBingToken].DisactivationTime = now
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
// 不减次数
} else if (message && typeof message === 'string' && message.indexOf('UnauthorizedRequest') > -1) {
// token过期了
let bingTokens = JSON.parse(await redis.get('CHATGPT:BING_TOKENS'))
const badBingToken = bingTokens.findIndex(element => element.Token === bingToken)
// 可能是微软抽风,给三次机会
if (bingTokens[badBingToken].exception) {
if (bingTokens[badBingToken].exception <= 3) {
bingTokens[badBingToken].exception += 1
} else {
bingTokens[badBingToken].exception = 0
bingTokens[badBingToken].State = '过期'
}
} else {
retry--
errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message
bingTokens[badBingToken].exception = 1
}
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(bingTokens))
errorMessage = 'UnauthorizedRequest必应token不正确或已过期'
// logger.warn(`token${bingToken}疑似不存在或已过期,再试试`)
// retry = retry - 1
} else {
retry--
errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message
}
}
} while (retry > 0)
if (errorMessage) {
@ -1759,7 +1754,7 @@ export class chatgpt extends plugin {
}
} else {
return {
text: response.response,
text: response?.response,
quote: response.quote,
suggestedResponses: response.suggestedResponses,
conversationId: response.conversationId,
@ -1906,8 +1901,8 @@ export class chatgpt extends plugin {
}
const ssoSessionId = Config.xinghuoToken
if (!ssoSessionId) {
//throw new Error('未绑定星火token请使用#chatgpt设置星火token命令绑定token。获取对话页面的ssoSessionId cookie值')
logger.warn(`未绑定星火token请使用#chatgpt设置星火token命令绑定token。获取对话页面的ssoSessionId cookie值`)
// throw new Error('未绑定星火token请使用#chatgpt设置星火token命令绑定token。获取对话页面的ssoSessionId cookie值')
logger.warn('未绑定星火token请使用#chatgpt设置星火token命令绑定token。获取对话页面的ssoSessionId cookie值')
}
let client = new XinghuoClient({
ssoSessionId,
@ -1937,8 +1932,8 @@ export class chatgpt extends plugin {
const client = new OpenAIClient(Config.azureUrl, new AzureKeyCredential(Config.azApiKey))
const deploymentName = Config.azureDeploymentName
const { choices } = await client.getChatCompletions(deploymentName, msg)
let completion = choices[0].message;
return {'text' : completion.content, 'message': completion}
let completion = choices[0].message
return { text: completion.content, message: completion }
}
case 'bard': {
// 处理cookie
@ -1966,15 +1961,17 @@ export class chatgpt extends plugin {
}
// 发送数据
let bot = new Bard(cookie, {
fetch: fetch,
fetch,
bardURL: Config.bardForceUseReverse ? Config.bardReverseProxy : 'https://bard.google.com'
})
let chat = await bot.createChat(conversation?.conversationId ? {
conversationID: conversation.conversationId,
responseID: conversation.parentMessageId,
choiceID: conversation.clientId,
_reqID: conversation.invocationId
} : {})
let chat = await bot.createChat(conversation?.conversationId
? {
conversationID: conversation.conversationId,
responseID: conversation.parentMessageId,
choiceID: conversation.clientId,
_reqID: conversation.invocationId
}
: {})
let response = await chat.ask(prompt, {
image: imageBuff,
format: Bard.JSON
@ -2218,7 +2215,7 @@ export class chatgpt extends plugin {
} else {
tools.push(new SerpImageTool())
tools.push(...[new SearchVideoTool(),
new SendVideoTool()])
new SendVideoTool()])
}
let funcMap = {}
let fullFuncMap = {}
@ -2301,7 +2298,7 @@ export class chatgpt extends plugin {
}
}
async newClaudeConversation(e) {
async newClaudeConversation (e) {
let presetName = e.msg.replace(/^#claude开启新对话/, '').trim()
let client = new SlackClaudeClient({
slackUserToken: Config.slackUserToken,
@ -2339,7 +2336,7 @@ export class chatgpt extends plugin {
return true
}
async newxhBotConversation(e) {
async newxhBotConversation (e) {
let botId = e.msg.replace(/^#星火助手/, '').trim()
if (Config.xhmode != 'web') {
await e.reply('星火助手仅支持体验版使用', true)
@ -2350,7 +2347,7 @@ export class chatgpt extends plugin {
} else {
const ssoSessionId = Config.xinghuoToken
if (!ssoSessionId) {
await e.reply(`未绑定星火token请使用#chatgpt设置星火token命令绑定token`, true)
await e.reply('未绑定星火token请使用#chatgpt设置星火token命令绑定token', true)
return true
}
let client = new XinghuoClient({
@ -2364,7 +2361,7 @@ export class chatgpt extends plugin {
headers: {
'Content-Type': 'application/json',
Cookie: 'ssoSessionId=' + ssoSessionId + ';',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/113.0.5672.69 Mobile/15E148 Safari/604.1',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/113.0.5672.69 Mobile/15E148 Safari/604.1'
}
})
if (botInfoRes.ok) {
@ -2392,7 +2389,7 @@ export class chatgpt extends plugin {
await e.reply(`创建助手对话失败,${botInfo.desc}`, true)
}
} else {
await e.reply(`创建助手对话失败,服务器异常`, true)
await e.reply('创建助手对话失败,服务器异常', true)
}
} catch (error) {
await e.reply(`创建助手对话失败 ${error}`, true)
@ -2401,11 +2398,11 @@ export class chatgpt extends plugin {
return true
}
async searchxhBot(e) {
async searchxhBot (e) {
let searchBot = e.msg.replace(/^#星火(搜索|查找)助手/, '').trim()
const ssoSessionId = Config.xinghuoToken
if (!ssoSessionId) {
await e.reply(`未绑定星火token请使用#chatgpt设置星火token命令绑定token`, true)
await e.reply('未绑定星火token请使用#chatgpt设置星火token命令绑定token', true)
return true
}
const cacheresOption = {
@ -2413,7 +2410,7 @@ export class chatgpt extends plugin {
headers: {
'Content-Type': 'application/json',
Cookie: 'ssoSessionId=' + ssoSessionId + ';',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/113.0.5672.69 Mobile/15E148 Safari/604.1',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/113.0.5672.69 Mobile/15E148 Safari/604.1'
},
body: JSON.stringify({
botType: '',
@ -2431,19 +2428,19 @@ export class chatgpt extends plugin {
if (bots.data.pageList.length > 0) {
this.reply(await makeForwardMsg(this.e, bots.data.pageList.map(msg => `${msg.bot.botId} - ${msg.bot.botName}`)))
} else {
await e.reply(`未查到相关助手`, true)
await e.reply('未查到相关助手', true)
}
} else {
await e.reply(`搜索助手失败`, true)
await e.reply('搜索助手失败', true)
}
}
async emptyQueue(e) {
async emptyQueue (e) {
await redis.lTrim('CHATGPT:CHAT_QUEUE', 1, 0)
await this.reply('已清空当前等待队列')
}
async removeQueueFirst(e) {
async removeQueueFirst (e) {
let uid = await redis.lPop('CHATGPT:CHAT_QUEUE', 0)
if (!uid) {
await this.reply('当前等待队列为空')
@ -2452,7 +2449,7 @@ export class chatgpt extends plugin {
}
}
async getAllConversations(e) {
async getAllConversations (e) {
const use = await redis.get('CHATGPT:USE')
if (use === 'api3') {
let conversations = await getConversations(e.sender.user_id, newFetch)
@ -2473,7 +2470,7 @@ export class chatgpt extends plugin {
}
}
async joinConversation(e) {
async joinConversation (e) {
let ats = e.message.filter(m => m.type === 'at')
let use = await redis.get('CHATGPT:USE') || 'api'
// if (use !== 'api3') {
@ -2504,7 +2501,7 @@ export class chatgpt extends plugin {
}
}
async attachConversation(e) {
async attachConversation (e) {
const use = await redis.get('CHATGPT:USE')
if (use !== 'api3') {
await this.reply('该功能目前仅支持API3模式')
@ -2521,7 +2518,7 @@ export class chatgpt extends plugin {
}
}
async totalAvailable(e) {
async totalAvailable (e) {
// 查询OpenAI API剩余试用额度
let subscriptionRes = await newFetch(`${Config.openAiBaseUrl}/dashboard/billing/subscription`, {
method: 'GET',
@ -2530,7 +2527,7 @@ export class chatgpt extends plugin {
}
})
function getDates() {
function getDates () {
const today = new Date()
const tomorrow = new Date(today)
tomorrow.setDate(tomorrow.getDate() + 1)
@ -2567,7 +2564,7 @@ export class chatgpt extends plugin {
* @param prompt 问题
* @param conversation 对话
*/
async chatgptBrowserBased(prompt, conversation) {
async chatgptBrowserBased (prompt, conversation) {
let option = { markdown: true }
if (Config['2captchaToken']) {
option.captchaToken = Config['2captchaToken']
@ -2587,7 +2584,7 @@ export class chatgpt extends plugin {
}
}
async function getAvailableBingToken(conversation, throttled = []) {
async function getAvailableBingToken (conversation, throttled = []) {
let allThrottled = false
if (!await redis.get('CHATGPT:BING_TOKENS')) {
return {

View file

@ -217,7 +217,7 @@ ${translateLangLabels}
const match = e.msg.trim().match(regExp)
const duration = !match[1] ? 12 : parseInt(match[1]) // default 12h
if(duration > 24) {
if (duration > 24) {
await e.reply('最多只能统计24小时内的记录哦')
return false
}

View file

@ -1,316 +1,323 @@
// modified from StarRail-plugin | 已经过StarRail-plugin作者本人同意
import plugin from '../../../lib/plugins/plugin.js'
import { createRequire } from 'module'
import _ from 'lodash'
import { Restart } from '../../other/restart.js'
import fs from 'fs'
import {} from "../utils/common.js";
const _path = process.cwd()
const require = createRequire(import.meta.url)
const { exec, execSync } = require('child_process')
const checkAuth = async function (e) {
if (!e.isMaster) {
e.reply(`只有主人才能命令ChatGPT哦~(*/ω\*)`)
return false
}
return true
}
// 是否在更新中
let uping = false
/**
* 处理插件更新
*/
export class Update extends plugin {
constructor () {
super({
name: 'chatgpt更新插件',
event: 'message',
priority: 1000,
rule: [
{
reg: '^#?(chatgpt|柴特寄批踢|GPT|ChatGPT|柴特鸡批踢|Chat|CHAT|CHATGPT|柴特|ChatGPT-Plugin|ChatGPT-plugin|chatgpt-plugin)(插件)?(强制)?更新$',
fnc: 'update'
}
]
})
}
/**
* rule - 更新chatgpt插件
* @returns
*/
async update () {
if (!this.e.isMaster) return false
/** 检查是否正在更新中 */
if (uping) {
await this.reply('已有命令更新中..请勿重复操作')
return
}
/** 检查git安装 */
if (!(await this.checkGit())) return
const isForce = this.e.msg.includes('强制')
/** 执行更新 */
await this.runUpdate(isForce)
/** 是否需要重启 */
if (this.isUp) {
// await this.reply("更新完毕,请重启云崽后生效")
setTimeout(() => this.restart(), 2000)
}
}
restart () {
new Restart(this.e).restart()
}
/**
* chatgpt插件更新函数
* @param {boolean} isForce 是否为强制更新
* @returns
*/
async runUpdate (isForce) {
let command = 'git -C ./plugins/chatgpt-plugin/ pull --no-rebase'
if (isForce) {
command = `git -C ./plugins/chatgpt-plugin/ checkout . && ${command}`
this.e.reply('正在执行强制更新操作,请稍等')
} else {
this.e.reply('正在执行更新操作,请稍等')
}
/** 获取上次提交的commitId用于获取日志时判断新增的更新日志 */
this.oldCommitId = await this.getcommitId('chatgpt-plugin')
uping = true
let ret = await this.execSync(command)
uping = false
if (ret.error) {
logger.mark(`${this.e.logFnc} 更新失败chatgpt-plugin`)
this.gitErr(ret.error, ret.stdout)
return false
}
/** 获取插件提交的最新时间 */
let time = await this.getTime('chatgpt-plugin')
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(ret.stdout)) {
await this.reply(`chatgpt-plugin已经是最新版本\n最后更新时间:${time}`)
} else {
await this.reply(`chatgpt-plugin\n最后更新时间:${time}`)
this.isUp = true
/** 获取chatgpt组件的更新日志 */
let log = await this.getLog('chatgpt-plugin')
await this.reply(log)
}
logger.mark(`${this.e.logFnc} 最后更新时间:${time}`)
return true
}
/**
* 获取chatgpt插件的更新日志
* @param {string} plugin 插件名称
* @returns
*/
async getLog (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -20 --oneline --pretty=format:"%h||[%cd] %s" --date=format:"%m-%d %H:%M"`
let logAll
try {
logAll = await execSync(cm, { encoding: 'utf-8' })
} catch (error) {
logger.error(error.toString())
this.reply(error.toString())
}
if (!logAll) return false
logAll = logAll.split('\n')
let log = []
for (let str of logAll) {
str = str.split('||')
if (str[0] == this.oldCommitId) break
if (str[1].includes('Merge branch')) continue
log.push(str[1])
}
let line = log.length
log = log.join('\n\n')
if (log.length <= 0) return ''
let end = ''
end =
'更多详细信息请前往github查看\nhttps://github.com/ikechan8370/chatgpt-plugin'
log = await this.makeForwardMsg(`chatgpt-plugin更新日志${line}`, log, end)
return log
}
/**
* 获取上次提交的commitId
* @param {string} plugin 插件名称
* @returns
*/
async getcommitId (plugin = '') {
let cm = `git -C ./plugins/${plugin}/ rev-parse --short HEAD`
let commitId = await execSync(cm, { encoding: 'utf-8' })
commitId = _.trim(commitId)
return commitId
}
/**
* 获取本次更新插件的最后一次提交时间
* @param {string} plugin 插件名称
* @returns
*/
async getTime (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -1 --oneline --pretty=format:"%cd" --date=format:"%m-%d %H:%M"`
let time = ''
try {
time = await execSync(cm, { encoding: 'utf-8' })
time = _.trim(time)
} catch (error) {
logger.error(error.toString())
time = '获取时间失败'
}
return time
}
/**
* 制作转发消息
* @param {string} title 标题 - 首条消息
* @param {string} msg 日志信息
* @param {string} end 最后一条信息
* @returns
*/
async makeForwardMsg (title, msg, end) {
let nickname = (this.e.bot ?? Bot).nickname
if (this.e.isGroup) {
let info = await (this.e.bot ?? Bot).getGroupMemberInfo(this.e.group_id, (this.e.bot ?? Bot).uin)
nickname = info.card || info.nickname
}
let userInfo = {
user_id: (this.e.bot ?? Bot).uin,
nickname
}
let forwardMsg = [
{
...userInfo,
message: title
},
{
...userInfo,
message: msg
}
]
if (end) {
forwardMsg.push({
...userInfo,
message: end
})
}
/** 制作转发内容 */
if (this.e.isGroup) {
forwardMsg = await this.e.group.makeForwardMsg(forwardMsg)
} else {
forwardMsg = await this.e.friend.makeForwardMsg(forwardMsg)
}
/** 处理描述 */
forwardMsg.data = forwardMsg.data
.replace(/\n/g, '')
.replace(/<title color="#777777" size="26">(.+?)<\/title>/g, '___')
.replace(/___+/, `<title color="#777777" size="26">${title}</title>`)
return forwardMsg
}
/**
* 处理更新失败的相关函数
* @param {string} err
* @param {string} stdout
* @returns
*/
async gitErr (err, stdout) {
let msg = '更新失败!'
let errMsg = err.toString()
stdout = stdout.toString()
if (errMsg.includes('Timed out')) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接超时:${remote}`)
return
}
if (/Failed to connect|unable to access/g.test(errMsg)) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接失败:${remote}`)
return
}
if (errMsg.includes('be overwritten by merge')) {
await this.reply(
msg +
`存在冲突:\n${errMsg}\n` +
'请解决冲突后再更新,或者执行#强制更新,放弃本地修改'
)
return
}
if (stdout.includes('CONFLICT')) {
await this.reply([
msg + '存在冲突\n',
errMsg,
stdout,
'\n请解决冲突后再更新或者执行#强制更新,放弃本地修改'
])
return
}
await this.reply([errMsg, stdout])
}
/**
* 异步执行git相关命令
* @param {string} cmd git命令
* @returns
*/
async execSync (cmd) {
return new Promise((resolve, reject) => {
exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
resolve({ error, stdout, stderr })
})
})
}
/**
* 检查git是否安装
* @returns
*/
async checkGit () {
let ret = await execSync('git --version', { encoding: 'utf-8' })
if (!ret || !ret.includes('git version')) {
await this.reply('请先安装git')
return false
}
return true
}
}
// modified from StarRail-plugin | 已经过StarRail-plugin作者本人同意
import plugin from '../../../lib/plugins/plugin.js'
import { createRequire } from 'module'
import _ from 'lodash'
import { Restart } from '../../other/restart.js'
import fs from 'fs'
import {} from '../utils/common.js'
const _path = process.cwd()
const require = createRequire(import.meta.url)
const { exec, execSync } = require('child_process')
const checkAuth = async function (e) {
if (!e.isMaster) {
e.reply('只有主人才能命令ChatGPT哦~(*/ω\*)')
return false
}
return true
}
// 是否在更新中
let uping = false
/**
* 处理插件更新
*/
export class Update extends plugin {
constructor () {
super({
name: 'chatgpt更新插件',
event: 'message',
priority: 1000,
rule: [
{
reg: '^#?(chatgpt|柴特寄批踢|GPT|ChatGPT|柴特鸡批踢|Chat|CHAT|CHATGPT|柴特|ChatGPT-Plugin|ChatGPT-plugin|chatgpt-plugin)(插件)?(强制)?更新$',
fnc: 'update'
}
]
})
}
/**
* rule - 更新chatgpt插件
* @returns
*/
async update () {
if (!this.e.isMaster) return false
/** 检查是否正在更新中 */
if (uping) {
await this.reply('已有命令更新中..请勿重复操作')
return
}
/** 检查git安装 */
if (!(await this.checkGit())) return
const isForce = this.e.msg.includes('强制')
/** 执行更新 */
await this.runUpdate(isForce)
/** 是否需要重启 */
if (this.isUp) {
// await this.reply("更新完毕,请重启云崽后生效")
setTimeout(() => this.restart(), 2000)
}
}
restart () {
new Restart(this.e).restart()
}
/**
* chatgpt插件更新函数
* @param {boolean} isForce 是否为强制更新
* @returns
*/
async runUpdate (isForce) {
let command = 'git -C ./plugins/chatgpt-plugin/ pull --no-rebase'
if (isForce) {
command = `git -C ./plugins/chatgpt-plugin/ checkout . && ${command}`
this.e.reply('正在执行强制更新操作,请稍等')
} else {
this.e.reply('正在执行更新操作,请稍等')
}
/** 获取上次提交的commitId用于获取日志时判断新增的更新日志 */
this.oldCommitId = await this.getcommitId('chatgpt-plugin')
uping = true
let ret = await this.execSync(command)
uping = false
if (ret.error) {
logger.mark(`${this.e.logFnc} 更新失败chatgpt-plugin`)
this.gitErr(ret.error, ret.stdout)
return false
}
/** 获取插件提交的最新时间 */
let time = await this.getTime('chatgpt-plugin')
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(ret.stdout)) {
await this.reply(`chatgpt-plugin已经是最新版本\n最后更新时间:${time}`)
} else {
await this.reply(`chatgpt-plugin\n最后更新时间:${time}`)
this.isUp = true
/** 获取chatgpt组件的更新日志 */
let log = await this.getLog('chatgpt-plugin')
await this.reply(log)
}
logger.mark(`${this.e.logFnc} 最后更新时间:${time}`)
return true
}
/**
* 获取chatgpt插件的更新日志
* @param {string} plugin 插件名称
* @returns
*/
async getLog (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -20 --oneline --pretty=format:"%h||[%cd] %s" --date=format:"%m-%d %H:%M"`
let logAll
try {
logAll = await execSync(cm, { encoding: 'utf-8' })
} catch (error) {
logger.error(error.toString())
this.reply(error.toString())
}
if (!logAll) return false
logAll = logAll.split('\n')
let log = []
for (let str of logAll) {
str = str.split('||')
if (str[0] == this.oldCommitId) break
if (str[1].includes('Merge branch')) continue
log.push(str[1])
}
let line = log.length
log = log.join('\n\n')
if (log.length <= 0) return ''
let end = ''
end =
'更多详细信息请前往github查看\nhttps://github.com/ikechan8370/chatgpt-plugin'
log = await this.makeForwardMsg(`chatgpt-plugin更新日志${line}`, log, end)
return log
}
/**
* 获取上次提交的commitId
* @param {string} plugin 插件名称
* @returns
*/
async getcommitId (plugin = '') {
let cm = `git -C ./plugins/${plugin}/ rev-parse --short HEAD`
let commitId = await execSync(cm, { encoding: 'utf-8' })
commitId = _.trim(commitId)
return commitId
}
/**
* 获取本次更新插件的最后一次提交时间
* @param {string} plugin 插件名称
* @returns
*/
async getTime (plugin = '') {
let cm = `cd ./plugins/${plugin}/ && git log -1 --oneline --pretty=format:"%cd" --date=format:"%m-%d %H:%M"`
let time = ''
try {
time = await execSync(cm, { encoding: 'utf-8' })
time = _.trim(time)
} catch (error) {
logger.error(error.toString())
time = '获取时间失败'
}
return time
}
/**
* 制作转发消息
* @param {string} title 标题 - 首条消息
* @param {string} msg 日志信息
* @param {string} end 最后一条信息
* @returns
*/
async makeForwardMsg (title, msg, end) {
let nickname = (this.e.bot ?? Bot).nickname
if (this.e.isGroup) {
let info = await (this.e.bot ?? Bot).getGroupMemberInfo(this.e.group_id, (this.e.bot ?? Bot).uin)
nickname = info.card || info.nickname
}
let userInfo = {
user_id: (this.e.bot ?? Bot).uin,
nickname
}
let forwardMsg = [
{
...userInfo,
message: title
},
{
...userInfo,
message: msg
}
]
if (end) {
forwardMsg.push({
...userInfo,
message: end
})
}
/** 制作转发内容 */
if (this.e.group?.makeForwardMsg) {
forwardMsg = await this.e.group.makeForwardMsg(forwardMsg)
} else if (this.e?.friend?.makeForwardMsg) {
forwardMsg = await this.e.friend.makeForwardMsg(forwardMsg)
} else {
return msg.join('\n')
}
let dec = 'chatgpt-plugin 更新日志'
/** 处理描述 */
if (typeof (forwardMsg.data) === 'object') {
let detail = forwardMsg.data?.meta?.detail
if (detail) {
detail.news = [{ text: dec }]
}
} else {
forwardMsg.data = forwardMsg.data
.replace(/\n/g, '')
.replace(/<title color="#777777" size="26">(.+?)<\/title>/g, '___')
.replace(/___+/, `<title color="#777777" size="26">${dec}</title>`)
}
return forwardMsg
}
/**
* 处理更新失败的相关函数
* @param {string} err
* @param {string} stdout
* @returns
*/
async gitErr (err, stdout) {
let msg = '更新失败!'
let errMsg = err.toString()
stdout = stdout.toString()
if (errMsg.includes('Timed out')) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接超时:${remote}`)
return
}
if (/Failed to connect|unable to access/g.test(errMsg)) {
let remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '')
await this.reply(msg + `\n连接失败:${remote}`)
return
}
if (errMsg.includes('be overwritten by merge')) {
await this.reply(
msg +
`存在冲突:\n${errMsg}\n` +
'请解决冲突后再更新,或者执行#强制更新,放弃本地修改'
)
return
}
if (stdout.includes('CONFLICT')) {
await this.reply([
msg + '存在冲突\n',
errMsg,
stdout,
'\n请解决冲突后再更新或者执行#强制更新,放弃本地修改'
])
return
}
await this.reply([errMsg, stdout])
}
/**
* 异步执行git相关命令
* @param {string} cmd git命令
* @returns
*/
async execSync (cmd) {
return new Promise((resolve, reject) => {
exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
resolve({ error, stdout, stderr })
})
})
}
/**
* 检查git是否安装
* @returns
*/
async checkGit () {
let ret = await execSync('git --version', { encoding: 'utf-8' })
if (!ret || !ret.includes('git version')) {
await this.reply('请先安装git')
return false
}
return true
}
}