fix: 修复一些功能易用性

This commit is contained in:
ikechan8370 2023-06-23 17:33:55 +08:00
parent be7ceafb4b
commit 599f37e627
8 changed files with 249 additions and 61 deletions

View file

@ -51,6 +51,9 @@ import { KickOutTool } from '../utils/tools/KickOutTool.js'
import { SendAvatarTool } from '../utils/tools/SendAvatarTool.js'
import { SendDiceTool } from '../utils/tools/SendDiceTool.js'
import { EditCardTool } from '../utils/tools/EditCardTool.js'
import {SearchVideoTool} from "../utils/tools/SearchBilibiliTool.js";
import {SearchMusicTool} from "../utils/tools/SearchMusicTool.js";
import {QueryStarRailTool} from "../utils/tools/QueryStarRailTool.js";
try {
await import('emoji-strip')
} catch (err) {
@ -1809,7 +1812,7 @@ export class chatgpt extends plugin {
let promptPrefix = `You are ${Config.assistantLabel} ${useCast?.api || Config.promptPrefixOverride || defaultPropmtPrefix}
Knowledge cutoff: 2021-09. Current date: ${currentDate}`
let maxModelTokens = getMaxModelTokens(completionParams.model)
let system = Config.promptPrefixOverride
let system = promptPrefix
if (maxModelTokens >= 16000 && Config.enableGroupContext) {
try {
let opt = {}
@ -1917,22 +1920,22 @@ export class chatgpt extends plugin {
timeoutMs: 120000
// systemMessage: promptPrefix
}
if (Math.floor(Math.random() * 100) < 5) {
// 小概率再次发送系统消息
option.systemMessage = promptPrefix
}
option.systemMessage = system
if (conversation) {
option = Object.assign(option, conversation)
}
let tools = [
new JinyanTool(),
new SearchVideoTool(),
new SendVideoTool(),
new SearchMusicTool(),
new SendMusicTool(),
new KickOutTool(),
new SendAvatarTool(),
new SendDiceTool(),
// new SendDiceTool(),
new KickOutTool(),
new EditCardTool()
new EditCardTool(),
new QueryStarRailTool()
]
let funcMap = {}
tools.forEach(tool => {

View file

@ -23,17 +23,22 @@ export class JinyanTool extends AbstractTool {
func = async function (opts) {
let { qq, groupId, time = '600' } = opts
let group = await Bot.pickGroup(groupId)
time = parseInt(time.trim())
if (time < 60) {
time = 60
}
if (time > 86400 * 30) {
time = 86400 * 30
}
let group = await Bot.pickGroup(groupId)
time = parseInt(time.trim())
if (qq.trim() === 'all') {
await group.sendMsg('[日志]试图开启全员禁言')
// await group.muteAll(time > 0)
if (time > 0) {
await group.sendMsg('[日志]试图开启全员禁言,但被系统阻止了')
return 'error: you are not allowed to mute all in this group'
} else {
await group.muteAll(false)
return '该群的全体禁言已经被解除'
}
} else {
qq = parseInt(qq.trim())
await group.muteMember(qq, time)
@ -41,5 +46,5 @@ export class JinyanTool extends AbstractTool {
return `the user ${qq} has been muted for ${time} seconds`
}
description = 'Useful when you want to ban someone. The input to this tool should be the group number, the qq number of the one who should be banned and the mute duration in seconds(at least 60, at most 180, the number should be an integer multiple of 60), these three number should be concated with a space. If you want to mute all, just replace the qq number with \'all\''
description = 'Useful when you want to ban someone. If you want to mute all, just replace the qq number with \'all\''
}

View file

@ -1,4 +1,4 @@
import {AbstractTool} from "./AbstractTool.js";
import { AbstractTool } from './AbstractTool.js'
export class KickOutTool extends AbstractTool {
name = 'kickOut'

View file

@ -0,0 +1,76 @@
import { AbstractTool } from './AbstractTool.js'
export class QueryStarRailTool extends AbstractTool {
name = 'queryStarRail'
parameters = {
properties: {
qq: {
type: 'string',
description: '要查询的用户的qq号将使用该qq号绑定的uid进行查询'
},
groupId: {
type: 'string',
description: '群号'
},
uid: {
type: 'string',
description: '游戏的uid如果用户提供了则传入并优先使用'
}
},
required: ['qq', 'groupId']
}
func = async function (opts) {
let { qq, groupId, uid } = opts
if (!uid) {
try {
let { Panel } = await import('../../../StarRail-plugin/apps/panel.js')
uid = await redis.get(`STAR_RAILWAY:UID:${qq}`)
if (!uid) {
return '用户没有绑定uid无法查询。可以让用户主动提供uid进行查询'
}
} catch (e) {
return '未安装StarRail-Plugin无法查询'
}
}
try {
let uidRes = await fetch('https://avocado.wiki/v1/info/' + uid)
uidRes = await uidRes.json()
let { assistAvatar, displayAvatars } = uidRes.playerDetailInfo
function dealAvatar (avatar) {
delete avatar.position
delete avatar.vo_tag
delete avatar.desc
delete avatar.promption
delete avatar.relics
delete avatar.behaviorList
delete avatar.images
delete avatar.ranks
if (avatar.equipment) {
avatar.equipment = {
level: avatar.equipment.level,
rank: avatar.equipment.rank,
name: avatar.equipment.name,
skill_desc: avatar.equipment.skill_desc
}
}
}
dealAvatar(assistAvatar)
if (displayAvatars) {
displayAvatars.forEach(avatar => {
dealAvatar(avatar)
})
}
uidRes.playerDetailInfo.assistAvatar = assistAvatar
uidRes.playerDetailInfo.displayAvatars = displayAvatars
delete uidRes.repository
delete uidRes.version
return `the player info in json format is: \n${JSON.stringify(uidRes)}`
} catch (err) {
return `failed to query, error: ${err.toString()}`
}
}
description = 'Useful when you want to query player information of Honkai Star Rail(崩坏:星穹铁道). '
}

View file

@ -0,0 +1,76 @@
import fetch from 'node-fetch'
import { formatDate, mkdirs } from '../common.js'
import fs from 'fs'
import { AbstractTool } from './AbstractTool.js'
export class SearchVideoTool extends AbstractTool {
name = 'searchVideo'
parameters = {
properties: {
keyword: {
type: 'string',
description: '要搜索的视频的标题或关键词'
}
},
required: ['keyword']
}
func = async function (opts) {
let { keyword } = opts
try {
return await searchBilibili(keyword)
} catch (err) {
logger.error(err)
return `fail to search video, error: ${err.toString()}`
}
}
description = 'Useful when you want to search a video by keywords. you should remember the id of the video if you want to share it'
}
export async function searchBilibili (name) {
let biliRes = await fetch('https://www.bilibili.com',
{
// headers: {
// accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
// Accept: '*/*',
// 'Accept-Encoding': 'gzip, deflate, br',
// 'accept-language': 'en-US,en;q=0.9',
// Connection: 'keep-alive',
// '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'
// }
})
const headers = biliRes.headers.raw()
const setCookieHeaders = headers['set-cookie']
if (setCookieHeaders) {
const cookies = []
setCookieHeaders.forEach(header => {
const cookie = header.split(';')[0]
cookies.push(cookie)
})
const cookieHeader = cookies.join('; ')
let headers = {
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'en-US,en;q=0.9',
Referer: 'https://www.bilibili.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',
cookie: cookieHeader
}
let response = await fetch(`https://api.bilibili.com/x/web-interface/search/type?keyword=${name}&search_type=video`,
{
headers
})
let json = await response.json()
if (json.data?.numResults > 0) {
let result = json.data.result.map(r => {
return `id: ${r.bvid},标题:${r.title},作者:${r.author},播放量:${r.play},发布日期:${formatDate(new Date(r.pubdate * 1000))}`
}).slice(0, Math.min(json.data?.numResults, 5)).join('\n')
return `这些是关键词“${name}”的搜索结果:\n${result}`
} else {
return `没有找到关键词“${name}”的搜索结果`
}
}
return {}
}

View file

@ -0,0 +1,39 @@
import fetch from 'node-fetch'
import { AbstractTool } from './AbstractTool.js'
export class SearchMusicTool extends AbstractTool {
name = 'searchMusic'
parameters = {
properties: {
keyword: {
type: 'string',
description: '音乐的标题或关键词'
}
},
required: ['keyword']
}
func = async function (opts) {
let { keyword } = opts
try {
let result = await searchMusic163(keyword)
return `search result: ${result}`
} catch (e) {
return `music search failed: ${e}`
}
}
description = 'Useful when you want to search music by keyword.'
}
export async function searchMusic163 (name) {
let response = await fetch(`http://music.163.com/api/search/get/web?s=${name}&type=1&offset=0&total=true&limit=6`)
let json = await response.json()
if (json.result?.songCount > 0) {
return json.result.songs.map(song => {
return `id: ${song.id}, name: ${song.name}, artists: ${song.artists.map(a => a.name).join('&')}, alias: ${song.alias || 'none'}`
}).join('\n')
}
return null
}

View file

@ -8,31 +8,33 @@ export class SendVideoTool extends AbstractTool {
parameters = {
properties: {
keyword: {
id: {
type: 'string',
description: '要发的视频的标题或关键词,用于搜索'
description: '要发的视频的id'
},
groupId: {
type: 'string',
description: '群号或qq号发送目标'
}
},
required: ['keyword', 'groupId']
required: ['id', 'groupId']
}
func = async function (opts) {
let { keyword, groupId } = opts
let { id, groupId } = opts
groupId = parseInt(groupId.trim())
let msg = []
try {
let { arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like } = await searchBilibili(keyword)
let { arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like, honor } = await getBilibili(id)
let group = await Bot.pickGroup(groupId)
console.log({ arcurl, title, pic, description, videoUrl })
msg.push(title.replace(/(<([^>]+)>)/ig, '') + '\n')
msg.push(`UP主${author} 发布日期:${formatDate(new Date(pubdate * 1000))} 播放量:${play} 点赞:${like}\n`)
msg.push(arcurl + '\n')
msg.push(segment.image('https:' + pic))
msg.push('\n' + description)
if (honor) {
msg.push(`本视频曾获得过${honor}称号`)
}
msg.push('\n视频在路上啦')
await group.sendMsg(msg)
const videoResponse = await fetch(videoUrl, { headers })
@ -45,7 +47,7 @@ export class SendVideoTool extends AbstractTool {
await fs.writeFileSync(fileLoc, buffer)
await group.sendMsg(segment.video(fileLoc))
})
return `the video ${title.replace(/(<([^>]+)>)/ig, '')} will be shared to ${groupId} after a while, please wait`
return `the video ${title.replace(/(<([^>]+)>)/ig, '')} was shared to ${groupId}. the video information: ${msg}`
} catch (err) {
logger.error(err)
if (msg.length > 0) {
@ -56,10 +58,10 @@ export class SendVideoTool extends AbstractTool {
}
}
description = 'Useful when you want to share a video. The input should be the group number and the keywords that can find the video, connected with a space. If you want to send a specific video, you can give more detailed keywords'
description = 'Useful when you want to share a video. You must use searchVideo to get search result and choose one video and get its id'
}
export async function searchBilibili (name) {
export async function getBilibili (bvid) {
let biliRes = await fetch('https://www.bilibili.com',
{
// headers: {
@ -87,28 +89,28 @@ export async function searchBilibili (name) {
'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',
cookie: cookieHeader
}
let response = await fetch(`https://api.bilibili.com/x/web-interface/search/type?keyword=${name}&search_type=video`,
{
headers
})
let json = await response.json()
if (json.data?.numResults > 0) {
let index = randomIndex()
let { arcurl, title, pic, description, bvid, author, play, pubdate, like } = json.data.result[Math.min(index, json.data.numResults)]
let videoInfo = await fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, {
headers
})
videoInfo = await videoInfo.json()
let cid = videoInfo.data.cid
let downloadInfo = await fetch(`https://api.bilibili.com/x/player/playurl?bvid=${bvid}&cid=${cid}`, { headers })
let videoUrl = (await downloadInfo.json()).data.durl[0].url
return {
arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like
}
let videoInfo = await fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, {
headers
})
videoInfo = await videoInfo.json()
let cid = videoInfo.data.cid
let arcurl = `http://www.bilibili.com/video/av${videoInfo.data.aid}`
let title = videoInfo.data.title
let pic = videoInfo.data.pic
let description = videoInfo.data.desc
let author = videoInfo.data.owner.name
let play = videoInfo.data.stat.view
let pubdate = videoInfo.data.pubdate
let like = videoInfo.data.stat.like
let honor = videoInfo.data.honor_reply?.honor?.map(h => h.desc)?.join('、')
let downloadInfo = await fetch(`https://api.bilibili.com/x/player/playurl?bvid=${bvid}&cid=${cid}`, {headers})
let videoUrl = (await downloadInfo.json()).data.durl[0].url
return {
arcurl, title, pic, description, videoUrl, headers, bvid, author, play, pubdate, like, honor
}
} else {
return {}
}
return {}
}
function randomIndex () {

View file

@ -1,14 +1,13 @@
import fetch from 'node-fetch'
import {AbstractTool} from "./AbstractTool.js";
import { AbstractTool } from './AbstractTool.js'
export class SendMusicTool extends AbstractTool {
name = 'sendMusic'
parameters = {
properties: {
keyword: {
id: {
type: 'string',
description: '音乐的标题或关键词'
description: '音乐的id'
},
groupId: {
type: 'string',
@ -19,28 +18,16 @@ export class SendMusicTool extends AbstractTool {
}
func = async function (opts) {
let { keyword, groupId } = opts
let { id, groupId } = opts
groupId = parseInt(groupId.trim())
try {
let { id, name } = await searchMusic163(keyword)
let group = await Bot.pickGroup(groupId)
await group.shareMusic('163', id)
return `the music ${name} has been shared to ${groupId}`
return `the music has been shared to ${groupId}`
} catch (e) {
return `music share failed: ${e}`
}
}
description = 'Useful when you want to share music. The input should be the group number and the name of the music to be sent or the keywords that can find the music, connected with a space'
}
export async function searchMusic163 (name) {
let response = await fetch(`http://music.163.com/api/search/get/web?s=${name}&type=1&offset=0&total=true&limit=20`)
let json = await response.json()
if (json.result?.songCount > 0) {
let id = json.result.songs[0].id
let name = json.result.songs[0].name
return { id, name }
}
return null
description = 'Useful when you want to share music. You must use searchMusic first to get the music id'
}