Skip to content

Commit 1b0315f

Browse files
authored
General cleanup & refactor (#106)
* refactor: avoid high cognitive complexity * fix(interactionCreate): bug due to already replied interaction * fix(interactionCreate): sometimes, the reply is not existing so it needs to be sent * fix(events): apply sonarcloud fixes
1 parent d977863 commit 1b0315f

10 files changed

Lines changed: 268 additions & 173 deletions

File tree

Database.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ FROM postgres:15.1
33
ARG POSTGRES_DB
44

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

88
# Add pg_cron to shared_preload_libraries
99
RUN echo "shared_preload_libraries='pg_cron'" >> /usr/share/postgresql/postgresql.conf.sample

Dockerfile

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
FROM node:lts-alpine as BUILD
1+
FROM node:lts-alpine AS BUILD
22

33
WORKDIR /app
44

5-
RUN apk add zip
5+
RUN apk --no-cache add zip
66

77
COPY . .
88
RUN yarn install --frozen-lockfile
@@ -12,16 +12,13 @@ RUN yarn install --production
1212
RUN zip -r app.zip ./node_modules ./build ./yarn.lock ./.env
1313

1414
# ------------------------------------------------------------
15-
FROM node:lts-alpine as APP
15+
FROM node:lts-alpine AS APP
1616

1717
WORKDIR /app
1818

19-
RUN apk add unzip
19+
RUN apk --no-cache add unzip
2020

2121
COPY --from=BUILD /app/app.zip .
22-
RUN unzip app.zip
23-
RUN rm app.zip
24-
RUN mv ./build/* .
25-
RUN rm -rf ./build
22+
RUN unzip app.zip && rm app.zip && mv ./build/* . && rm -rf ./build
2623

2724
CMD ["sh", "-c", "sleep 2 && node ./index.js"]

src/commands/announce.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default {
3939
max: 1,
4040
time: 30000
4141
});
42-
const confirmation = collectedMessages && collectedMessages.first();
42+
const confirmation = collectedMessages?.first();
4343
if (confirmation && confirmation.content.toLowerCase() === 'yes') {
4444
const annoncesChannel = await message.guild.channels.fetch(announce.channelid);
4545
if (!annoncesChannel) return;

src/commands/help.ts

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
import { ChannelType, ColorResolvable, EmbedBuilder, Message } from 'discord.js';
1+
import { Collection, ColorResolvable, EmbedBuilder, Message } from 'discord.js';
22

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

5-
function buildEmbed(title: string, color: ColorResolvable, description: string): EmbedBuilder {
6-
return new EmbedBuilder().setTitle(title).setColor(color).setDescription(description);
7-
}
8-
95
export default {
106
name: 'help',
117
description:
@@ -16,36 +12,22 @@ export default {
1612
execute: async (client: DatadropClient, message: Message, args: string[]) => {
1713
const { prefix } = client.config;
1814
const { commands } = client;
19-
const data: string[] = [];
2015
let embed: EmbedBuilder;
2116

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

3623
if (!command) {
37-
if (message.channel.isSendable())
24+
if (message.channel.isSendable()) {
3825
await message.channel.send("Ce n'est pas une commande valide.");
26+
}
3927
return;
4028
}
4129

42-
data.push(`**Nom:** ${command.name}`);
43-
if (command.aliases) data.push(`**Alias:** ${command.aliases.join(', ')}`);
44-
if (command.description) data.push(`**Description:** ${command.description}`);
45-
if (command.usage) data.push(`**Usage:** \`${prefix}${command.name} ${command.usage}\``);
46-
data.push(`**Cooldown:** ${command.cooldown || 3} seconde(s)`);
47-
48-
embed = buildEmbed(`Aide pour '${command.name}'`, 'Random', data.join('\n'));
30+
embed = buildCommandUsage(prefix, command);
4931
}
5032

5133
try {
@@ -57,3 +39,35 @@ export default {
5739
}
5840
}
5941
};
42+
43+
function buildEmbed(title: string, color: ColorResolvable, description: string): EmbedBuilder {
44+
return new EmbedBuilder().setTitle(title).setColor(color).setDescription(description);
45+
}
46+
47+
function listAvailableCommands(prefix: string | undefined, commands: Collection<string, any>): EmbedBuilder {
48+
const data: string[] = [];
49+
50+
data.push(`Utilisez \`${prefix}${module.exports.name} [commande]\` pour lire le message d'aide sur une commande spécifique.\n`);
51+
data.push('Commandes disponibles :');
52+
data.push(`- ${commands.map((command) => command.name).join('\n- ')}`);
53+
54+
return buildEmbed('Liste des commandes', 'Random', data.join('\n'));
55+
}
56+
57+
function buildCommandUsage(prefix: string | undefined, command: any): EmbedBuilder {
58+
const data: string[] = [];
59+
60+
data.push(`**Nom:** ${command.name}`);
61+
if (command.aliases) {
62+
data.push(`**Alias:** ${command.aliases.join(', ')}`);
63+
}
64+
if (command.description) {
65+
data.push(`**Description:** ${command.description}`);
66+
}
67+
if (command.usage) {
68+
data.push(`**Usage:** \`${prefix}${command.name} ${command.usage}\``);
69+
}
70+
data.push(`**Cooldown:** ${command.cooldown || 3} seconde(s)`);
71+
72+
return buildEmbed(`Aide pour '${command.name}'`, 'Random', data.join('\n'));
73+
}

src/commands/pinmsg.ts

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Message, MessageResolvable } from 'discord.js';
1+
import { Message, MessageReference, MessageResolvable } from 'discord.js';
22

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

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

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

19-
const hasAnyRequiredRole = [communitymanagerRoleid, adminRoleid, delegatesRoleid, professorRoleid].some(r => message.member!.roles.cache.has(r));
20-
if (!hasAnyRequiredRole) {
21-
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.`);
22-
else await message.react('❌');
18+
const hasRequiredRoles = await isAuthorized(message, [communitymanagerRoleid, adminRoleid, delegatesRoleid, professorRoleid], client, verboseIsActive);
19+
if (!hasRequiredRoles) return;
2320

24-
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.`);
25-
return;
21+
const referencedMessage = await getMessage(message, client, verboseIsActive);
22+
if (!referencedMessage) return;
23+
24+
if (referencedMessage.pinned) {
25+
await referencedMessage.unpin();
26+
await replyOnAction(message, '✅', 'Message désépinglé!', verboseIsActive);
27+
} else {
28+
await referencedMessage.pin();
29+
await replyOnAction(message, '✅', 'Message épinglé!', verboseIsActive);
2630
}
31+
client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a épinglé/désépinglé le message <${referencedMessage.id}>.`);
32+
}
33+
};
2734

28-
if (!reference || reference.channelId != message.channel.id) {
29-
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>)');
30-
else await message.react('❌');
35+
async function replyOnAction(message: Message, emoji: string, content: string, verboseIsActive?: boolean): Promise<void> {
36+
if (verboseIsActive && message.channel.isSendable()) {
37+
await message.channel.send(`${emoji} ${content}`);
38+
} else {
39+
await message.react(emoji);
40+
}
41+
}
3142

32-
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.`);
33-
return;
34-
}
43+
async function isAuthorized(message: Message, requiredRoles: string[], client: DatadropClient, verboseIsActive?: boolean): Promise<boolean> {
44+
if (!requiredRoles.some(r => message.member!.roles.cache.has(r))) {
45+
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);
3546

36-
const channel = message.channel;
37-
const parentMessage = await channel.messages.fetch(reference.messageId as MessageResolvable);
38-
if (!parentMessage) {
39-
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('❌ **Oups!** - Message non trouvé. Peut-être a-t-il été supprimé?');
40-
else await message.react('❌');
47+
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.`);
48+
return false;
49+
}
50+
return true;
51+
}
4152

42-
client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a tenté d'épingler/désépingler un message non-trouvé.`);
43-
return;
44-
}
53+
async function getReference(message: Message, client: DatadropClient, verboseIsActive?: boolean): Promise<MessageReference | null> {
54+
const reference = message.reference;
55+
if (!reference || reference.channelId != message.channel.id) {
56+
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);
4557

46-
if (parentMessage.pinned) {
47-
await parentMessage.unpin();
48-
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('✅ Message désépinglé!');
49-
else await message.react('✅');
50-
} else {
51-
await parentMessage.pin();
52-
if (verboseIsActive && message.channel.isSendable()) await message.channel.send('✅ Message épinglé!');
53-
else await message.react('✅');
54-
}
55-
client.logger.info(`Le membre <${message.member.displayName}> (${message.member.id}) a épinglé/désépinglé le message <${parentMessage.id}>.`);
58+
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.`);
59+
return null;
5660
}
57-
};
61+
return reference;
62+
}
63+
64+
async function getMessage(message: Message, client: DatadropClient, verboseIsActive?: boolean): Promise<Message | null> {
65+
const reference = await getReference(message, client, verboseIsActive);
66+
if (!reference) return null;
67+
68+
const channel = message.channel;
69+
const referencedMessage = await channel.messages.fetch(reference.messageId as MessageResolvable);
70+
if (!referencedMessage) {
71+
await replyOnAction(message, '❌', '**Oups!** - Message non trouvé. Peut-être a-t-il été supprimé?', verboseIsActive);
72+
73+
client.logger.info(`Le membre <${message.member?.displayName}> (${message.member?.id}) a tenté d'épingler/désépingler un message non-trouvé.`);
74+
return null;
75+
}
76+
77+
return referencedMessage;
78+
}

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export async function readConfig(): Promise<Configuration> {
4242

4343
const json = JSON.parse(process.env.CONFIG ?? '{}');
4444
for (const prop in json) {
45-
if (prop.match(/regex/i)) {
45+
if (/regex/i.exec(prop)) {
4646
json[prop] = new RegExp(json[prop]);
4747
}
4848
}

src/datadrop.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ export class DatadropClient extends Client {
131131
.map(role => role as Role)
132132
.filter(requiredRole => !!requiredRole);
133133

134-
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(', ')}.`);
134+
const roleNames = requiredRolesMissing.map(role => `${role.name} (<${role.id}>)`).join(', ');
135+
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}.`);
135136
await interaction.editReply(`Tu ne peux pas t'assigner le rôle ${role}! Tu dois d'abord avoir les rôles suivants: ${requiredRolesMissing.join(', ')}.`);
136137
});
137138
this.selfRoleManager.on(SelfRoleManagerEvents.maxRolesReach, async (member: GuildMember, interaction: StringSelectMenuInteraction | ButtonInteraction, currentRolesNumber: number, maxRolesNumber: number, role: Role) => {

0 commit comments

Comments
 (0)