Skip to content

Commit

Permalink
Implement stability fixes for voting and queueing
Browse files Browse the repository at this point in the history
  • Loading branch information
pattyjogal committed Sep 27, 2021
1 parent 664e5f1 commit 7c7055f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/commands/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export abstract class RegisteredUserExecutable<
if (!user) {
this.interaction.reply({
content:
"Who are you? Copy of me?! You need to register with me before participating in 10mans! Please visit #rules for more info.",
"Who are you? You need to register with me before participating in 10mans! Please visit #rules for more info.",
ephemeral: true,
});

Expand Down
149 changes: 106 additions & 43 deletions src/commands/tenmans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ abstract class BaseQueueAction<
/// Verify that the queue id stored in the constructor is correct.
/// If not, allow child class to throw custom error message and
/// fail gracefully.
abstract verifyQueueId() : string;
abstract verifyQueueId(): string;

/// Perform actions after verifying queue id. Should not modify any
/// messages related to queue state.
abstract updateQueue();
abstract updateQueue(): Promise<boolean>;

/// Perform actions after updating queue state. This includes things
/// like updating related embed messages, etc.
Expand All @@ -57,9 +57,11 @@ abstract class BaseQueueAction<
return;
}

await this.updateQueue();
const shouldRender = await this.updateQueue();

await this.updateUserInterface();
if (shouldRender) {
this.updateUserInterface();
}
}
}

Expand All @@ -86,81 +88,116 @@ abstract class VoteQueueAction<
return "";
}

updateUserInterface () {
updateUserInterface() {
const votesStillNeeded = botConfig.minVoteCount - tenmansQueue.length;

if (!!activeVoteMessage) {
activeVoteMessage.edit({
embeds: [createVoteEmbed(votesStillNeeded, time, voteClosingTime)]
});
}
activeVoteMessage.edit({
embeds: [createVoteEmbed(votesStillNeeded, time, voteClosingTime)],
});
}
}

