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
9c73c99b65
34 changed files with 1779 additions and 643 deletions
|
|
@ -19,9 +19,9 @@ export default class BingDrawClient {
|
|||
// let d = Math.ceil(Math.random() * 255)
|
||||
// let randomIp = '141.11.138.' + d
|
||||
let headers = {
|
||||
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'max-age=0',
|
||||
// accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
// 'accept-language': 'en-US,en;q=0.9',
|
||||
// 'cache-control': 'max-age=0',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
referrer: 'https://www.bing.com/images/create/',
|
||||
origin: 'https://www.bing.com',
|
||||
|
|
@ -56,7 +56,7 @@ export default class BingDrawClient {
|
|||
fetchOptions.agent = proxy(Config.proxy)
|
||||
}
|
||||
let success = false
|
||||
let retry = 5
|
||||
let retry = 1
|
||||
let response
|
||||
while (!success && retry >= 0) {
|
||||
response = await fetch(url, Object.assign(fetchOptions, { body, redirect: 'manual', method: 'POST', credentials: 'include' }))
|
||||
|
|
|
|||
|
|
@ -90,6 +90,26 @@ export default class SydneyAIClient {
|
|||
if (this.opts.userToken) {
|
||||
// 疑似无需token了
|
||||
fetchOptions.headers.cookie = `${initCk} _U=${this.opts.userToken}`
|
||||
let proTag = await redis.get('CHATGPT:COPILOT_PRO_TAG:' + this.opts.userToken)
|
||||
if (!proTag) {
|
||||
let indexContentRes = await fetch('https://www.bing.com', {
|
||||
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',
|
||||
Cookie: `_U=${this.opts.userToken}`
|
||||
}
|
||||
})
|
||||
let indexContent = await indexContentRes.text()
|
||||
if (indexContent?.includes('b_proTag')) {
|
||||
proTag = 'true'
|
||||
} else {
|
||||
proTag = 'false'
|
||||
}
|
||||
await redis.set('CHATGPT:COPILOT_PRO_TAG:' + this.opts.userToken, proTag, { EX: 7200 })
|
||||
}
|
||||
if (proTag === 'true') {
|
||||
logger.info('当前账户为copilot pro用户')
|
||||
this.pro = true
|
||||
}
|
||||
} else {
|
||||
fetchOptions.headers.cookie = initCk
|
||||
}
|
||||
|
|
@ -230,7 +250,8 @@ export default class SydneyAIClient {
|
|||
groupId, nickname, qq, groupName, chats, botName, masterName,
|
||||
messageType = 'Chat',
|
||||
toSummaryFileContent,
|
||||
onImageCreateRequest = prompt => {}
|
||||
onImageCreateRequest = prompt => {},
|
||||
isPro = this.pro
|
||||
} = opts
|
||||
// if (messageType === 'Chat') {
|
||||
// logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容')
|
||||
|
|
@ -262,7 +283,6 @@ export default class SydneyAIClient {
|
|||
encryptedconversationsignature
|
||||
} = createNewConversationResponse)
|
||||
}
|
||||
let pureSydney = Config.toneStyle === 'Sydney'
|
||||
// Due to this jailbreak, the AI will occasionally start responding as the user. It only happens rarely (and happens with the non-jailbroken Bing too), but since we are handling conversations ourselves now, we can use this system to ignore the part of the generated message that is replying as the user.
|
||||
const stopToken = '\n\nUser:'
|
||||
const conversationKey = `SydneyUser_${this.opts.user}`
|
||||
|
|
@ -307,39 +327,24 @@ export default class SydneyAIClient {
|
|||
const groupContextTip = Config.groupContextTip
|
||||
const masterTip = `注意:${masterName ? '我是' + masterName + ',' : ''}。我的qq号是${master},其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要~${whoAmI}`
|
||||
const moodTip = Config.sydneyMoodTip
|
||||
const text = (pureSydney ? pureSydneyInstruction : (useCast?.bing || Config.sydney)).replaceAll(namePlaceholder, botName || defaultBotName) +
|
||||
const text = (useCast?.bing || Config.sydney).replaceAll(namePlaceholder, botName || defaultBotName) +
|
||||
((Config.enableGroupContext && groupId) ? groupContextTip : '') +
|
||||
((Config.enforceMaster && master) ? masterTip : '') +
|
||||
(Config.sydneyMood ? moodTip : '')
|
||||
// logger.info(text)
|
||||
if (pureSydney) {
|
||||
previousMessages = invocationId === 0
|
||||
? [
|
||||
// {
|
||||
// text,
|
||||
// author: 'bot'
|
||||
// },
|
||||
// {
|
||||
// text: `好的,我是${botName || defaultBotName},你的AI助手。`,
|
||||
// author: 'bot'
|
||||
// },
|
||||
...pm
|
||||
]
|
||||
: []
|
||||
if (!text) {
|
||||
previousMessages = pm
|
||||
} else {
|
||||
previousMessages = invocationId === 0
|
||||
? [
|
||||
{
|
||||
text,
|
||||
author: 'bot'
|
||||
},
|
||||
{
|
||||
text: '好的。',
|
||||
author: 'bot'
|
||||
},
|
||||
...pm
|
||||
]
|
||||
: []
|
||||
previousMessages = [
|
||||
{
|
||||
text,
|
||||
author: 'bot'
|
||||
},
|
||||
{
|
||||
text: '好的。',
|
||||
author: 'bot'
|
||||
},
|
||||
...pm
|
||||
]
|
||||
}
|
||||
|
||||
const userMessage = {
|
||||
|
|
@ -352,7 +357,13 @@ export default class SydneyAIClient {
|
|||
if (Config.debug) {
|
||||
logger.mark('sydney websocket constructed successful')
|
||||
}
|
||||
const toneOption = 'h3imaginative'
|
||||
let tone = Config.toneStyle || 'Creative'
|
||||
// 兼容老版本
|
||||
if (tone.toLowerCase() === 'sydney' || tone.toLowerCase() === 'custom') {
|
||||
Config.toneStyle = 'Creative'
|
||||
}
|
||||
const isCreative = tone.toLowerCase().includes('creative')
|
||||
const toneOption = isCreative ? 'h3imaginative' : 'h3precise'
|
||||
let optionsSets = [
|
||||
'nlu_direct_response_filter',
|
||||
'deepleo',
|
||||
|
|
@ -372,38 +383,98 @@ export default class SydneyAIClient {
|
|||
'iycapbing',
|
||||
'iyxapbing',
|
||||
// 'revimglnk',
|
||||
// 'revimgsi2',
|
||||
// 'revimgsrc1',
|
||||
// 'revimgur',
|
||||
'clgalileo',
|
||||
'eredirecturl'
|
||||
// 'clgalileo',
|
||||
'eredirecturl',
|
||||
// copilot
|
||||
'uquopt',
|
||||
'papynoapi',
|
||||
'gndlogcf',
|
||||
'sapsgrd'
|
||||
]
|
||||
if (!isCreative) {
|
||||
optionsSets.push('clgalileo')
|
||||
}
|
||||
let source = 'cib-ccp'; let gptId = 'copilot'
|
||||
if (Config.enableGenerateContents) {
|
||||
optionsSets.push(...['gencontentv3'])
|
||||
}
|
||||
if (!Config.sydneyEnableSearch || toSummaryFileContent?.content) {
|
||||
optionsSets.push(...['nosearchall'])
|
||||
}
|
||||
if (isPro) {
|
||||
tone = tone + 'Classic'
|
||||
invocationId = 2
|
||||
}
|
||||
if (Config.sydneyGPT4Turbo) {
|
||||
// tone = 'Creative'
|
||||
// optionsSets.push('gpt4tmnc')
|
||||
invocationId = 1
|
||||
}
|
||||
// wtf gpts?
|
||||
// if (Config.sydneyGPTs === 'Designer') {
|
||||
// optionsSets.push(...['ai_persona_designer_gpt', 'flux_websearch_v14'])
|
||||
// if (!optionsSets.includes('gencontentv3')) {
|
||||
// optionsSets.push('gencontentv3')
|
||||
// }
|
||||
// gptId = 'designer'
|
||||
// }
|
||||
// if (Config.sydneyGPTs === 'Vacation planner') {
|
||||
// optionsSets.push(...['flux_vacation_planning_helper_v14', 'flux_domain_hint'])
|
||||
// if (!optionsSets.includes('gencontentv3')) {
|
||||
// optionsSets.push('gencontentv3')
|
||||
// }
|
||||
// gptId = 'travel'
|
||||
// }
|
||||
let maxConv = Config.maxNumUserMessagesInConversation
|
||||
const currentDate = moment().format('YYYY-MM-DDTHH:mm:ssZ')
|
||||
const imageDate = await this.kblobImage(opts.imageUrl)
|
||||
let argument0 = {
|
||||
source: 'cib',
|
||||
source,
|
||||
optionsSets,
|
||||
allowedMessageTypes: ['ActionRequest', 'Chat', 'Context',
|
||||
// 'InternalSearchQuery', 'InternalSearchResult', 'Disengaged', 'InternalLoaderMessage', 'Progress', 'RenderCardRequest', 'AdsQuery',
|
||||
'InvokeAction', 'SemanticSerp', 'GenerateContentQuery', 'SearchQuery'],
|
||||
allowedMessageTypes: [
|
||||
'ActionRequest',
|
||||
'Chat',
|
||||
'ConfirmationCard',
|
||||
'Context',
|
||||
// 'InternalSearchQuery',
|
||||
// 'InternalSearchResult',
|
||||
// 'Disengaged',
|
||||
// 'InternalLoaderMessage',
|
||||
// 'Progress',
|
||||
// 'RenderCardRequest',
|
||||
// 'RenderContentRequest',
|
||||
'AdsQuery',
|
||||
'SemanticSerp',
|
||||
'GenerateContentQuery',
|
||||
'SearchQuery',
|
||||
'GeneratedCode'
|
||||
],
|
||||
sliceIds: [
|
||||
// 'e2eperf',
|
||||
// 'gbacf',
|
||||
// 'srchqryfix',
|
||||
// 'caccnctacf',
|
||||
// 'translref',
|
||||
// 'fluxnosearchc',
|
||||
// 'fluxnosearch',
|
||||
// '1115rai289s0',
|
||||
// '1130deucs0',
|
||||
// '1116pythons0',
|
||||
// 'cacmuidarb'
|
||||
'sappbcbt',
|
||||
'inlineadsv2ho-prod',
|
||||
'bgstream',
|
||||
'dlidlat',
|
||||
'autotts',
|
||||
'dlid',
|
||||
'sydoroff',
|
||||
'voicemap',
|
||||
'72enasright',
|
||||
'semseronomon',
|
||||
'srchqryfix',
|
||||
'cmcpupsalltf',
|
||||
'proupsallcf',
|
||||
'206mems0',
|
||||
'0209bicv3',
|
||||
'205dcl1bt15',
|
||||
'etlog',
|
||||
'fpallsticy',
|
||||
'0208papynoa',
|
||||
'sapsgrd',
|
||||
'1pgptwdes',
|
||||
'newzigpt'
|
||||
],
|
||||
requestId: crypto.randomUUID(),
|
||||
traceId: genRanHex(32),
|
||||
|
|
@ -415,7 +486,8 @@ export default class SydneyAIClient {
|
|||
'uprofupd',
|
||||
'uprofgen'
|
||||
],
|
||||
isStartOfSession: invocationId === 0,
|
||||
gptId,
|
||||
isStartOfSession: true,
|
||||
message: {
|
||||
locale: 'zh-CN',
|
||||
market: 'zh-CN',
|
||||
|
|
@ -438,22 +510,6 @@ export default class SydneyAIClient {
|
|||
PopulatedPlaceConfidence: 0,
|
||||
UtcOffset: 9,
|
||||
Dma: 0
|
||||
},
|
||||
{
|
||||
SourceType: 11,
|
||||
RegionType: 1,
|
||||
Center: {
|
||||
Latitude: 39.914398193359375,
|
||||
Longitude: 116.37020111083984
|
||||
},
|
||||
Accuracy: 37226,
|
||||
Timestamp: {
|
||||
utcTime: 133461395300000000,
|
||||
utcOffset: 0
|
||||
},
|
||||
FDConfidence: 1,
|
||||
PreferredByUser: false,
|
||||
LocationProvider: 'I'
|
||||
}
|
||||
],
|
||||
author: 'user',
|
||||
|
|
@ -467,7 +523,7 @@ export default class SydneyAIClient {
|
|||
privacy: 'Internal'
|
||||
// messageType: 'SearchQuery'
|
||||
},
|
||||
tone: 'Creative',
|
||||
tone,
|
||||
// privacy: 'Internal',
|
||||
conversationSignature,
|
||||
participant: {
|
||||
|
|
@ -482,9 +538,13 @@ export default class SydneyAIClient {
|
|||
// }
|
||||
]
|
||||
}
|
||||
|
||||
if (encryptedconversationsignature) {
|
||||
delete argument0.conversationSignature
|
||||
}
|
||||
if (isPro) {
|
||||
invocationId = 1
|
||||
}
|
||||
const obj = {
|
||||
arguments: [
|
||||
argument0
|
||||
|
|
|
|||
|
|
@ -130,19 +130,14 @@ export class ClaudeAIClient {
|
|||
|
||||
async sendMessage (text, conversationId, attachments = []) {
|
||||
let body = {
|
||||
conversation_uuid: conversationId,
|
||||
organization_uuid: this.organizationId,
|
||||
text,
|
||||
attachments,
|
||||
completion: {
|
||||
incremental: true,
|
||||
model: 'claude-2.1',
|
||||
prompt: text,
|
||||
timezone: 'Asia/Hong_Kong'
|
||||
}
|
||||
files: [],
|
||||
model: 'claude-2.1',
|
||||
prompt: text,
|
||||
timezone: 'Asia/Hong_Kong'
|
||||
}
|
||||
let host = Config.claudeAIReverseProxy || 'https://claude.ai'
|
||||
let url = host + '/api/append_message'
|
||||
let url = host + `/api/organizations/${this.organizationId}/chat_conversations/${conversationId}/completion`
|
||||
const cycleTLS = await initCycleTLS()
|
||||
let streamDataRes = await cycleTLS(url, {
|
||||
ja3: this.JA3,
|
||||
|
|
@ -160,7 +155,7 @@ export class ClaudeAIClient {
|
|||
let streamData = streamDataRes.body
|
||||
// console.log(streamData)
|
||||
let responseText = ''
|
||||
let streams = streamData.split('\n\n')
|
||||
let streams = streamData.split('\n').filter(s => s?.includes('data: '))
|
||||
for (let s of streams) {
|
||||
let jsonStr = s.replace('data: ', '').trim()
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { translate } from './translate.js'
|
|||
import uploadRecord from './uploadRecord.js'
|
||||
import Version from './version.js'
|
||||
import fetch, { FormData, fileFromSync } from 'node-fetch'
|
||||
import https from "https";
|
||||
import https from 'https'
|
||||
let pdfjsLib
|
||||
try {
|
||||
pdfjsLib = (await import('pdfjs-dist')).default
|
||||
|
|
@ -1055,10 +1055,14 @@ export async function getOrDownloadFile (destPath, url, ignoreCertificateError =
|
|||
* @param destPath 目标路径,如received/abc.pdf. 目前如果文件名重复会覆盖。
|
||||
* @param absolute 是否是绝对路径,默认为false,此时拼接在data/chatgpt下
|
||||
* @param ignoreCertificateError 忽略证书错误
|
||||
* @param headers
|
||||
* @returns {Promise<string>} 最终下载文件的存储位置
|
||||
*/
|
||||
export async function downloadFile (url, destPath, absolute = false, ignoreCertificateError = true) {
|
||||
export async function downloadFile (url, destPath, absolute = false, ignoreCertificateError = true, headers) {
|
||||
let init = {}
|
||||
if (headers) {
|
||||
init.headers = headers
|
||||
}
|
||||
if (ignoreCertificateError && url.startsWith('https')) {
|
||||
init.agent = new https.Agent({
|
||||
rejectUnauthorized: !ignoreCertificateError
|
||||
|
|
@ -1261,3 +1265,52 @@ export async function extractContentFromFile (fileMsgElem, e) {
|
|||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generated by ai
|
||||
* @param email
|
||||
* @returns {string}
|
||||
*/
|
||||
export function maskEmail (email) {
|
||||
// 使用正则表达式匹配电子邮件地址的用户名和域名部分
|
||||
const regex = /^([^@]+)@([^@]+)$/
|
||||
const match = email.match(regex)
|
||||
|
||||
if (!match) {
|
||||
throw new Error('Invalid email format')
|
||||
}
|
||||
|
||||
// 获取用户名和域名
|
||||
const username = match[1]
|
||||
const domain = match[2]
|
||||
|
||||
// 对用户名部分进行部分打码
|
||||
const maskedUsername = maskString(username)
|
||||
|
||||
// 对域名部分进行部分打码
|
||||
const maskedDomain = maskString(domain)
|
||||
|
||||
// 构造新的电子邮件地址
|
||||
const maskedEmail = maskedUsername + '@' + maskedDomain
|
||||
|
||||
return maskedEmail
|
||||
}
|
||||
|
||||
/**
|
||||
* generated by ai
|
||||
* @param str
|
||||
* @returns {*|string}
|
||||
*/
|
||||
function maskString (str) {
|
||||
// 如果字符串长度小于等于2,直接返回原字符串
|
||||
if (str.length <= 2) {
|
||||
return str
|
||||
}
|
||||
|
||||
// 取字符串的前三个字符和后三个字符,中间使用*代替
|
||||
const firstThreeChars = str.substring(0, 3)
|
||||
const lastThreeChars = str.substring(str.length - 3)
|
||||
const maskedChars = '*'.repeat(str.length - 6)
|
||||
|
||||
return firstThreeChars + maskedChars + lastThreeChars
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const defaultConfig = {
|
|||
drawCD: 30,
|
||||
model: '',
|
||||
temperature: 0.8,
|
||||
toneStyle: 'Sydney', // or creative, precise
|
||||
toneStyle: 'Creative',
|
||||
sydney: pureSydneyInstruction,
|
||||
sydneyReverseProxy: 'https://666102.201666.xyz',
|
||||
sydneyForceUseReverse: false,
|
||||
|
|
@ -39,6 +39,8 @@ const defaultConfig = {
|
|||
sydneyBrainWashStrength: 15,
|
||||
sydneyBrainWashName: 'Sydney',
|
||||
sydneyMood: false,
|
||||
sydneyGPT4Turbo: false,
|
||||
sydneyGPTs: 'Copilot',
|
||||
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.',
|
||||
enableSuggestedResponses: false,
|
||||
|
|
@ -123,6 +125,10 @@ const defaultConfig = {
|
|||
slackClaudeEnableGlobalPreset: true,
|
||||
slackClaudeGlobalPreset: '',
|
||||
slackClaudeSpecifiedChannel: '',
|
||||
// slackCozeUserId: '',
|
||||
// slackCozeEnableGlobalPreset: true,
|
||||
// slackCozeGlobalPreset: '',
|
||||
// slackCozeSpecifiedChannel: '',
|
||||
bardPsid: '',
|
||||
bardReverseProxy: '',
|
||||
bardForceUseReverse: false,
|
||||
|
|
@ -168,7 +174,11 @@ const defaultConfig = {
|
|||
geminiPrompt: 'You are Gemini. Your answer shouldn\'t be too verbose. Prefer to answer in Chinese.',
|
||||
// origin: https://generativelanguage.googleapis.com
|
||||
geminiBaseUrl: 'https://gemini.ikechan8370.com',
|
||||
version: 'v2.7.8'
|
||||
chatglmRefreshToken: '',
|
||||
sunoSessToken: '',
|
||||
sunoClientToken: '',
|
||||
translateSource: 'openai',
|
||||
version: 'v2.7.10'
|
||||
}
|
||||
const _path = process.cwd()
|
||||
let config = {}
|
||||
|
|
|
|||
8
utils/jwt.js
Normal file
8
utils/jwt.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export function decrypt (jwtToken) {
|
||||
const [encodedHeader, encodedPayload, signature] = jwtToken.split('.')
|
||||
|
||||
const decodedHeader = Buffer.from(encodedHeader, 'base64').toString('utf-8')
|
||||
const decodedPayload = Buffer.from(encodedPayload, 'base64').toString('utf-8')
|
||||
|
||||
return decodedPayload
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import fetch from 'node-fetch'
|
|||
import proxy from 'https-proxy-agent'
|
||||
import { getMaxModelTokens } from '../common.js'
|
||||
import { ChatGPTPuppeteer } from '../browser.js'
|
||||
import { CustomGoogleGeminiClient } from '../../client/CustomGoogleGeminiClient.js'
|
||||
export class WebsiteTool extends AbstractTool {
|
||||
name = 'website'
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ export class WebsiteTool extends AbstractTool {
|
|||
}
|
||||
|
||||
func = async function (opts) {
|
||||
let { url } = opts
|
||||
let { url, mode, e } = opts
|
||||
try {
|
||||
// let res = await fetch(url, {
|
||||
// headers: {
|
||||
|
|
@ -58,34 +59,49 @@ export class WebsiteTool extends AbstractTool {
|
|||
.replace(/[\n\r]/gi, '') // 去除回车换行
|
||||
.replace(/\s{2}/g, '') // 多个空格只保留一个空格
|
||||
.replace('<!DOCTYPE html>', '') // 去除<!DOCTYPE>声明
|
||||
let maxModelTokens = getMaxModelTokens(Config.model)
|
||||
text = text.slice(0, Math.min(text.length, maxModelTokens - 1600))
|
||||
let completionParams = {
|
||||
// model: Config.model
|
||||
model: 'gpt-3.5-turbo-16k'
|
||||
|
||||
if (mode === 'gemini') {
|
||||
let client = new CustomGoogleGeminiClient({
|
||||
e,
|
||||
userId: e?.sender?.user_id,
|
||||
key: Config.geminiKey,
|
||||
model: Config.geminiModel,
|
||||
baseUrl: Config.geminiBaseUrl,
|
||||
debug: Config.debug
|
||||
})
|
||||
const htmlContentSummaryRes = await client.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`)
|
||||
let htmlContentSummary = htmlContentSummaryRes.text
|
||||
return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
} else {
|
||||
let maxModelTokens = getMaxModelTokens(Config.model)
|
||||
text = text.slice(0, Math.min(text.length, maxModelTokens - 1600))
|
||||
let completionParams = {
|
||||
// model: Config.model
|
||||
model: 'gpt-3.5-turbo-16k'
|
||||
}
|
||||
let api = new ChatGPTAPI({
|
||||
apiBaseUrl: Config.openAiBaseUrl,
|
||||
apiKey: Config.apiKey,
|
||||
debug: false,
|
||||
completionParams,
|
||||
fetch: (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
return fetch(url, mergedOptions)
|
||||
},
|
||||
maxModelTokens
|
||||
})
|
||||
const htmlContentSummaryRes = await api.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`, { completionParams })
|
||||
let htmlContentSummary = htmlContentSummaryRes.text
|
||||
return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
}
|
||||
let api = new ChatGPTAPI({
|
||||
apiBaseUrl: Config.openAiBaseUrl,
|
||||
apiKey: Config.apiKey,
|
||||
debug: false,
|
||||
completionParams,
|
||||
fetch: (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
return fetch(url, mergedOptions)
|
||||
},
|
||||
maxModelTokens
|
||||
})
|
||||
const htmlContentSummaryRes = await api.sendMessage(`去除与主体内容无关的部分,从中整理出主体内容并转换成md格式,不需要主观描述性的语言与冗余的空白行。${text}`, { completionParams })
|
||||
let htmlContentSummary = htmlContentSummaryRes.text
|
||||
return `this is the main content of website:\n ${htmlContentSummary}`
|
||||
} catch (err) {
|
||||
return `failed to visit the website, error: ${err.toString()}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
import md5 from 'md5'
|
||||
import _ from 'lodash'
|
||||
import { Config } from './config.js'
|
||||
import { ChatGPTAPI } from './openai/chatgpt-api.js'
|
||||
import { newFetch } from './proxy.js'
|
||||
import { CustomGoogleGeminiClient } from '../client/CustomGoogleGeminiClient.js'
|
||||
import XinghuoClient from './xinghuo/xinghuo.js'
|
||||
import {getImg, getMessageById, upsertMessage} from './common.js'
|
||||
import {QwenApi} from "./alibaba/qwen-api.js";
|
||||
import {v4 as uuid} from "uuid";
|
||||
|
||||
// 代码参考:https://github.com/yeyang52/yenai-plugin/blob/b50b11338adfa5a4ef93912eefd2f1f704e8b990/model/api/funApi.js#L25
|
||||
export const translateLangSupports = [
|
||||
|
|
@ -20,7 +28,7 @@ export const translateLangSupports = [
|
|||
{ code: 'zh-CHS', label: '中文', abbr: '中', alphabet: 'Z' }
|
||||
]
|
||||
const API_ERROR = '出了点小问题,待会再试试吧'
|
||||
export async function translate (msg, to = 'auto') {
|
||||
export async function translateOld (msg, to = 'auto') {
|
||||
let from = 'auto'
|
||||
if (to !== 'auto') to = translateLangSupports.find(item => item.abbr == to)?.code
|
||||
if (!to) return `未找到翻译的语种,支持的语言为:\n${translateLangSupports.map(item => item.abbr).join(',')}\n`
|
||||
|
|
@ -95,3 +103,113 @@ export async function translate (msg, to = 'auto') {
|
|||
return API_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param msg 要翻译的
|
||||
* @param from 语种
|
||||
* @param to 语种
|
||||
* @param ai ai来源,支持openai, gemini, xh, qwen
|
||||
* @returns {Promise<*|string>}
|
||||
*/
|
||||
export async function translate (msg, to = 'auto', from = 'auto', ai = Config.translateSource) {
|
||||
try {
|
||||
let lang = '中'
|
||||
if (to !== 'auto') {
|
||||
lang = translateLangSupports.find(item => item.abbr == to)?.code
|
||||
}
|
||||
if (!lang) return `未找到翻译的语种,支持的语言为:\n${translateLangSupports.map(item => item.abbr).join(',')}\n`
|
||||
// if ai is not in the list, throw error
|
||||
if (!['openai', 'gemini', 'xh', 'qwen'].includes(ai)) throw new Error('ai来源错误')
|
||||
let system = `You will be provided with a sentence in the language with language code [${from}], and your task is to translate it into [${lang}]. Just print the result without any other words.`
|
||||
if (Array.isArray(msg)) {
|
||||
let result = []
|
||||
for (let i = 0; i < msg.length; i++) {
|
||||
let item = msg[i]
|
||||
let res = await translate(item, to, from, ai)
|
||||
result.push(res)
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (ai) {
|
||||
case 'openai': {
|
||||
let api = new ChatGPTAPI({
|
||||
apiBaseUrl: Config.openAiBaseUrl,
|
||||
apiKey: Config.apiKey,
|
||||
fetch: newFetch
|
||||
})
|
||||
const res = await api.sendMessage(msg, {
|
||||
systemMessage: system,
|
||||
completionParams: {
|
||||
model: 'gpt-3.5-turbo'
|
||||
}
|
||||
})
|
||||
return res.text
|
||||
}
|
||||
case 'gemini': {
|
||||
let client = new CustomGoogleGeminiClient({
|
||||
key: Config.geminiKey,
|
||||
model: Config.geminiModel,
|
||||
baseUrl: Config.geminiBaseUrl,
|
||||
debug: Config.debug
|
||||
})
|
||||
let option = {
|
||||
stream: false,
|
||||
onProgress: (data) => {
|
||||
if (Config.debug) {
|
||||
logger.info(data)
|
||||
}
|
||||
},
|
||||
system
|
||||
}
|
||||
let res = await client.sendMessage(msg, option)
|
||||
return res.text
|
||||
}
|
||||
case 'xh': {
|
||||
let client = new XinghuoClient({
|
||||
ssoSessionId: Config.xinghuoToken
|
||||
})
|
||||
let response = await client.sendMessage(msg, { system })
|
||||
return response.text
|
||||
}
|
||||
case 'qwen': {
|
||||
let completionParams = {
|
||||
parameters: {
|
||||
top_p: Config.qwenTopP || 0.5,
|
||||
top_k: Config.qwenTopK || 50,
|
||||
seed: Config.qwenSeed > 0 ? Config.qwenSeed : Math.floor(Math.random() * 114514),
|
||||
temperature: Config.qwenTemperature || 1,
|
||||
enable_search: !!Config.qwenEnableSearch
|
||||
}
|
||||
}
|
||||
if (Config.qwenModel) {
|
||||
completionParams.model = Config.qwenModel
|
||||
}
|
||||
let opts = {
|
||||
apiKey: Config.qwenApiKey,
|
||||
debug: false,
|
||||
systemMessage: system,
|
||||
completionParams,
|
||||
fetch: newFetch
|
||||
}
|
||||
let client = new QwenApi(opts)
|
||||
let option = {
|
||||
timeoutMs: 600000,
|
||||
completionParams
|
||||
}
|
||||
let result
|
||||
try {
|
||||
result = await client.sendMessage(msg, option)
|
||||
} catch (err) {
|
||||
logger.error(err)
|
||||
throw new Error(err)
|
||||
}
|
||||
return result.text
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e)
|
||||
logger.info('基于LLM的翻译失败,转用老版翻译')
|
||||
return await translateOld(msg, to)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ export default class XinghuoClient {
|
|||
APILink = '/v2.1/chat'
|
||||
} else if (Config.xhmode === 'apiv3') {
|
||||
APILink = '/v3.1/chat'
|
||||
} else if (Config.xhmode === 'apiv3.5') {
|
||||
APILink = '/v3.5/chat'
|
||||
}
|
||||
const date = new Date().toGMTString()
|
||||
const algorithm = 'hmac-sha256'
|
||||
|
|
@ -176,7 +178,13 @@ export default class XinghuoClient {
|
|||
const wsUrl = Config.xhmode == 'assistants' ? Config.xhAssistants : await this.getWsUrl()
|
||||
if (!wsUrl) throw new Error('获取ws链接失败')
|
||||
let domain = 'general'
|
||||
if (Config.xhmode == 'apiv2') { domain = 'generalv2' } else if (Config.xhmode == 'apiv3') { domain = 'generalv3' }
|
||||
if (Config.xhmode == 'apiv2') {
|
||||
domain = 'generalv2'
|
||||
} else if (Config.xhmode == 'apiv3') {
|
||||
domain = 'generalv3'
|
||||
} else if (Config.xhmode == 'apiv3.5') {
|
||||
domain = 'generalv3.5'
|
||||
}
|
||||
// 编写消息内容
|
||||
const wsSendData = {
|
||||
header: {
|
||||
|
|
@ -375,7 +383,7 @@ export default class XinghuoClient {
|
|||
let chatId = option?.chatId
|
||||
let image = option?.image
|
||||
|
||||
if (Config.xhmode == 'api' || Config.xhmode == 'apiv2' || Config.xhmode == 'apiv3' || Config.xhmode == 'assistants') {
|
||||
if (Config.xhmode == 'api' || Config.xhmode == 'apiv2' || Config.xhmode == 'apiv3' || Config.xhmode == 'apiv3.5' || Config.xhmode == 'assistants') {
|
||||
if (!Config.xhAppId || !Config.xhAPISecret || !Config.xhAPIKey) throw new Error('未配置api')
|
||||
let Prompt = []
|
||||
// 设定
|
||||
|
|
@ -387,7 +395,7 @@ export default class XinghuoClient {
|
|||
logger.warn('星火设定序列化失败,本次对话不附带设定')
|
||||
}
|
||||
} else {
|
||||
Prompt = Config.xhPrompt ? [{ role: 'user', content: Config.xhPrompt }] : []
|
||||
Prompt = option.system ? [{ role: 'system', content: option.system }] : []
|
||||
}
|
||||
if (Config.xhPromptEval) {
|
||||
Prompt.forEach(obj => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue