Compare commits
2 Commits
dafa81e2a6
...
832863c1e4
| Author | SHA1 | Date | |
|---|---|---|---|
| 832863c1e4 | |||
| 7c49e05aec |
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -1,40 +0,0 @@
|
||||
require("dotenv").config();
|
||||
|
||||
const { REST, Routes } = require("discord.js");
|
||||
|
||||
const rest = new REST({ version: "10" })
|
||||
.setToken(process.env.D_TOKEN);
|
||||
|
||||
async function clearCommands() {
|
||||
try {
|
||||
if (!process.env.D_TOKEN)
|
||||
throw new Error("Missing D_TOKEN environment variable.");
|
||||
|
||||
if (!process.env.D_ID)
|
||||
throw new Error("Missing D_ID environment variable.");
|
||||
|
||||
if (!process.env.D_GID)
|
||||
throw new Error("Missing G_GID environment variable.");
|
||||
|
||||
console.log("Clearing guild application commands...");
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(
|
||||
process.env.D_ID,
|
||||
process.env.D_GID
|
||||
),
|
||||
{
|
||||
body: []
|
||||
}
|
||||
);
|
||||
|
||||
console.log("Successfully cleared all guild application commands.");
|
||||
} catch (error) {
|
||||
console.error("Failed to clear application commands:");
|
||||
console.error(error);
|
||||
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
clearCommands();
|
||||
@@ -3,7 +3,8 @@ const {
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ActionRowBuilder
|
||||
ActionRowBuilder,
|
||||
MessageFlags
|
||||
} = require("discord.js");
|
||||
|
||||
const { ObjectId } = require("mongodb");
|
||||
@@ -20,6 +21,7 @@ const RAID_TYPES = [
|
||||
"AllianceRaid"
|
||||
];
|
||||
|
||||
|
||||
const RAID_DIFFICULTIES = [
|
||||
"Extreme",
|
||||
"Unreal",
|
||||
@@ -42,7 +44,9 @@ module.exports = {
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName("duty")
|
||||
.setDescription("Select the duty you want to run.")
|
||||
.setDescription(
|
||||
"Select the duty you want to run."
|
||||
)
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
@@ -50,7 +54,9 @@ module.exports = {
|
||||
|
||||
|
||||
async execute(interaction) {
|
||||
switch (interaction.options.getSubcommand()) {
|
||||
switch (
|
||||
interaction.options.getSubcommand()
|
||||
) {
|
||||
case "plan":
|
||||
return planRaid(interaction);
|
||||
}
|
||||
@@ -63,6 +69,10 @@ module.exports = {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* AUTOCOMPLETE
|
||||
*/
|
||||
|
||||
async function autocompleteDuty(interaction) {
|
||||
const focused =
|
||||
interaction.options.getFocused();
|
||||
@@ -109,7 +119,21 @@ async function autocompleteDuty(interaction) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* /raid plan
|
||||
*/
|
||||
|
||||
async function planRaid(interaction) {
|
||||
if (!interaction.guild) {
|
||||
return interaction.reply({
|
||||
content:
|
||||
"Raid planning can only be used inside a server.",
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const dutyId =
|
||||
interaction.options.getString(
|
||||
"duty",
|
||||
@@ -121,7 +145,8 @@ async function planRaid(interaction) {
|
||||
return interaction.reply({
|
||||
content:
|
||||
"Please select a duty from the autocomplete results.",
|
||||
ephemeral: true
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,8 +156,10 @@ async function planRaid(interaction) {
|
||||
.collection(DUTY_COLLECTION);
|
||||
|
||||
|
||||
const duty = await collection.findOne({
|
||||
_id: new ObjectId(dutyId),
|
||||
const duty =
|
||||
await collection.findOne({
|
||||
_id:
|
||||
new ObjectId(dutyId),
|
||||
|
||||
Type: {
|
||||
$in: RAID_TYPES
|
||||
@@ -148,25 +175,26 @@ async function planRaid(interaction) {
|
||||
return interaction.reply({
|
||||
content:
|
||||
"That duty could not be found or is not eligible for raid planning.",
|
||||
ephemeral: true
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The duty ObjectId is embedded in the modal customId.
|
||||
*
|
||||
* The modal handler can then retrieve the selected duty
|
||||
* without trusting editable modal input.
|
||||
*/
|
||||
const modal =
|
||||
buildRaidModal(duty);
|
||||
|
||||
|
||||
return interaction.showModal(modal);
|
||||
return interaction.showModal(
|
||||
modal
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MODAL
|
||||
*/
|
||||
|
||||
function buildRaidModal(duty) {
|
||||
const modal =
|
||||
new ModalBuilder()
|
||||
@@ -209,29 +237,16 @@ function buildRaidModal(duty) {
|
||||
.setRequired(true);
|
||||
|
||||
|
||||
/*
|
||||
* Display only.
|
||||
*
|
||||
* We don't use this field to identify the duty.
|
||||
* The ObjectId in the modal customId is authoritative.
|
||||
*/
|
||||
const dutyInput =
|
||||
new TextInputBuilder()
|
||||
.setCustomId("raid-duty")
|
||||
.setLabel("Duty")
|
||||
.setValue(duty.Name)
|
||||
.setStyle(
|
||||
TextInputStyle.Short
|
||||
)
|
||||
.setRequired(true);
|
||||
|
||||
|
||||
const dateInput =
|
||||
new TextInputBuilder()
|
||||
.setCustomId("raid-date")
|
||||
.setLabel("Date")
|
||||
.setPlaceholder("DD-MM-YYYY")
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setPlaceholder(
|
||||
"DD-MM-YYYY"
|
||||
)
|
||||
.setStyle(
|
||||
TextInputStyle.Short
|
||||
)
|
||||
.setMinLength(10)
|
||||
.setMaxLength(10)
|
||||
.setRequired(true);
|
||||
@@ -240,29 +255,39 @@ function buildRaidModal(duty) {
|
||||
const timeInput =
|
||||
new TextInputBuilder()
|
||||
.setCustomId("raid-time")
|
||||
.setLabel("Start - End (24h clock)")
|
||||
.setPlaceholder("20:00 - 23:00")
|
||||
.setStyle(TextInputStyle.Short)
|
||||
.setMinLength(13)
|
||||
.setMaxLength(13)
|
||||
.setLabel(
|
||||
"Start - End (24h clock)"
|
||||
)
|
||||
.setPlaceholder(
|
||||
"20:00 - 23:00"
|
||||
)
|
||||
.setStyle(
|
||||
TextInputStyle.Short
|
||||
)
|
||||
.setRequired(true);
|
||||
|
||||
|
||||
modal.addComponents(
|
||||
new ActionRowBuilder()
|
||||
.addComponents(nameInput),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(descriptionInput),
|
||||
.addComponents(
|
||||
nameInput
|
||||
),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(dutyInput),
|
||||
.addComponents(
|
||||
descriptionInput
|
||||
),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(dateInput),
|
||||
.addComponents(
|
||||
dateInput
|
||||
),
|
||||
|
||||
new ActionRowBuilder()
|
||||
.addComponents(timeInput)
|
||||
.addComponents(
|
||||
timeInput
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -270,6 +295,10 @@ function buildRaidModal(duty) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HELPERS
|
||||
*/
|
||||
|
||||
function buildAutocompleteName(duty) {
|
||||
const label =
|
||||
`${duty.Name} — ${formatDutyType(duty.Type)} / ${duty.Difficulty}`;
|
||||
|
||||
@@ -221,12 +221,16 @@ async function selectMenuHandler(interaction) {
|
||||
|
||||
|
||||
async function modalHandler(interaction) {
|
||||
const baseCustomId =
|
||||
interaction.customId.split(":")[0];
|
||||
const customId =
|
||||
interaction.customId
|
||||
.split(":")[0];
|
||||
|
||||
|
||||
const modal =
|
||||
interaction.client.modals.get(
|
||||
baseCustomId
|
||||
interaction.client
|
||||
.modals
|
||||
.get(
|
||||
customId
|
||||
);
|
||||
|
||||
|
||||
@@ -245,6 +249,7 @@ async function modalHandler(interaction) {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
|
||||
if (
|
||||
interaction.replied ||
|
||||
interaction.deferred
|
||||
@@ -252,6 +257,7 @@ async function modalHandler(interaction) {
|
||||
await interaction.followUp({
|
||||
content:
|
||||
"There was an error while executing this modal!",
|
||||
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
@@ -260,6 +266,7 @@ async function modalHandler(interaction) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
"There was an error while executing this modal!",
|
||||
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
const {
|
||||
Events
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
handleRaidReaction
|
||||
} = require(
|
||||
"../utils/raidPlanner"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
Events.MessageReactionAdd,
|
||||
|
||||
once:
|
||||
false,
|
||||
|
||||
|
||||
async execute(
|
||||
reaction,
|
||||
user
|
||||
) {
|
||||
try {
|
||||
await handleRaidReaction(
|
||||
reaction,
|
||||
user,
|
||||
true
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Raid planner reaction add error:",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
const {
|
||||
Events
|
||||
} = require("discord.js");
|
||||
|
||||
const {
|
||||
handleRaidReaction
|
||||
} = require(
|
||||
"../utils/raidPlanner"
|
||||
);
|
||||
|
||||
|
||||
module.exports = {
|
||||
name:
|
||||
Events.MessageReactionRemove,
|
||||
|
||||
once:
|
||||
false,
|
||||
|
||||
|
||||
async execute(
|
||||
reaction,
|
||||
user
|
||||
) {
|
||||
try {
|
||||
await handleRaidReaction(
|
||||
reaction,
|
||||
user,
|
||||
false
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Raid planner reaction remove error:",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -15,8 +15,15 @@ const client = new Client({
|
||||
GatewayIntentBits.GuildVoiceStates,
|
||||
GatewayIntentBits.DirectMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
|
||||
],
|
||||
partials: [Partials.Channel, Partials.Message, Partials.User, Partials.GuildMember, Partials.Reaction]
|
||||
partials: [
|
||||
Partials.Channel,
|
||||
Partials.Message,
|
||||
Partials.User,
|
||||
Partials.GuildMember,
|
||||
Partials.Reaction
|
||||
]
|
||||
});
|
||||
exports.client = client
|
||||
|
||||
|
||||
+285
-211
@@ -8,42 +8,60 @@ const {
|
||||
} = require("discord.js");
|
||||
|
||||
const { ObjectId } = require("mongodb");
|
||||
const { randomUUID } = require("node:crypto");
|
||||
const path = require("node:path");
|
||||
|
||||
const { mClient } = require("..");
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CONFIG
|
||||
*/
|
||||
|
||||
const DATABASE = "neo-db";
|
||||
const DUTY_COLLECTION = "items_duties";
|
||||
|
||||
const DUTY_COLLECTION =
|
||||
"items_duties";
|
||||
|
||||
const EVENT_COLLECTION =
|
||||
"items_events";
|
||||
|
||||
|
||||
const RAID_PLANNER_CHANNEL_ID =
|
||||
process.env.RAID_PLANNER_CHANNEL_ID;
|
||||
|
||||
|
||||
const RAID_TIMEZONE =
|
||||
process.env.RAID_TIMEZONE || "Europe/Berlin";
|
||||
process.env.RAID_TIMEZONE ||
|
||||
"Europe/Berlin";
|
||||
|
||||
|
||||
/*
|
||||
* LOCAL DUTY ASSETS
|
||||
* LOCAL ASSETS
|
||||
*/
|
||||
|
||||
const dutyIconDirectory = path.resolve(
|
||||
const dutyIconDirectory =
|
||||
path.resolve(
|
||||
__dirname,
|
||||
"../assets/duties"
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
const DUTY_ICONS = {
|
||||
Guildhest: "Guildhest.png",
|
||||
Dungeon: "Dungeon.png",
|
||||
Trial: "Trial.png",
|
||||
Raid: "Raid.png",
|
||||
AllianceRaid: "Raid.png",
|
||||
Guildhest:
|
||||
"Guildhest.png",
|
||||
|
||||
Dungeon:
|
||||
"Dungeon.png",
|
||||
|
||||
Trial:
|
||||
"Trial.png",
|
||||
|
||||
Raid:
|
||||
"Raid.png",
|
||||
|
||||
AllianceRaid:
|
||||
"Raid.png",
|
||||
|
||||
VariantDungeon:
|
||||
"Variant_and_criterion_dungeons_icon1.png",
|
||||
@@ -54,61 +72,72 @@ const DUTY_ICONS = {
|
||||
|
||||
|
||||
const DUTY_DIFFICULTY_ICONS = {
|
||||
Ultimate: "Ultimate_Raid.png",
|
||||
Chaotic: "Chaotic_Alliance_Raid.png"
|
||||
Ultimate:
|
||||
"Ultimate_Raid.png",
|
||||
|
||||
Chaotic:
|
||||
"Chaotic_Alliance_Raid.png"
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* RAID SIGNUP EMOJIS
|
||||
* ROLE EMOJIS
|
||||
*/
|
||||
|
||||
const RAID_EMOJIS = {
|
||||
Tank: {
|
||||
id: "1537931883369726042",
|
||||
id:
|
||||
"1537931883369726042",
|
||||
|
||||
display:
|
||||
"<:TankRole:1537931883369726042>"
|
||||
},
|
||||
|
||||
Healer: {
|
||||
id: "1537931880756805704",
|
||||
id:
|
||||
"1537931880756805704",
|
||||
|
||||
display:
|
||||
"<:HealerRole:1537931880756805704>"
|
||||
},
|
||||
|
||||
DPS: {
|
||||
id: "1537931878668050442",
|
||||
id:
|
||||
"1537931878668050442",
|
||||
|
||||
display:
|
||||
"<:DPSRole:1537931878668050442>"
|
||||
},
|
||||
|
||||
Flex: {
|
||||
id:
|
||||
"1537948665606574210",
|
||||
|
||||
display:
|
||||
"<:AllRounderRole:1537948665606574210>"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* MODAL EXPORT
|
||||
*
|
||||
* Your modal handler should use the base customId:
|
||||
* raidPlan:<dutyObjectId>
|
||||
*
|
||||
* and resolve "raidPlan".
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
customId: "raidPlan",
|
||||
|
||||
async execute(interaction) {
|
||||
return handleRaidPlan(interaction);
|
||||
return handleRaidPlan(
|
||||
interaction
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* MAIN HANDLER
|
||||
* MAIN MODAL HANDLER
|
||||
*/
|
||||
|
||||
async function handleRaidPlan(interaction) {
|
||||
await interaction.deferReply({
|
||||
flags: MessageFlags.Ephemeral
|
||||
flags:
|
||||
MessageFlags.Ephemeral
|
||||
});
|
||||
|
||||
|
||||
@@ -129,14 +158,15 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* customId:
|
||||
*
|
||||
* raidPlan:<ObjectId>
|
||||
* raidPlan:<dutyId>
|
||||
*/
|
||||
|
||||
const [
|
||||
prefix,
|
||||
dutyId
|
||||
] = interaction.customId.split(":");
|
||||
] =
|
||||
interaction.customId
|
||||
.split(":");
|
||||
|
||||
|
||||
if (
|
||||
@@ -151,17 +181,23 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* Fetch selected duty.
|
||||
* LOAD DUTY
|
||||
*/
|
||||
|
||||
const collection = mClient
|
||||
.db(DATABASE)
|
||||
.collection(DUTY_COLLECTION);
|
||||
const db =
|
||||
mClient.db(DATABASE);
|
||||
|
||||
|
||||
const duty =
|
||||
await collection.findOne({
|
||||
_id: new ObjectId(dutyId)
|
||||
await db
|
||||
.collection(
|
||||
DUTY_COLLECTION
|
||||
)
|
||||
.findOne({
|
||||
_id:
|
||||
new ObjectId(
|
||||
dutyId
|
||||
)
|
||||
});
|
||||
|
||||
|
||||
@@ -174,7 +210,7 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* Read modal fields.
|
||||
* MODAL VALUES
|
||||
*/
|
||||
|
||||
const name =
|
||||
@@ -193,14 +229,6 @@ async function handleRaidPlan(interaction) {
|
||||
.trim();
|
||||
|
||||
|
||||
const enteredDuty =
|
||||
interaction.fields
|
||||
.getTextInputValue(
|
||||
"raid-duty"
|
||||
)
|
||||
.trim();
|
||||
|
||||
|
||||
const date =
|
||||
interaction.fields
|
||||
.getTextInputValue(
|
||||
@@ -218,30 +246,10 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* Duty field is display-only in practice.
|
||||
* Verify user didn't modify it.
|
||||
* DATE
|
||||
*/
|
||||
|
||||
if (
|
||||
enteredDuty !==
|
||||
duty.Name
|
||||
) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"The Duty field was changed. Please create the raid again."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Validate date.
|
||||
*/
|
||||
|
||||
if (
|
||||
!isValidDateString(
|
||||
date
|
||||
)
|
||||
) {
|
||||
if (!isValidDateString(date)) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"Invalid date. Use `DD-MM-YYYY`, for example `22-08-2026`."
|
||||
@@ -250,7 +258,7 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* Validate time range.
|
||||
* TIME
|
||||
*/
|
||||
|
||||
const parsedTimeRange =
|
||||
@@ -262,7 +270,7 @@ async function handleRaidPlan(interaction) {
|
||||
if (!parsedTimeRange) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"Invalid time. Use the 24-hour format `HH:mm - HH:mm`, for example `20:00 - 23:00`."
|
||||
"Invalid time. Use `HH:mm - HH:mm` using the 24-hour clock, for example `20:00 - 23:00`."
|
||||
});
|
||||
}
|
||||
|
||||
@@ -270,11 +278,12 @@ async function handleRaidPlan(interaction) {
|
||||
const {
|
||||
startTime,
|
||||
endTime
|
||||
} = parsedTimeRange;
|
||||
} =
|
||||
parsedTimeRange;
|
||||
|
||||
|
||||
/*
|
||||
* Convert configured timezone to JS Date.
|
||||
* DATES
|
||||
*/
|
||||
|
||||
const startDate =
|
||||
@@ -305,16 +314,13 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* If the end time is before/equal to the start,
|
||||
* assume the raid crosses midnight.
|
||||
* CROSSING MIDNIGHT
|
||||
*
|
||||
* Example:
|
||||
* 23:00 - 02:00
|
||||
*/
|
||||
|
||||
if (
|
||||
endDate <=
|
||||
startDate
|
||||
endDate <= startDate
|
||||
) {
|
||||
endDate =
|
||||
new Date(
|
||||
@@ -324,23 +330,19 @@ async function handleRaidPlan(interaction) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start must be in the future.
|
||||
*/
|
||||
|
||||
if (
|
||||
startDate.getTime() <=
|
||||
Date.now()
|
||||
) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"The raid must be scheduled for a future date and time."
|
||||
"The raid must start in the future."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Fetch planner channel.
|
||||
* PLANNER CHANNEL
|
||||
*/
|
||||
|
||||
const channel =
|
||||
@@ -359,13 +361,13 @@ async function handleRaidPlan(interaction) {
|
||||
) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"The configured raid planner channel could not be found or is not a text channel."
|
||||
"The configured raid planner channel could not be found."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Bot member.
|
||||
* PERMISSIONS
|
||||
*/
|
||||
|
||||
const me =
|
||||
@@ -380,13 +382,10 @@ async function handleRaidPlan(interaction) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Event permission.
|
||||
*/
|
||||
|
||||
if (
|
||||
!me.permissions.has(
|
||||
PermissionFlagsBits.CreateEvents
|
||||
PermissionFlagsBits
|
||||
.CreateEvents
|
||||
)
|
||||
) {
|
||||
return interaction.editReply({
|
||||
@@ -396,10 +395,6 @@ async function handleRaidPlan(interaction) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Destination channel permissions.
|
||||
*/
|
||||
|
||||
const channelPermissions =
|
||||
channel.permissionsFor(me);
|
||||
|
||||
@@ -407,29 +402,65 @@ async function handleRaidPlan(interaction) {
|
||||
if (
|
||||
!channelPermissions ||
|
||||
!channelPermissions.has([
|
||||
PermissionFlagsBits.ViewChannel,
|
||||
PermissionFlagsBits.SendMessages,
|
||||
PermissionFlagsBits.EmbedLinks,
|
||||
PermissionFlagsBits.AddReactions
|
||||
PermissionFlagsBits
|
||||
.ViewChannel,
|
||||
|
||||
PermissionFlagsBits
|
||||
.SendMessages,
|
||||
|
||||
PermissionFlagsBits
|
||||
.EmbedLinks,
|
||||
|
||||
PermissionFlagsBits
|
||||
.AttachFiles,
|
||||
|
||||
PermissionFlagsBits
|
||||
.AddReactions
|
||||
])
|
||||
) {
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"I need **View Channel**, **Send Messages**, **Embed Links**, and **Add Reactions** in the raid planner channel."
|
||||
"I am missing one or more permissions in the raid planner channel."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create scheduled event.
|
||||
* UNIQUE RAID IDENTIFIER
|
||||
*/
|
||||
|
||||
let event;
|
||||
const raidId =
|
||||
randomUUID();
|
||||
|
||||
|
||||
const createdAt =
|
||||
getUnixTime();
|
||||
|
||||
|
||||
const startAt =
|
||||
Math.floor(
|
||||
startDate.getTime() /
|
||||
1000
|
||||
);
|
||||
|
||||
|
||||
const endAt =
|
||||
Math.floor(
|
||||
endDate.getTime() /
|
||||
1000
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* CREATE SCHEDULED EVENT
|
||||
*/
|
||||
|
||||
let scheduledEvent;
|
||||
|
||||
|
||||
try {
|
||||
event =
|
||||
await createRaidEvent(
|
||||
scheduledEvent =
|
||||
await createScheduledEvent(
|
||||
interaction,
|
||||
{
|
||||
name,
|
||||
@@ -442,20 +473,20 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Raid event creation failed:",
|
||||
"Scheduled raid event creation failed:",
|
||||
error
|
||||
);
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"I couldn't create the scheduled event."
|
||||
"I couldn't create the Discord scheduled event."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create planner embed/message.
|
||||
* CREATE PLANNER POST
|
||||
*/
|
||||
|
||||
let message;
|
||||
@@ -467,13 +498,14 @@ async function handleRaidPlan(interaction) {
|
||||
interaction,
|
||||
channel,
|
||||
{
|
||||
raidId,
|
||||
name,
|
||||
description,
|
||||
duty,
|
||||
startDate,
|
||||
endDate
|
||||
},
|
||||
event
|
||||
scheduledEvent
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
@@ -483,17 +515,13 @@ async function handleRaidPlan(interaction) {
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Remove event if planner post fails.
|
||||
*/
|
||||
try {
|
||||
await event.delete(
|
||||
"Raid planner message failed"
|
||||
await scheduledEvent.delete(
|
||||
"Raid planner post failed"
|
||||
);
|
||||
|
||||
} catch (cleanupError) {
|
||||
console.error(
|
||||
"Raid event cleanup failed:",
|
||||
"Failed to remove scheduled event:",
|
||||
cleanupError
|
||||
);
|
||||
}
|
||||
@@ -507,9 +535,78 @@ async function handleRaidPlan(interaction) {
|
||||
|
||||
|
||||
/*
|
||||
* Confirmation to raid creator.
|
||||
* STORE EVENT IN MONGO
|
||||
*/
|
||||
|
||||
try {
|
||||
await db
|
||||
.collection(
|
||||
EVENT_COLLECTION
|
||||
)
|
||||
.insertOne({
|
||||
raidId,
|
||||
|
||||
eventType:
|
||||
"Raid",
|
||||
|
||||
guildId:
|
||||
interaction.guildId,
|
||||
|
||||
channelId:
|
||||
channel.id,
|
||||
|
||||
messageId:
|
||||
message.id,
|
||||
|
||||
scheduledEventId:
|
||||
scheduledEvent.id,
|
||||
|
||||
dutyId:
|
||||
duty._id,
|
||||
|
||||
creatorId:
|
||||
interaction.user.id,
|
||||
|
||||
createdAt,
|
||||
|
||||
startAt,
|
||||
|
||||
endAt,
|
||||
|
||||
signups: {
|
||||
Tank: [],
|
||||
Healer: [],
|
||||
DPS: [],
|
||||
Flex: []
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to persist raid event:",
|
||||
error
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
await message.delete();
|
||||
} catch {}
|
||||
|
||||
|
||||
try {
|
||||
await scheduledEvent.delete(
|
||||
"Raid database insert failed"
|
||||
);
|
||||
} catch {}
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
"I couldn't save the raid planner."
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return interaction.editReply({
|
||||
content:
|
||||
`Raid created successfully!\n${message.url}`
|
||||
@@ -521,7 +618,7 @@ async function handleRaidPlan(interaction) {
|
||||
* CREATE DISCORD SCHEDULED EVENT
|
||||
*/
|
||||
|
||||
async function createRaidEvent(
|
||||
async function createScheduledEvent(
|
||||
interaction,
|
||||
data
|
||||
) {
|
||||
@@ -533,10 +630,7 @@ async function createRaidEvent(
|
||||
`Organized by: ${interaction.user.displayName}`
|
||||
]
|
||||
.join("\n")
|
||||
.slice(
|
||||
0,
|
||||
1000
|
||||
);
|
||||
.slice(0, 1000);
|
||||
|
||||
|
||||
return interaction.guild
|
||||
@@ -582,33 +676,31 @@ async function createRaidEvent(
|
||||
|
||||
|
||||
/*
|
||||
* CREATE RAID PLANNER MESSAGE
|
||||
* CREATE PLANNER EMBED
|
||||
*/
|
||||
|
||||
async function createRaidMessage(
|
||||
interaction,
|
||||
channel,
|
||||
data,
|
||||
event
|
||||
scheduledEvent
|
||||
) {
|
||||
const startUnix =
|
||||
Math.floor(
|
||||
data.startDate
|
||||
.getTime() /
|
||||
data.startDate.getTime() /
|
||||
1000
|
||||
);
|
||||
|
||||
|
||||
const endUnix =
|
||||
Math.floor(
|
||||
data.endDate
|
||||
.getTime() /
|
||||
data.endDate.getTime() /
|
||||
1000
|
||||
);
|
||||
|
||||
|
||||
const eventUrl =
|
||||
`https://discord.com/events/${interaction.guildId}/${event.id}`;
|
||||
`https://discord.com/events/${interaction.guildId}/${scheduledEvent.id}`;
|
||||
|
||||
|
||||
const icon =
|
||||
@@ -693,12 +785,19 @@ async function createRaidMessage(
|
||||
|
||||
{
|
||||
name:
|
||||
"Sign Up",
|
||||
"Party",
|
||||
|
||||
value:
|
||||
`${RAID_EMOJIS.Tank.display} Tank\n` +
|
||||
`${RAID_EMOJIS.Healer.display} Healer\n` +
|
||||
`${RAID_EMOJIS.DPS.display} DPS`,
|
||||
buildPartyField(
|
||||
data.duty,
|
||||
{
|
||||
Tank: 0,
|
||||
Healer: 0,
|
||||
DPS: 0,
|
||||
Flex: 0
|
||||
},
|
||||
0
|
||||
),
|
||||
|
||||
inline:
|
||||
false
|
||||
@@ -707,15 +806,9 @@ async function createRaidMessage(
|
||||
|
||||
.setFooter({
|
||||
text:
|
||||
`Created by ${interaction.user.displayName}`
|
||||
})
|
||||
`raidPlanner:${data.raidId}`
|
||||
});
|
||||
|
||||
.setTimestamp();
|
||||
|
||||
|
||||
/*
|
||||
* Local duty icon attachment.
|
||||
*/
|
||||
|
||||
const files = [];
|
||||
|
||||
@@ -742,22 +835,16 @@ async function createRaidMessage(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send only into raid planner channel.
|
||||
*/
|
||||
|
||||
const message =
|
||||
await channel.send({
|
||||
embeds: [
|
||||
embed
|
||||
],
|
||||
|
||||
files
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Signup reactions.
|
||||
* Initial signup reactions.
|
||||
*/
|
||||
|
||||
await message.react(
|
||||
@@ -775,12 +862,36 @@ async function createRaidMessage(
|
||||
);
|
||||
|
||||
|
||||
await message.react(
|
||||
RAID_EMOJIS.Flex.id
|
||||
);
|
||||
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GET DUTY ICON
|
||||
* PARTY DISPLAY
|
||||
*/
|
||||
|
||||
function buildPartyField(
|
||||
duty,
|
||||
counts,
|
||||
uniqueParticipants
|
||||
) {
|
||||
return (
|
||||
`${RAID_EMOJIS.Tank.display} Tank **(${counts.Tank}/${duty.Party.Tanks})**\n` +
|
||||
`${RAID_EMOJIS.Healer.display} Healer **(${counts.Healer}/${duty.Party.Healers})**\n` +
|
||||
`${RAID_EMOJIS.DPS.display} DPS **(${counts.DPS}/${duty.Party.DPS})**\n` +
|
||||
`${RAID_EMOJIS.Flex.display} Flex **(${counts.Flex})**\n\n` +
|
||||
`**Participants: ${uniqueParticipants}/${duty.Party.Size}**`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DUTY ICON
|
||||
*/
|
||||
|
||||
function getDutyIconFile(duty) {
|
||||
@@ -811,7 +922,7 @@ function getDutyIconFile(duty) {
|
||||
|
||||
|
||||
/*
|
||||
* FORMAT DUTY TYPE
|
||||
* DUTY TYPE
|
||||
*/
|
||||
|
||||
function formatDutyType(type) {
|
||||
@@ -829,7 +940,7 @@ function formatDutyType(type) {
|
||||
|
||||
|
||||
/*
|
||||
* VALIDATE DD-MM-YYYY
|
||||
* DATE VALIDATION
|
||||
*/
|
||||
|
||||
function isValidDateString(value) {
|
||||
@@ -843,21 +954,13 @@ function isValidDateString(value) {
|
||||
|
||||
|
||||
const day =
|
||||
Number(
|
||||
match[1]
|
||||
);
|
||||
|
||||
Number(match[1]);
|
||||
|
||||
const month =
|
||||
Number(
|
||||
match[2]
|
||||
);
|
||||
|
||||
Number(match[2]);
|
||||
|
||||
const year =
|
||||
Number(
|
||||
match[3]
|
||||
);
|
||||
Number(match[3]);
|
||||
|
||||
|
||||
const date =
|
||||
@@ -884,11 +987,7 @@ function isValidDateString(value) {
|
||||
|
||||
|
||||
/*
|
||||
* PARSE:
|
||||
*
|
||||
* 20:00 - 23:00
|
||||
*
|
||||
* 24-hour clock
|
||||
* TIME RANGE
|
||||
*/
|
||||
|
||||
function parseTimeRange(value) {
|
||||
@@ -902,37 +1001,22 @@ function parseTimeRange(value) {
|
||||
|
||||
|
||||
const startHour =
|
||||
Number(
|
||||
match[1]
|
||||
);
|
||||
|
||||
Number(match[1]);
|
||||
|
||||
const startMinute =
|
||||
Number(
|
||||
match[2]
|
||||
);
|
||||
|
||||
Number(match[2]);
|
||||
|
||||
const endHour =
|
||||
Number(
|
||||
match[3]
|
||||
);
|
||||
|
||||
Number(match[3]);
|
||||
|
||||
const endMinute =
|
||||
Number(
|
||||
match[4]
|
||||
);
|
||||
Number(match[4]);
|
||||
|
||||
|
||||
if (
|
||||
startHour < 0 ||
|
||||
startHour > 23 ||
|
||||
startMinute < 0 ||
|
||||
startMinute > 59 ||
|
||||
endHour < 0 ||
|
||||
endHour > 23 ||
|
||||
endMinute < 0 ||
|
||||
endMinute > 59
|
||||
) {
|
||||
return null;
|
||||
@@ -950,8 +1034,7 @@ function parseTimeRange(value) {
|
||||
|
||||
|
||||
/*
|
||||
* CONVERT DD-MM-YYYY + HH:mm FROM
|
||||
* CONFIGURED TIMEZONE INTO JS DATE.
|
||||
* TIMEZONE CONVERSION
|
||||
*/
|
||||
|
||||
function zonedDateTimeToDate(
|
||||
@@ -984,15 +1067,10 @@ function zonedDateTimeToDate(
|
||||
month - 1,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
0
|
||||
minute
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Two passes helps account for DST.
|
||||
*/
|
||||
|
||||
for (
|
||||
let i = 0;
|
||||
i < 2;
|
||||
@@ -1000,9 +1078,7 @@ function zonedDateTimeToDate(
|
||||
) {
|
||||
const offset =
|
||||
getTimeZoneOffset(
|
||||
new Date(
|
||||
timestamp
|
||||
),
|
||||
new Date(timestamp),
|
||||
timeZone
|
||||
);
|
||||
|
||||
@@ -1013,8 +1089,7 @@ function zonedDateTimeToDate(
|
||||
month - 1,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
0
|
||||
minute
|
||||
)
|
||||
-
|
||||
offset;
|
||||
@@ -1022,9 +1097,7 @@ function zonedDateTimeToDate(
|
||||
|
||||
|
||||
const result =
|
||||
new Date(
|
||||
timestamp
|
||||
);
|
||||
new Date(timestamp);
|
||||
|
||||
|
||||
return Number.isNaN(
|
||||
@@ -1035,10 +1108,6 @@ function zonedDateTimeToDate(
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GET UTC OFFSET OF IANA TIMEZONE
|
||||
*/
|
||||
|
||||
function getTimeZoneOffset(
|
||||
date,
|
||||
timeZone
|
||||
@@ -1075,9 +1144,7 @@ function getTimeZoneOffset(
|
||||
|
||||
const parts =
|
||||
formatter
|
||||
.formatToParts(
|
||||
date
|
||||
)
|
||||
.formatToParts(date)
|
||||
.reduce(
|
||||
(
|
||||
result,
|
||||
@@ -1119,3 +1186,10 @@ function getTimeZoneOffset(
|
||||
date.getTime()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function getUnixTime() {
|
||||
return Math.floor(
|
||||
Date.now() / 1000
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
const {
|
||||
EmbedBuilder
|
||||
} = require("discord.js");
|
||||
|
||||
const { mClient } = require("../index");
|
||||
|
||||
|
||||
const DATABASE =
|
||||
"neo-db";
|
||||
|
||||
const DUTY_COLLECTION =
|
||||
"items_duties";
|
||||
|
||||
const EVENT_COLLECTION =
|
||||
"items_events";
|
||||
|
||||
|
||||
/*
|
||||
* RAID SIGNUP EMOJIS
|
||||
*/
|
||||
|
||||
const RAID_EMOJIS = {
|
||||
Tank: {
|
||||
id:
|
||||
"1537931883369726042",
|
||||
|
||||
display:
|
||||
"<:TankRole:1537931883369726042>"
|
||||
},
|
||||
|
||||
Healer: {
|
||||
id:
|
||||
"1537931880756805704",
|
||||
|
||||
display:
|
||||
"<:HealerRole:1537931880756805704>"
|
||||
},
|
||||
|
||||
DPS: {
|
||||
id:
|
||||
"1537931878668050442",
|
||||
|
||||
display:
|
||||
"<:DPSRole:1537931878668050442>"
|
||||
},
|
||||
|
||||
Flex: {
|
||||
id:
|
||||
"1537948665606574210",
|
||||
|
||||
display:
|
||||
"<:AllRounderRole:1537948665606574210>"
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* MAIN REACTION HANDLER
|
||||
*
|
||||
* added = true
|
||||
* MessageReactionAdd
|
||||
*
|
||||
* added = false
|
||||
* MessageReactionRemove
|
||||
*/
|
||||
|
||||
async function handleRaidReaction(
|
||||
reaction,
|
||||
user,
|
||||
added
|
||||
) {
|
||||
|
||||
/*
|
||||
* Ignore bots.
|
||||
*
|
||||
* This prevents ShaiBot's own initial
|
||||
* signup reactions from being counted.
|
||||
*/
|
||||
|
||||
if (user.bot)
|
||||
return;
|
||||
|
||||
|
||||
/*
|
||||
* Resolve partial reactions/messages.
|
||||
*/
|
||||
|
||||
if (reaction.partial) {
|
||||
try {
|
||||
await reaction.fetch();
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to fetch partial reaction:",
|
||||
error
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (reaction.message.partial) {
|
||||
try {
|
||||
await reaction.message.fetch();
|
||||
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to fetch partial raid planner message:",
|
||||
error
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const message =
|
||||
reaction.message;
|
||||
|
||||
|
||||
/*
|
||||
* Raid planner messages always contain
|
||||
* an embed.
|
||||
*/
|
||||
|
||||
if (
|
||||
!message.embeds ||
|
||||
message.embeds.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const embed =
|
||||
message.embeds[0];
|
||||
|
||||
|
||||
/*
|
||||
* Footer format:
|
||||
*
|
||||
* raidPlanner:<raidId>
|
||||
*/
|
||||
|
||||
const footer =
|
||||
embed.footer?.text;
|
||||
|
||||
|
||||
if (
|
||||
!footer ||
|
||||
!footer.startsWith(
|
||||
"raidPlanner:"
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const [
|
||||
prefix,
|
||||
raidId
|
||||
] =
|
||||
footer.split(":");
|
||||
|
||||
|
||||
if (
|
||||
prefix !== "raidPlanner" ||
|
||||
!raidId
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Determine which signup role
|
||||
* this reaction represents.
|
||||
*/
|
||||
|
||||
const role =
|
||||
getRoleFromEmoji(
|
||||
reaction.emoji
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Ignore unrelated reactions.
|
||||
*/
|
||||
|
||||
if (!role)
|
||||
return;
|
||||
|
||||
|
||||
const db =
|
||||
mClient.db(
|
||||
DATABASE
|
||||
);
|
||||
|
||||
|
||||
const events =
|
||||
db.collection(
|
||||
EVENT_COLLECTION
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Make sure event exists.
|
||||
*/
|
||||
|
||||
const raid =
|
||||
await events.findOne({
|
||||
raidId
|
||||
});
|
||||
|
||||
|
||||
if (!raid)
|
||||
return;
|
||||
|
||||
|
||||
/*
|
||||
* REACTION ADDED
|
||||
*
|
||||
* Each role is independent.
|
||||
* Users may select multiple roles.
|
||||
*/
|
||||
|
||||
if (added) {
|
||||
await events.updateOne(
|
||||
{
|
||||
raidId
|
||||
},
|
||||
{
|
||||
$addToSet: {
|
||||
[`signups.${role}`]:
|
||||
user.id
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* REACTION REMOVED
|
||||
*
|
||||
* Remove only this role.
|
||||
*/
|
||||
|
||||
else {
|
||||
await events.updateOne(
|
||||
{
|
||||
raidId
|
||||
},
|
||||
{
|
||||
$pull: {
|
||||
[`signups.${role}`]:
|
||||
user.id
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Rebuild planner from current Mongo state.
|
||||
*/
|
||||
|
||||
await updateRaidPlanner(
|
||||
message,
|
||||
raidId
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UPDATE RAID PLANNER EMBED
|
||||
*/
|
||||
|
||||
async function updateRaidPlanner(
|
||||
message,
|
||||
raidId
|
||||
) {
|
||||
const db =
|
||||
mClient.db(
|
||||
DATABASE
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Fetch raid state.
|
||||
*/
|
||||
|
||||
const raid =
|
||||
await db
|
||||
.collection(
|
||||
EVENT_COLLECTION
|
||||
)
|
||||
.findOne({
|
||||
raidId
|
||||
});
|
||||
|
||||
|
||||
if (!raid)
|
||||
return;
|
||||
|
||||
|
||||
/*
|
||||
* Fetch duty.
|
||||
*/
|
||||
|
||||
const duty =
|
||||
await db
|
||||
.collection(
|
||||
DUTY_COLLECTION
|
||||
)
|
||||
.findOne({
|
||||
_id:
|
||||
raid.dutyId
|
||||
});
|
||||
|
||||
|
||||
if (!duty)
|
||||
return;
|
||||
|
||||
|
||||
/*
|
||||
* Role counts.
|
||||
*/
|
||||
|
||||
const counts = {
|
||||
Tank:
|
||||
raid.signups?.Tank
|
||||
?.length
|
||||
?? 0,
|
||||
|
||||
Healer:
|
||||
raid.signups?.Healer
|
||||
?.length
|
||||
?? 0,
|
||||
|
||||
DPS:
|
||||
raid.signups?.DPS
|
||||
?.length
|
||||
?? 0,
|
||||
|
||||
Flex:
|
||||
raid.signups?.Flex
|
||||
?.length
|
||||
?? 0
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Count UNIQUE participants.
|
||||
*
|
||||
* A user who reacts Tank + DPS + Flex
|
||||
* still counts as one participant.
|
||||
*/
|
||||
|
||||
const uniqueParticipants =
|
||||
new Set([
|
||||
...(
|
||||
raid.signups?.Tank
|
||||
?? []
|
||||
),
|
||||
|
||||
...(
|
||||
raid.signups?.Healer
|
||||
?? []
|
||||
),
|
||||
|
||||
...(
|
||||
raid.signups?.DPS
|
||||
?? []
|
||||
),
|
||||
|
||||
...(
|
||||
raid.signups?.Flex
|
||||
?? []
|
||||
)
|
||||
]).size;
|
||||
|
||||
|
||||
/*
|
||||
* Clone existing embed.
|
||||
*/
|
||||
|
||||
const oldEmbed =
|
||||
message.embeds[0];
|
||||
|
||||
|
||||
const updatedEmbed =
|
||||
EmbedBuilder.from(
|
||||
oldEmbed
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Replace only Party field.
|
||||
*/
|
||||
|
||||
const fields =
|
||||
oldEmbed.fields.map(
|
||||
field => {
|
||||
|
||||
if (
|
||||
field.name !==
|
||||
"Party"
|
||||
) {
|
||||
return {
|
||||
name:
|
||||
field.name,
|
||||
|
||||
value:
|
||||
field.value,
|
||||
|
||||
inline:
|
||||
field.inline
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
name:
|
||||
"Party",
|
||||
|
||||
value:
|
||||
buildPartyField(
|
||||
duty,
|
||||
counts,
|
||||
uniqueParticipants
|
||||
),
|
||||
|
||||
inline:
|
||||
false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
updatedEmbed.setFields(
|
||||
fields
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
* Update message.
|
||||
*/
|
||||
|
||||
await message.edit({
|
||||
embeds: [
|
||||
updatedEmbed
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BUILD PARTY FIELD
|
||||
*/
|
||||
|
||||
function buildPartyField(
|
||||
duty,
|
||||
counts,
|
||||
uniqueParticipants
|
||||
) {
|
||||
return (
|
||||
`${RAID_EMOJIS.Tank.display} Tank **(${counts.Tank}/${duty.Party.Tanks})**\n` +
|
||||
`${RAID_EMOJIS.Healer.display} Healer **(${counts.Healer}/${duty.Party.Healers})**\n` +
|
||||
`${RAID_EMOJIS.DPS.display} DPS **(${counts.DPS}/${duty.Party.DPS})**\n` +
|
||||
`${RAID_EMOJIS.Flex.display} Flex **(${counts.Flex})**\n\n` +
|
||||
`**Participants: ${uniqueParticipants}/${duty.Party.Size}**`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EMOJI -> ROLE
|
||||
*/
|
||||
|
||||
function getRoleFromEmoji(
|
||||
emoji
|
||||
) {
|
||||
|
||||
if (
|
||||
emoji.id ===
|
||||
RAID_EMOJIS.Tank.id
|
||||
) {
|
||||
return "Tank";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
emoji.id ===
|
||||
RAID_EMOJIS.Healer.id
|
||||
) {
|
||||
return "Healer";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
emoji.id ===
|
||||
RAID_EMOJIS.DPS.id
|
||||
) {
|
||||
return "DPS";
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
emoji.id ===
|
||||
RAID_EMOJIS.Flex.id
|
||||
) {
|
||||
return "Flex";
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
handleRaidReaction,
|
||||
updateRaidPlanner
|
||||
};
|
||||
Reference in New Issue
Block a user