522 lines
7.7 KiB
JavaScript
522 lines
7.7 KiB
JavaScript
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
|
|
}; |