pissbot-9000/commands/message/core/help.js
2025-01-02 17:36:48 +01:00

215 lines
No EOL
8.8 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 { paginator } = require('#client');
const { DISCORD_INVITES, DEFAULT_PREFIXES, PERMISSION_GROUPS } = require('#constants');
const { createEmbed, formatPaginationEmbeds, page } = require('#utils/embed');
const { acknowledge } = require('#utils/interactions');
const { codeblock, icon, link, pill, smallPill, iconPill, stringwrap } = require('#utils/markdown')
const { editOrReply } = require('#utils/message');
const { hasFeature } = require('#utils/testing');
function createHelpPage(context, title, contents, descriptions) {
return page(createEmbed("default", context, {
description: `### ${title}\n\n` +
renderCommandList(contents, descriptions) +
`\n\n${icon("question")} Use ${smallPill(`${DEFAULT_PREFIXES[0]}help <command>`)} to view more information about a command.` +
`\n${icon("discord")} Need help with anything else? ${DISCORD_INVITES.help}`
}))
}
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 = stringwrap(desc, 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, slashCommands) {
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('channel_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 `
if (a.default !== "") argument += ` ${smallPill(`default: ${a.default}`)}`
if (!a.required) argument += ` ${smallPill('optional')}`
args.push(argument)
}
}
let cPage = createEmbed("default", context, {
description: `### ${icon("slash")} ${command.name}\n${alias}${explicit}\n${command.metadata.description}`,
fields: []
})
if(args.length >= 1) cPage.description += `\n\n${args.join('\n\n')}`;
// Adds the slash command hint, if available
if(command.metadata.slashCommand){
let cmd = slashCommands.filter((c)=>c.name === command.metadata.slashCommand);
if(cmd.length >= 1) {
switch(cmd[0].type){
case 1:
cPage.description += `\n\n${icon("slash")} Available via **Slash Commands**.\n-# ${icon("subtext_lightbulb")} Click on </${cmd[0].name}:${cmd[0].ids.first()}> to try it out! ${link(context.application.oauth2UrlFormat().replace("ptb.discordapp.com","discord.com"), `Add ${context.client.user.username}`, `Add ${context.client.user.username} to use this command anywhere.`)}`
break;
case 2:
cPage.description += `\n\n${icon("slash")} Available via **Context Menu Commands**\n-# ${icon("subtext_lightbulb")} Right-Click on someone's avatar! ${link(context.application.oauth2UrlFormat().replace("ptb.discordapp.com","discord.com"), `Add ${context.client.user.username}`, `Add ${context.client.user.username} to use this command anywhere.`)}`
break;
case 3:
cPage.description += `\n\n${icon("slash")} Available via **User Context Commands**.\n-# ${icon("subtext_lightbulb")} Right-Click on any message! ${link(context.application.oauth2UrlFormat().replace("ptb.discordapp.com","discord.com"), `Add ${context.client.user.username}`, `Add ${context.client.user.username} to use this command anywhere.`)}`
break;
default:
break;
}
}
}
// TODO: maybe try building a little parser that highlights things via ansi
if (command.metadata.usage) cPage.fields.push({
name: `${icon("settings")} 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)
cPage.fields.push({
name: `${icon("example")} Examples`,
value: '```' + ex.join('``````') + '```',
inline: false
})
}
return page(cPage);
}
// These categories will be displayed to users, add them in the correct order
const categories = {
"core": `${icon("home")} Core Commands`,
"info": `${icon("information")} Information Commands`,
"search": `${icon("mag")} Search Commands`,
"utils": `${icon("tools")} Utility Commands`,
"fun": `${icon("stars")} Fun Commands`,
"mod": `${icon("shield")} "Moderation Command}`
}
module.exports = {
name: 'help',
label: 'command',
aliases: ['cmds', 'cmd', 'commands', 'command', 'pleasehelpmeiamgoingtoexplode', 'h', '?'],
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: [...PERMISSION_GROUPS.baseline],
run: async (context, args) => {
if(await hasFeature(context, "core/help")) categories["limited"] = `${iconPill("stars", "Limited Test Commands")}`;
else if (categories["limited"]) delete categories["limited"]
await acknowledge(context);
if (args.command) {
// 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_PREFIXES[0]
try {
if (results.length == 0) return editOrReply(context, 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(page(createEmbed("default", context, {
description: `Check the pages for full command details.\n\n` + renderCommandList(cmds, dscs, 15) + `\n\n${icon("discord")} Need help with anything else? ${DISCORD_INVITES.support}`
})))
// Generate command detail pages
for (const c of results) {
pages.push(createCommandPage(context, prefix, c, context.interactionCommandClient.commands))
}
await paginator.createPaginator({
context,
pages: formatPaginationEmbeds(pages)
});
return;
} else {
return editOrReply(context, createCommandPage(context, prefix, results[0], context.interactionCommandClient.commands))
}
} 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]))
}
await paginator.createPaginator({
context,
pages: formatPaginationEmbeds(pages)
});
}
},
};