Skip to content

Commit

Permalink
added global ban command for labs
Browse files Browse the repository at this point in the history
  • Loading branch information
GhomKrosmonaute committed Jul 3, 2024
1 parent 4c86d02 commit 8f7e96e
Show file tree
Hide file tree
Showing 19 changed files with 240 additions and 153 deletions.
5 changes: 4 additions & 1 deletion Gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ async function __importOrInstall(packageName, importDefault = false) {
}
}

console.log(`[${dayjs().format("HH:mm:ss")}] Imported '${chalk.cyan(packageName)}'`)
// eslint-disable-next-line no-undef
console.log(
`[${dayjs().format("HH:mm:ss")}] Imported '${chalk.cyan(packageName)}'`,
)

return importDefault ? namespace.default : namespace
}
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
},
"devDependencies": {
"@esbuild/linux-x64": "^0.20.1",
"@ghom/bot.ts-cli": "^8.0.0",
"@ghom/bot.ts-cli": ">=8.0.3",
"@types/boxen": "^3.0.1",
"@types/cron": "^1.7.3",
"@types/dotenv": "^8.2.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const commandHandler = new handler.Handler(
return file.default as ICommand
},
onLoad: async (filepath, command) => {
if (filepath.endsWith(".native.js")) command.native = true
command.native = filepath.endsWith(".native.js")
command.filepath = filepath
return commands.add(command)
},
Expand Down
8 changes: 2 additions & 6 deletions src/app/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,15 @@ export interface Config {
detailCommand?: (
message: command.IMessage,
command: command.ICommand,
) =>
| Promise<command.MessageCreateOptionsResolvable>
| command.MessageCreateOptionsResolvable
) => Promise<util.SystemMessage> | util.SystemMessage

/**
* Custom help command for slash commands
*/
detailSlashCommand?: (
interaction: slash.ISlashCommandInteraction,
command: discord.ApplicationCommand,
) =>
| Promise<slash.InteractionReplyOptionsResolvable>
| slash.InteractionReplyOptionsResolvable
) => Promise<util.SystemMessage> | util.SystemMessage

