Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General cleanup & refactor #106

Merged
merged 4 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Database.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM postgres:15.1
ARG POSTGRES_DB

# Install pg_cron extension
RUN apt-get update && apt-get install -y postgresql-15-cron
RUN apt-get update && apt-get install -y postgresql-15-cron && apt-get clean

# Add pg_cron to shared_preload_libraries
RUN echo "shared_preload_libraries='pg_cron'" >> /usr/share/postgresql/postgresql.conf.sample
Expand Down
13 changes: 5 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM node:lts-alpine as BUILD
FROM node:lts-alpine AS BUILD

WORKDIR /app

RUN apk add zip
RUN apk --no-cache add zip

COPY . .
RUN yarn install --frozen-lockfile
Expand All @@ -12,16 +12,13 @@ RUN yarn install --production
RUN zip -r app.zip ./node_modules ./build ./yarn.lock ./.env

# ------------------------------------------------------------
FROM node:lts-alpine as APP
FROM node:lts-alpine AS APP

WORKDIR /app

RUN apk add unzip
RUN apk --no-cache add unzip

COPY --from=BUILD /app/app.zip .
RUN unzip app.zip
RUN rm app.zip
RUN mv ./build/* .
RUN rm -rf ./build
RUN unzip app.zip && rm app.zip && mv ./build/* . && rm -rf ./build

CMD ["sh", "-c", "sleep 2 && node ./index.js"]
2 changes: 1 addition & 1 deletion src/commands/announce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default {
max: 1,
time: 30000
});
const confirmation = collectedMessages && collectedMessages.first();
const confirmation = collectedMessages?.first();
if (confirmation && confirmation.content.toLowerCase() === 'yes') {
const annoncesChannel = await message.guild.channels.fetch(announce.channelid);
if (!annoncesChannel) return;
Expand Down
64 changes: 39 additions & 25 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { ChannelType, ColorResolvable, EmbedBuilder, Message } from 'discord.js';
import { Collection, ColorResolvable, EmbedBuilder, Message } from 'discord.js';

import { DatadropClient } from '../datadrop.js';

function buildEmbed(title: string, color: ColorResolvable, description: string): EmbedBuilder {
return new EmbedBuilder().setTitle(title).setColor(color).setDescription(description);
}

export default {
name: 'help',
description:
Expand All @@ -16,36 +12,22 @@ export default {
execute: async (client: DatadropClient, message: Message, args: string[]) => {
const { prefix } = client.config;
const { commands } = client;
const data: string[] = [];
let embed: EmbedBuilder;

if (!args.length) {
// lister les commandes
data.push(`Utilisez \`${prefix}${module.exports.name} [commande]\` pour lire le message d'aide sur une commande spécifique.\n`);
data.push('Commandes disponibles :');
data.push(`- ${commands.map((command) => command.name).join('\n- ')}`);
embed = buildEmbed('Liste des commandes', 'Random', data.join('\n'));
}
else {
// afficher le message d'aide de la commande args[0]
embed = listAvailableCommands(prefix, commands);
} else {
const name = args[0].toLowerCase();
const command =
commands.get(name) ||
commands.find((c) => c.aliases && c.aliases.includes(name));
const command = commands.get(name) || commands.find((c) => c.aliases?.includes(name));

if (!command) {
if (message.channel.isSendable())
if (message.channel.isSendable()) {
await message.channel.send("Ce n'est pas une commande valide.");
}
return;
}

data.push(`**Nom:** ${command.name}`);
if (command.aliases) data.push(`**Alias:** ${command.aliases.join(', ')}`);
if (command.description) data.push(`**Description:** ${command.description}`);
if (command.usage) data.push(`**Usage:** \`${prefix}${command.name} ${command.usage}\``);
data.push(`**Cooldown:** ${command.cooldown || 3} seconde(s)`);

embed = buildEmbed(`Aide pour '${command.name}'`, 'Random', data.join('\n'));
embed = buildCommandUsage(prefix, command);
}

try {
Expand All @@ -57,3 +39,35 @@ export default {
}
}
};

function buildEmbed(title: string, color: ColorResolvable, description: string): EmbedBuilder {
return new EmbedBuilder().setTitle(title).setColor(color).setDescription(description);
}

function listAvailableCommands(prefix: string | undefined, commands: Collection<string, any>): EmbedBuilder {
const data: string[] = [];

data.push(`Utilisez \`${prefix}${module.exports.name} [commande]\` pour lire le message d'aide sur une commande spécifique.\n`);
data.push('Commandes disponibles :');
data.push(`- ${commands.map((command) => command.name).join('\n- ')}`);

return buildEmbed('Liste des commandes', 'Random', data.join('\n'));
}

function buildCommandUsage(prefix: string | undefined, command: any): EmbedBuilder {
const data: string[] = [];

data.push(`**Nom:** ${command.name}`);
if (command.aliases) {
data.push(`**Alias:** ${command.aliases.join(', ')}`);
}
if (command.description) {
data.push(`**Description:** ${command.description}`);
}
if (command.usage) {
data.push(`**Usage:** \`${prefix}${command.name} ${command.usage}\``);
}
data.push(`**Cooldown:** ${command.cooldown || 3} seconde(s)`);

return buildEmbed(`Aide pour '${command.name}'`, 'Random', data.join('\n'));
}
87 changes: 54 additions & 33 deletions src/commands/pinmsg.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Message, MessageResolvable } from 'discord.js';
import { Message, MessageReference, MessageResolvable } from 'discord.js';

import { DatadropClient } from '../datadrop.js';

Expand All @@ -12,46 +12,67 @@ export default {
async execute(client: DatadropClient, message: Message, args: string[]) {
if (!message.guild || !message.member || !message.reference) return;

const reference = message.reference;
const { communitymanagerRoleid, adminRoleid, delegatesRoleid, professorRoleid } = client.config;
const verboseIsActive = args && args.length >= 1 && (args[0] === '--verbose' || args[0] === '-v');

const hasAnyRequiredRole = [communitymanagerRoleid, adminRoleid, delegatesRoleid, professorRoleid].some(r => message.member!.roles.cache.has(r));
if (!hasAnyRequiredRole) {
if (verboseIsActive && message.channel.isSendable()) await message.channel.send(`❌ **Oups!** - Tu n'es pas membre d'un des rôles nécessaires et n'es donc pas éligible à cette commande.`);
else await message.react('❌');
const hasRequiredRoles = await isAuthorized(message, [communitymanagerRoleid, adminRoleid, delegatesRoleid, professorRoleid], client, verboseIsActive);
if (!hasRequiredRoles) return;

client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a tenté d'épingler/désépingler le message <${reference.messageId}> mais n'a pas les droits nécessaires.`);
return;
const referencedMessage = await getMessage(message, client, verboseIsActive);
if (!referencedMessage) return;

if (referencedMessage.pinned) {
await referencedMessage.unpin();
await replyOnAction(message, '✅', 'Message désépinglé!', verboseIsActive);
} else {
await referencedMessage.pin();
await replyOnAction(message, '✅', 'Message épinglé!', verboseIsActive);
}
client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a épinglé/désépinglé le message <${referencedMessage.id}>.`);
}
};

if (!reference || reference.channelId != message.channel.id) {
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('❌ **Oups!** - Pas de référence. Peut-être avez-vous oublié de sélectionner le message à (dés)épingler en y répondant? (cfr <https://support.discord.com/hc/fr/articles/360057382374-Replies-FAQ>)');
else await message.react('❌');
async function replyOnAction(message: Message, emoji: string, content: string, verboseIsActive?: boolean): Promise<void> {
if (verboseIsActive && message.channel.isSendable()) {
await message.channel.send(`${emoji} ${content}`);
} else {
await message.react(emoji);
}
}

client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a tenté d'épingler/désépingler un message sans le référencer.`);
return;
}
async function isAuthorized(message: Message, requiredRoles: string[], client: DatadropClient, verboseIsActive?: boolean): Promise<boolean> {
if (!requiredRoles.some(r => message.member!.roles.cache.has(r))) {
await replyOnAction(message, '❌', '**Oups!** - Tu n\'es pas membre d\'un des rôles nécessaires et n\'es donc pas éligible à cette commande.', verboseIsActive);

const channel = message.channel;
const parentMessage = await channel.messages.fetch(reference.messageId as MessageResolvable);
if (!parentMessage) {
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('❌ **Oups!** - Message non trouvé. Peut-être a-t-il été supprimé?');
else await message.react('❌');
client.logger.info(`Le membre <${message.member?.displayName}> (${message.member?.id}) a tenté d'épingler/désépingler un message mais n'a pas les droits nécessaires.`);
return false;
}
return true;
}

client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a tenté d'épingler/désépingler un message non-trouvé.`);
return;
}
async function getReference(message: Message, client: DatadropClient, verboseIsActive?: boolean): Promise<MessageReference | null> {
const reference = message.reference;
if (!reference || reference.channelId != message.channel.id) {
await replyOnAction(message, '❌', '**Oups!** - Pas de référence. Peut-être avez-vous oublié de sélectionner le message à (dés)épingler en y répondant? (cfr <https://support.discord.com/hc/fr/articles/360057382374-Replies-FAQ>)', verboseIsActive);

if (parentMessage.pinned) {
await parentMessage.unpin();
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('✅ Message désépinglé!');
else await message.react('✅');
} else {
await parentMessage.pin();
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('✅ Message épinglé!');
else await message.react('✅');
}
client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a épinglé/désépinglé le message <${parentMessage.id}>.`);
client.logger.info(`Le membre <${message.member?.displayName}> (${message.member?.id}) a tenté d'épingler/désépingler un message sans le référencer.`);
return null;
}
};
return reference;
}

async function getMessage(message: Message, client: DatadropClient, verboseIsActive?: boolean): Promise<Message | null> {
const reference = await getReference(message, client, verboseIsActive);
if (!reference) return null;

const channel = message.channel;
const referencedMessage = await channel.messages.fetch(reference.messageId as MessageResolvable);
if (!referencedMessage) {
await replyOnAction(message, '❌', '**Oups!** - Message non trouvé. Peut-être a-t-il été supprimé?', verboseIsActive);

client.logger.info(`Le membre <${message.member?.displayName}> (${message.member?.id}) a tenté d'épingler/désépingler un message non-trouvé.`);
return null;
}

return referencedMessage;
}
2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function readConfig(): Promise<Configuration> {

const json = JSON.parse(process.env.CONFIG ?? '{}');
for (const prop in json) {
if (prop.match(/regex/i)) {
if (/regex/i.exec(prop)) {
json[prop] = new RegExp(json[prop]);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/datadrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ export class DatadropClient extends Client {
.map(role => role as Role)
.filter(requiredRole => !!requiredRole);

this.logger.info(`Le rôle ${role.name} (<${role.id}>) n'a pas pu être donné à <${member.user.tag}> parce que tous les rôles requis ne sont pas assignés à ce membre: ${requiredRolesMissing.map(role => `${role.name} (<${role.id}>)`).join(', ')}.`);
const roleNames = requiredRolesMissing.map(role => `${role.name} (<${role.id}>)`).join(', ');
this.logger.info(`Le rôle ${role.name} (<${role.id}>) n'a pas pu être donné à <${member.user.tag}> parce que tous les rôles requis ne sont pas assignés à ce membre: ${roleNames}.`);
await interaction.editReply(`Tu ne peux pas t'assigner le rôle ${role}! Tu dois d'abord avoir les rôles suivants: ${requiredRolesMissing.join(', ')}.`);
});
this.selfRoleManager.on(SelfRoleManagerEvents.maxRolesReach, async (member: GuildMember, interaction: StringSelectMenuInteraction | ButtonInteraction, currentRolesNumber: number, maxRolesNumber: number, role: Role) => {
Expand Down
Loading
Loading