refactor: improve code quality

This commit is contained in:
2026-05-12 15:42:21 +02:00
parent 8abdfc3b2f
commit 528fff3a5b
42 changed files with 1756 additions and 1255 deletions

View File

@@ -0,0 +1,169 @@
import { ChannelType, EmbedBuilder, type Client, type Message, type MessageCreateOptions } from "discord.js";
import { createLogger } from "@lbf-bot/utils";
import { env } from "~/env";
import { checkForNewQuest, type QuestResult } from "~/wov";
import { getAccountBalance, setAccountBalance } from "~/economy";
const logger = createLogger({ prefix: "quests" });
// --- rewards ---
export const makeResultEmbed = async (result: QuestResult, exclude: Array<string>): Promise<MessageCreateOptions> => {
const imageUrl = result.quest.promoImageUrl;
const color = parseInt(result.quest.promoImagePrimaryColor.substring(1), 16);
const participants = result.participants.toSorted((a, b) => b.xp - a.xp);
let rewardsEmbed: EmbedBuilder | undefined;
if (env.QUEST_REWARDS) {
const rewarded = participants
.map((x) => ({ id: x.playerId, username: x.username }))
.filter((x) => !exclude.includes(x.username));
const medals = ["🥇", "🥈", "🥉"].concat(new Array(rewarded.length).fill("🏅"));
const rewards = rewarded
.slice(0, Math.min(rewarded.length, env.QUEST_REWARDS.length))
.map((x, i) => `- ${medals[i]} ${x.username} - ${env.QUEST_REWARDS![i]} ${env.QUEST_REWARDS_ARE_GEMS ? "gemmes" : ""}`);
if (env.QUEST_REWARDS_ARE_GEMS) {
const arr = rewarded.slice(0, Math.min(rewarded.length, env.QUEST_REWARDS.length));
for (let i = 0; i < arr.length; i++) {
const balance = await getAccountBalance(arr[i].id);
await setAccountBalance(arr[i].id, balance + parseInt(env.QUEST_REWARDS[i]));
}
}
rewardsEmbed = new EmbedBuilder()
.setTitle("Récompenses")
.setDescription(`${rewards.join("\n")}\n\n-# \`@LBF gemmes\` pour voir votre nombre de gemmes. Puis avec ${env.DISCORD_REWARDS_GIVER} pour échanger contre des cadeaux !`)
.setColor(color);
}
return {
content: `-# ||${env.DISCORD_MENTION}||`,
embeds: [
new EmbedBuilder()
.setDescription(`# Résultats de quête\n\nMerci à toutes et à tous d'avoir participé 🫡`)
.setColor(color)
.setImage(imageUrl),
...(rewardsEmbed ? [rewardsEmbed] : []),
new EmbedBuilder()
.setTitle("Classement")
.setColor(color)
.setDescription(
participants
.filter((x) => !exclude.includes(x.username))
.filter((_, i) => i < 8)
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
.join("\n"),
),
],
};
};
export const askForGrinders = async (quest: QuestResult, client: Client) => {
const adminChannel = await client.channels.fetch(env.DISCORD_ADMIN_CHANNEL);
if (!adminChannel || adminChannel.type !== ChannelType.GuildText) {
return logger.fatal("Invalid 'DISCORD_ADMIN_CHANNEL'");
}
let exclude: string[] = [];
if (env.QUEST_REWARDS) {
const top10 = quest.participants
.filter((x) => !env.QUEST_EXCLUDE.includes(x.username))
.sort((a, b) => b.xp - a.xp)
.slice(0, 10)
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
.join("\n");
const color = parseInt(quest.quest.promoImagePrimaryColor.substring(1), 16);
await adminChannel.send({
content: `-# ||${env.DISCORD_ADMIN_MENTION}||`,
embeds: [
new EmbedBuilder()
.setTitle("Quête terminée !")
.setColor(color),
new EmbedBuilder()
.setTitle("Top 10 XP")
.setDescription(top10)
.setColor(color),
new EmbedBuilder()
.setTitle("Qui a grind ?")
.setDescription("Merci d'entrer les pseudos des joueurs qui ont grind.\n\nFormat:```@LBF onion,Yuno,...```\n**Attention les majuscules comptent**\nPour entrer la liste des joueurs, il faut __mentionner le bot__, si personne n'a grind, `@LBF tg`")
.setColor(color),
],
});
const filter = (msg: Message) =>
msg.channel.id === adminChannel.id &&
!msg.author.bot &&
msg.content.startsWith(`<@${client.user!.id}>`);
let confirmed = false;
let answer: string | null = null;
while (!confirmed) {
const collected = await adminChannel.awaitMessages({ filter, max: 1 });
answer = collected.first()?.content || null;
if (!answer) continue;
answer = answer.replace(`<@${client.user!.id}>`, "").trim();
if (answer.toLowerCase() === "tg") {
answer = "";
break;
}
const players = answer.split(",").map((x) => x.trim()).filter(Boolean);
await adminChannel.send({
content: `Est-ce correct ? (oui/non)`,
embeds: [
new EmbedBuilder()
.setTitle("Joueurs entrés")
.setDescription(players.length ? players.map((name) => `- ${name}`).join("\n") : "*Aucun joueur entré*")
.setColor(color),
],
});
const confirmFilter = (msg: Message) =>
msg.channel.id === adminChannel.id &&
!msg.author.bot &&
["oui", "non", "yes", "no"].includes(msg.content.toLowerCase());
const confirmCollected = await adminChannel.awaitMessages({ filter: confirmFilter, max: 1 });
const confirmation = confirmCollected.first()?.content.toLowerCase();
if (confirmation === "oui" || confirmation === "yes") {
confirmed = true;
await adminChannel.send({ content: "Ok" });
} else {
await adminChannel.send({ content: "D'accord, veuillez réessayer. Qui a grind ?" });
}
}
if (answer === null) {
return logger.fatal("Answer was 'null', this should be unreachable");
}
exclude = answer.split(",").map((x) => x.trim()).filter(Boolean);
}
const embed = await makeResultEmbed(quest, [...env.QUEST_EXCLUDE, ...exclude]);
const rewardChannel = await client.channels.fetch(env.DISCORD_REWARDS_CHANNEL);
if (!rewardChannel || rewardChannel.type !== ChannelType.GuildText) {
return logger.fatal("Invalid 'DISCORD_REWARDS_CHANNEL'");
}
await rewardChannel.send(embed);
if (env.QUEST_EXCLUDE) await adminChannel.send("Envoyé !");
logger.info(`Results posted at: ${new Date().toISOString()}`);
};
export const questCheckCron = async (client: Client) => {
logger.info("Checking for new quest");
const quest = await checkForNewQuest();
if (quest) {
logger.info(`New quest found: '${quest.quest.id}'`);
await askForGrinders(quest, client);
} else {
logger.info("No new quest found");
}
};