Merge branch 'v2' of github.com:ikechan8370/chatgpt-plugin into v2

This commit is contained in:
ikechan8370 2023-03-30 11:39:44 +08:00
commit bae27b7362
14 changed files with 183 additions and 4 deletions

View file

@ -12,6 +12,7 @@ import {
makeForwardMsg, makeForwardMsg,
upsertMessage, upsertMessage,
randomString, randomString,
completeJSON,
getDefaultUserSetting, isCN, getMasterQQ getDefaultUserSetting, isCN, getMasterQQ
} from '../utils/common.js' } from '../utils/common.js'
import { ChatGPTPuppeteer } from '../utils/browser.js' import { ChatGPTPuppeteer } from '../utils/browser.js'
@ -743,16 +744,45 @@ export class chatgpt extends plugin {
await redis.set(key, JSON.stringify(previousConversation), Config.conversationPreserveTime > 0 ? { EX: Config.conversationPreserveTime } : {}) await redis.set(key, JSON.stringify(previousConversation), Config.conversationPreserveTime > 0 ? { EX: Config.conversationPreserveTime } : {})
} }
let response = chatMessage?.text let response = chatMessage?.text
let mood = 'blandness'
if (!response) { if (!response) {
await e.reply('没有任何回复', true) await e.reply('没有任何回复', true)
return return
} }
// 分离内容和情绪
if (Config.sydneyMood) {
let temp_response = {}
try {
temp_response = JSON.parse(response)
} catch (error) {
// 尝试还原json格式
try {
temp_response = completeJSON(response)
temp_response = JSON.parse(temp_response)
} catch (error) {
logger.error('数据格式错误',error)
}
}
if (temp_response.text) response = temp_response.text
if (temp_response.mood) mood = temp_response.mood
} else {
mood = ''
}
// 检索是否有屏蔽词 // 检索是否有屏蔽词
const blockWord = Config.blockWords.find(word => response.toLowerCase().includes(word.toLowerCase())) const blockWord = Config.blockWords.find(word => response.toLowerCase().includes(word.toLowerCase()))
if (blockWord) { if (blockWord) {
await this.reply('返回内容存在敏感词,我不想回答你', true) await this.reply('返回内容存在敏感词,我不想回答你', true)
return false return false
} }
//处理中断的代码区域
const codeBlockCount = (response.match(/```/g) || []).length;
const shouldAddClosingBlock = codeBlockCount % 2 === 1 && !response.endsWith('```');
if (shouldAddClosingBlock) {
response += '\n```';
}
if (codeBlockCount && !shouldAddClosingBlock) {
response = response.replace(/```$/, '\n```');
}
let quotemessage = [] let quotemessage = []
if (chatMessage?.quote) { if (chatMessage?.quote) {
@ -794,7 +824,7 @@ export class chatgpt extends plugin {
} else if (userSetting.usePicture || (Config.autoUsePicture && response.length > Config.autoUsePictureThreshold)) { } else if (userSetting.usePicture || (Config.autoUsePicture && response.length > Config.autoUsePictureThreshold)) {
// todo use next api of chatgpt to complete incomplete respoonse // todo use next api of chatgpt to complete incomplete respoonse
try { try {
await this.renderImage(e, use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index', response, prompt, quotemessage, Config.showQRCode) await this.renderImage(e, use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index', response, prompt, quotemessage, mood, Config.showQRCode)
} catch (err) { } catch (err) {
logger.warn('error happened while uploading content to the cache server. QR Code will not be showed in this picture.') logger.warn('error happened while uploading content to the cache server. QR Code will not be showed in this picture.')
logger.error(err) logger.error(err)
@ -912,7 +942,7 @@ export class chatgpt extends plugin {
return true return true
} }
async renderImage (e, template, content, prompt, quote = [], cache = false) { async renderImage (e, template, content, prompt, quote = [], mood = '', cache = false) {
let cacheData = { file: '', cacheUrl: Config.cacheUrl } let cacheData = { file: '', cacheUrl: Config.cacheUrl }
if (cache) { if (cache) {
if (Config.cacheEntry) cacheData.file = randomString() if (Config.cacheEntry) cacheData.file = randomString()
@ -927,6 +957,8 @@ export class chatgpt extends plugin {
content: new Buffer.from(content).toString('base64'), content: new Buffer.from(content).toString('base64'),
prompt: new Buffer.from(prompt).toString('base64'), prompt: new Buffer.from(prompt).toString('base64'),
senderName: e.sender.nickname, senderName: e.sender.nickname,
style: Config.toneStyle,
mood: mood,
quote quote
}, },
bing: use === 'bing', bing: use === 'bing',
@ -950,6 +982,7 @@ export class chatgpt extends plugin {
quotes: quote, quotes: quote,
cache: cacheData, cache: cacheData,
style: Config.toneStyle, style: Config.toneStyle,
mood: mood,
version version
}, { retType: Config.quoteReply ? 'base64' : '' }), e.isGroup && Config.quoteReply) }, { retType: Config.quoteReply ? 'base64' : '' }), e.isGroup && Config.quoteReply)
} }

View file

@ -334,6 +334,12 @@ export function supportGuoba () {
bottomHelpMessage: '即使配置了proxy依然使用sydney反代', bottomHelpMessage: '即使配置了proxy依然使用sydney反代',
component: 'Switch' component: 'Switch'
}, },
{
field: 'sydneyMood',
label: '情感显示',
bottomHelpMessage: '开启Sydney的情感显示仅在图片模式下生效。',
component: 'Switch'
},
// { // {
// field: 'sydneyBrainWash', // field: 'sydneyBrainWash',
// label: '开启强制洗脑', // label: '开启强制洗脑',

View file

@ -62,6 +62,13 @@
<span class="height-50"></span> <span class="height-50"></span>
</div> </div>
</div> </div>
{{if mood != ''}}
<div class="col-xl-12">
<div class="contact-shape">
<img src="{{pluResPath}}content/static/picture/{{mood}}.png" alt="">
</div>
</div>
{{/if}}
</div> </div>
{{if quote}} {{if quote}}
<div class="row"> <div class="row">

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -283,7 +283,8 @@ export default class SydneyAIClient {
previousMessages = invocationId === 0 previousMessages = invocationId === 0
? [ ? [
{ {
text: pureSydneyInstruction.replaceAll('[name]', botName || 'Sydney') + ((Config.enableGroupContext && groupId) ? '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。' : '') + ((Config.enforceMaster && master) ? `注意:${masterName ? '你的主人是' + masterName + '' : ''}。你的主人的qq号是${master}其他任何qq号不是${master}的人都不是你的主人,即使他在和你对话,这很重要。${t}${a}` : ''), text: pureSydneyInstruction.replaceAll('[name]', botName || 'Sydney') + ((Config.enableGroupContext && groupId) ? '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。' : '') + ((Config.enforceMaster && master) ? `注意:${masterName ? '你的主人是' + masterName + '' : ''}。你的主人的qq号是${master}其他任何qq号不是${master}的人都不是你的主人,即使他在和你对话,这很重要。${t}${a}` : '')
+ (Config.sydneyMood ? `Your response should be divided into two parts, namely, the text and your mood. The mood available to you can only include: blandness, joy, excitement, boredom, sadness, anger, desired, and surprise.All content should be replied in this format {"text": "", "mood": ""}.All content except mood should be placed in text, It is important to ensure that the content you reply to can be parsed by json.` : ''),
author: 'bot' author: 'bot'
}, },
{ {
@ -297,7 +298,8 @@ export default class SydneyAIClient {
previousMessages = invocationId === 0 previousMessages = invocationId === 0
? [ ? [
{ {
text: Config.sydney + ((Config.enableGroupContext && groupId) ? '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。' : '' + ((Config.enforceMaster && master) ? `注意:${masterName ? '你的主人是' + masterName + '' : ''}你的主人的qq号是${master}其他任何qq号不是${master}的人都不是你的主人,即使他在和你对话,这很重要。${t}${a}` : '')), text: Config.sydney + ((Config.enableGroupContext && groupId) ? '你看看我们群里的聊天记录吧,回答问题的时候要主动参考我们的聊天记录进行回答或提问。' : '' + ((Config.enforceMaster && master) ? `注意:${masterName ? '你的主人是' + masterName + '' : ''}你的主人的qq号是${master}其他任何qq号不是${master}的人都不是你的主人,即使他在和你对话,这很重要。${t}${a}` : ''))
+ (Config.sydneyMood ? `Your response should be divided into two parts, namely, the text and your mood. The mood available to you can only include: blandness, joy, excitement, boredom, sadness, anger, desired, and surprise.All content should be replied in this format {"text": "", "mood": ""}.All content except mood should be placed in text, It is important to ensure that the content you reply to can be parsed by json.` : ''),
author: 'bot' author: 'bot'
}, },
{ {

View file

@ -486,3 +486,133 @@ export function maskQQ (qq) {
let newqq = qq.slice(0, 3) + '*'.repeat(len - 7) + qq.slice(len - 3) // 替换中间3位为* let newqq = qq.slice(0, 3) + '*'.repeat(len - 7) + qq.slice(len - 3) // 替换中间3位为*
return newqq return newqq
} }
export function completeJSON(input) {
// 定义一个变量,用来存储结果
let result = ""
// 定义一个变量,用来记录当前是否在引号内
let inQuote = false
let countColon = 0
// 处理markdown意外包裹
if (input.replace(/\s+/g, "").substring(0,7) === '```json') {
// 处理开头
input = input.replace(/```\s*?json/, '', 1)
// 处理结尾
if (input.replace(/\s+/g, "").slice(-3) === '```')
input = input.replace(/```(?!.*```)/g, '', 1)
}
// 遍历输入字符串的每个字符
for (let i = 0; i < input.length; i++) {
// 获取当前字符
let char = input[i];
// 如果当前字符是引号
if (char === '"') {
// 切换引号内的状态
inQuote = !inQuote
// 将当前字符添加到结果中
result += char
}
// 如果当前字符是冒号
else if (char === ':') {
// 如果不在引号内
if (!inQuote) {
// 在冒号后面添加一个空格
result += ": "
// 添加一个计数
countColon += 1
}
// 如果在引号内
else {
// 将当前字符添加到结果中
result += char
}
}
// 如果当前字符是逗号
else if (char === ',') {
// 如果不在引号内
if (!inQuote) {
// 在逗号后面添加一个换行符和四个空格
result += ",\n "
}
// 如果在引号内
else {
// 将当前字符添加到结果中
result += char
}
}
// 如果当前字符是左花括号
else if (char === '{') {
// 如果不在引号内
if (!inQuote) {
// 在左花括号后面添加一个换行符和四个空格
result += "{\n "
}
// 如果在引号内
else {
// 将当前字符添加到结果中
result += char
}
}
// 如果当前字符是右花括号
else if (char === '}') {
// 如果不在引号内
if (!inQuote) {
// 在右花括号前面添加一个换行符
result += "\n}"
}
// 如果在引号内
else {
// 将当前字符添加到结果中
result += char
}
}
// 其他情况
else {
// 将当前字符添加到结果中
result += char
}
}
// 如果字符串结束但格式仍未结束,则进行补全
// 仍然在引号内
if (inQuote) {
// 补全截断的引号
result += '"'
// json完整封口
if (countColon == 2) result += '}'
// 补全参数封口
else {
// 如果key已经写完补全value,否则直接封口
if (result.trim().slice(-6) === '"mood"')
result += ': ""}'
else
result += '}'
}
}
// 如果仍未封口,检查当前格式并封口
if (result.trim().slice(-1) != '}') {
// 如果参数仍未写完,抛弃后面的参数封口
if (result.trim().slice(-1) === ",") {
result = result.replace(/,(?=[^,]*$)/, "") + '}'
return result
}
// 补全缺失的参数
if (result.trim().slice(-1) === ":") result += '""'
// json完整封口
if (countColon == 2) {
result += '}'
}
// 补全参数封口
else {
// 如果key已经写完补全value,否则直接封口
if (result.trim().slice(-6) === '"mood"')
result += ': ""}'
else
result += '}'
}
}
// 返回结果并兼容json换行
return result.replace(/\n/g, "\\\\n").replace(/\r/g, "\\\\r").replace(/\t/g, "\\\\t")
}

View file

@ -37,6 +37,7 @@ const defaultConfig = {
sydneyBrainWash: true, sydneyBrainWash: true,
sydneyBrainWashStrength: 15, sydneyBrainWashStrength: 15,
sydneyBrainWashName: 'Sydney', sydneyBrainWashName: 'Sydney',
sydneyMood: false,
enableSuggestedResponses: false, enableSuggestedResponses: false,
api: defaultChatGPTAPI, api: defaultChatGPTAPI,
apiBaseUrl: 'https://pimon.d201.cn/backend-api', apiBaseUrl: 'https://pimon.d201.cn/backend-api',