feat(discord-bot): change bot prefix and improve quest results interaction
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { parseEnv, logger } from "@lbf-bot/utils";
|
||||
import { parseEnv } from "@lbf-bot/utils";
|
||||
|
||||
// TODO: use parseEnv from utils
|
||||
|
||||
|
||||
@@ -16,14 +16,12 @@ const onReady = async (client: Client<true>) => {
|
||||
setInterval(() => void trackingCron(client), env.WOV_TRACKING_INTERVAL);
|
||||
};
|
||||
|
||||
const onMessage = async (message: OmitPartialGroupDMChannel<Message>, client: Client) => {
|
||||
const onMessage = async (message: OmitPartialGroupDMChannel<Message>) => {
|
||||
if (message.author.bot) return;
|
||||
|
||||
if (message.content.startsWith(`<@${client.user!.id}>`)) {
|
||||
const [commandName, ...args] = message.content
|
||||
.replace(`<@${client.user!.id}>`, "")
|
||||
.trim()
|
||||
.split(" ");
|
||||
const parts = message.content.trim().split(/\s+/);
|
||||
if (parts[0]?.toLowerCase() === "lbf") {
|
||||
const [commandName, ...args] = parts.slice(1);
|
||||
|
||||
const command = commands[commandName];
|
||||
if (!command) return;
|
||||
@@ -41,5 +39,5 @@ const onMessage = async (message: OmitPartialGroupDMChannel<Message>, client: Cl
|
||||
|
||||
export const setupBotMode = (client: Client) => {
|
||||
client.on("clientReady", (client) => { void onReady(client); });
|
||||
client.on("messageCreate", (message) => { void onMessage(message, client); });
|
||||
client.on("messageCreate", (message) => { void onMessage(message); });
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChannelType, EmbedBuilder, type Client, type Message, type MessageCreateOptions } from "discord.js";
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ComponentType, EmbedBuilder, StringSelectMenuBuilder, type Client, type MessageCreateOptions } from "discord.js";
|
||||
import { createLogger } from "@lbf-bot/utils";
|
||||
import { env } from "~/env";
|
||||
import { checkForNewQuest, type QuestResult } from "~/wov";
|
||||
@@ -6,8 +6,6 @@ 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);
|
||||
@@ -34,7 +32,7 @@ export const makeResultEmbed = async (result: QuestResult, exclude: Array<string
|
||||
|
||||
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 !`)
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -49,9 +47,8 @@ export const makeResultEmbed = async (result: QuestResult, exclude: Array<string
|
||||
new EmbedBuilder()
|
||||
.setTitle("Classement")
|
||||
.setColor(color)
|
||||
.setDescription(
|
||||
participants
|
||||
.filter((x) => !exclude.includes(x.username))
|
||||
.setDescription(participants
|
||||
.filter((x) => !exclude.includes(x.username) && x.xp > 0)
|
||||
.filter((_, i) => i < 8)
|
||||
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
|
||||
.join("\n"),
|
||||
@@ -68,16 +65,42 @@ export const askForGrinders = async (quest: QuestResult, client: Client) => {
|
||||
|
||||
let exclude: string[] = [];
|
||||
if (env.QUEST_REWARDS) {
|
||||
const color = parseInt(quest.quest.promoImagePrimaryColor.substring(1), 16);
|
||||
|
||||
const top10 = quest.participants
|
||||
.filter((x) => !env.QUEST_EXCLUDE.includes(x.username))
|
||||
.filter((x) => !env.QUEST_EXCLUDE.includes(x.username) && x.xp > 0)
|
||||
.sort((a, b) => b.xp - a.xp)
|
||||
.slice(0, 10)
|
||||
.slice(0, 10);
|
||||
|
||||
if (!top10.length) {
|
||||
logger.error("No eligible participants for grinder selection");
|
||||
return;
|
||||
}
|
||||
|
||||
const selectOptions = top10
|
||||
.map((p) => ({ label: `${p.username} (${p.xp}xp)`, value: p.username }));
|
||||
|
||||
const row1 = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId("grinders")
|
||||
.setPlaceholder("Sélectionne les tricheurs")
|
||||
.setMinValues(0)
|
||||
.setMaxValues(selectOptions.length)
|
||||
.addOptions(selectOptions),
|
||||
);
|
||||
|
||||
const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId("validate")
|
||||
.setLabel("Valider")
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
);
|
||||
|
||||
const top10Text = top10
|
||||
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
|
||||
.join("\n");
|
||||
|
||||
const color = parseInt(quest.quest.promoImagePrimaryColor.substring(1), 16);
|
||||
|
||||
await adminChannel.send({
|
||||
const msg = await adminChannel.send({
|
||||
content: `-# ||${env.DISCORD_ADMIN_MENTION}||`,
|
||||
embeds: [
|
||||
new EmbedBuilder()
|
||||
@@ -85,64 +108,32 @@ export const askForGrinders = async (quest: QuestResult, client: Client) => {
|
||||
.setColor(color),
|
||||
new EmbedBuilder()
|
||||
.setTitle("Top 10 XP")
|
||||
.setDescription(top10)
|
||||
.setDescription(top10Text)
|
||||
.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`")
|
||||
.setDescription("Sélectionne les joueurs qui ont grind (tricheurs en gros), puis clique sur **Valider**.")
|
||||
.setColor(color),
|
||||
],
|
||||
components: [row1, row2],
|
||||
});
|
||||
|
||||
const filter = (msg: Message) =>
|
||||
msg.channel.id === adminChannel.id &&
|
||||
!msg.author.bot &&
|
||||
msg.content.startsWith(`<@${client.user!.id}>`);
|
||||
let grinders: string[] = [];
|
||||
|
||||
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;
|
||||
let done = false;
|
||||
while (!done) {
|
||||
const interaction = await msg.awaitMessageComponent({ filter: (i) => !i.user.bot });
|
||||
|
||||
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 (interaction.componentType === ComponentType.StringSelect) {
|
||||
grinders = interaction.values;
|
||||
await interaction.deferUpdate();
|
||||
} else if (interaction.componentType === ComponentType.Button && interaction.customId === "validate") {
|
||||
await interaction.update({ components: [] });
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (answer === null) {
|
||||
return logger.fatal("Answer was 'null', this should be unreachable");
|
||||
}
|
||||
|
||||
exclude = answer.split(",").map((x) => x.trim()).filter(Boolean);
|
||||
exclude = grinders;
|
||||
}
|
||||
|
||||
const embed = await makeResultEmbed(quest, [...env.QUEST_EXCLUDE, ...exclude]);
|
||||
|
||||
Reference in New Issue
Block a user