mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-17 05:47:11 +00:00
feat: 添加星火支持(beta)
This commit is contained in:
parent
76f0328a8e
commit
926f07c8e9
7 changed files with 268 additions and 5 deletions
62
apps/chat.js
62
apps/chat.js
|
|
@ -35,6 +35,7 @@ import { ChatgptManagement } from './management.js'
|
||||||
import { getPromptByName } from '../utils/prompts.js'
|
import { getPromptByName } from '../utils/prompts.js'
|
||||||
import Translate from '../utils/baiduTranslate.js'
|
import Translate from '../utils/baiduTranslate.js'
|
||||||
import emojiStrip from 'emoji-strip'
|
import emojiStrip from 'emoji-strip'
|
||||||
|
import XinghuoClient from "../utils/xinghuo/xinghuo.js";
|
||||||
try {
|
try {
|
||||||
await import('keyv')
|
await import('keyv')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -115,6 +116,12 @@ export class chatgpt extends plugin {
|
||||||
/** 执行方法 */
|
/** 执行方法 */
|
||||||
fnc: 'claude'
|
fnc: 'claude'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/** 命令正则匹配 */
|
||||||
|
reg: '^#xh[sS]*',
|
||||||
|
/** 执行方法 */
|
||||||
|
fnc: 'xh'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
/** 命令正则匹配 */
|
/** 命令正则匹配 */
|
||||||
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
reg: toggleMode === 'at' ? '^[^#][sS]*' : '^#chat[^gpt][sS]*',
|
||||||
|
|
@ -239,6 +246,11 @@ export class chatgpt extends plugin {
|
||||||
await e.reply('claude对话已结束')
|
await e.reply('claude对话已结束')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (use === 'xh') {
|
||||||
|
await redis.del(`CHATGPT:CONVERSATIONS_XH:${e.sender.user_id}`)
|
||||||
|
await e.reply('星火对话已结束')
|
||||||
|
return
|
||||||
|
}
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (ats.length === 0) {
|
if (ats.length === 0) {
|
||||||
if (use === 'api3') {
|
if (use === 'api3') {
|
||||||
|
|
@ -389,6 +401,17 @@ export class chatgpt extends plugin {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'xh': {
|
||||||
|
let cs = await redis.keys('CHATGPT:CONVERSATIONS_XH:*')
|
||||||
|
for (let i = 0; i < cs.length; i++) {
|
||||||
|
await redis.del(cs[i])
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info('delete slack conversation of qq: ' + cs[i])
|
||||||
|
}
|
||||||
|
deleted++
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
case 'bing': {
|
case 'bing': {
|
||||||
let cs = await redis.keys('CHATGPT:CONVERSATIONS_BING:*')
|
let cs = await redis.keys('CHATGPT:CONVERSATIONS_BING:*')
|
||||||
let we = await redis.keys('CHATGPT:WRONG_EMOTION:*')
|
let we = await redis.keys('CHATGPT:WRONG_EMOTION:*')
|
||||||
|
|
@ -929,6 +952,10 @@ export class chatgpt extends plugin {
|
||||||
key = `CHATGPT:CONVERSATIONS_BROWSER:${e.sender.user_id}`
|
key = `CHATGPT:CONVERSATIONS_BROWSER:${e.sender.user_id}`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'xh': {
|
||||||
|
key = `CHATGPT:CONVERSATIONS_XH:${e.sender.user_id}`
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let ctime = new Date()
|
let ctime = new Date()
|
||||||
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
previousConversation = (key ? await redis.get(key) : null) || JSON.stringify({
|
||||||
|
|
@ -1351,7 +1378,7 @@ export class chatgpt extends plugin {
|
||||||
let ats = e.message.filter(m => m.type === 'at')
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
if (!e.atme && ats.length > 0) {
|
if (!e.atme && ats.length > 0) {
|
||||||
if (Config.debug) {
|
if (Config.debug) {
|
||||||
logger.mark('艾特别人了,没艾特我,忽略#bing')
|
logger.mark('艾特别人了,没艾特我,忽略#claude')
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -1362,6 +1389,28 @@ export class chatgpt extends plugin {
|
||||||
await this.abstractChat(e, prompt, 'claude')
|
await this.abstractChat(e, prompt, 'claude')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
async xh (e) {
|
||||||
|
if (!e.isMaster && e.isPrivate && !Config.enablePrivateChat) {
|
||||||
|
// await this.reply('ChatGpt私聊通道已关闭。')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!Config.allowOtherMode) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let ats = e.message.filter(m => m.type === 'at')
|
||||||
|
if (!e.atme && ats.length > 0) {
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.mark('艾特别人了,没艾特我,忽略#xh')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let prompt = _.replace(e.raw_message.trimStart(), '#xh', '').trim()
|
||||||
|
if (prompt.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
await this.abstractChat(e, prompt, 'xh')
|
||||||
|
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: '', cacheUrl: Config.cacheUrl, status: '' }
|
let cacheData = { file: '', cacheUrl: Config.cacheUrl, status: '' }
|
||||||
|
|
@ -1744,6 +1793,17 @@ export class chatgpt extends plugin {
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'xh': {
|
||||||
|
const ssoSessionId = Config.xinghuoToken
|
||||||
|
if (!ssoSessionId) {
|
||||||
|
throw new Error('未绑定星火token,请使用#chatgpt设置星火token命令绑定token。(获取对话页面的ssoSessionId cookie值)')
|
||||||
|
}
|
||||||
|
let client = new XinghuoClient({
|
||||||
|
ssoSessionId
|
||||||
|
})
|
||||||
|
let response = await client.sendMessage(prompt, conversation?.conversationId)
|
||||||
|
return response
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
let completionParams = {}
|
let completionParams = {}
|
||||||
if (Config.model) {
|
if (Config.model) {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ let helpData = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'chat',
|
icon: 'chat',
|
||||||
title: '#chat1/#chat3/#chatglm/#bing',
|
title: '#chat1/#chat3/#chatglm/#bing/#claude/#xh',
|
||||||
desc: '分别使用API/API3/ChatGLM/Bing模式与机器人聊天,无论主人设定了何种全局模式'
|
desc: '分别使用API/API3/ChatGLM/Bing/Claude/星火模式与机器人聊天,无论主人设定了何种全局模式'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'chat-private',
|
icon: 'chat-private',
|
||||||
|
|
@ -191,6 +191,11 @@ let helpData = [
|
||||||
title: '#chatgpt设置APIKey',
|
title: '#chatgpt设置APIKey',
|
||||||
desc: '设置APIKey'
|
desc: '设置APIKey'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'key',
|
||||||
|
title: '#chatgpt设置星火token',
|
||||||
|
desc: '设置星火ssoSessionId(对话页面的ssoSessionId cookie值)'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'eat',
|
icon: 'eat',
|
||||||
title: '#chatgpt设置(API|Sydney)设定',
|
title: '#chatgpt设置(API|Sydney)设定',
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'useSlackClaudeBasedSolution',
|
fnc: 'useSlackClaudeBasedSolution',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt切换星火$',
|
||||||
|
fnc: 'useXinghuoBasedSolution',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt(必应|Bing)切换',
|
reg: '^#chatgpt(必应|Bing)切换',
|
||||||
fnc: 'changeBingTone',
|
fnc: 'changeBingTone',
|
||||||
|
|
@ -149,6 +154,11 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'setAPIPromptPrefix',
|
fnc: 'setAPIPromptPrefix',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt设置星火token',
|
||||||
|
fnc: 'setXinghuoToken',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt设置(Bing|必应|Sydney|悉尼|sydney|bing)设定',
|
reg: '^#chatgpt设置(Bing|必应|Sydney|悉尼|sydney|bing)设定',
|
||||||
fnc: 'setBingPromptPrefix',
|
fnc: 'setBingPromptPrefix',
|
||||||
|
|
@ -805,6 +815,16 @@ export class ChatgptManagement extends plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async useXinghuoBasedSolution () {
|
||||||
|
let use = await redis.get('CHATGPT:USE')
|
||||||
|
if (use !== 'xh') {
|
||||||
|
await redis.set('CHATGPT:USE', 'xh')
|
||||||
|
await this.reply('已切换到基于星火的解决方案')
|
||||||
|
} else {
|
||||||
|
await this.reply('当前已经是星火模式了')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async changeBingTone (e) {
|
async changeBingTone (e) {
|
||||||
let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '')
|
let tongStyle = e.msg.replace(/^#chatgpt(必应|Bing)切换/, '')
|
||||||
if (!tongStyle) {
|
if (!tongStyle) {
|
||||||
|
|
@ -1083,6 +1103,21 @@ export class ChatgptManagement extends plugin {
|
||||||
this.finish('saveAPIKey')
|
this.finish('saveAPIKey')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setXinghuoToken () {
|
||||||
|
this.setContext('saveXinghuoToken')
|
||||||
|
await this.reply('请发送星火的ssoSessionId', true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveXinghuoToken () {
|
||||||
|
if (!this.e.msg) return
|
||||||
|
let token = this.e.msg
|
||||||
|
// todo
|
||||||
|
Config.xinghuoToken = token
|
||||||
|
await this.reply('星火ssoSessionId设置成功', true)
|
||||||
|
this.finish('saveXinghuoToken')
|
||||||
|
}
|
||||||
|
|
||||||
async setAPIPromptPrefix (e) {
|
async setAPIPromptPrefix (e) {
|
||||||
this.setContext('saveAPIPromptPrefix')
|
this.setContext('saveAPIPromptPrefix')
|
||||||
await this.reply('请发送用于API模式的设定', true)
|
await this.reply('请发送用于API模式的设定', true)
|
||||||
|
|
|
||||||
|
|
@ -555,6 +555,16 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '如 http://localhost:8080',
|
bottomHelpMessage: '如 http://localhost:8080',
|
||||||
component: 'Input'
|
component: 'Input'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '以下为星火方式的配置',
|
||||||
|
component: 'Divider'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'xinghuoToken',
|
||||||
|
label: '星火Cookie',
|
||||||
|
bottomHelpMessage: '获取对话页面的ssoSessionId cookie。不要带等号和分号',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '以下为杂七杂八的配置',
|
label: '以下为杂七杂八的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@
|
||||||
"chatgpt": "^5.1.1",
|
"chatgpt": "^5.1.1",
|
||||||
"delay": "^5.0.0",
|
"delay": "^5.0.0",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.1.0",
|
||||||
|
"emoji-strip": "^1.0.1",
|
||||||
"eventsource": "^2.0.2",
|
"eventsource": "^2.0.2",
|
||||||
"eventsource-parser": "^1.0.0",
|
"eventsource-parser": "^1.0.0",
|
||||||
"fastify": "^4.13.0",
|
"fastify": "^4.13.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"keyv": "^4.5.2",
|
"keyv": "^4.5.2",
|
||||||
"keyv-file": "^0.2.0",
|
"keyv-file": "^0.2.0",
|
||||||
|
|
@ -26,8 +28,7 @@
|
||||||
"random": "^4.1.0",
|
"random": "^4.1.0",
|
||||||
"undici": "^5.21.0",
|
"undici": "^5.21.0",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0"
|
||||||
"emoji-strip": "^1.0.1"
|
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@node-rs/jieba": "^1.6.2",
|
"@node-rs/jieba": "^1.6.2",
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ const defaultConfig = {
|
||||||
apiForceUseReverse: false,
|
apiForceUseReverse: false,
|
||||||
plus: false,
|
plus: false,
|
||||||
useGPT4: false,
|
useGPT4: false,
|
||||||
|
xinghuoToken: '',
|
||||||
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,
|
// thinkingTips: true,
|
||||||
|
|
|
||||||
151
utils/xinghuo/xinghuo.js
Normal file
151
utils/xinghuo/xinghuo.js
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import { Config } from '../config.js'
|
||||||
|
import { createParser } from 'eventsource-parser'
|
||||||
|
import https from 'https'
|
||||||
|
|
||||||
|
const referer = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNuL2NoYXQ/aWQ9')
|
||||||
|
const origin = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNu')
|
||||||
|
const createChatUrl = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNuL2lmbHlncHQvdS9jaGF0LWxpc3QvdjEvY3JlYXRlLWNoYXQtbGlzdA==')
|
||||||
|
const chatUrl = atob('aHR0cHM6Ly94aW5naHVvLnhmeXVuLmNuL2lmbHlncHQtY2hhdC91L2NoYXRfbWVzc2FnZS9jaGF0')
|
||||||
|
let FormData
|
||||||
|
try {
|
||||||
|
FormData = (await import('form-data')).default
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('未安装form-data,无法使用星火模式')
|
||||||
|
}
|
||||||
|
export default class XinghuoClient {
|
||||||
|
constructor (opts) {
|
||||||
|
this.ssoSessionId = opts.ssoSessionId
|
||||||
|
this.headers = {
|
||||||
|
Referer: referer,
|
||||||
|
Cookie: 'ssoSessionId=' + this.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',
|
||||||
|
Origin: origin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage (prompt, chatId) {
|
||||||
|
if (!FormData) {
|
||||||
|
throw new Error('缺少依赖:form-data。请安装依赖后重试')
|
||||||
|
}
|
||||||
|
if (!chatId) {
|
||||||
|
chatId = (await this.createChatList()).chatListId
|
||||||
|
}
|
||||||
|
let requestP = new Promise((resolve, reject) => {
|
||||||
|
let formData = new FormData()
|
||||||
|
formData.setBoundary('----WebKitFormBoundarycATE2QFHDn9ffeWF')
|
||||||
|
formData.append('clientType', '2')
|
||||||
|
formData.append('chatId', chatId)
|
||||||
|
formData.append('text', prompt)
|
||||||
|
let randomNumber = Math.floor(Math.random() * 1000)
|
||||||
|
let fd = '439' + randomNumber.toString().padStart(3, '0')
|
||||||
|
formData.append('fd', fd)
|
||||||
|
this.headers.Referer = referer + chatId
|
||||||
|
let option = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: Object.assign(this.headers, {
|
||||||
|
Accept: 'text/event-stream',
|
||||||
|
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundarycATE2QFHDn9ffeWF'
|
||||||
|
}),
|
||||||
|
// body: formData,
|
||||||
|
referrer: this.headers.Referer
|
||||||
|
}
|
||||||
|
let statusCode
|
||||||
|
const req = https.request(chatUrl, option, (res) => {
|
||||||
|
statusCode = res.statusCode
|
||||||
|
if (statusCode !== 200) {
|
||||||
|
logger.error('星火statusCode:' + statusCode)
|
||||||
|
}
|
||||||
|
let response = ''
|
||||||
|
function onMessage (data) {
|
||||||
|
// console.log(data)
|
||||||
|
if (data === '<end>') {
|
||||||
|
return resolve({
|
||||||
|
error: null,
|
||||||
|
response
|
||||||
|
})
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (data) {
|
||||||
|
response += atob(data.trim())
|
||||||
|
if (Config.debug) {
|
||||||
|
logger.info(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('fetchSSE onMessage unexpected error', err)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = createParser((event) => {
|
||||||
|
if (event.type === 'event') {
|
||||||
|
onMessage(event.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const errBody = []
|
||||||
|
res.on('data', (chunk) => {
|
||||||
|
if (statusCode === 200) {
|
||||||
|
let str = chunk.toString()
|
||||||
|
parser.feed(str)
|
||||||
|
}
|
||||||
|
errBody.push(chunk)
|
||||||
|
})
|
||||||
|
|
||||||
|
// const body = []
|
||||||
|
// res.on('data', (chunk) => body.push(chunk))
|
||||||
|
res.on('end', () => {
|
||||||
|
const resString = Buffer.concat(errBody).toString()
|
||||||
|
// logger.info({ resString })
|
||||||
|
reject(resString)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
formData.pipe(req)
|
||||||
|
req.on('error', (err) => {
|
||||||
|
logger.error(err)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
req.on('timeout', () => {
|
||||||
|
req.destroy()
|
||||||
|
reject(new Error('Request time out'))
|
||||||
|
})
|
||||||
|
// req.write(formData.stringify())
|
||||||
|
req.end()
|
||||||
|
})
|
||||||
|
const { response } = await requestP
|
||||||
|
// logger.info(response)
|
||||||
|
// let responseText = atob(response)
|
||||||
|
return {
|
||||||
|
conversationId: chatId,
|
||||||
|
text: response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createChatList () {
|
||||||
|
let createChatListRes = await fetch(createChatUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: Object.assign(this.headers, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}),
|
||||||
|
body: '{}'
|
||||||
|
})
|
||||||
|
if (createChatListRes.status !== 200) {
|
||||||
|
let errorRes = await createChatListRes.text()
|
||||||
|
let errorText = '星火对话创建失败:' + errorRes
|
||||||
|
logger.error(errorText)
|
||||||
|
throw new Error(errorText)
|
||||||
|
}
|
||||||
|
createChatListRes = await createChatListRes.json()
|
||||||
|
if (createChatListRes.data?.id) {
|
||||||
|
logger.info('星火对话创建成功:' + createChatListRes.data.id)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
chatListId: createChatListRes.data?.id,
|
||||||
|
title: createChatListRes.data?.title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function atob (s) {
|
||||||
|
return Buffer.from(s, 'base64').toString()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue