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 { z } from "zod";
|
||||||
import { parseEnv, logger } from "@lbf-bot/utils";
|
import { parseEnv } from "@lbf-bot/utils";
|
||||||
|
|
||||||
// TODO: use parseEnv from utils
|
// TODO: use parseEnv from utils
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,12 @@ const onReady = async (client: Client<true>) => {
|
|||||||
setInterval(() => void trackingCron(client), env.WOV_TRACKING_INTERVAL);
|
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.author.bot) return;
|
||||||
|
|
||||||
if (message.content.startsWith(`<@${client.user!.id}>`)) {
|
const parts = message.content.trim().split(/\s+/);
|
||||||
const [commandName, ...args] = message.content
|
if (parts[0]?.toLowerCase() === "lbf") {
|
||||||
.replace(`<@${client.user!.id}>`, "")
|
const [commandName, ...args] = parts.slice(1);
|
||||||
.trim()
|
|
||||||
.split(" ");
|
|
||||||
|
|
||||||
const command = commands[commandName];
|
const command = commands[commandName];
|
||||||
if (!command) return;
|
if (!command) return;
|
||||||
@@ -41,5 +39,5 @@ const onMessage = async (message: OmitPartialGroupDMChannel<Message>, client: Cl
|
|||||||
|
|
||||||
export const setupBotMode = (client: Client) => {
|
export const setupBotMode = (client: Client) => {
|
||||||
client.on("clientReady", (client) => { void onReady(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 { createLogger } from "@lbf-bot/utils";
|
||||||
import { env } from "~/env";
|
import { env } from "~/env";
|
||||||
import { checkForNewQuest, type QuestResult } from "~/wov";
|
import { checkForNewQuest, type QuestResult } from "~/wov";
|
||||||
@@ -6,8 +6,6 @@ import { getAccountBalance, setAccountBalance } from "~/economy";
|
|||||||
|
|
||||||
const logger = createLogger({ prefix: "quests" });
|
const logger = createLogger({ prefix: "quests" });
|
||||||
|
|
||||||
// --- rewards ---
|
|
||||||
|
|
||||||
export const makeResultEmbed = async (result: QuestResult, exclude: Array<string>): Promise<MessageCreateOptions> => {
|
export const makeResultEmbed = async (result: QuestResult, exclude: Array<string>): Promise<MessageCreateOptions> => {
|
||||||
const imageUrl = result.quest.promoImageUrl;
|
const imageUrl = result.quest.promoImageUrl;
|
||||||
const color = parseInt(result.quest.promoImagePrimaryColor.substring(1), 16);
|
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()
|
rewardsEmbed = new EmbedBuilder()
|
||||||
.setTitle("Récompenses")
|
.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);
|
.setColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +47,8 @@ export const makeResultEmbed = async (result: QuestResult, exclude: Array<string
|
|||||||
new EmbedBuilder()
|
new EmbedBuilder()
|
||||||
.setTitle("Classement")
|
.setTitle("Classement")
|
||||||
.setColor(color)
|
.setColor(color)
|
||||||
.setDescription(
|
.setDescription(participants
|
||||||
participants
|
.filter((x) => !exclude.includes(x.username) && x.xp > 0)
|
||||||
.filter((x) => !exclude.includes(x.username))
|
|
||||||
.filter((_, i) => i < 8)
|
.filter((_, i) => i < 8)
|
||||||
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
|
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
@@ -68,16 +65,42 @@ export const askForGrinders = async (quest: QuestResult, client: Client) => {
|
|||||||
|
|
||||||
let exclude: string[] = [];
|
let exclude: string[] = [];
|
||||||
if (env.QUEST_REWARDS) {
|
if (env.QUEST_REWARDS) {
|
||||||
|
const color = parseInt(quest.quest.promoImagePrimaryColor.substring(1), 16);
|
||||||
|
|
||||||
const top10 = quest.participants
|
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)
|
.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`)
|
.map((p, i) => `${i + 1}. ${p.username} - ${p.xp}xp`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
const color = parseInt(quest.quest.promoImagePrimaryColor.substring(1), 16);
|
const msg = await adminChannel.send({
|
||||||
|
|
||||||
await adminChannel.send({
|
|
||||||
content: `-# ||${env.DISCORD_ADMIN_MENTION}||`,
|
content: `-# ||${env.DISCORD_ADMIN_MENTION}||`,
|
||||||
embeds: [
|
embeds: [
|
||||||
new EmbedBuilder()
|
new EmbedBuilder()
|
||||||
@@ -85,64 +108,32 @@ export const askForGrinders = async (quest: QuestResult, client: Client) => {
|
|||||||
.setColor(color),
|
.setColor(color),
|
||||||
new EmbedBuilder()
|
new EmbedBuilder()
|
||||||
.setTitle("Top 10 XP")
|
.setTitle("Top 10 XP")
|
||||||
.setDescription(top10)
|
.setDescription(top10Text)
|
||||||
.setColor(color),
|
.setColor(color),
|
||||||
new EmbedBuilder()
|
new EmbedBuilder()
|
||||||
.setTitle("Qui a grind ?")
|
.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),
|
.setColor(color),
|
||||||
],
|
],
|
||||||
|
components: [row1, row2],
|
||||||
});
|
});
|
||||||
|
|
||||||
const filter = (msg: Message) =>
|
let grinders: string[] = [];
|
||||||
msg.channel.id === adminChannel.id &&
|
|
||||||
!msg.author.bot &&
|
|
||||||
msg.content.startsWith(`<@${client.user!.id}>`);
|
|
||||||
|
|
||||||
let confirmed = false;
|
let done = false;
|
||||||
let answer: string | null = null;
|
while (!done) {
|
||||||
while (!confirmed) {
|
const interaction = await msg.awaitMessageComponent({ filter: (i) => !i.user.bot });
|
||||||
const collected = await adminChannel.awaitMessages({ filter, max: 1 });
|
|
||||||
answer = collected.first()?.content || null;
|
|
||||||
if (!answer) continue;
|
|
||||||
|
|
||||||
answer = answer.replace(`<@${client.user!.id}>`, "").trim();
|
if (interaction.componentType === ComponentType.StringSelect) {
|
||||||
if (answer.toLowerCase() === "tg") {
|
grinders = interaction.values;
|
||||||
answer = "";
|
await interaction.deferUpdate();
|
||||||
break;
|
} else if (interaction.componentType === ComponentType.Button && interaction.customId === "validate") {
|
||||||
}
|
await interaction.update({ components: [] });
|
||||||
|
done = true;
|
||||||
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) {
|
exclude = grinders;
|
||||||
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 embed = await makeResultEmbed(quest, [...env.QUEST_EXCLUDE, ...exclude]);
|
||||||
|
|||||||
Reference in New Issue
Block a user