/**
* Options for the Discord Client constructor
Expand Down
2 changes: 1 addition & 1 deletion src/app/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ const orm = new ORM({

export * from "@ghom/orm"

export default orm
export default orm
109 changes: 57 additions & 52 deletions src/app/slash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export const slashCommandHandler = new handler.Handler(
return file.default as ISlashCommand
},
onLoad: async (filepath, command) => {
command.filepath = filepath
command.native = filepath.endsWith("native.js")
slashCommands.add(command)
command.filepath = filepath
return slashCommands.add(command)
},
},
)
Expand Down Expand Up @@ -123,6 +123,7 @@ export interface ISlashCommand {

export interface ISlashCommandInteraction {
base: discord.CommandInteraction
client: discord.Client<true>
guild?: discord.Guild
guildId?: string
channel:
Expand All @@ -142,13 +143,11 @@ export interface SlashCommandInteraction<
string,
SlashCommandOption<SlashCommandOptionTypeName>
>,
> extends Omit<
discord.CommandInteraction,
"guild" | "guildId" | "channel" | "options"
> {
> {
base: GuildOnly extends true
? discord.CommandInteraction<"raw" | "cached">
: discord.CommandInteraction
client: discord.Client<true>
guild: GuildOnly extends true ? discord.Guild : undefined
guildId: GuildOnly extends true ? string : undefined
channel: ChannelType extends "dm"
Expand Down Expand Up @@ -281,29 +280,26 @@ export async function registerSlashCommands(guildId?: string) {
export async function prepareSlashCommand(
interaction: discord.CommandInteraction,
command: ISlashCommand,
): Promise<ISlashCommandInteraction | discord.EmbedBuilder> {
): Promise<ISlashCommandInteraction | never> {
const output: ISlashCommandInteraction = {
base: interaction,
client: interaction.client,
guild: undefined,
guildId: undefined,
channel: interaction.channel!,
options: {},
}

if (command.options.botOwnerOnly && interaction.user.id !== env.BOT_OWNER)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription("This command can only be used by the bot owner")
throw new Error("This command can only be used by the bot owner")

if (
command.options.guildOnly ||
(command.options.guildOnly !== false &&
command.options.channelType !== "dm")
) {
if (!interaction.inGuild() || !interaction.guild)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription("This command can only be used in a guild")
throw new Error("This command can only be used in a guild")

output.guild = interaction.guild
output.guildId = interaction.guildId
Expand All @@ -312,9 +308,7 @@ export async function prepareSlashCommand(
command.options.guildOwnerOnly &&
interaction.user.id !== interaction.guild.ownerId
)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription("This command can only be used by the guild owner")
throw new Error("This command can only be used by the guild owner")

if (command.options.allowRoles || command.options.denyRoles) {
const member = await interaction.guild.members.fetch(interaction.user.id)
Expand All @@ -325,11 +319,9 @@ export async function prepareSlashCommand(
command.options.allowRoles?.includes(role.id),
)
)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription(
"You don't have the required role to use this command",
)
throw new Error(
"You don't have the required role to use this command",
)
}

if (command.options.denyRoles) {
Expand All @@ -338,25 +330,19 @@ export async function prepareSlashCommand(
command.options.denyRoles?.includes(role.id),
)
)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription(
"You have a role that is not allowed to use this command",
)
throw new Error(
"You have a role that is not allowed to use this command",
)
}
}
}

if (command.options.channelType === "thread") {
if (!interaction.channel || !interaction.channel.isThread())
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription("This command can only be used in a thread")
throw new Error("This command can only be used in a thread")
} else if (command.options.channelType === "dm") {
if (!interaction.channel || !interaction.channel.isDMBased())
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription("This command can only be used in a DM")
throw new Error("This command can only be used in a DM")
}

if (command.options.options) {
Expand All @@ -366,14 +352,23 @@ export async function prepareSlashCommand(
const value = interaction.options.get(name) ?? option.default ?? null

if (option.required && value === null)
return new discord.EmbedBuilder()
.setColor("Red")
.setDescription(`Option "${name}" is required`)
throw new Error(`Option "${name}" is required`)

output.options[name] = value
}
}

if (interaction.options.data.length > 0) {
output.options = interaction.options.data.reduce(
(acc, option) => {
if (option.name in output.options) return acc
acc[option.name] = option.value
return acc
},
{} as Record<string, any>,
)
}

return output
}

Expand All @@ -391,28 +386,38 @@ export async function sendSlashCommandDetails(
) {
const { detailSlashCommand } = config

interaction.base.reply(
const command = slashCommands.get(computed.name)

if (!command) throw new Error(`Command ${computed.name} not found`)

await interaction.base.reply(
detailSlashCommand
? await detailSlashCommand(interaction, computed)
: {
embeds: [
new discord.EmbedBuilder()
.setColor("Blurple")
.setAuthor({
name: computed.name,
iconURL: computed.client.user?.displayAvatarURL(),
})
.setDescription(computed.description || "no description"),
],
},
: await util.getSystemMessage("default", {
author: {
name: computed.name,
iconURL: computed.client.user?.displayAvatarURL(),
url: config.openSource
? await util.getFileGitURL(command.filepath!)
: undefined,
},
description: `Use directly: </${computed.name}:${computed.id}>\nDescription: ${computed.description || "no description"}`,
footer: config.openSource
? {
text: util.convertDistPathToSrc(
util.rootPath(command.filepath!),
),
}
: undefined,
fields: computed.options.map((option) => ({
name: option.name,
value: option.description || "no description",
inline: true,
})),
}),
)
}

export type InteractionReplyOptionsResolvable =
| string
| discord.MessagePayload
| discord.InteractionReplyOptions

export type SlashCommandOptionTypeName =
keyof typeof discord.ApplicationCommandOptionType

Expand Down
12 changes: 8 additions & 4 deletions src/app/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ export async function checkUpdates() {
if (isOlder(packageJSON.version, remoteJSON.version)) {
logger.warn(
`a new major version of ${chalk.blue(
"bot.ts",
"@ghom/bot.ts",
)} is available: ${chalk.magenta(
packageJSON.version,
)} => ${chalk.magenta(remoteJSON.version)}`,
)
logger.warn(
`you can update ${chalk.blue("bot.ts")} by running ${chalk.bgWhite.black(
`you can update ${chalk.blue("@ghom/bot.ts")} by running ${chalk.bgWhite.black(
`gulp update`,
)}`,
)
Expand All @@ -79,7 +79,7 @@ export async function checkUpdates() {
} else {
logger.log(
`you are using the latest version of ${chalk.blue(
"bot.ts",
"@ghom/bot.ts",
)} and ${chalk.blue("@ghom/bot.ts-cli")}`,
)
}
Expand Down Expand Up @@ -423,6 +423,10 @@ export const code = {
format: prettify.format,
}

export function convertDistPathToSrc(path: string) {
return path.replace(/dist([/\\])/, "src$1").replace(".js", ".ts")
}

export async function getFileGitURL(
filepath: string,
): Promise<string | undefined> {
Expand All @@ -440,7 +444,7 @@ export async function getFileGitURL(

if (!remote) return

return `${remote.refs.fetch.replace(".git", "")}/blob/${branchName}/${rootPath(filepath).replace("dist/", "src/").replace(".js", ".ts")}`
return `${remote.refs.fetch.replace(".git", "")}/blob/${branchName}/${convertDistPathToSrc(rootPath(filepath)).replace(/\\/g, "/")}`
} catch (error) {
return
}
Expand Down
32 changes: 32 additions & 0 deletions src/commands/ban.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as app from "#app"

export default new app.Command({
name: "ban",
description: "Ban a user from all labs",
channelType: "guild",
middlewares: [app.staffOnly(), app.labOnly()],
positional: [
{
name: "target",
description: "The target user to ban",
type: "user",
required: true,
},
{
name: "reason",
description: "The reason for the ban",
type: "string",
required: true,
},
],
async run(message) {
const result = await app.globalBan(message.args.target, message.args.reason)

const fails = result.filter((r) => r.status === "rejected")

await app.sendLog(
message.guild,
`**${message.args.target.tag}** has been banned by **${message.author.tag}** from **${result.length - fails.length}** labs.`,
)
},
})
2 changes: 1 addition & 1 deletion src/listeners/automod.messageCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const listener: app.Listener<"messageCreate"> = {
async run(message) {
app
.detectAndBanSpammer(message)
.catch((error) => app.error("automod.messageCreate", error))
.catch((error) => app.error(error, "automod.messageCreate"))
},
}

Expand Down
2 changes: 1 addition & 1 deletion src/listeners/log.afterReady.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const listener: app.Listener<"afterReady"> = {
app.log(
`ok i'm ready! ${chalk.blue(
"My default prefix is",
)} ${chalk.bgBlueBright.black(app.env.BOT_PREFIX)}`,
)} ${chalk.bold.blueBright(app.env.BOT_PREFIX)}`,
)

figlet(app.packageJSON.name, (err, value) => {
Expand Down
Loading

0 comments on commit 8f7e96e

Please sign in to comment.