diff --git a/src/commands/make_playoff_channels.ts b/src/commands/make_playoff_channels.ts new file mode 100644 index 0000000..2ed6a92 --- /dev/null +++ b/src/commands/make_playoff_channels.ts @@ -0,0 +1,44 @@ +import { + type ChatInputCommandInteraction, + SlashCommandBuilder, + PermissionFlagsBits, + ChannelType, +} from "discord.js"; +import logger from "../config/logger"; + +export const data = new SlashCommandBuilder() + .setName("make_playoff_channels") + .setDescription("Creates voice channels for each alliance in the playoffs") + .setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles) + .addIntegerOption((option) => + option + .setName("num_alliances") + .setDescription("Number of alliances in the playoffs") + .setRequired(false) + .setMinValue(1) + .setMaxValue(8) + ) + .setDMPermission(false); + +export const execute = async (interaction: ChatInputCommandInteraction) => { + await interaction.deferReply(); + + const numAlliances = interaction.options.getInteger("num_alliances") ?? 8; + + for (let i = 1; i <= numAlliances; i++) { + const channel = await interaction.guild?.channels.create({ + name: `Alliance ${i}`, + type: ChannelType.GuildVoice, + parent: process.env.DISCORD_CATEGORY_ID, + }); + if (!channel) { + logger.error(`Failed to create voice channel for alliance ${i}`); + await interaction.followUp( + `❌ Failed to create voice channel for alliance ${i}` + ); + return; + } + } + + await interaction.followUp(`✅ Successfully created playoff voice channels!`); +}; diff --git a/src/commands/save_match_result.ts b/src/commands/save_match_result.ts index c1e4c70..86b4b8a 100644 --- a/src/commands/save_match_result.ts +++ b/src/commands/save_match_result.ts @@ -11,7 +11,7 @@ import { } from "../lib/googleSheet"; import { saveField } from "../lib/saver"; import { summonPlayersForMatch } from "../lib/summonPlayers"; -import { sendMatchResultEmbed } from "../lib/resultEmbed"; +import { sendQualMatchEmbed } from "../lib/resultEmbed"; import { setMatchNumber } from "../lib/field"; import logger from "../config/logger"; @@ -85,7 +85,7 @@ export const execute = async (interaction: ChatInputCommandInteraction) => { await interaction.editReply(`✅ Saved match ${matchNumber} results!`); // Send an embed with the match results - if (interaction.guild) sendMatchResultEmbed(interaction.guild, match); + if (interaction.guild) sendQualMatchEmbed(interaction.guild, match); // Get the match data from the schedule sheet (discord ids) const players = await getMatchPlayers( diff --git a/src/commands/summon_for_match.ts b/src/commands/summon_for_match.ts index 3f1bf0a..aa23841 100644 --- a/src/commands/summon_for_match.ts +++ b/src/commands/summon_for_match.ts @@ -23,8 +23,17 @@ export const execute = async (interaction: ChatInputCommandInteraction) => { await interaction.deferReply(); // Get the current match number based on the matches sheet - const { matchNumber: nextMatchNumber, secondMatchNumber } = + let { matchNumber: nextMatchNumber, secondMatchNumber } = await getSoonestUnplayedMatch(interaction.client.matchesSheet); + let matchType: "Qual" | "Playoff" = "Qual"; + if (!nextMatchNumber) { + const playoffMatches = await getSoonestUnplayedMatch( + interaction.client.playoffsSheet + ); + nextMatchNumber = playoffMatches.matchNumber; + secondMatchNumber = playoffMatches.secondMatchNumber; + matchType = "Playoff"; + } let matchNumber = nextMatchNumber; // If the user specified a match number, use that instead @@ -34,6 +43,7 @@ export const execute = async (interaction: ChatInputCommandInteraction) => { } const res = await summonPlayersForMatch( + matchType, matchNumber, interaction.client.scheduleSheet, interaction.guild @@ -47,6 +57,7 @@ export const execute = async (interaction: ChatInputCommandInteraction) => { if (matchNumber === nextMatchNumber && secondMatchNumber) { const res = await summonPlayersForMatch( + matchType, secondMatchNumber, interaction.client.scheduleSheet, interaction.guild, diff --git a/src/config/client.ts b/src/config/client.ts index d1e7705..0c031b5 100644 --- a/src/config/client.ts +++ b/src/config/client.ts @@ -16,5 +16,7 @@ declare module "discord.js" { commands: Collection; scheduleSheet: GoogleSpreadsheetWorksheet; matchesSheet: GoogleSpreadsheetWorksheet; + alliancesSheet: GoogleSpreadsheetWorksheet; + playoffsSheet: GoogleSpreadsheetWorksheet; } } diff --git a/src/index.ts b/src/index.ts index 33943da..1eead2f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,8 @@ const client = new Client({ setupGoogleSheets().then((sheets) => { client.scheduleSheet = sheets.scheduleSheet; client.matchesSheet = sheets.matchesSheet; + client.alliancesSheet = sheets.alliancesSheet; + client.playoffsSheet = sheets.playoffsSheet; }); // Register bot slash commands. diff --git a/src/lib/googleSheet.ts b/src/lib/googleSheet.ts index 9b207c4..7a45298 100644 --- a/src/lib/googleSheet.ts +++ b/src/lib/googleSheet.ts @@ -53,7 +53,33 @@ export async function setupConnection() { }); } - return { matchesSheet, scheduleSheet }; + let alliancesSheet = doc.sheetsByTitle.Alliances; + if (!alliancesSheet) { + alliancesSheet = await doc.addSheet({ + title: "Alliances", + gridProperties: { + columnCount: 3, + frozenRowCount: 1, + rowCount: 9, + }, + headerValues: ["captain", "pick1", "pick2"], + }); + } + + let playoffsSheet = doc.sheetsByTitle["Playoff Matches"]; + if (!playoffsSheet) { + playoffsSheet = await doc.addSheet({ + title: "Playoff Matches", + gridProperties: { + columnCount: headerValues.length, + frozenRowCount: 1, + rowCount: 2, + }, + headerValues: headerValues, + }); + } + + return { matchesSheet, scheduleSheet, alliancesSheet, playoffsSheet }; } export async function updateMatch( diff --git a/src/lib/resultEmbed.ts b/src/lib/resultEmbed.ts index 896d826..d137bb3 100644 --- a/src/lib/resultEmbed.ts +++ b/src/lib/resultEmbed.ts @@ -8,7 +8,11 @@ const codeBlock = (str: string) => `\`\`\`\n${str}\n\`\`\``; * @param guild server to send the embed to * @param match object containing match data */ -export async function sendMatchResultEmbed(guild: Guild, match: Match) { +async function sendMatchResultEmbed( + guild: Guild, + match: Match, + matchTitle: string +) { // Get the channel to send the embed to const channel = guild.channels.cache.get(process.env.DISCORD_CHANNEL_ID); @@ -38,15 +42,30 @@ export async function sendMatchResultEmbed(guild: Guild, match: Match) { color = 0x0000ff; } + const { + redAuto, + redTeleop, + redChargeStation, + redPenalty, + redGamePieces, + redRP, + blueAuto, + blueTeleop, + blueChargeStation, + bluePenalty, + blueGamePieces, + blueRP, + } = match; + const breakdown = codeBlock( [ - [match.redAuto, " | auto | ", match.blueAuto], - [match.redTeleop, " | teleop | ", match.blueTeleop], - [match.redEnd, " | endgame | ", match.blueEnd], - [match.redPenalty, " | penalties | ", match.bluePenalty], - [match.redGamePieces, " | game pieces | ", match.blueGamePieces], + [redAuto, " | auto | ", blueAuto], + [redTeleop, " | teleop | ", blueTeleop], + [redChargeStation, " | charge station | ", blueChargeStation], + [redPenalty, " | penalties | ", bluePenalty], + [redGamePieces, " | game pieces | ", blueGamePieces], ["", " | | ", ""], - [match.redRP, " | ranking points | ", match.blueRP], + [redRP, " | ranking points | ", blueRP], ] .map( (x) => @@ -60,7 +79,7 @@ export async function sendMatchResultEmbed(guild: Guild, match: Match) { const embed = new EmbedBuilder() .setColor(color) .setTitle( - `Match ${match.matchNumber} Results ${match.redScore + `${matchTitle.padEnd(24, " ")} ${match.redScore .toString() .padEnd(3, " ")} - ${match.blueScore.toString().padEnd(3, " ")}` ) @@ -75,3 +94,17 @@ export async function sendMatchResultEmbed(guild: Guild, match: Match) { await channel.send({ embeds: [embed] }); } } + +export async function sendQualMatchEmbed(guild: Guild, match: Match) { + await sendMatchResultEmbed(guild, match, `Qual ${match.matchNumber} Results`); +} + +export async function sendPlayoffMatchEmbed(guild: Guild, match: Match) { + await sendMatchResultEmbed( + guild, + match, + match.matchNumber > 13 + ? `Finals ${match.matchNumber - 13} Results` + : `Playoff ${match.matchNumber} Results` + ); +} diff --git a/src/lib/summonPlayers.ts b/src/lib/summonPlayers.ts index e6b2417..eb2e42b 100644 --- a/src/lib/summonPlayers.ts +++ b/src/lib/summonPlayers.ts @@ -4,6 +4,7 @@ import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet"; import logger from "../config/logger"; export async function summonPlayersForMatch( + matchType: "Qual" | "Playoff", matchNumber: number | null, scheduleSheet: GoogleSpreadsheetWorksheet, guild: Guild | null,