formatter: Use semicolons (#4686)

I prefer semicolors, they also help avoiding certain pitfalls in JavaScript/TypeScript, such as the following code sample:
```js
const xyz = "test"
(something.else as string) = "another"
```
This results in a TypeError: "test" is not a function, this is because js thinks we are trying to call the string "test" as a function.
To fix this it requires a `;` somewhere before the `(`, such as `;(something ... ` which in my opinion is ugly and less clean overall.
This commit is contained in:
Fleny
2026-01-17 21:54:15 +01:00
committed by GitHub
parent f713b4ab7b
commit 27c261fee2
403 changed files with 11250 additions and 11217 deletions
+7 -7
View File
@@ -1,12 +1,12 @@
import 'dotenv/config'
import 'dotenv/config';
import { createBot } from '@discordeno/bot'
import events from './events/index.js'
import { createBot } from '@discordeno/bot';
import events from './events/index.js';
const token = process.env.TOKEN
const token = process.env.TOKEN;
// Ensure the existence of the TOKEN env
if (!token) throw new Error('The TOKEN environment variable needs to be defined.')
if (!token) throw new Error('The TOKEN environment variable needs to be defined.');
export const bot = createBot({
token,
@@ -41,6 +41,6 @@ export const bot = createBot({
id: true,
},
},
})
});
bot.events = events
bot.events = events;
+3 -3
View File
@@ -1,12 +1,12 @@
import { EventEmitter } from 'node:events'
import { EventEmitter } from 'node:events';
// Extremely minimal collector class
export default class ItemCollector<T> extends EventEmitter {
onItem(callback: (item: T) => unknown): void {
this.on('item', callback)
this.on('item', callback);
}
collect(item: T): void {
this.emit('item', item)
this.emit('item', item);
}
}
@@ -1,12 +1,12 @@
import type { CreateSlashApplicationCommand } from '@discordeno/types'
import type { bot } from '../bot.js'
import roles from './roles.js'
import type { CreateSlashApplicationCommand } from '@discordeno/types';
import type { bot } from '../bot.js';
import roles from './roles.js';
export const commands = new Map<string, Command>([roles].map((cmd) => [cmd.name, cmd]))
export const commands = new Map<string, Command>([roles].map((cmd) => [cmd.name, cmd]));
export default commands
export default commands;
export interface Command extends CreateSlashApplicationCommand {
/** Handler that will be executed when this command is triggered */
execute: (interaction: typeof bot.transformers.$inferredTypes.interaction, args: Record<string, unknown>) => Promise<unknown>
execute: (interaction: typeof bot.transformers.$inferredTypes.interaction, args: Record<string, unknown>) => Promise<unknown>;
}
+97 -97
View File
@@ -1,4 +1,4 @@
import assert from 'node:assert'
import assert from 'node:assert';
import {
type ActionRow,
type ButtonComponent,
@@ -6,12 +6,12 @@ import {
MessageComponentTypes,
type SelectMenuComponent,
TextStyles,
} from '@discordeno/bot'
import { ApplicationCommandOptionTypes, ButtonStyles } from '@discordeno/types'
import { bot } from '../bot.js'
import ItemCollector from '../collector.js'
import { collectors } from '../events/interactionCreate.js'
import type { Command } from './index.js'
} from '@discordeno/bot';
import { ApplicationCommandOptionTypes, ButtonStyles } from '@discordeno/types';
import { bot } from '../bot.js';
import ItemCollector from '../collector.js';
import { collectors } from '../events/interactionCreate.js';
import type { Command } from './index.js';
const command: Command = {
name: 'roles',
@@ -73,22 +73,22 @@ const command: Command = {
if (args.reactions?.create) {
// Ensure that there is a channelId
if (!interaction.channelId) {
await interaction.respond('Could not get the current channel.', { isPrivate: true })
return
await interaction.respond('Could not get the current channel.', { isPrivate: true });
return;
}
// This array is used to store all the roles for this reaction roles
let roles = [args.reactions.create]
let roles = [args.reactions.create];
// Send the message that uses will use to get the role
const roleMessage = await bot.helpers.sendMessage(interaction.channelId, {
content: 'Pick your roles',
components: getRoleButtons(roles),
})
});
// Create a copy of the actionRow for the main message
// NOTE: we use a copy so when we edit this actionRow the edits don't get applied to all the command executions, only this one, for example we do disable some buttons in some conditional cases
const messageActionRow = structuredClone(messageActionRowTemplate)
const messageActionRow = structuredClone(messageActionRowTemplate);
const message = await interaction.respond(
{
@@ -96,230 +96,230 @@ const command: Command = {
components: [messageActionRow],
},
{ isPrivate: true, withResponse: true },
)
);
if (!message) {
await interaction.respond('❌ Unable to send the message correctly. Cancelling', { isPrivate: true })
return
await interaction.respond('❌ Unable to send the message correctly. Cancelling', { isPrivate: true });
return;
}
assert('resource' in message && message.resource?.message)
assert('resource' in message && message.resource?.message);
// Create the collector for the menu
const itemCollector = new ItemCollector<typeof bot.transformers.$inferredTypes.interaction>()
collectors.add(itemCollector)
const itemCollector = new ItemCollector<typeof bot.transformers.$inferredTypes.interaction>();
collectors.add(itemCollector);
// For the new reaction role, we need to keep track of what the user gave us
let partialRoleInfo: Partial<(typeof roles)[number]> | undefined
let partialRoleInfo: Partial<(typeof roles)[number]> | undefined;
itemCollector.onItem(async (i) => {
// We need to verify the interaction is for us.
if (i.message?.id !== message.resource?.message?.id) {
return
return;
}
// Save button
if (i.data?.customId === 'reactionRoles-save') {
// Remove this item collector from the list of collectors (we aren't correcting anymore)
collectors.delete(itemCollector)
collectors.delete(itemCollector);
// Delete the edit message
await i.deferEdit()
await i.delete()
await i.deferEdit();
await i.delete();
return
return;
}
// New button
if (i.data?.customId === 'reactionRoles-add') {
partialRoleInfo = {}
partialRoleInfo = {};
// Ask the user for the role
await i.edit({ content: 'Pick a role for the new reaction role', components: [selectRoleActionRow] })
return
await i.edit({ content: 'Pick a role for the new reaction role', components: [selectRoleActionRow] });
return;
}
// New button - role select menu
if (partialRoleInfo && i.data?.customId === 'reactionRoles-add-role') {
const roleToAdd = i.data?.resolved?.roles?.first()
const roleToAdd = i.data?.resolved?.roles?.first();
// Verify that we could get the role from discord
if (!roleToAdd) {
throw new Error('Unable to get the information for the role to add')
throw new Error('Unable to get the information for the role to add');
}
// Save it to our partial role information
partialRoleInfo.role = roleToAdd
partialRoleInfo.role = roleToAdd;
// Ask the user for the color of the button
await i.edit({
content: 'Pick a color for the reaction role',
components: [selectColorActionRow],
})
});
return
return;
}
// New button - color select menu
if (partialRoleInfo && i.data?.customId === 'reactionRoles-add-color') {
const color = parseInt(i.data?.values?.[0] ?? 'NaN')
const color = parseInt(i.data?.values?.[0] ?? 'NaN');
// Verify that we could get the color information
if (isNaN(color)) {
throw new Error('Unable to get the information for the role to add')
throw new Error('Unable to get the information for the role to add');
}
// Save the color to our partial
partialRoleInfo.color = color
partialRoleInfo.color = color;
// Ask the user to input the emoji and optionally a label for the button
await i.respond({
title: 'Pick an emoji and label for the reaction role',
components: [selectEmojiActionRow, selectLabelActionRow],
customId: 'reactionRoles-add-modal',
})
});
return
return;
}
// New button - emoji & label modal
if (partialRoleInfo && i.data?.customId === 'reactionRoles-add-modal') {
// Ensure that we can get the channelId from the interaction
if (!interaction.channelId) {
throw new Error('Unable to get current channel')
throw new Error('Unable to get current channel');
}
// Get the data from discord
const emoji = i.data.components?.[0]?.components?.[0].value
const label = i.data.components?.[1]?.components?.[0].value
const emoji = i.data.components?.[0]?.components?.[0].value;
const label = i.data.components?.[1]?.components?.[0].value;
// Verify that the emoji was given
if (!emoji) {
throw new Error('Unable to get the information for the role to add')
throw new Error('Unable to get the information for the role to add');
}
// Save them to our partial
partialRoleInfo.emoji = emoji
partialRoleInfo.label = label
partialRoleInfo.emoji = emoji;
partialRoleInfo.label = label;
// Save role and display the new message editing the old one
// We are sure that in this place the entire object has been assembled
roles.push(partialRoleInfo as (typeof roles)[number])
roles.push(partialRoleInfo as (typeof roles)[number]);
await bot.helpers.editMessage(interaction.channelId, roleMessage.id, {
components: getRoleButtons(roles),
})
});
// Clear our partial roleInfo, we are done with it
partialRoleInfo = undefined
partialRoleInfo = undefined;
// In case the delete button was disabled (all the roles were deleted) re-enable it
messageActionRow.components[1]!.disabled = false
messageActionRow.components[1]!.disabled = false;
// Discord imposes a limit of 5 action rows and 5 buttons for actionRow = 25 buttons max
// more than 25 will give an error, so we disable the new button
if (roles.length === 25) {
const button = messageActionRow.components[0] as ButtonComponent
button.disabled = true
const button = messageActionRow.components[0] as ButtonComponent;
button.disabled = true;
}
// Show again the main edit menu
await interaction.edit({
content: 'Use the buttons in this message to edit the message below.',
components: [messageActionRow],
})
});
// Respond to the modal. A modal submit (type 5) interaction can't edit the original response
await i.respond('Reaction role created successfully. You can use the message above to add/remove a role', { isPrivate: true })
await i.respond('Reaction role created successfully. You can use the message above to add/remove a role', { isPrivate: true });
return
return;
}
// Remove button
if (i.data?.customId === 'reactionRoles-remove') {
// Clone the actionRow for the remove select menu, this is to prevent unwanted data to appear to other users
const removeActionRow = structuredClone(removeActionRowTemplate)
const selectMenu = removeActionRow.components[0] as SelectMenuComponent
const removeActionRow = structuredClone(removeActionRowTemplate);
const selectMenu = removeActionRow.components[0] as SelectMenuComponent;
// Add the possible values for this select menu
for (const roleInfo of roles) {
selectMenu.options.push({
label: `${roleInfo.emoji} ${roleInfo.label ?? ''}`,
value: roleInfo.role.id.toString(),
})
});
}
// Ask the user for what reaction role they want to remove
await i.edit({
content: 'Select what reaction role to remove',
components: [removeActionRow],
})
});
return
return;
}
// Remove button - role select menu
if (i.data?.customId === 'reactionRoles-remove-selectMenu') {
// Ensure that we can get the channelId from the interaction
if (!interaction.channelId) {
throw new Error('Unable to get current channel')
throw new Error('Unable to get current channel');
}
// Get the role to delete from discord
const roleToRemove = i.data?.values?.[0]
const roleToRemove = i.data?.values?.[0];
// Ensure we got it
if (!roleToRemove) {
throw new Error('Unable to get the role to remove')
throw new Error('Unable to get the role to remove');
}
await i.deferEdit()
await i.deferEdit();
// Remove the role from the list
roles = roles.filter((roleInfo) => roleInfo.role.id.toString() !== roleToRemove)
roles = roles.filter((roleInfo) => roleInfo.role.id.toString() !== roleToRemove);
// Edit the main button
await bot.helpers.editMessage(interaction.channelId, roleMessage.id, {
components: getRoleButtons(roles),
})
});
// If the new button was disabled (we were at 25 buttons) we re-enable it
const button = messageActionRow.components[0] as ButtonComponent
button.disabled = false
const button = messageActionRow.components[0] as ButtonComponent;
button.disabled = false;
// If we are at 0 roles, and the user tried to delete a role they will get locked in the menu, so we disable it
if (roles.length === 0) {
messageActionRow.components[1]!.disabled = true
messageActionRow.components[1]!.disabled = true;
}
// Show the main edit ui (new, remove, save)
await i.edit({
content: 'Use the buttons in this message to edit the message below.',
components: [messageActionRow],
})
});
return
return;
}
// We don't know what code to run for this interaction
throw new Error('Unknown button')
})
throw new Error('Unknown button');
});
}
},
}
};
export default command
export default command;
// Interface to type the arguments that we receive from discord
interface CommandArgs {
reactions?: {
create?: {
role: typeof bot.transformers.$inferredTypes.role
emoji: string
color: ButtonStyles
label?: string
}
}
role: typeof bot.transformers.$inferredTypes.role;
emoji: string;
color: ButtonStyles;
label?: string;
};
};
}
// Templates/ActionRows for the command to then be referenced in the various part of the code
@@ -357,7 +357,7 @@ const messageActionRowTemplate: ActionRow = {
label: 'Save',
},
],
} as const
} as const;
const removeActionRowTemplate: ActionRow = {
type: MessageComponentTypes.ActionRow,
@@ -371,7 +371,7 @@ const removeActionRowTemplate: ActionRow = {
options: [],
},
],
} as const
} as const;
const selectRoleActionRow: ActionRow = {
type: MessageComponentTypes.ActionRow,
@@ -384,7 +384,7 @@ const selectRoleActionRow: ActionRow = {
placeholder: 'Select a role',
},
],
} as const
} as const;
const selectColorActionRow: ActionRow = {
type: MessageComponentTypes.ActionRow,
@@ -400,7 +400,7 @@ const selectColorActionRow: ActionRow = {
],
},
],
} as const
} as const;
const selectEmojiActionRow: ActionRow = {
type: MessageComponentTypes.ActionRow,
@@ -413,7 +413,7 @@ const selectEmojiActionRow: ActionRow = {
required: true,
},
],
} as const
} as const;
const selectLabelActionRow: ActionRow = {
type: MessageComponentTypes.ActionRow,
@@ -427,37 +427,37 @@ const selectLabelActionRow: ActionRow = {
maxLength: 80,
},
],
} as const
} as const;
// Function to get all the actionRows with buttons for the reaction roles message
function getRoleButtons(
roles: Array<{
role: typeof bot.transformers.$inferredTypes.role
emoji: string
color: ButtonStyles
label?: string | undefined
role: typeof bot.transformers.$inferredTypes.role;
emoji: string;
color: ButtonStyles;
label?: string | undefined;
}>,
): ActionRow[] {
const actionRows: ActionRow[] = []
const actionRows: ActionRow[] = [];
// If there aren't any roles, we don't need any buttons
if (roles.length === 0) return actionRows
if (roles.length === 0) return actionRows;
// We add the components later, so we need to make typescript know that we are sure that it will be a compatibile components array
actionRows.push({ type: MessageComponentTypes.ActionRow, components: [] as unknown as ActionRow['components'] })
actionRows.push({ type: MessageComponentTypes.ActionRow, components: [] as unknown as ActionRow['components'] });
for (const roleInfo of roles) {
let actionRow = actionRows.at(-1)
let actionRow = actionRows.at(-1);
// Ensure that we were able to get the actionRow
if (!actionRow) {
throw new Error('Unable to get actionRow')
throw new Error('Unable to get actionRow');
}
// If the actionRow is full (has 5 buttons) add a new one
if (actionRow.components.length === 5) {
actionRow = { type: MessageComponentTypes.ActionRow, components: [] as unknown as ActionRow['components'] }
actionRows.push(actionRow)
actionRow = { type: MessageComponentTypes.ActionRow, components: [] as unknown as ActionRow['components'] };
actionRows.push(actionRow);
}
// Add the new button to this actionRow
@@ -469,8 +469,8 @@ function getRoleButtons(
},
label: roleInfo.label,
customId: `reactionRoles-role-${roleInfo.role.id}`,
})
});
}
return actionRows
return actionRows;
}
+5 -5
View File
@@ -1,10 +1,10 @@
import type { bot } from '../bot.js'
import { event as interactionCreateEvent } from './interactionCreate.js'
import { event as readyEvent } from './ready.js'
import type { bot } from '../bot.js';
import { event as interactionCreateEvent } from './interactionCreate.js';
import { event as readyEvent } from './ready.js';
export const events = {
interactionCreate: interactionCreateEvent,
ready: readyEvent,
} as typeof bot.events
} as typeof bot.events;
export default events
export default events;
@@ -1,59 +1,59 @@
import { commandOptionsParser, InteractionTypes, MessageComponentTypes } from '@discordeno/bot'
import { bot } from '../bot.js'
import type ItemCollector from '../collector.js'
import commands from '../commands/index.js'
import { commandOptionsParser, InteractionTypes, MessageComponentTypes } from '@discordeno/bot';
import { bot } from '../bot.js';
import type ItemCollector from '../collector.js';
import commands from '../commands/index.js';
export const collectors = new Set<ItemCollector<typeof bot.transformers.$inferredTypes.interaction>>()
export const collectors = new Set<ItemCollector<typeof bot.transformers.$inferredTypes.interaction>>();
export const event: typeof bot.events.interactionCreate = async (interaction) => {
// Give to all the collectors the interaction to use
for (const collector of collectors) {
collector.collect(interaction)
collector.collect(interaction);
}
// If the interaction is a command check if it is a command and run it
if (interaction.type === InteractionTypes.ApplicationCommand) {
if (!interaction.data) return
if (!interaction.data) return;
const command = commands.get(interaction.data.name)
if (!command) return
const command = commands.get(interaction.data.name);
if (!command) return;
try {
await command.execute(interaction, commandOptionsParser(interaction))
await command.execute(interaction, commandOptionsParser(interaction));
} catch (error) {
console.error(error)
console.error(error);
}
}
// If the interaction is a button it might be the button press on our reaction role message
if (interaction.type === InteractionTypes.MessageComponent && interaction.data?.componentType === MessageComponentTypes.Button) {
// The interaction is not a button press on the role button
if (!interaction.data?.customId?.startsWith('reactionRoles-role-')) return
if (!interaction.guildId || !interaction.member) return
if (!interaction.data?.customId?.startsWith('reactionRoles-role-')) return;
if (!interaction.guildId || !interaction.member) return;
// Remove the prefix and get the roleId
const roleId = BigInt(interaction.data.customId.slice('reactionRoles-role-'.length))
const roleId = BigInt(interaction.data.customId.slice('reactionRoles-role-'.length));
// Check if we need to remove or add the role to the user
const alreadyHasRole = !!interaction.member.roles.find((role) => role === roleId)
const alreadyHasRole = !!interaction.member.roles.find((role) => role === roleId);
try {
if (alreadyHasRole) {
await bot.helpers.removeRole(interaction.guildId, interaction.user.id, roleId, `Reaction role button for role id ${roleId}`)
await interaction.respond(`I removed from you the <@&${roleId}> role.`, { isPrivate: true })
return
await bot.helpers.removeRole(interaction.guildId, interaction.user.id, roleId, `Reaction role button for role id ${roleId}`);
await interaction.respond(`I removed from you the <@&${roleId}> role.`, { isPrivate: true });
return;
}
// You will get an invalid request made if the bot attempts to give a bot role, a role higher then him hightest role, a link role or if it does not have the Manage Roles permission
// This could be prevented by checking for the roles that the bot owns and the role that the bot is trying to add
await bot.helpers.addRole(interaction.guildId, interaction.user.id, roleId, `Reaction role button for role id ${roleId}`)
await interaction.respond(`I added to you the <@&${roleId}> role.`, { isPrivate: true })
await bot.helpers.addRole(interaction.guildId, interaction.user.id, roleId, `Reaction role button for role id ${roleId}`);
await interaction.respond(`I added to you the <@&${roleId}> role.`, { isPrivate: true });
} catch {
// Respond with an error message
await interaction.respond(
'I could not give you the role. Possible reasons are:\n- My permissions are not configured correctly, make sure i have the `Manage Roles` permission\n- The role is **above** my hightest role in the server setup\n- The role does not exist or is non-manageable (for example: bot roles, link roles or @everyone)',
{ isPrivate: true },
)
);
}
}
}
};
+3 -3
View File
@@ -1,6 +1,6 @@
import { bot } from '../bot.js'
import { bot } from '../bot.js';
export const event: typeof bot.events.ready = () => {
// Print to the console when the bot has connected to discord and is ready to handle the events
bot.logger.info('The bot is ready!')
}
bot.logger.info('The bot is ready!');
};
+3 -3
View File
@@ -1,5 +1,5 @@
import { bot } from './bot.js'
import { bot } from './bot.js';
await bot.start()
await bot.start();
process.on('unhandledRejection', bot.logger.error)
process.on('unhandledRejection', bot.logger.error);
@@ -1,12 +1,12 @@
import 'dotenv/config'
import 'dotenv/config';
import { bot } from './bot.js'
import commands from './commands/index.js'
import { bot } from './bot.js';
import commands from './commands/index.js';
const guildId = 'REPLACE WITH YOUR GUILD ID'
const guildId = 'REPLACE WITH YOUR GUILD ID';
await bot.rest
.upsertGuildApplicationCommands(guildId, [...commands.values()])
.catch((e) => bot.logger.error('There was an error when updating the global commands', e))
.catch((e) => bot.logger.error('There was an error when updating the global commands', e));
process.exit(0)
process.exit(0);