class JoinQueueButtonAction extends StandardQueueAction<ButtonInteraction> {
updateQueue() {
async updateQueue(): Promise<boolean> {
if (tenmansQueue.some(queueUser => this.user.discordId === queueUser.discordId)) {
this.interaction.reply({
content: "Who are you? Copy of me?! You're already in the queue!",
ephemeral: true,
});

return false;
}

tenmansQueue.push(this.user);
this.interaction.reply({
content: "Greetings! You've been added to the queue.",
ephemeral: true,
});

return true;
}
}

class LeaveQueueButtonAction extends StandardQueueAction<ButtonInteraction> {
updateQueue() {
async updateQueue(): Promise<boolean> {
tenmansQueue = tenmansQueue.filter(
(member) => member.discordId !== this.user.discordId
);
this.interaction.reply({
content: "This is no problem; You've been removed from the queue.",
ephemeral: true,
});

return true;
}
}

class VoteQueueButtonAction extends VoteQueueAction<ButtonInteraction> {
async updateQueue() {
async updateQueue(): Promise<boolean> {
const queueChannel = botConfig.queueMsgChannel as TextChannel;

// Verify that a pingable role exists for 10 mans on this server
const roleId = await this.interaction.guild.roles.fetch()
.then((roles: Collection<String, Role>) => {
for (const role of roles.values()) {
if (role.name === "10 Mans") {
return role.id;
const roleId = await this.interaction.guild.roles
.fetch()
.then((roles: Collection<String, Role>) => {
for (const role of roles.values()) {
if (role.name === "10 Mans") {
return role.id;
}
}
}
})
.catch(console.error);
})
.catch(console.error);

if (!roleId) {
this.interaction.reply({
content: "My camera is destroyed - cannot find a 10 mans role on this server. Message an admin.",
content:
"My camera is destroyed - cannot find a 10 mans role on this server. Message an admin.",
ephemeral: true,
});

return;
return false;
}

if (tenmansQueue.some(queueUser => this.user.discordId === queueUser.discordId)) {
this.interaction.reply({
content: "Who are you? Copy of me?! You've already voted!",
ephemeral: true,
});

return false;
}

tenmansQueue.push(this.user);

this.interaction.reply({
content: "Greetings! Your vote has been counted.",
ephemeral: true,
});

if (tenmansQueue.length >= botConfig.minVoteCount) {
// Generate proper interactable queue once min votes reached
await activeVoteMessage.delete();
voteClosingTime = null;
activeVoteMessage = null;

activeTenmansMessage = await queueChannel.send({
embeds: [createEmbed(time)]
})
embeds: [createEmbed(time)],
components: [createQueueActionRow(this.queueId)],
});

await queueChannel.send({
content: `<@${roleId}> that Radianite must be ours! A queue has been created!`,
});

return false;
}

return true;
}
}

class ManualAddUserToQueue extends StandardQueueAction<CommandInteraction> {
async updateQueue() {
async updateQueue(): Promise<boolean> {
const targetUser = this.interaction.options.getUser("member");
const targetMember = (await this.db.collection("members").findOne({
discordId: targetUser.id,
Expand All @@ -170,11 +207,13 @@ class ManualAddUserToQueue extends StandardQueueAction<CommandInteraction> {
content: `User \`${targetUser.username}\` added to the queue.`,
ephemeral: true,
});

return true;
}
}

class ManualRemoveUserToQueue extends StandardQueueAction<CommandInteraction> {
async updateQueue() {
async updateQueue(): Promise<boolean> {
const targetUser = this.interaction.options.getUser("member");
const targetMember = (await this.db.collection("members").findOne({
discordId: targetUser.id,
Expand All @@ -186,6 +225,8 @@ class ManualRemoveUserToQueue extends StandardQueueAction<CommandInteraction> {
content: `User \`${targetUser.username}\` removed from the queue.`,
ephemeral: true,
});

return true;
}
}

Expand Down Expand Up @@ -250,7 +291,7 @@ class TenmansCloseSubcommand extends MessageExecutable<CommandInteraction> {
}

// Teardown - clear current queue
tenmansQueue = []
tenmansQueue = [];
await activeTenmansMessage?.delete();
}
}
Expand All @@ -260,31 +301,37 @@ class TenmansVoteSubcommand extends RegisteredUserExecutable<CommandInteraction>
// Verify queue not already active
if (activeTenmansMessage) {
this.interaction.reply({
content: "You should have been looking more closely. There's already a queue - use that one instead!",
ephemeral: true
content:
"You should have been looking more closely. There's already a queue - use that one instead!",
ephemeral: true,
});

return;
}

// Verify bot config is valid
const requiredConfigs = ["hoursTillVoteClose", "minVoteCount", "queueMsgChannel"];
for (const setting in requiredConfigs) {
const requiredConfigs = [
"hoursTillVoteClose",
"minVoteCount",
"queueMsgChannel",
];
for (const setting of requiredConfigs) {
if (!botConfig[setting]) {
this.interaction.reply({
content:
`Careful now. ${setting} not configured. Please ask an admin to configure this value.`,
content: `Careful now. \`${setting}\` not configured. Please ask an admin to configure this value.`,
ephemeral: true,
});

return;
}
}

if (!tenmansQueue?.length) {
// init queue if it doesn't exist
voteClosingTime = new Date()
voteClosingTime.setHours(voteClosingTime.getHours() + botConfig.hoursTillVoteClose)
voteClosingTime = new Date();
voteClosingTime.setHours(
voteClosingTime.getHours() + botConfig.hoursTillVoteClose
);
time = this.interaction.options.getString("time");

tenmansQueue = [];
Expand All @@ -293,10 +340,18 @@ class TenmansVoteSubcommand extends RegisteredUserExecutable<CommandInteraction>
const queueChannel = botConfig.queueMsgChannel as TextChannel;
const queueId = "stub";

const votesStillNeeded = botConfig.minVoteCount - tenmansQueue.length;
activeVoteMessage = await queueChannel.send({
embeds: [createVoteEmbed(botConfig.minVoteCount, time, voteClosingTime)],
embeds: [
createVoteEmbed(votesStillNeeded, time, voteClosingTime),
],
components: [createVoteQueueActionRow(queueId)],
});

this.interaction.reply({
content: "Vote started!",
ephemeral: true,
});
} else {
this.interaction.reply({
content: "Cage triggered. A vote is already active, check it out!",
Expand Down Expand Up @@ -335,7 +390,11 @@ export async function cmd_tenmans(interaction: CommandInteraction, db: Db) {
});
return;
}
const command = new Action(interaction, db, interaction.options.getString("queueId"));
const command = new Action(
interaction,
db,
interaction.options.getString("queueId")
);
command.execute();
}

Expand Down Expand Up @@ -394,7 +453,11 @@ const createVoteEmbed = (votesStillNeeded: number, time, closingTime: Date) =>
new MessageEmbed()
.setColor("#0099ff")
.setTitle(`Ten Mans Vote For ${time}`)
.addField("Votes needed to start queue: ", votesStillNeeded.toString(), true)
.addField(
"Votes needed to start queue: ",
votesStillNeeded.toString(),
true
)
.addField(
"Discord Member",
tenmansQueue.length > 0
Expand All @@ -403,7 +466,7 @@ const createVoteEmbed = (votesStillNeeded: number, time, closingTime: Date) =>
true
)
.setTimestamp()
.setFooter("Vote ends at", closingTime.toLocaleString());
.setFooter(`Vote ends at ${closingTime.toLocaleString()}`);

const createQueueActionRow = (queueId) => {
return new MessageActionRow().addComponents(
Expand All @@ -423,7 +486,7 @@ const createVoteQueueActionRow = (queueId) => {
new MessageButton()
.setCustomId(`tenmans.vote:${queueId}`)
.setLabel("Vote")
.setStyle(Constants.MessageButtonStyles.SUCCESS),
.setStyle(Constants.MessageButtonStyles.SUCCESS)
);
};

Expand All @@ -432,7 +495,7 @@ export async function handleVoteCleaning() {
// Close vote if it has expired
if (voteClosingTime < new Date()) {
await activeVoteMessage.delete();

tenmansQueue = [];
voteClosingTime = null;
}
Expand Down

0 comments on commit 7c7055f

Please sign in to comment.