mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 13:57:10 +00:00
Merge branch 'v2' of github.com:ikechan8370/chatgpt-plugin into v2
This commit is contained in:
commit
2ca27dae8d
8 changed files with 168 additions and 32 deletions
|
|
@ -1258,7 +1258,7 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
|
|
||||||
async setClaudeKey (e) {
|
async setClaudeKey (e) {
|
||||||
this.setContext('saveClaudeKey')
|
this.setContext('saveClaudeKey')
|
||||||
await this.reply('请发送Claude API Key', true)
|
await this.reply('请发送Claude API Key。\n如果要设置多个key请用逗号隔开。\n此操作会覆盖当前配置,请谨慎操作', true)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1697,7 +1697,7 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
|
|
||||||
async setClaudeModel (e) {
|
async setClaudeModel (e) {
|
||||||
this.setContext('saveClaudeModel')
|
this.setContext('saveClaudeModel')
|
||||||
await this.reply('请发送Claude模型,官方推荐模型:\nclaude-3-opus-20240229\nclaude-3-sonnet-20240229', true)
|
await this.reply('请发送Claude模型,官方推荐模型:\nclaude-3-opus-20240229\nclaude-3-sonnet-20240229\nclaude-3-haiku-20240307', true)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,20 @@ export class help extends plugin {
|
||||||
if (use === 'xh') {
|
if (use === 'xh') {
|
||||||
Config.xhPromptSerialize = false
|
Config.xhPromptSerialize = false
|
||||||
}
|
}
|
||||||
|
if (use === 'bing') {
|
||||||
|
/**
|
||||||
|
* @type {{user: string, bot: string}[]} examples
|
||||||
|
*/
|
||||||
|
let examples = prompt.example
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
Config[`chatExampleUser${i}`] = ''
|
||||||
|
Config[`chatExampleBot${i}`] = ''
|
||||||
|
}
|
||||||
|
for (let i = 1; i <= examples.length; i++) {
|
||||||
|
Config[`chatExampleUser${i}`] = examples[i - 1].user
|
||||||
|
Config[`chatExampleBot${i}`] = examples[i - 1].bot
|
||||||
|
}
|
||||||
|
}
|
||||||
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
await redis.set(`CHATGPT:PROMPT_USE_${use}`, promptName)
|
||||||
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
await e.reply(`你当前正在使用${use}模式,已将该模式设定应用为"${promptName}"。更该设定后建议结束对话以使设定更好生效`, true)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -330,13 +344,23 @@ export class help extends plugin {
|
||||||
let extraData = JSON.parse(await redis.get('CHATGPT:UPLOAD_PROMPT'))
|
let extraData = JSON.parse(await redis.get('CHATGPT:UPLOAD_PROMPT'))
|
||||||
const { currentUse, description } = extraData
|
const { currentUse, description } = extraData
|
||||||
const { content } = getPromptByName(currentUse)
|
const { content } = getPromptByName(currentUse)
|
||||||
|
let examples = []
|
||||||
|
for (let i = 1; i < 4; i++) {
|
||||||
|
if (Config[`chatExampleUser${i}`]) {
|
||||||
|
examples.push({
|
||||||
|
user: Config[`chatExampleUser${i}`],
|
||||||
|
bot: Config[`chatExampleBot${i}`]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
let toUploadBody = {
|
let toUploadBody = {
|
||||||
title: currentUse,
|
title: currentUse,
|
||||||
prompt: content,
|
prompt: content,
|
||||||
qq: master || (getUin(this.e) + ''), // 上传者设定为主人qq或机器人qq
|
qq: master || (getUin(this.e) + ''), // 上传者设定为主人qq或机器人qq
|
||||||
use: extraData.use === 'bing' ? 'Bing' : 'ChatGPT',
|
use: extraData.use === 'bing' ? 'Bing' : 'ChatGPT',
|
||||||
r18,
|
r18,
|
||||||
description
|
description,
|
||||||
|
examples
|
||||||
}
|
}
|
||||||
logger.info(toUploadBody)
|
logger.info(toUploadBody)
|
||||||
let response = await fetch('https://chatgpt.roki.best/prompt', {
|
let response = await fetch('https://chatgpt.roki.best/prompt', {
|
||||||
|
|
@ -431,8 +455,8 @@ export class help extends plugin {
|
||||||
await e.reply('没有这个设定', true)
|
await e.reply('没有这个设定', true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const { prompt, title } = r.data
|
const { prompt, title, examples } = r.data
|
||||||
saveOnePrompt(title, prompt)
|
saveOnePrompt(title, prompt, examples)
|
||||||
e.reply(`导入成功。您现在可以使用 #chatgpt使用设定${title} 来体验这个设定了。`)
|
e.reply(`导入成功。您现在可以使用 #chatgpt使用设定${title} 来体验这个设定了。`)
|
||||||
} else {
|
} else {
|
||||||
await e.reply('导入失败:' + r.msg)
|
await e.reply('导入失败:' + r.msg)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ const BASEURL = 'https://api.anthropic.com'
|
||||||
* input_tokens: number,
|
* input_tokens: number,
|
||||||
* output_tokens: number,
|
* output_tokens: number,
|
||||||
* }>} usage
|
* }>} usage
|
||||||
*
|
* @property {{
|
||||||
|
* type: string,
|
||||||
|
* message: string,
|
||||||
|
* }} error
|
||||||
* Claude响应的基本格式
|
* Claude响应的基本格式
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -172,6 +175,10 @@ export class ClaudeAPIClient extends BaseClient {
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
console.log(JSON.stringify(response))
|
console.log(JSON.stringify(response))
|
||||||
}
|
}
|
||||||
|
if (response.type === 'error') {
|
||||||
|
logger.error(response.error.message)
|
||||||
|
throw new Error(response.error.type)
|
||||||
|
}
|
||||||
await this.upsertMessage(thisMessage)
|
await this.upsertMessage(thisMessage)
|
||||||
const respMessage = Object.assign(response, {
|
const respMessage = Object.assign(response, {
|
||||||
id: idModel,
|
id: idModel,
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,42 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '开启Sydney的图片识别功能,建议和OCR只保留一个开启',
|
bottomHelpMessage: '开启Sydney的图片识别功能,建议和OCR只保留一个开启',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleUser1',
|
||||||
|
label: '前置对话第一轮(用户)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleBot1',
|
||||||
|
label: '前置对话第一轮(AI)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleUser2',
|
||||||
|
label: '前置对话第二轮(用户)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleBot2',
|
||||||
|
label: '前置对话第二轮(AI)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleUser3',
|
||||||
|
label: '前置对话第三轮(用户)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'chatExampleBot3',
|
||||||
|
label: '前置对话第三轮(AI)',
|
||||||
|
bottomHelpMessage: '会强行插入该轮对话,能有效抑制抱歉',
|
||||||
|
component: 'InputTextArea'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '以下为API3方式的配置',
|
label: '以下为API3方式的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
@ -346,7 +382,7 @@ export function supportGuoba () {
|
||||||
{
|
{
|
||||||
field: 'claudeApiKey',
|
field: 'claudeApiKey',
|
||||||
label: 'claude API Key',
|
label: 'claude API Key',
|
||||||
bottomHelpMessage: '前往 https://console.anthropic.com/settings/keys 注册和生成',
|
bottomHelpMessage: '前往 https://console.anthropic.com/settings/keys 注册和生成。可以填写多个,用英文逗号隔开',
|
||||||
component: 'InputPassword'
|
component: 'InputPassword'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -424,20 +424,52 @@ class Core {
|
||||||
return await this.chatGPTApi.sendMessage(prompt, conversation)
|
return await this.chatGPTApi.sendMessage(prompt, conversation)
|
||||||
} else if (use === 'claude') {
|
} else if (use === 'claude') {
|
||||||
// slack已经不可用,移除
|
// slack已经不可用,移除
|
||||||
const client = new ClaudeAPIClient({
|
let keys = Config.claudeApiKey?.split(/[,;]/).map(key => key.trim()).filter(key => key)
|
||||||
key: Config.claudeApiKey,
|
let choiceIndex = Math.floor(Math.random() * keys.length)
|
||||||
model: Config.claudeApiModel || 'claude-3-sonnet-20240229',
|
let key = keys[choiceIndex]
|
||||||
debug: true,
|
logger.info(`使用API Key:${key}`)
|
||||||
baseUrl: Config.claudeApiBaseUrl
|
while (keys.length >= 0) {
|
||||||
// temperature: Config.claudeApiTemperature || 0.5
|
let errorMessage = ''
|
||||||
})
|
const client = new ClaudeAPIClient({
|
||||||
let rsp = await client.sendMessage(prompt, {
|
key,
|
||||||
stream: false,
|
model: Config.claudeApiModel || 'claude-3-sonnet-20240229',
|
||||||
parentMessageId: conversation.parentMessageId,
|
debug: true,
|
||||||
conversationId: conversation.conversationId,
|
baseUrl: Config.claudeApiBaseUrl
|
||||||
system: Config.claudeSystemPrompt
|
// temperature: Config.claudeApiTemperature || 0.5
|
||||||
})
|
})
|
||||||
return rsp
|
try {
|
||||||
|
let rsp = await client.sendMessage(prompt, {
|
||||||
|
stream: false,
|
||||||
|
parentMessageId: conversation.parentMessageId,
|
||||||
|
conversationId: conversation.conversationId,
|
||||||
|
system: Config.claudeSystemPrompt
|
||||||
|
})
|
||||||
|
return rsp
|
||||||
|
} catch (err) {
|
||||||
|
errorMessage = err.message
|
||||||
|
switch (err.message) {
|
||||||
|
case 'rate_limit_error': {
|
||||||
|
// api没钱了或者当月/日/时/分额度耗尽
|
||||||
|
// throw new Error('claude API额度耗尽或触发速率限制')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'authentication_error': {
|
||||||
|
// 无效的key
|
||||||
|
// throw new Error('claude API key无效')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
logger.warn(`claude api 错误:[${key}] ${errorMessage}`)
|
||||||
|
}
|
||||||
|
if (keys.length === 0) {
|
||||||
|
throw new Error(errorMessage)
|
||||||
|
}
|
||||||
|
keys.splice(choiceIndex, 1)
|
||||||
|
choiceIndex = Math.floor(Math.random() * keys.length)
|
||||||
|
key = keys[choiceIndex]
|
||||||
|
logger.info(`使用API Key:${key}`)
|
||||||
|
}
|
||||||
} else if (use === 'claude2') {
|
} else if (use === 'claude2') {
|
||||||
let { conversationId } = conversation
|
let { conversationId } = conversation
|
||||||
let client = new ClaudeAIClient({
|
let client = new ClaudeAIClient({
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export default class SydneyAIClient {
|
||||||
'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.3 OS/macOS',
|
'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.3 OS/macOS',
|
||||||
// cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
|
// cookie: this.opts.cookies || `_U=${this.opts.userToken}`,
|
||||||
Referer: 'https://edgeservices.bing.com/edgesvc/chat?udsframed=1&form=SHORUN&clientscopes=chat,noheader,channelstable,',
|
Referer: 'https://edgeservices.bing.com/edgesvc/chat?udsframed=1&form=SHORUN&clientscopes=chat,noheader,channelstable,',
|
||||||
'Referrer-Policy': 'origin-when-cross-origin',
|
'Referrer-Policy': 'origin-when-cross-origin'
|
||||||
// Workaround for request being blocked due to geolocation
|
// Workaround for request being blocked due to geolocation
|
||||||
// 'x-forwarded-for': '1.1.1.1'
|
// 'x-forwarded-for': '1.1.1.1'
|
||||||
}
|
}
|
||||||
|
|
@ -94,9 +94,11 @@ export default class SydneyAIClient {
|
||||||
} else {
|
} else {
|
||||||
fetchOptions.headers.cookie = this.opts.cookies
|
fetchOptions.headers.cookie = this.opts.cookies
|
||||||
}
|
}
|
||||||
let proTag = await redis.get('CHATGPT:COPILOT_PRO_TAG:' + this.opts.userToken)
|
// let hash = md5(this.opts.cookies || this.opts.userToken)
|
||||||
|
let hash = crypto.createHash('md5').update(this.opts.cookies || this.opts.userToken).digest('hex')
|
||||||
|
let proTag = await redis.get('CHATGPT:COPILOT_PRO_TAG:' + hash)
|
||||||
if (!proTag) {
|
if (!proTag) {
|
||||||
let indexContentRes = await fetch('https://www.bing.com', {
|
let indexContentRes = await fetch('https://www.bing.com/chat', {
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0',
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0',
|
||||||
Cookie: `_U=${this.opts.userToken}`
|
Cookie: `_U=${this.opts.userToken}`
|
||||||
|
|
@ -108,7 +110,7 @@ export default class SydneyAIClient {
|
||||||
} else {
|
} else {
|
||||||
proTag = 'false'
|
proTag = 'false'
|
||||||
}
|
}
|
||||||
await redis.set('CHATGPT:COPILOT_PRO_TAG:' + this.opts.userToken, proTag, { EX: 7200 })
|
await redis.set('CHATGPT:COPILOT_PRO_TAG:' + hash, proTag, { EX: 7200 })
|
||||||
}
|
}
|
||||||
if (proTag === 'true') {
|
if (proTag === 'true') {
|
||||||
logger.info('当前账户为copilot pro用户')
|
logger.info('当前账户为copilot pro用户')
|
||||||
|
|
@ -338,6 +340,21 @@ export default class SydneyAIClient {
|
||||||
if (!text) {
|
if (!text) {
|
||||||
previousMessages = pm
|
previousMessages = pm
|
||||||
} else {
|
} else {
|
||||||
|
let example = []
|
||||||
|
for (let i = 1; i < 4; i++) {
|
||||||
|
if (Config[`chatExampleUser${i}`]) {
|
||||||
|
example.push(...[
|
||||||
|
{
|
||||||
|
text: Config[`chatExampleUser${i}`],
|
||||||
|
author: 'user'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: Config[`chatExampleBot${i}`],
|
||||||
|
author: 'bot'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
previousMessages = [
|
previousMessages = [
|
||||||
{
|
{
|
||||||
text,
|
text,
|
||||||
|
|
@ -347,6 +364,7 @@ export default class SydneyAIClient {
|
||||||
text: '好的。',
|
text: '好的。',
|
||||||
author: 'bot'
|
author: 'bot'
|
||||||
},
|
},
|
||||||
|
...example,
|
||||||
...pm
|
...pm
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,12 @@ const defaultConfig = {
|
||||||
sydneyGPTs: 'Copilot',
|
sydneyGPTs: 'Copilot',
|
||||||
sydneyImageRecognition: false,
|
sydneyImageRecognition: false,
|
||||||
sydneyMoodTip: 'Your response should be divided into two parts, namely, the text and your mood. The mood available to you can only include: blandness, happy, shy, frustrated, disgusted, and frightened.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.',
|
sydneyMoodTip: 'Your response should be divided into two parts, namely, the text and your mood. The mood available to you can only include: blandness, happy, shy, frustrated, disgusted, and frightened.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.',
|
||||||
|
chatExampleUser1: '',
|
||||||
|
chatExampleUser2: '',
|
||||||
|
chatExampleUser3: '',
|
||||||
|
chatExampleBot1: '',
|
||||||
|
chatExampleBot2: '',
|
||||||
|
chatExampleBot3: '',
|
||||||
enableSuggestedResponses: false,
|
enableSuggestedResponses: false,
|
||||||
sydneyEnableSearch: false,
|
sydneyEnableSearch: false,
|
||||||
api: defaultChatGPTAPI,
|
api: defaultChatGPTAPI,
|
||||||
|
|
@ -66,13 +72,8 @@ const defaultConfig = {
|
||||||
xhRetReplace: '',
|
xhRetReplace: '',
|
||||||
promptPrefixOverride: 'Your answer shouldn\'t be too verbose. Prefer to answer in Chinese.',
|
promptPrefixOverride: 'Your answer shouldn\'t be too verbose. Prefer to answer in Chinese.',
|
||||||
assistantLabel: 'ChatGPT',
|
assistantLabel: 'ChatGPT',
|
||||||
// thinkingTips: true,
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
UA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
|
|
||||||
headless: false,
|
headless: false,
|
||||||
chromePath: '',
|
chromePath: '',
|
||||||
'2captchaToken': '',
|
|
||||||
proxy: '',
|
proxy: '',
|
||||||
debug: true,
|
debug: true,
|
||||||
defaultTimeoutMs: 120000,
|
defaultTimeoutMs: 120000,
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,19 @@ export function readPrompts () {
|
||||||
txtFiles.forEach(txtFile => {
|
txtFiles.forEach(txtFile => {
|
||||||
let name = _.trimEnd(txtFile, '.txt')
|
let name = _.trimEnd(txtFile, '.txt')
|
||||||
const content = fs.readFileSync(`${_path}/plugins/chatgpt-plugin/prompts/${txtFile}`, 'utf8')
|
const content = fs.readFileSync(`${_path}/plugins/chatgpt-plugin/prompts/${txtFile}`, 'utf8')
|
||||||
|
let example = []
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(`${_path}/plugins/chatgpt-plugin/prompts/${name}_example.json`)) {
|
||||||
|
example = fs.readFileSync(`${_path}/plugins/chatgpt-plugin/prompts/${name}_example.json`, 'utf8')
|
||||||
|
example = JSON.parse(example)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.debug(err)
|
||||||
|
}
|
||||||
prompts.push({
|
prompts.push({
|
||||||
name,
|
name,
|
||||||
content
|
content,
|
||||||
|
example
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -34,11 +44,15 @@ export function getPromptByName (name) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveOnePrompt (name, content) {
|
export function saveOnePrompt (name, content, examples) {
|
||||||
const _path = process.cwd()
|
const _path = process.cwd()
|
||||||
mkdirs(`${_path}/plugins/chatgpt-plugin/prompts`)
|
mkdirs(`${_path}/plugins/chatgpt-plugin/prompts`)
|
||||||
let filePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}.txt`
|
let filePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}.txt`
|
||||||
fs.writeFileSync(filePath, content)
|
fs.writeFileSync(filePath, content)
|
||||||
|
if (examples) {
|
||||||
|
let examplePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}_example.json`
|
||||||
|
fs.writeFileSync(examplePath, JSON.stringify(examples))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteOnePrompt (name) {
|
export function deleteOnePrompt (name) {
|
||||||
|
|
@ -46,4 +60,8 @@ export function deleteOnePrompt (name) {
|
||||||
mkdirs(`${_path}/plugins/chatgpt-plugin/prompts`)
|
mkdirs(`${_path}/plugins/chatgpt-plugin/prompts`)
|
||||||
let filePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}.txt`
|
let filePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}.txt`
|
||||||
fs.unlinkSync(filePath)
|
fs.unlinkSync(filePath)
|
||||||
|
try {
|
||||||
|
let examplePath = `${_path}/plugins/chatgpt-plugin/prompts/${name}_example.json`
|
||||||
|
fs.unlinkSync(examplePath)
|
||||||
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue