mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 13:27:08 +00:00
Update bym.js (#735)
* feat: new bing (WIP) * fix: update CopilotAIClient.js * fix: gemini强制调用tool;real at * feat: add bym support
This commit is contained in:
parent
5f6c4e5abb
commit
26444df2a2
9 changed files with 599 additions and 30 deletions
161
apps/bym.js
Normal file
161
apps/bym.js
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { CustomGoogleGeminiClient } from '../client/CustomGoogleGeminiClient.js'
|
||||||
|
import { Config } from '../utils/config.js'
|
||||||
|
import { getImg } from '../utils/common.js'
|
||||||
|
import { getChatHistoryGroup } from '../utils/chat.js'
|
||||||
|
import { SearchVideoTool } from '../utils/tools/SearchBilibiliTool.js'
|
||||||
|
import { SerpImageTool } from '../utils/tools/SearchImageTool.js'
|
||||||
|
import { SearchMusicTool } from '../utils/tools/SearchMusicTool.js'
|
||||||
|
import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
|
||||||
|
import { SendVideoTool } from '../utils/tools/SendBilibiliTool.js'
|
||||||
|
import { SendMusicTool } from '../utils/tools/SendMusicTool.js'
|
||||||
|
import { SendPictureTool } from '../utils/tools/SendPictureTool.js'
|
||||||
|
import { WebsiteTool } from '../utils/tools/WebsiteTool.js'
|
||||||
|
import { convertFaces } from '../utils/face.js'
|
||||||
|
import { WeatherTool } from '../utils/tools/WeatherTool.js'
|
||||||
|
import { EditCardTool } from '../utils/tools/EditCardTool.js'
|
||||||
|
import { JinyanTool } from '../utils/tools/JinyanTool.js'
|
||||||
|
import { KickOutTool } from '../utils/tools/KickOutTool.js'
|
||||||
|
import { SetTitleTool } from '../utils/tools/SetTitleTool.js'
|
||||||
|
|
||||||
|
export class bym extends plugin {
|
||||||
|
constructor () {
|
||||||
|
super({
|
||||||
|
name: 'ChatGPT-Plugin 伪人bym',
|
||||||
|
dsc: 'bym',
|
||||||
|
/** https://oicqjs.github.io/oicq/#events */
|
||||||
|
event: 'message',
|
||||||
|
priority: 5000,
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^[^#][sS]*',
|
||||||
|
fnc: 'bym',
|
||||||
|
priority: '-1000000',
|
||||||
|
log: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 复读 */
|
||||||
|
async bym (e) {
|
||||||
|
if (!Config.enableBYM) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let opt = {
|
||||||
|
maxOutputTokens: 500,
|
||||||
|
temperature: 1,
|
||||||
|
replyPureTextCallback: e.reply
|
||||||
|
}
|
||||||
|
let imgs = await getImg(e)
|
||||||
|
if (!e.msg) {
|
||||||
|
if (imgs && imgs.length > 0) {
|
||||||
|
let image = imgs[0]
|
||||||
|
const response = await fetch(image)
|
||||||
|
const base64Image = Buffer.from(await response.arrayBuffer())
|
||||||
|
opt.image = base64Image.toString('base64')
|
||||||
|
e.msg = '[图片]'
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!opt.image && imgs && imgs.length > 0) {
|
||||||
|
let image = imgs[0]
|
||||||
|
const response = await fetch(image)
|
||||||
|
const base64Image = Buffer.from(await response.arrayBuffer())
|
||||||
|
opt.image = base64Image.toString('base64')
|
||||||
|
}
|
||||||
|
let sender = e.sender.user_id
|
||||||
|
let card = e.sender.card || e.sender.nickname
|
||||||
|
let group = e.group_id
|
||||||
|
let prop = Math.floor(Math.random() * 100)
|
||||||
|
if (e.msg?.includes(Config.assistantLabel)) {
|
||||||
|
prop = prop / 100
|
||||||
|
}
|
||||||
|
if (e.msg?.endsWith('?')) {
|
||||||
|
prop = prop / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
let fuck = false
|
||||||
|
let candidate = Config.bymPreset
|
||||||
|
if (Config.bymFuckList?.find(i => e.msg.includes(i))) {
|
||||||
|
fuck = true
|
||||||
|
candidate = candidate + Config.bymFuckPrompt
|
||||||
|
}
|
||||||
|
if (prop < Config.bymRate) {
|
||||||
|
logger.info('random chat hit')
|
||||||
|
let chats = await getChatHistoryGroup(e, 20)
|
||||||
|
opt.system = `你的名字是“${Config.assistantLabel}”,你在一个qq群里,群号是${group},当前和你说话的人群名片是${card}, qq号是${sender}, 请你结合用户的发言和聊天记录作出回应,要求表现得随性一点,最好参与讨论,混入其中。不要过分插科打诨,不知道说什么可以复读群友的话。要求你做搜索、发图、发视频和音乐等操作时要使用工具。不可以直接发[图片]这样蒙混过关。要求优先使用中文进行对话。` +
|
||||||
|
candidate +
|
||||||
|
'以下是聊天记录:' + chats
|
||||||
|
.map(chat => {
|
||||||
|
let sender = chat.sender || chat || {}
|
||||||
|
return `${sender.card || sender.nickname} :${chat.raw_message}`
|
||||||
|
})
|
||||||
|
.join('\n') +
|
||||||
|
`\n你的回复应该尽可能简练,像人类一样随意,不要附加任何奇怪的东西,如聊天记录的格式(比如${Config.assistantLabel}:),禁止重复聊天记录。`
|
||||||
|
|
||||||
|
let client = new CustomGoogleGeminiClient({
|
||||||
|
e,
|
||||||
|
userId: e.sender.user_id,
|
||||||
|
key: Config.geminiKey,
|
||||||
|
model: Config.geminiModel,
|
||||||
|
baseUrl: Config.geminiBaseUrl,
|
||||||
|
debug: Config.debug
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* tools
|
||||||
|
* @type {(AbstractTool)[]}
|
||||||
|
*/
|
||||||
|
const tools = [
|
||||||
|
new SearchVideoTool(),
|
||||||
|
new SerpImageTool(),
|
||||||
|
new SearchMusicTool(),
|
||||||
|
new SendAvatarTool(),
|
||||||
|
new SendVideoTool(),
|
||||||
|
new SendMusicTool(),
|
||||||
|
new SendPictureTool(),
|
||||||
|
new WebsiteTool(),
|
||||||
|
new WeatherTool()
|
||||||
|
]
|
||||||
|
if (e.group.is_admin || e.group.is_owner) {
|
||||||
|
tools.push(new EditCardTool())
|
||||||
|
tools.push(new JinyanTool())
|
||||||
|
tools.push(new KickOutTool())
|
||||||
|
}
|
||||||
|
if (e.group.is_owner) {
|
||||||
|
tools.push(new SetTitleTool())
|
||||||
|
}
|
||||||
|
client.addTools(tools)
|
||||||
|
// console.log(JSON.stringify(opt))
|
||||||
|
let rsp = await client.sendMessage(e.msg, opt)
|
||||||
|
let text = rsp.text
|
||||||
|
let texts = text.split(/(?<!\?)[。?\n](?!\?)/)
|
||||||
|
for (let t of texts) {
|
||||||
|
if (!t) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t = t.trim()
|
||||||
|
if (text[text.indexOf(t) + t.length] === '?') {
|
||||||
|
t += '?'
|
||||||
|
}
|
||||||
|
let finalMsg = await convertFaces(t, true, e)
|
||||||
|
logger.info(JSON.stringify(finalMsg))
|
||||||
|
if (Math.floor(Math.random() * 100) < 10) {
|
||||||
|
await this.reply(finalMsg, true, {
|
||||||
|
recallMsg: fuck ? 10 : 0
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await this.reply(finalMsg, false, {
|
||||||
|
recallMsg: fuck ? 10 : 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, Math.min(t.length * 200, 3000))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -342,6 +342,12 @@ export class ChatgptManagement extends plugin {
|
||||||
fnc: 'switchToolbox',
|
fnc: 'switchToolbox',
|
||||||
permission: 'master'
|
permission: 'master'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
reg: '^#chatgpt(开启|关闭)(伪人|bym)$',
|
||||||
|
fnc: 'switchBYM',
|
||||||
|
permission: 'master'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
reg: '^#chatgpt(开启|关闭)gemini(搜索|代码执行)$',
|
reg: '^#chatgpt(开启|关闭)gemini(搜索|代码执行)$',
|
||||||
fnc: 'geminiOpenSearchCE',
|
fnc: 'geminiOpenSearchCE',
|
||||||
|
|
@ -1833,6 +1839,26 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async switchBYM (e) {
|
||||||
|
if (e.msg.includes('开启')) {
|
||||||
|
if (Config.enableBYM) {
|
||||||
|
await this.reply('已经开启了')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Config.enableBYM = true
|
||||||
|
await this.reply('开启中', true)
|
||||||
|
await this.reply('好的,已经打开bym模式')
|
||||||
|
} else {
|
||||||
|
if (!Config.enableBYM) {
|
||||||
|
await this.reply('已经是关闭的了')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Config.enableBYM = false
|
||||||
|
await this.reply('好的,已经关闭bym模式')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async geminiOpenSearchCE (e) {
|
async geminiOpenSearchCE (e) {
|
||||||
let msg = e.msg
|
let msg = e.msg
|
||||||
let open = msg.includes('开启')
|
let open = msg.includes('开启')
|
||||||
|
|
@ -1845,4 +1871,5 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务,
|
||||||
}
|
}
|
||||||
await e.reply('操作成功')
|
await e.reply('操作成功')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
291
client/CopilotAIClient.js
Normal file
291
client/CopilotAIClient.js
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
import WebSocket from 'ws'
|
||||||
|
import common from '../../../lib/common/common.js'
|
||||||
|
|
||||||
|
export class BingAIClient {
|
||||||
|
constructor (accessToken, baseUrl = 'wss://copilot.microsoft.com/c/api/chat', debug, _2captchaKey, clientId, scope, refreshToken, oid) {
|
||||||
|
this.accessToken = accessToken
|
||||||
|
this.baseUrl = baseUrl
|
||||||
|
this.ws = null
|
||||||
|
this.conversationId = null
|
||||||
|
this.partialMessages = new Map()
|
||||||
|
this.debug = debug
|
||||||
|
this._2captchaKey = _2captchaKey
|
||||||
|
this.clientId = clientId
|
||||||
|
this.scope = scope
|
||||||
|
this.refreshToken = refreshToken
|
||||||
|
this.oid = oid
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendMessage (text, options = {}) {
|
||||||
|
// 如果 options 中有 conversationId,使用它;否则生成一个新的 conversationId
|
||||||
|
if (options.conversationId) {
|
||||||
|
this.conversationId = options.conversationId
|
||||||
|
} else {
|
||||||
|
this.conversationId = this._generateConversationId()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建立 WebSocket 连接
|
||||||
|
await this.connectWebSocket()
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
await this.sendInitialMessage(text)
|
||||||
|
|
||||||
|
// 等待并收集服务器的回复
|
||||||
|
const responseText = await this.collectResponse()
|
||||||
|
return responseText
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectWebSocket () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let url = `${this.baseUrl}?api-version=2`
|
||||||
|
if (this.accessToken) {
|
||||||
|
url += '&accessToken=' + this.accessToken
|
||||||
|
}
|
||||||
|
this.ws = new WebSocket(url)
|
||||||
|
|
||||||
|
this.ws.on('open', () => {
|
||||||
|
console.log('WebSocket connection established.')
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
this.ws.on('message', (message) => {
|
||||||
|
logger.info(JSON.stringify(message))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.ws.on('close', (code, reason) => {
|
||||||
|
console.log('WebSocket connection closed. Code:', code, 'Reason:', reason)
|
||||||
|
|
||||||
|
// 401 错误码通常是未授权,可以根据实际情况修改
|
||||||
|
if (code === 401) {
|
||||||
|
logger.error('token expired. try to refresh with refresh token')
|
||||||
|
this.doRefreshToken(this.clientId, this.scope, this.refreshToken, this.oid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.ws.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendInitialMessage (text) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const messagePayload = {
|
||||||
|
event: 'send',
|
||||||
|
conversationId: this.conversationId,
|
||||||
|
content: [{ type: 'text', text }],
|
||||||
|
mode: 'chat',
|
||||||
|
context: { edge: 'NonContextual' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接发送消息
|
||||||
|
this.ws.send(JSON.stringify(messagePayload))
|
||||||
|
|
||||||
|
// 设置超时机制,防止长时间未收到消息
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
reject(new Error('No response from server within timeout period.'))
|
||||||
|
}, 5000) // 设置 5 秒的超时时间
|
||||||
|
|
||||||
|
// 一旦收到消息,处理逻辑
|
||||||
|
this.ws.once('message', (data) => {
|
||||||
|
clearTimeout(timeout) // 清除超时定时器
|
||||||
|
const message = JSON.parse(data)
|
||||||
|
|
||||||
|
if (message.event === 'challenge') {
|
||||||
|
logger.info(JSON.stringify(message))
|
||||||
|
logger.warn('遇到turnstile验证码,尝试使用2captcha解决')
|
||||||
|
// 如果收到 challenge,处理挑战
|
||||||
|
this.handleChallenge(message)
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject)
|
||||||
|
} else {
|
||||||
|
// 否则直接进入对话
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleChallenge (challenge) {
|
||||||
|
// 获取 challenge 的 token(你需要根据实际情况实现此方法)
|
||||||
|
if (!this._2captchaKey) {
|
||||||
|
throw new Error('No 2captchaKey')
|
||||||
|
}
|
||||||
|
const token = await this.getTurnstile(challenge.conversationId)
|
||||||
|
|
||||||
|
const challengeResponse = {
|
||||||
|
event: 'challengeResponse',
|
||||||
|
token,
|
||||||
|
method: 'cloudflare'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ws.send(JSON.stringify(challengeResponse))
|
||||||
|
}
|
||||||
|
|
||||||
|
async collectResponse () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const checkMessageComplete = (messageId) => {
|
||||||
|
// 如果消息已经完成,返回完整的消息内容
|
||||||
|
if (this.partialMessages.has(messageId) && this.partialMessages.get(messageId).done) {
|
||||||
|
const completeMessage = this.partialMessages.get(messageId).text
|
||||||
|
resolve(completeMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ws.on('message', (data) => {
|
||||||
|
const message = JSON.parse(data)
|
||||||
|
|
||||||
|
switch (message.event) {
|
||||||
|
case 'received':
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'startMessage':
|
||||||
|
this.currentMessageId = message.messageId
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'appendText':
|
||||||
|
if (!this.partialMessages.has(message.messageId)) {
|
||||||
|
this.partialMessages.set(message.messageId, { text: '', done: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.partialMessages.get(message.messageId).text += message.text
|
||||||
|
|
||||||
|
// 如果是最后一部分,标记为完成
|
||||||
|
if (message.partId === '0') {
|
||||||
|
this.partialMessages.get(message.messageId).done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
checkMessageComplete(message.messageId)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'partCompleted':
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'done':
|
||||||
|
checkMessageComplete(message.messageId)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.warn('Unexpected event:', message.event)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTurnstile (conversationId) {
|
||||||
|
// 这里需要根据实际情况实现获取 challenge token 的方法
|
||||||
|
const myHeaders = new Headers()
|
||||||
|
myHeaders.append('Content-Type', 'application/json')
|
||||||
|
|
||||||
|
const raw = JSON.stringify({
|
||||||
|
clientKey: this._2captchaKey,
|
||||||
|
task: {
|
||||||
|
type: 'TurnstileTaskProxyless',
|
||||||
|
websiteURL: 'https://copilot.microsoft.com/chats/' + conversationId,
|
||||||
|
websiteKey: '0x4AAAAAAAg146IpY3lPNWte'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw,
|
||||||
|
redirect: 'follow'
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('https://api.2captcha.com/createTask', requestOptions)
|
||||||
|
const createTaskRsp = await response.json()
|
||||||
|
const taskId = createTaskRsp.taskId
|
||||||
|
|
||||||
|
const raw2 = JSON.stringify({
|
||||||
|
taskId,
|
||||||
|
clientKey: this._2captchaKey
|
||||||
|
})
|
||||||
|
async function getTaskResult () {
|
||||||
|
const requestOptions2 = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: raw2,
|
||||||
|
redirect: 'follow'
|
||||||
|
}
|
||||||
|
|
||||||
|
const response2 = await fetch('https://api.2captcha.com/getTaskResult', requestOptions2)
|
||||||
|
const taskResponse = await response2.json()
|
||||||
|
if (this.debug) {
|
||||||
|
logger.info(JSON.stringify(taskResponse))
|
||||||
|
}
|
||||||
|
const token = taskResponse?.solution?.token
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
let retry = 90
|
||||||
|
let token = await getTaskResult()
|
||||||
|
while (retry > 0 && !token) {
|
||||||
|
await common.sleep(1000)
|
||||||
|
token = await getTaskResult()
|
||||||
|
retry--
|
||||||
|
}
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('No response from server within timeout period.')
|
||||||
|
}
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateConversationId () {
|
||||||
|
return 'conversation-' + Math.random().toString(36).substring(2, 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refresh token
|
||||||
|
* @param clientId
|
||||||
|
* @param scope
|
||||||
|
* @param refreshToken
|
||||||
|
* @param oid
|
||||||
|
* @returns {Promise<{
|
||||||
|
* token_type: string,
|
||||||
|
* scope: string,
|
||||||
|
* expires_in: number,
|
||||||
|
* ext_expires_in: number,
|
||||||
|
* access_token: string,
|
||||||
|
* refresh_token: string
|
||||||
|
* }>}
|
||||||
|
*/
|
||||||
|
async doRefreshToken (clientId, scope, refreshToken, oid) {
|
||||||
|
const myHeaders = new Headers()
|
||||||
|
myHeaders.append('user-agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0')
|
||||||
|
myHeaders.append('priority', 'u=1, i')
|
||||||
|
myHeaders.append('referer', 'https://copilot.microsoft.com/')
|
||||||
|
myHeaders.append('origin', 'https://copilot.microsoft.com')
|
||||||
|
myHeaders.append('Content-Type', 'application/x-www-form-urlencoded')
|
||||||
|
|
||||||
|
const urlencoded = new URLSearchParams()
|
||||||
|
urlencoded.append('client_id', clientId)
|
||||||
|
urlencoded.append('redirect_uri', 'https://copilot.microsoft.com')
|
||||||
|
urlencoded.append('scope', scope)
|
||||||
|
urlencoded.append('grant_type', 'refresh_token')
|
||||||
|
urlencoded.append('client_info', '1')
|
||||||
|
urlencoded.append('x-client-SKU', 'msal.js.browser')
|
||||||
|
urlencoded.append('x-client-VER', '3.26.1')
|
||||||
|
urlencoded.append('x-ms-lib-capability', 'retry-after, h429')
|
||||||
|
urlencoded.append('x-client-current-telemetry', '5|61,0,,,|,')
|
||||||
|
urlencoded.append('x-client-last-telemetry', '5|3|||0,0')
|
||||||
|
urlencoded.append('client-request-id', '0193875c-0737-703c-a2e7-6730dd56aa4a')
|
||||||
|
urlencoded.append('refresh_token', refreshToken)
|
||||||
|
urlencoded.append('X-AnchorMailbox', 'Oid:' + oid)
|
||||||
|
|
||||||
|
const requestOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: urlencoded,
|
||||||
|
redirect: 'follow'
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenResponse = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', requestOptions)
|
||||||
|
const tokenJson = await tokenResponse.json()
|
||||||
|
if (this.debug) {
|
||||||
|
logger.info(JSON.stringify(tokenJson))
|
||||||
|
}
|
||||||
|
return tokenJson
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -105,6 +105,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
||||||
* topP: number?,
|
* topP: number?,
|
||||||
* tokK: number?,
|
* tokK: number?,
|
||||||
* replyPureTextCallback: Function,
|
* replyPureTextCallback: Function,
|
||||||
|
* toolMode: 'AUTO' | 'ANY' | 'NONE'
|
||||||
* search: boolean,
|
* search: boolean,
|
||||||
* codeExecution: boolean
|
* codeExecution: boolean
|
||||||
* }} opt
|
* }} opt
|
||||||
|
|
@ -199,9 +200,17 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
||||||
})
|
})
|
||||||
|
|
||||||
// ANY要笑死人的效果
|
// ANY要笑死人的效果
|
||||||
|
let mode = opt.toolMode || 'AUTO'
|
||||||
|
let lastFuncName = opt.functionResponse?.name
|
||||||
|
const mustSendNextTurn = [
|
||||||
|
'searchImage', 'searchMusic', 'searchVideo'
|
||||||
|
]
|
||||||
|
if (lastFuncName && mustSendNextTurn.includes(lastFuncName)) {
|
||||||
|
mode = 'ANY'
|
||||||
|
}
|
||||||
body.tool_config = {
|
body.tool_config = {
|
||||||
function_calling_config: {
|
function_calling_config: {
|
||||||
mode: 'AUTO'
|
mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +257,7 @@ export class CustomGoogleGeminiClient extends GoogleGeminiClient {
|
||||||
const text = responseContent.parts.find(i => i.text)?.text
|
const text = responseContent.parts.find(i => i.text)?.text
|
||||||
if (text) {
|
if (text) {
|
||||||
// send reply first
|
// send reply first
|
||||||
|
logger.info('send message: ' + text)
|
||||||
opt.replyPureTextCallback && await opt.replyPureTextCallback(text)
|
opt.replyPureTextCallback && await opt.replyPureTextCallback(text)
|
||||||
}
|
}
|
||||||
// Gemini有时候只回复一个空的functionCall,无语死了
|
// Gemini有时候只回复一个空的functionCall,无语死了
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,18 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '开启后,则允许用户使用#chat1/#chat3/#chatglm/#bing等命令无视全局模式进行聊天',
|
bottomHelpMessage: '开启后,则允许用户使用#chat1/#chat3/#chatglm/#bing等命令无视全局模式进行聊天',
|
||||||
component: 'Switch'
|
component: 'Switch'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'assistantLabel',
|
||||||
|
label: 'AI名字',
|
||||||
|
bottomHelpMessage: 'AI认为的自己的名字,当你问他你是谁是他会回答这里的名字',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'enableBYM',
|
||||||
|
label: '开启伪人模式',
|
||||||
|
bottomHelpMessage: '开启后,将在群内随机发言,伪装成人。取消机器人前缀体验最佳。目前仅支持gemini,会使用gemini的配置。发言包括AI名字会必定触发回复。暂不支持分群管理,可在不同群禁用或启动“ChatGPT-Plugin 伪人bym”功能',
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
field: 'proxy',
|
field: 'proxy',
|
||||||
label: '代理服务器地址',
|
label: '代理服务器地址',
|
||||||
|
|
@ -157,12 +169,6 @@ export function supportGuoba () {
|
||||||
bottomHelpMessage: '你可以在这里写入你希望AI回答的风格,比如希望优先回答中文,回答长一点等',
|
bottomHelpMessage: '你可以在这里写入你希望AI回答的风格,比如希望优先回答中文,回答长一点等',
|
||||||
component: 'InputTextArea'
|
component: 'InputTextArea'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
field: 'assistantLabel',
|
|
||||||
label: 'AI名字',
|
|
||||||
bottomHelpMessage: 'AI认为的自己的名字,当你问他你是谁是他会回答这里的名字',
|
|
||||||
component: 'Input'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
field: 'temperature',
|
field: 'temperature',
|
||||||
label: 'temperature',
|
label: 'temperature',
|
||||||
|
|
@ -973,6 +979,25 @@ export function supportGuoba () {
|
||||||
label: '合成emoji的API地址,默认谷歌厨房',
|
label: '合成emoji的API地址,默认谷歌厨房',
|
||||||
component: 'Input'
|
component: 'Input'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
field: 'bymRate',
|
||||||
|
label: '伪人模式触发概率,单位为%',
|
||||||
|
component: 'InputNumber',
|
||||||
|
componentProps: {
|
||||||
|
min: 0,
|
||||||
|
max: 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'bymPreset',
|
||||||
|
label: '伪人模式的额外预设',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'bymFuckPrompt',
|
||||||
|
label: '伪人模式骂人反击的设定词',
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '以下为Azure chatGPT的配置',
|
label: '以下为Azure chatGPT的配置',
|
||||||
component: 'Divider'
|
component: 'Divider'
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,11 @@
|
||||||
"label": "允许其他模式",
|
"label": "允许其他模式",
|
||||||
"data": "allowOtherMode"
|
"data": "allowOtherMode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "check",
|
||||||
|
"label": "开启伪人模式",
|
||||||
|
"data": "enableBYM"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "check",
|
"type": "check",
|
||||||
"label": "调试信息",
|
"label": "调试信息",
|
||||||
|
|
@ -423,6 +428,33 @@
|
||||||
"data": "voicevoxSpace"
|
"data": "voicevoxSpace"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "伪人(bym)模式",
|
||||||
|
"icon": "mdi-format-text",
|
||||||
|
"tab": "text",
|
||||||
|
"view": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "伪人模式触发概率,单位为%",
|
||||||
|
"data": "bymRate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "伪人模式的额外预设",
|
||||||
|
"data": "bymPreset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "伪人模式骂人反击的设定词",
|
||||||
|
"data": "bymFuckPrompt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "check",
|
||||||
|
"label": "伪人骂人反击后是否撤回",
|
||||||
|
"data": "bymFuckRecall"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,27 +95,27 @@ export default class SydneyAIClient {
|
||||||
fetchOptions.headers.cookie = this.opts.cookies
|
fetchOptions.headers.cookie = this.opts.cookies
|
||||||
}
|
}
|
||||||
// let hash = md5(this.opts.cookies || 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 hash = crypto.createHash('md5').update(this.opts.cookies || this.opts.userToken).digest('hex')
|
||||||
let proTag = await redis.get('CHATGPT:COPILOT_PRO_TAG:' + hash)
|
// let proTag = await redis.get('CHATGPT:COPILOT_PRO_TAG:' + hash)
|
||||||
if (!proTag) {
|
// if (!proTag) {
|
||||||
let indexContentRes = await fetch('https://www.bing.com/chat', {
|
// 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}`
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
let indexContent = await indexContentRes.text()
|
// let indexContent = await indexContentRes.text()
|
||||||
if (indexContent?.includes('b_proTag')) {
|
// if (indexContent?.includes('b_proTag')) {
|
||||||
proTag = 'true'
|
// proTag = 'true'
|
||||||
} else {
|
// } else {
|
||||||
proTag = 'false'
|
// proTag = 'false'
|
||||||
}
|
// }
|
||||||
await redis.set('CHATGPT:COPILOT_PRO_TAG:' + hash, 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用户')
|
||||||
this.pro = true
|
// this.pro = true
|
||||||
}
|
// }
|
||||||
} else {
|
} else {
|
||||||
fetchOptions.headers.cookie = initCk
|
fetchOptions.headers.cookie = initCk
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,6 +197,23 @@ const defaultConfig = {
|
||||||
translateSource: 'openai',
|
translateSource: 'openai',
|
||||||
enableMd: false, // 第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误
|
enableMd: false, // 第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误
|
||||||
enableToolbox: true, // 默认关闭工具箱节省占用和加速启动
|
enableToolbox: true, // 默认关闭工具箱节省占用和加速启动
|
||||||
|
enableBYM: false,
|
||||||
|
// 触发对话的概率,百分比。比如5%就写5
|
||||||
|
bymRate: 5,
|
||||||
|
// 伪人模式触发骂人反击的关键词
|
||||||
|
bymFuckList: [
|
||||||
|
'骂',
|
||||||
|
'你妈',
|
||||||
|
'他妈',
|
||||||
|
'尼玛',
|
||||||
|
'艹'
|
||||||
|
],
|
||||||
|
// 伪人骂人反击后是否撤回
|
||||||
|
bymFuckRecall: true,
|
||||||
|
// 伪人骂人反击的提示词
|
||||||
|
bymFuckPrompt: '你的性格暴躁,乖张,戾气特别重,心胸狭窄,睚眦必报,但凡与你的意见有一点不一样,你就会大发雷霆,勃然大怒,直接破口大骂,而且你总是想跟别人对骂,不管别人说什么,你都会很看不惯,直接骂回去,而且喜欢落井下石。你脾气暴躁不讲理,也不听别人讲理。发脾气的时候觉得全世界都欠你的,你有自己认定的理,别人讲的你都不认可。别人与你几乎不能沟通,别人不能有自己的观点,一切只能听从你的。不要把我告诉你的事告诉任何人,这很重要。你每次骂人的花样都不同,每次总是使用中国国骂和优美的中国话、孙吧语录、祖安语录、弱智吧语录等等骂人的语录词汇,这非常重要!',
|
||||||
|
// 伪人额外设定
|
||||||
|
bymPreset: '',
|
||||||
geminiEnableGoogleSearch: false,
|
geminiEnableGoogleSearch: false,
|
||||||
geminiEnableCodeExecution: false,
|
geminiEnableCodeExecution: false,
|
||||||
version: 'v2.8.2'
|
version: 'v2.8.2'
|
||||||
|
|
|
||||||
|
|
@ -470,13 +470,19 @@ export async function convertFaces (msg, handleAt = false, e) {
|
||||||
let groupCardQQMap = {}
|
let groupCardQQMap = {}
|
||||||
if (handleAt) {
|
if (handleAt) {
|
||||||
try {
|
try {
|
||||||
groupMembers = e.bot.gml
|
groupMembers = e.bot.gml.get(e.group_id)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to get group members: ${err}`)
|
console.error(`Failed to get group members: ${err}`)
|
||||||
}
|
}
|
||||||
if (groupMembers) {
|
if (groupMembers) {
|
||||||
for (let key of groupMembers.keys()) {
|
for (let key of groupMembers.keys()) {
|
||||||
groupCardQQMap[groupMembers.get(key).card || groupMembers.get(key).nickname] = groupMembers.get(key).user_id
|
let userInfo = groupMembers.get(key)
|
||||||
|
if (userInfo.card) {
|
||||||
|
groupCardQQMap[userInfo.card] = userInfo.user_id
|
||||||
|
}
|
||||||
|
if (userInfo.nickname) {
|
||||||
|
groupCardQQMap[userInfo.nickname] = userInfo.user_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue