refactor: improve code quality
This commit is contained in:
169
apps/discord-bot/src/quests.ts
Normal file
169
apps/discord-bot/src/quests.ts
Normal 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");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user