pissbot-9000/commands/message/core/help.js
2023-08-09 11:48:58 +02:00

201 lines
No EOL
7.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { codeblock, highlight, icon, link, pill, smallPill } = require('../../../labscore/utils/markdown')
const { createEmbed, formatPaginationEmbeds } = require('../../../labscore/utils/embed')
const { DISCORD_INVITES, DEFAULT_BOT_PREFIX } = require('../../../labscore/constants')
const { paginator } = require('../../../labscore/client');
const { editOrReply } = require('../../../labscore/utils/message');
const { Permissions } = require("detritus-client/lib/constants");
const { canUseLimitedTestCommands } = require('../utils/testing');
function createHelpPage(context, title, contents, descriptions){
return {
"embeds": [
createEmbed("default", context, {
description: `${title}\n\n` +
renderCommandList(contents, descriptions) +
`\n\n${icon("question")} Use ${pill(`${DEFAULT_BOT_PREFIX}help <command>`)} to view more information about a command.`
})
]
}
}
function renderCommandList(commands, descriptions, limit){
let len = Math.max(...(commands.map(el => el.length))) + 3;
let render = []
let i = 0;
for(const c of commands){
let pad = len - c.length;
let desc = descriptions[i]
if(desc.includes('\n')) desc = desc.split('\n')[0]
if(desc.length >= 41) desc = desc.substr(0, 40) + '...'
render.push(` **\` ${c}${' '.repeat(pad)}\`** ${desc}`)
i++
}
if(limit && render.length > limit) render.splice(limit, 999)
return render.join('\n')
}
function createCommandPage(context, prefix, command){
alias = ' '
if(command.aliases.length >= 1){
for(const al of command.aliases) alias += smallPill(al)
alias += "\n"
}
let explicit = '';
if(command.metadata.explicit) explicit = `\n${icon('nsfw')} This command contains explicit content and can only be used in Age-Restricted channels. ${link("https://support.discord.com/hc/en-us/articles/115000084051-Age-Restricted-Channels-and-Content", "Learn More")}\n`
// Render argument pills if present
let args = [];
if(command.argParser.args){
for(const a of command.argParser.args){
let argument = `-${a._name} <${a._type.replace('bool','true/false')}>`
argument = pill(argument)
if(a.help) argument += ` ${a.help}`
argument += `\n ${smallPill(`default: ${a.default}`)} `
if(!a.required) argument += smallPill('optional')
args.push(argument)
}
}
let page = createEmbed("default", context, {
description: `${icon("command")} ${pill(command.name)}\n${alias}${explicit}\n${command.metadata.description}\n\n${args.join('\n\n')}`,
fields: []
})
// TODO: maybe try building a little parser that highlights things via ansi
if(command.metadata.usage) page.fields.push({
name: `${icon("util")} Usage`,
value: codeblock("py", [prefix + command.metadata.usage]),
inline: true
})
if(command.metadata.examples){
let ex = []
for(const e of command.metadata.examples) ex.push(prefix + e)
page.fields.push({
name: `${icon("info")} Examples`,
value: '```' + ex.join('``````') + '```',
inline: false
})
}
return {
embeds: [page]
};
}
// These categories will be displayed to users, add them in the correct order
const categories = {
"core": `${icon("house")} Core Commands`,
"info": `${icon("info")} Information Commands`,
"search": `${icon("search")} Search Commands`,
"utils": `${icon("utils")} Utility Commands`,
"fun": `${icon("fun")} Fun Commands`,
"image": `${icon("image")} Image Commands`,
"mod": `${icon("moderation")} Moderation Commands`
}
module.exports = {
name: 'help',
label: 'command',
aliases: ['cmds', 'cmd', 'commands', 'command'],
metadata: {
description: 'List all commands, get more information about individual commands.',
description_short: 'Show full command list',
examples: ['help ping'],
category: 'core',
usage: 'help [<command>]'
},
permissionsClient: [Permissions.EMBED_LINKS, Permissions.SEND_MESSAGES, Permissions.USE_EXTERNAL_EMOJIS, Permissions.READ_MESSAGE_HISTORY],
run: async (context, args) => {
if(canUseLimitedTestCommands(context)) categories["limited"] = `${icon("fun")} Limited Test Commands`;
if(args.command){
await context.triggerTyping()
// Detailed command view
let resultScores = {}
let resultMappings = {}
for(const c of context.commandClient.commands){
if(c.name.includes(args.command.toLowerCase()) || c.aliases.filter((f)=>{return f.includes(args.command.toLowerCase())}).length >= 1){
if(c.metadata.explicit && !context.channel.nsfw) continue;
if(!categories[c.metadata.category] && !context.user.isClientOwner) continue;
resultScores[c.name] = 1
resultMappings[c.name] = c
}
// Boost exact matches to rank higher in the result list
if(c.name == args.command.toLowerCase()) resultScores[c.name] += 1
if(c.aliases.filter((f)=>{return f == args.command.toLowerCase()}).length >= 1) resultScores[c.name] += 1
}
let results = [];
resultScores = Object.fromEntries(Object.entries(resultScores).sort(([,a],[,b]) => b-a));
for(const k of Object.keys(resultScores)) results.push(resultMappings[k])
let pages = []
let prefix = DEFAULT_BOT_PREFIX
try{
if(results.length == 0) return editOrReply(context, {embeds: [createEmbed("warning", context, "No commands found for the provided query.")]})
if(results.length > 1) {
// Command overview
let cmds = results.map((m)=>{return m.name})
let dscs = results.map((m)=>{return m.metadata.description_short})
pages.push({embeds:[
createEmbed("default", context, {
description: `Check the pages for full command details.\n\n` + renderCommandList(cmds, dscs, 15) + `\n\n${icon("question")} Need help with something else? Contact us via our ${link(DISCORD_INVITES.support, "Support Server")}.`
})
]})
// Generate command detail pages
for(const c of results){
pages.push(createCommandPage(context, prefix, c))
}
pages = formatPaginationEmbeds(pages)
const paging = await paginator.createPaginator({
context,
pages
});
return;
} else {
return editOrReply(context, createCommandPage(context, prefix, results[0]))
}
}catch(e){
console.log(e)
}
} else {
// Full command list
let commands = {}
let descriptions = {}
for(const c of context.commandClient.commands){
if(!categories[c.metadata.category]) continue;
if(c.metadata.explicit && !context.channel.nsfw) continue;
if(!commands[c.metadata.category]) commands[c.metadata.category] = []
if(!descriptions[c.metadata.category]) descriptions[c.metadata.category] = []
commands[c.metadata.category].push(`${c.name}`);
descriptions[c.metadata.category].push(`${c.metadata.description_short}`);
}
let pages = []
for(const cat of Object.keys(categories)){
pages.push(createHelpPage(context, categories[cat], commands[cat], descriptions[cat]))
}
pages = formatPaginationEmbeds(pages)
const paging = await paginator.createPaginator({
context,
pages
});
}
},
};