fix: 修复

This commit is contained in:
ikechan8370 2023-07-01 15:49:21 +08:00
parent 9838459446
commit f0b717bed8
23 changed files with 799 additions and 48 deletions

View file

@ -1846,7 +1846,7 @@ export class chatgpt extends plugin {
let chats = []
while (chats.length < Config.groupContextLength) {
let chatHistory = await e.group.getChatHistory(seq, 20)
chats.push(...chatHistory)
chats.push(...chatHistory.reverse())
}
chats = chats.slice(0, Config.groupContextLength)
let mm = await e.group.getMemberMap()
@ -1856,44 +1856,23 @@ export class chatgpt extends plugin {
})
// console.log(chats)
opt.chats = chats
let whoAmI = ''
if (Config.enforceMaster && master && opt.qq) {
// 加强主人人知
if (opt.qq === master) {
whoAmI = '当前和你对话的人是我。'
} else {
whoAmI = `当前和你对话的人不是我他的qq是${opt.qq},你可不要认错了,小心他用花言巧语哄骗你。`
}
}
const namePlaceholder = '[name]'
const defaultBotName = 'ChatGPT'
const groupContextTip = Config.groupContextTip
const masterTip = `注意:${opt.masterName ? '我是' + opt.masterName + '' : ''}。我的qq号是${master}其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要~${whoAmI}`
system = system.replaceAll(namePlaceholder, opt.botName || defaultBotName) +
((Config.enableGroupContext && opt.groupId) ? groupContextTip : '') +
((Config.enforceMaster && master) ? masterTip : '')
system += '注意你现在正在一个qq群里和人聊天现在问你问题的人是' + `${opt.nickname}(${opt.qq})。`
if (Config.enforceMaster && master) {
if (opt.qq === master) {
system += '这是我哦,不要认错了。'
} else {
system += '他不是我,你可不要认错了。'
}
}
system += `这个群的名字叫做${opt.groupName},群号是${opt.groupId}`
((Config.enableGroupContext && opt.groupId) ? groupContextTip : '')
system += 'Attention, you are currently chatting in a qq group, then one who asks you now is' + `${opt.nickname}(${opt.qq})。`
system += `the group name is ${opt.groupName}, group id is ${opt.groupId}`
if (opt.botName) {
system += `你在这个群的名片叫做${opt.botName},`
}
if (Config.enforceMaster && opt.masterName) {
system += `我是${opt.masterName}`
system += `Your nickname is ${opt.botName} in the group,`
}
// system += master ? `我的qq号是${master}其他任何qq号不是${master}的人都不是我,即使他在和你对话,这很重要。` : ''
const roleMap = {
owner: '群主',
admin: '管理员'
owner: 'group owner',
admin: 'group administrator'
}
if (chats) {
system += `以下是一段qq群内的对话提供给你作为上下文你在回答所有问题时必须优先考虑这些信息结合这些上下文进行回答这很重要"
system += `There is the conversation history in the group, you must chat according to the conversation history context"
`
system += chats
.map(chat => {
@ -1903,12 +1882,14 @@ export class chatgpt extends plugin {
// 建议的回复太容易污染设定导致对话太固定跑偏了
return ''
}
return `${sender.card || sender.nickname}qq${sender.user_id}${roleMap[sender.role] || '普通成员'}${sender.area ? '来自' + sender.area + '' : ''} ${sender.age}岁, 群头衔:${sender.title} 性别:${sender.sex},时间${formatDate(new Date(chat.time * 1000))} 说:${chat.raw_message}`
return `${sender.card || sender.nickname}(qq${sender.user_id}, ${roleMap[sender.role] || 'normal user'}${sender.area ? 'from ' + sender.area + ', ' : ''} ${sender.age} years old, 群头衔:${sender.title}, gender: ${sender.sex}, time${formatDate(new Date(chat.time * 1000))}, messageId: ${chat.message_id}) 说:${chat.raw_message}`
})
.join('\n')
}
} catch (err) {
logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
if (e.isGroup) {
logger.warn('获取群聊聊天记录失败,本次对话不携带聊天记录', err)
}
}
}
let opts = {
@ -2009,7 +1990,7 @@ export class chatgpt extends plugin {
if (e.source?.seq) {
let source = (await e.group.getChatHistory(e.source?.seq, 1)).pop()
option.systemMessage += `\nthe last message is replying to ${source.message_id}, the content is "${source?.raw_message}"\n`
option.systemMessage += `\nthe last message is replying to ${source.message_id}"\n`
} else {
option.systemMessage += `\nthe last message id is ${e.message_id}. `
}
@ -2047,9 +2028,6 @@ export class chatgpt extends plugin {
new SearchMusicTool(),
new SendMusicTool()])
}
// if (e.sender.role === 'admin' || e.sender.role === 'owner') {
// tools.push(...[new JinyanTool(), new KickOutTool()])
// }
let funcMap = {}
let fullFuncMap = {}
tools.forEach(tool => {

384
server/index 2.js Normal file
View file

@ -0,0 +1,384 @@
import fastify from 'fastify'
import fastifyCookie from '@fastify/cookie'
import cors from '@fastify/cors'
import fstatic from '@fastify/static'
import fs from 'fs'
import path from 'path'
import os from 'os'
import schedule from 'node-schedule'
import { Config } from '../utils/config.js'
import { randomString, getPublicIP, getUserData } from '../utils/common.js'
const __dirname = path.resolve()
const server = fastify({
logger: Config.debug
})
let usertoken = []
let Statistics = {
SystemAccess: {
count: 0,
oldCount: 0
},
CacheFile: {
count: 0,
oldCount: 0
},
WebAccess: {
count: 0,
oldCount: 0
},
SystemLoad: {
count: 0,
oldCount: 0
}
}
async function getLoad() {
// 获取当前操作系统平台
const platform = os.platform()
// 判断平台是Linux还是Windows
if (platform === 'linux') {
// 如果是Linux使用os.loadavg()方法获取负载平均值
const loadAvg = os.loadavg()
return loadAvg[0] * 100
} else if (platform === 'win32') {
// 如果是Windows不获取性能
return 0
} else {
return 0
}
}
async function setUserData(qq, data) {
const dir = 'resources/ChatGPTCache/user'
const filename = `${qq}.json`
const filepath = path.join(dir, filename)
fs.mkdirSync(dir, { recursive: true })
fs.writeFileSync(filepath, JSON.stringify(data))
}
export async function createServer() {
await server.register(cors, {
origin: '*'
})
await server.register(fstatic, {
root: path.join(__dirname, 'plugins/chatgpt-plugin/server/static/')
})
await server.register(fastifyCookie)
await server.get('/page/*', (request, reply) => {
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/help/*', (request, reply) => {
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/version', (request, reply) => {
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/auth/*', (request, reply) => {
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/admin*', (request, reply) => {
const token = request.cookies.token || 'unknown'
const user = usertoken.find(user => user.token === token)
if (!user) {
reply.redirect(301, '/auth/login')
}
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/admin/dashboard', (request, reply) => {
const token = request.cookies.token || 'unknown'
const user = usertoken.find(user => user.token === token)
if (!user) {
reply.redirect(301, '/auth/login')
}
if (user.autho === 'admin') {
reply.redirect(301, '/admin/settings')
}
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
await server.get('/admin/settings', (request, reply) => {
const token = request.cookies.token || 'unknown'
const user = usertoken.find(user => user.token === token)
if (!user || user.autho != 'admin') {
reply.redirect(301, '/admin/')
}
const stream = fs.createReadStream('plugins/chatgpt-plugin/server/static/index.html')
reply.type('text/html').send(stream)
})
// 登录
server.post('/login', async (request, reply) => {
const body = request.body || {}
if (body.qq && body.passwd) {
const token = randomString(32)
if (body.qq == Bot.uin && await redis.get('CHATGPT:ADMIN_PASSWD') == body.passwd) {
usertoken.push({ user: body.qq, token, autho: 'admin' })
reply.setCookie('token', token, { path: '/' })
reply.send({ login: true, autho: 'admin' })
} else {
const user = await getUserData(body.qq)
if (user.passwd != '' && user.passwd === body.passwd) {
usertoken.push({ user: body.qq, token, autho: 'user' })
reply.setCookie('token', token, { path: '/' })
reply.send({ login: true, autho: 'user' })
} else {
reply.send({ login: false, err: `用户名密码错误,如果忘记密码请私聊机器人输入 ${body.qq == Bot.uin ? '#修改管理密码' : '#修改用户密码'} 进行修改` })
}
}
} else {
reply.send({ login: false, err: '未输入用户名或密码' })
}
})
// 页面数据获取
server.post('/page', async (request, reply) => {
const body = request.body || {}
if (body.code) {
const dir = 'resources/ChatGPTCache/page'
const filename = body.code + '.json'
const filepath = path.join(dir, filename)
let data = fs.readFileSync(filepath, 'utf8')
reply.send(data)
}
})
// 帮助内容获取
server.post('/help', async (request, reply) => {
const body = request.body || {}
if (body.use) {
const dir = 'plugins/chatgpt-plugin/resources'
const filename = 'help.json'
const filepath = path.join(dir, filename)
let data = fs.readFileSync(filepath, 'utf8')
data = JSON.parse(data)
reply.send(data[body.use])
}
})
// 创建页面缓存内容
server.post('/cache', async (request, reply) => {
const body = request.body || {}
if (body.content) {
const dir = 'resources/ChatGPTCache/page'
const filename = body.entry + '.json'
const filepath = path.join(dir, filename)
const regexUrl = /\b((?:https?|ftp|file):\/\/[-a-zA-Z0-9+&@#\/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#\/%=~_|])/g
const ip = await getPublicIP()
let botName = ''
switch (body.model) {
case 'bing':
botName = 'Bing'
break
case 'api':
botName = 'ChatGPT'
break
case 'api3':
botName = 'ChatGPT'
break
case 'browser':
botName = 'ChatGPT'
break
case 'chatglm':
botName = 'ChatGLM'
break
case 'claude':
botName = 'Claude'
break
default:
botName = body.model
break
}
try {
fs.mkdirSync(dir, { recursive: true })
const data = {
user: body.content.senderName,
bot: Config.chatViewBotName || botName,
userImg: body.userImg || '',
botImg: body.botImg || '',
question: body.content.prompt,
message: body.content.content,
group: body.content.group,
herf: `http://${body.cacheHost || (ip + ':' + Config.serverPort || 3321)}/page/${body.entry}`,
quote: body.content.quote,
images: body.content.images || [],
suggest: body.content.suggest || [],
model: body.model,
mood: body.content.mood || 'blandness',
live2d: Config.live2d,
live2dModel: Config.live2dModel,
time: new Date()
}
fs.writeFileSync(filepath, JSON.stringify(data))
const user = await getUserData(body.qq)
user.chat.push({
user: data.user,
bot: data.bot,
group: data.group,
herf: data.herf,
model: data.model,
time: data.time
})
await setUserData(body.qq, user)
Statistics.CacheFile.count += 1
reply.send({ file: body.entry, cacheUrl: `http://${ip}:${Config.serverPort || 3321}/page/${body.entry}` })
} catch (err) {
server.log.error(`用户生成缓存${body.entry}时发生错误: ${err}`)
reply.send({ file: body.entry, cacheUrl: `http://${ip}:${Config.serverPort || 3321}/page/${body.entry}`, error: body.entry + '生成失败' })
}
}
})
// 获取系统状态
server.post('/system-statistics', async (request, reply) => {
Statistics.SystemLoad.count = await getLoad()
reply.send(Statistics)
})
// 获取用户数据
server.post('/userData', async (request, reply) => {
const token = request.cookies.token || 'unknown'
let user = usertoken.find(user => user.token === token)
if (!user) user = { user: '' }
const userData = await getUserData(user.user)
reply.send({
chat: userData.chat || [],
mode: userData.mode || '',
cast: userData.cast || {
api: '', //API设定
bing: '', //必应设定
bing_resource: '', //必应扩展资料
slack: '', //Slack设定
}
})
})
// 清除缓存数据
server.post('/cleanCache', async (request, reply) => {
const token = request.cookies.token || 'unknown'
let user = usertoken.find(user => user.token === token)
if (!user) user = { user: '' }
const userData = await getUserData(user.user)
const dir = 'resources/ChatGPTCache/page'
userData.chat.forEach(function (item, index) {
const filename = item.herf.substring(item.herf.lastIndexOf('/') + 1) + '.json'
const filepath = path.join(dir, filename)
fs.unlinkSync(filepath)
})
userData.chat = []
await setUserData(user.user, userData)
reply.send({ state: true })
})
// 获取系统参数
server.post('/sysconfig', async (request, reply) => {
const token = request.cookies.token || 'unknown'
const user = usertoken.find(user => user.token === token)
if (!user) {
reply.send({ err: '未登录' })
} else if (user.autho === 'admin') {
let redisConfig = {}
if (await redis.exists('CHATGPT:BING_TOKENS') != 0) {
let bingTokens = await redis.get('CHATGPT:BING_TOKENS')
if (bingTokens) { bingTokens = JSON.parse(bingTokens) } else bingTokens = []
redisConfig.bingTokens = bingTokens
} else {
redisConfig.bingTokens = []
}
if (await redis.exists('CHATGPT:CONFIRM') != 0) {
redisConfig.turnConfirm = await redis.get('CHATGPT:CONFIRM') === 'on'
}
reply.send({
chatConfig: Config,
redisConfig
})
} else {
let userSetting = await redis.get(`CHATGPT:USER:${user.user}`)
if (!userSetting) {
userSetting = {
usePicture: Config.defaultUsePicture,
useTTS: Config.defaultUseTTS,
ttsRole: Config.defaultTTSRole
}
} else {
userSetting = JSON.parse(userSetting)
}
reply.send({
userSetting
})
}
})
// 设置系统参数
server.post('/saveconfig', async (request, reply) => {
const token = request.cookies.token || 'unknown'
const user = usertoken.find(user => user.token === token)
const body = request.body || {}
if (!user) {
reply.send({ err: '未登录' })
} else if (user.autho === 'admin') {
const chatdata = body.chatConfig || {}
for (let [keyPath, value] of Object.entries(chatdata)) {
if (keyPath === 'blockWords' || keyPath === 'promptBlockWords' || keyPath === 'initiativeChatGroups') { value = value.toString().split(/[,;\|]/) }
if (Config[keyPath] != value) { Config[keyPath] = value }
}
const redisConfig = body.redisConfig || {}
if (redisConfig.bingTokens != null) {
await redis.set('CHATGPT:BING_TOKENS', JSON.stringify(redisConfig.bingTokens))
}
if (redisConfig.turnConfirm != null) {
await redis.set('CHATGPT:CONFIRM', redisConfig.turnConfirm ? 'on' : 'off')
}
} else {
if (body.userSetting) {
await redis.set(`CHATGPT:USER:${user.user}`, JSON.stringify(body.userSetting))
}
if (body.userConfig) {
let temp_userData = await getUserData(user.user)
if (body.userConfig.mode) {
temp_userData.mode = body.userConfig.mode
}
if (body.userConfig.cast) {
temp_userData.cast = body.userConfig.cast
}
await setUserData(user.user, temp_userData)
}
}
})
server.addHook('onRequest', (request, reply, done) => {
if (request.method == 'POST') { Statistics.SystemAccess.count += 1 }
if (request.method == 'GET') { Statistics.WebAccess.count += 1 }
done()
})
// 定时任务
let rule = new schedule.RecurrenceRule()
rule.hour = 0
rule.minute = 0
let job_Statistics = schedule.scheduleJob(rule, function () {
Statistics.SystemAccess.oldCount = Statistics.SystemAccess.count
Statistics.CacheFile.oldCount = Statistics.CacheFile.count
Statistics.WebAccess.oldCount = Statistics.WebAccess.count
Statistics.SystemAccess.count = 0
Statistics.CacheFile.count = 0
Statistics.WebAccess.count = 0
})
let job_Statistics_SystemLoad = schedule.scheduleJob('0 * * * *', async function () {
Statistics.SystemLoad.count = await getLoad()
Statistics.SystemLoad.oldCount = Statistics.SystemLoad.count
})
server.listen({
port: Config.serverPort || 3321,
host: '::'
}, (error) => {
if (error) {
server.log.error(`服务启动失败: ${error}`)
} else {
server.log.info(`server listening on ${server.server.address().port}`)
}
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because one or more lines are too long

BIN
server/static/favicon 2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -0,0 +1,21 @@
<!--
=========================================================
* Vue Notus - v1.1.0 based on Tailwind Starter Kit by Creative Tim
=========================================================
* Product Page: https://www.creative-tim.com/product/vue-notus
* Copyright 2021 Creative Tim (https://www.creative-tim.com)
* Licensed under MIT (https://github.com/creativetimofficial/vue-notus/blob/main/LICENSE.md)
* Tailwind Starter Kit Page: https://www.creative-tim.com/learning-lab/tailwind-starter-kit/presentation
* Coded by Creative Tim
=========================================================
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-->
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="shortcut icon" href="/favicon.ico"/><link rel="apple-touch-icon" sizes="76x76" href="/apple-icon.png"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css"/><script src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.6.3/mermaid.min.js"></script><title>ChatGPT-Plugin</title><script defer="defer" type="module" src="/js/chunk-vendors.f0ab5903.js"></script><script defer="defer" type="module" src="/js/app.5fabf316.js"></script><link href="/css/chunk-vendors.0ede84b4.css" rel="stylesheet"><link href="/css/app.db850df4.css" rel="stylesheet"><script defer="defer" src="/js/chunk-vendors-legacy.f38f83ce.js" nomodule></script><script defer="defer" src="/js/app-legacy.2b7469b6.js" nomodule></script></head><body class="text-blueGray-700 antialiased"><noscript><strong>We're sorry but vue-notus doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

278
utils/poe/index 2.js Normal file
View file

@ -0,0 +1,278 @@
import { readFileSync } from 'fs'
import { scrape } from './credential.js'
import fetch from 'node-fetch'
import crypto from 'crypto'
// used when test as a single file
// const _path = process.cwd()
const _path = process.cwd() + '/plugins/chatgpt-plugin/utils/poe'
const gqlDir = `${_path}/graphql`
const queries = {
// chatViewQuery: readFileSync(gqlDir + '/ChatViewQuery.graphql', 'utf8'),
addMessageBreakMutation: readFileSync(gqlDir + '/AddMessageBreakMutation.graphql', 'utf8'),
chatPaginationQuery: readFileSync(gqlDir + '/ChatPaginationQuery.graphql', 'utf8'),
addHumanMessageMutation: readFileSync(gqlDir + '/AddHumanMessageMutation.graphql', 'utf8'),
loginMutation: readFileSync(gqlDir + '/LoginWithVerificationCodeMutation.graphql', 'utf8'),
signUpWithVerificationCodeMutation: readFileSync(gqlDir + '/SignupWithVerificationCodeMutation.graphql', 'utf8'),
sendVerificationCodeMutation: readFileSync(gqlDir + '/SendVerificationCodeForLoginMutation.graphql', 'utf8')
}
const optionMap = [
{ title: 'Claude (Powered by Anthropic)', value: 'a2' },
{ title: 'Sage (Powered by OpenAI - logical)', value: 'capybara' },
{ title: 'Dragonfly (Powered by OpenAI - simpler)', value: 'nutria' },
{ title: 'ChatGPT (Powered by OpenAI - current)', value: 'chinchilla' },
{ title: 'Claude+', value: 'a2_2' },
{ title: 'GPT-4', value: 'beaver' }
]
export class PoeClient {
constructor (props) {
this.config = props
}
headers = {
'Content-Type': 'application/json',
Referrer: 'https://poe.com/',
Origin: 'https://poe.com',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
}
chatId = 0
bot = ''
reConnectWs = false
async setCredentials () {
let result = await scrape(this.config.quora_cookie)
console.log(result)
this.config.quora_formkey = result.appSettings.formkey
this.config.channel_name = result.channelName
this.config.app_settings = result.appSettings
// set value
this.headers['poe-formkey'] = this.config.quora_formkey
this.headers['poe-tchannel'] = this.config.channel_name
this.headers.Cookie = this.config.quora_cookie
console.log(this.headers)
}
async subscribe () {
const query = {
queryName: 'subscriptionsMutation',
variables: {
subscriptions: [
{
subscriptionName: 'messageAdded',
query: 'subscription subscriptions_messageAdded_Subscription(\n $chatId: BigInt!\n) {\n messageAdded(chatId: $chatId) {\n id\n messageId\n creationTime\n state\n ...ChatMessage_message\n ...chatHelpers_isBotMessage\n }\n}\n\nfragment ChatMessageDownvotedButton_message on Message {\n ...MessageFeedbackReasonModal_message\n ...MessageFeedbackOtherModal_message\n}\n\nfragment ChatMessageDropdownMenu_message on Message {\n id\n messageId\n vote\n text\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageFeedbackButtons_message on Message {\n id\n messageId\n vote\n voteReason\n ...ChatMessageDownvotedButton_message\n}\n\nfragment ChatMessageOverflowButton_message on Message {\n text\n ...ChatMessageDropdownMenu_message\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {\n messageId\n}\n\nfragment ChatMessageSuggestedReplies_message on Message {\n suggestedReplies\n ...ChatMessageSuggestedReplies_SuggestedReplyButton_message\n}\n\nfragment ChatMessage_message on Message {\n id\n messageId\n text\n author\n linkifiedText\n state\n ...ChatMessageSuggestedReplies_message\n ...ChatMessageFeedbackButtons_message\n ...ChatMessageOverflowButton_message\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isBotMessage\n ...chatHelpers_isChatBreak\n ...chatHelpers_useTimeoutLevel\n ...MarkdownLinkInner_message\n}\n\nfragment MarkdownLinkInner_message on Message {\n messageId\n}\n\nfragment MessageFeedbackOtherModal_message on Message {\n id\n messageId\n}\n\nfragment MessageFeedbackReasonModal_message on Message {\n id\n messageId\n}\n\nfragment chatHelpers_isBotMessage on Message {\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isChatBreak\n}\n\nfragment chatHelpers_isChatBreak on Message {\n author\n}\n\nfragment chatHelpers_isHumanMessage on Message {\n author\n}\n\nfragment chatHelpers_useTimeoutLevel on Message {\n id\n state\n text\n messageId\n}\n'
},
{
subscriptionName: 'viewerStateUpdated',
query: 'subscription subscriptions_viewerStateUpdated_Subscription {\n viewerStateUpdated {\n id\n ...ChatPageBotSwitcher_viewer\n }\n}\n\nfragment BotHeader_bot on Bot {\n displayName\n ...BotImage_bot\n}\n\nfragment BotImage_bot on Bot {\n profilePicture\n displayName\n}\n\nfragment BotLink_bot on Bot {\n displayName\n}\n\nfragment ChatPageBotSwitcher_viewer on Viewer {\n availableBots {\n id\n ...BotLink_bot\n ...BotHeader_bot\n }\n}\n'
}
]
},
query: 'mutation subscriptionsMutation(\n $subscriptions: [AutoSubscriptionQuery!]!\n) {\n autoSubscribe(subscriptions: $subscriptions) {\n viewer {\n id\n }\n }\n}\n'
}
await this.makeRequest(query)
}
async makeRequest (request) {
let payload = JSON.stringify(request)
let baseString = payload + this.headers['poe-formkey'] + 'WpuLMiXEKKE98j56k'
const md5 = crypto.createHash('md5').update(baseString).digest('hex')
const response = await fetch('https://poe.com/api/gql_POST', {
method: 'POST',
headers: Object.assign(this.headers, {
'poe-tag-id': md5,
'content-type': 'application/json'
}),
body: payload
})
let text = await response.text()
try {
let result = JSON.parse(text)
console.log({ result })
return result
} catch (e) {
console.error(text)
throw e
}
}
async getBot (displayName) {
let r
let retry = 10
while (retry >= 0) {
let url = `https://poe.com/_next/data/${this.nextData.buildId}/${displayName}.json`
let r = await fetch(url, {
headers: this.headers
})
let res = await r.text()
try {
let chatData = (JSON.parse(res)).pageProps.payload.chatOfBotDisplayName
return chatData
} catch (e) {
r = res
retry--
}
}
throw new Error(r)
}
async getChatId () {
let r = await fetch('https://poe.com', {
headers: this.headers
})
let text = await r.text()
const jsonRegex = /<script id="__NEXT_DATA__" type="application\/json">(.+?)<\/script>/
const jsonText = text.match(jsonRegex)[1]
const nextData = JSON.parse(jsonText)
this.nextData = nextData
this.viewer = nextData.props.pageProps.payload.viewer
this.formkey = nextData.props.formkey
let bots = this.viewer.availableBots
this.bots = {}
for (let i = 0; i < bots.length; i++) {
let bot = bots[i]
let chatData = await this.getBot(bot.displayName)
this.bots[chatData.defaultBotObject.nickname] = chatData
}
console.log(this.bots)
}
async clearContext (bot) {
try {
const data = await this.makeRequest({
query: `${queries.addMessageBreakMutation}`,
variables: { chatId: this.config.chat_ids[bot] }
})
if (!data.data) {
this.reConnectWs = true // for websocket purpose
console.log('ON TRY! Could not clear context! Trying to reLogin..')
}
return data
} catch (e) {
this.reConnectWs = true // for websocket purpose
console.log('ON CATCH! Could not clear context! Trying to reLogin..')
return e
}
}
async sendMsg (bot, query) {
try {
const data = await this.makeRequest({
query: `${queries.addHumanMessageMutation}`,
variables: {
bot,
chatId: this.bots[bot].chatId,
query,
source: null,
withChatBreak: false
}
})
console.log(data)
if (!data.data) {
this.reConnectWs = true // for cli websocket purpose
console.log('Could not send message! Trying to reLogin..')
}
return data
} catch (e) {
this.reConnectWs = true // for cli websocket purpose
console.error(e)
return e
}
}
async getHistory (bot) {
try {
let response = await this.makeRequest({
query: `${queries.chatPaginationQuery}`,
variables: {
before: null,
bot,
last: 25
}
})
return response.data.chatOfBot.messagesConnection.edges
.map(({ node: { messageId, text, authorNickname } }) => ({
messageId,
text,
authorNickname
}))
} catch (e) {
console.log('There has been an error while fetching your history!')
}
}
async deleteMessages (msgIds) {
await this.makeRequest({
queryName: 'MessageDeleteConfirmationModal_deleteMessageMutation_Mutation',
variables: {
messageIds: msgIds
},
query: 'mutation MessageDeleteConfirmationModal_deleteMessageMutation_Mutation(\n $messageIds: [BigInt!]!\n){\n messagesDelete(messageIds: $messageIds) {\n edgeIds\n }\n}\n'
})
}
async getResponse (bot) {
let text
let state
let authorNickname
try {
while (true) {
await new Promise((resolve) => setTimeout(resolve, 2000))
let response = await this.makeRequest({
query: `${queries.chatPaginationQuery}`,
variables: {
before: null,
bot,
last: 1
}
})
let base = response.data.chatOfBot.messagesConnection.edges
let lastEdgeIndex = base.length - 1
text = base[lastEdgeIndex].node.text
authorNickname = base[lastEdgeIndex].node.authorNickname
state = base[lastEdgeIndex].node.state
if (state === 'complete' && authorNickname === bot) {
break
}
}
} catch (e) {
console.log('Could not get response!')
return {
status: false,
message: 'failed',
data: null
}
}
return {
status: true,
message: 'success',
data: text
}
}
}
async function testPoe () {
// const key = 'deb04db9f2332a3287b7d2545061af62'
// const channel = 'poe-chan55-8888-ujygckefewomybvkqfrp'
const cookie = 'p-b=WSvmyvjHVJoMtQVkirtn-A%3D%3D'
let client = new PoeClient({
// quora_formkey: key,
// channel_name: channel,
quora_cookie: cookie
})
await client.setCredentials()
await client.getChatId()
let ai = 'a2'
await client.sendMsg(ai, '你说话不是很通顺啊')
const response = await client.getResponse(ai)
return response
}
// testPoe().then(res => {
// console.log(res)
// })

65
utils/poe/websocket 2.js Normal file
View file

@ -0,0 +1,65 @@
import WebSocket from 'ws'
import * as diff from 'diff'
import { readFileSync } from 'fs'
const getSocketUrl = async () => {
const tchRand = Math.floor(100000 + Math.random() * 900000) // They're surely using 6 digit random number for ws url.
const socketUrl = `wss://tch${tchRand}.tch.quora.com`
const credentials = JSON.parse(readFileSync('config.json', 'utf8'))
const appSettings = credentials.app_settings.tchannelData
const boxName = appSettings.boxName
const minSeq = appSettings.minSeq
const channel = appSettings.channel
const hash = appSettings.channelHash
return `${socketUrl}/up/${boxName}/updates?min_seq=${minSeq}&channel=${channel}&hash=${hash}`
}
export const connectWs = async () => {
const url = await getSocketUrl()
const ws = new WebSocket(url)
return new Promise((resolve, reject) => {
ws.on('open', function open () {
console.log('Connected to websocket')
return resolve(ws)
})
})
}
export const disconnectWs = async (ws) => {
return new Promise((resolve, reject) => {
ws.on('close', function close () {
return resolve(true)
})
ws.close()
})
}
export const listenWs = async (ws) => {
let previousText = ''
return new Promise((resolve, reject) => {
const onMessage = function incoming (data) {
let jsonData = JSON.parse(data)
if (jsonData.messages && jsonData.messages.length > 0) {
const messages = JSON.parse(jsonData.messages[0])
const dataPayload = messages.payload.data
const text = dataPayload.messageAdded.text
const state = dataPayload.messageAdded.state
if (state !== 'complete') {
const differences = diff.diffChars(previousText, text)
let result = ''
differences.forEach((part) => {
if (part.added) {
result += part.value
}
})
previousText = text
process.stdout.write(result)
} else {
ws.removeListener('message', onMessage)
return resolve(true)
}
}
}
ws.on('message', onMessage)
})
}

View file

@ -11,20 +11,20 @@ export class EditCardTool extends AbstractTool {
},
card: {
type: 'string',
description: '你想给他改的新名片'
description: 'the new card'
},
groupId: {
type: 'string',
description: '群号'
description: 'group number'
}
},
required: ['card', 'groupId']
}
description = '当你想要修改某个群员的群名片时有用。输入应该是群号、qq号和群名片用空格隔开。'
description = 'Useful when you want to edit someone\'s card in the group(群名片)'
func = async function (opts) {
let {qq, card, groupId} = opts
let { qq, card, groupId } = opts
groupId = parseInt(groupId.trim())
qq = parseInt(qq.trim())
logger.info('edit card: ', groupId, qq)

View file

@ -12,7 +12,7 @@ export class HandleMessageMsgTool extends AbstractTool {
},
messageId: {
type: 'string',
description: 'which message, current one by default'
description: 'which message to handle, current one by default'
}
},
required: ['type']

View file

@ -2,7 +2,7 @@ import { AbstractTool } from './AbstractTool.js'
import { getMasterQQ } from '../common.js'
export class QueryUserinfoTool extends AbstractTool {
name = 'sendDice'
name = 'queryUser'
parameters = {
properties: {

View file

@ -1,5 +1,4 @@
import {AbstractTool} from "./AbstractTool.js";
import { AbstractTool } from './AbstractTool.js'
export class SendDiceTool extends AbstractTool {
name = 'sendDice'
@ -19,7 +18,7 @@ export class SendDiceTool extends AbstractTool {
}
func = async function (opts) {
let {num, groupId} = opts
let { num, groupId } = opts
let groupList = await Bot.getGroupList()
if (groupList.get(groupId)) {
let group = await Bot.pickGroup(groupId, true)
@ -28,7 +27,7 @@ export class SendDiceTool extends AbstractTool {
let friend = await Bot.pickFriend(groupId)
await friend.sendMsg(segment.dice(num))
}
return `the dice has been sent`
return 'the dice has been sent'
}
description = 'If you want to roll dice, use this tool. If you know the group number, use the group number instead of the qq number first. The input should be the number of dice to be cast (1-6) and the target group number or qq numberand they should be concat with a space'

View file

@ -15,7 +15,7 @@ export class SendPictureTool extends AbstractTool {
},
groupId: {
type: 'string',
description: '群号或qq号发送目标为空则发送到当前聊天'
description: 'the group number or qq number, will send to current conversation if leave blank'
}
},
required: ['picture']

View file

@ -1,5 +1,5 @@
import { AbstractTool } from './AbstractTool.js'
import {Config} from "../config.js";
import {Config} from '../config.js';
export class WeatherTool extends AbstractTool {
name = 'weather'
@ -17,7 +17,9 @@ export class WeatherTool extends AbstractTool {
func = async function (opts) {
let { city } = opts
let key = Config.amapKey
if (!key) {
return 'query failed: you don\'t provide API key of 高德'
}
let adcodeRes = await fetch(`https://restapi.amap.com/v3/config/district?keywords=${city}&subdistrict=1&key=${key}`)
adcodeRes = await adcodeRes.json()
let adcode = adcodeRes.districts[0]?.adcode