From 73cf236a68feb84fa18611220c6cf5a057944158 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:50:11 -0700 Subject: [PATCH 01/10] introduce new `FormatDailyWebhookOptions` --- api/api.ts | 1 + api/dailies.ts | 73 +++++++++++++++++++++++---- lib/leaderboard/leaderboard_client.ts | 2 + 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/api/api.ts b/api/api.ts index b11affe..e184331 100644 --- a/api/api.ts +++ b/api/api.ts @@ -34,6 +34,7 @@ export function makeAPIRouter( new URLPattern({ pathname: "/webhook/:token" }), makeDailyWebhookPostHandler( lcClient, + leaderboardClient, webhookURL, webhookToken, ), diff --git a/api/dailies.ts b/api/dailies.ts index 5d3d322..c39e0e4 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -1,6 +1,7 @@ import * as discord from "lc-dailies/lib/discord/mod.ts"; import * as router from "lc-dailies/lib/router/mod.ts"; import * as lc from "lc-dailies/lib/lc/mod.ts"; +import * as leaderboard from "lc-dailies/lib/leaderboard/mod.ts"; import * as snacks from "./snacks.ts"; /** @@ -8,6 +9,7 @@ import * as snacks from "./snacks.ts"; */ export function makeDailyWebhookPostHandler( lcClient: lc.LCClient, + leaderboardClient: leaderboard.LeaderboardClient, webhookURL: string, webhookToken?: string, ) { @@ -17,37 +19,90 @@ export function makeDailyWebhookPostHandler( return async function handlePostDailyWebhook( request: router.RouterRequest, ): Promise { + // Check the webhook token. const token = request.params["token"]; if (webhookToken && token !== webhookToken) { return new Response("Invalid token", { status: 401 }); } - return await handleExecuteDailyWebhook(lcClient, webhookURL); + // Get the season ID if applicable. + const seasonID = request.url.searchParams.get("season_id") || undefined; + + // Execute the webhook. + return await executeDailyWebhook( + lcClient, + leaderboardClient, + webhookURL, + seasonID, + ); }; } -async function handleExecuteDailyWebhook( +async function executeDailyWebhook( lcClient: lc.LCClient, + leaderboardClient: leaderboard.LeaderboardClient, webhookURL: string, + seasonID?: string, ): Promise { + // Get the daily question. const question = await lcClient.getDailyQuestion(); - const content = formatLCDailyQuestion(question); + + // Get the season data if a season ID is provided or if it is Sunday. + const isSunday = new Date(question.date).getDay() === 0; + const season = seasonID + ? await leaderboardClient.getSeason(seasonID) + : isSunday + ? await leaderboardClient.getCurrentSeason() + : null; + + // Format the webhook content. + const content = formatDailyWebhook({ question, season }); + + // Execute the webhook. await discord.executeWebhook({ url: webhookURL, data: { content }, }); + // Acknowledge the request. return new Response("OK"); } -function formatLCDailyQuestion(question: lc.DailyQuestion): string { - return [ - `## Daily Leetcode Question for ${question.date}`, - `**Question**: ${question.title}`, - `**Difficulty**: ${question.difficulty}`, - `**Link**: <${question.url}>`, +/** + * FormatDailyWebhookOptions are the options for formatDailyWebhook. + */ +export interface FormatDailyWebhookOptions { + /** + * question is the daily question. + */ + question: lc.DailyQuestion; + + /** + * season is the season to recap. + */ + season: leaderboard.Season | null; +} + +/** + * formatDailyWebhook formats a daily webhook. + */ +export function formatDailyWebhook( + options: FormatDailyWebhookOptions, +): string { + const content = [ + `## Daily Leetcode Question for ${options.question.date}`, + `**Question**: ${options.question.title}`, + `**Difficulty**: ${options.question.difficulty}`, + `**Link**: <${options.question.url}>`, `**Snack**: Here is a snack to get your brain working: ${snacks.pickRandom()}`, "", "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! ([more info](https://acmcsuf.com/lc-dailies-handbook))", ].join("\n"); + + if (!options.season) { + return content; + } + + // TODO: Render the leaderboard. + return content; } diff --git a/lib/leaderboard/leaderboard_client.ts b/lib/leaderboard/leaderboard_client.ts index b03bf1b..4ab679d 100644 --- a/lib/leaderboard/leaderboard_client.ts +++ b/lib/leaderboard/leaderboard_client.ts @@ -121,3 +121,5 @@ export interface LeaderboardClient { */ listSeasons(): Promise; } + +// TODO: Calculate the top scorers of a given season to eventually be formatted in a leaderboard table. From ea62cac7ca1a1db505757287364715ca01e116ac Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:34:54 -0700 Subject: [PATCH 02/10] run `deno task all` --- deno.lock | 214 +++++++++++++++++++++++++++--------------------------- deps.ts | 10 +-- 2 files changed, 112 insertions(+), 112 deletions(-) diff --git a/deno.lock b/deno.lock index 7a5aaa1..f23ce6c 100644 --- a/deno.lock +++ b/deno.lock @@ -1,114 +1,114 @@ { - "version": "2", + "version": "3", "remote": { "https://cdn.skypack.dev/-/tweetnacl@v1.0.3-G4yM3nQ8lnXXlGGQADqJ/dist=es2019,mode=imports/optimized/tweetnacl.js": "d26554516df57e5cb58954e90c633c8871b4e66016b9fe4e07a36db5430bc8c7", "https://cdn.skypack.dev/tweetnacl@1.0.3": "6610aad2ac175c2d575995fc7de8ed552c2e5e05aef80ed8588cf3c6e2db61d7", - "https://deno.land/std@0.201.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.201.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", - "https://deno.land/std@0.201.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.201.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", - "https://deno.land/std@0.201.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", - "https://deno.land/std@0.201.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", - "https://deno.land/std@0.201.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.201.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", - "https://deno.land/std@0.201.0/assert/assert_false.ts": "a9962749f4bf5844e3fa494257f1de73d69e4fe0e82c34d0099287552163a2dc", - "https://deno.land/std@0.201.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", - "https://deno.land/std@0.201.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", - "https://deno.land/std@0.201.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", - "https://deno.land/std@0.201.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.201.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", - "https://deno.land/std@0.201.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", - "https://deno.land/std@0.201.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", - "https://deno.land/std@0.201.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", - "https://deno.land/std@0.201.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", - "https://deno.land/std@0.201.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", - "https://deno.land/std@0.201.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", - "https://deno.land/std@0.201.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", - "https://deno.land/std@0.201.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.201.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", - "https://deno.land/std@0.201.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", - "https://deno.land/std@0.201.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", - "https://deno.land/std@0.201.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.201.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.201.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", - "https://deno.land/std@0.201.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", - "https://deno.land/std@0.201.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", - "https://deno.land/std@0.201.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", - "https://deno.land/std@0.201.0/datetime/constants.ts": "b63a6b702e06fa028fb2ffa25e0cf775e3b21cf7f38e53a6f219e9641894dfbb", - "https://deno.land/std@0.201.0/dotenv/mod.ts": "1da8c6d0e7f7d8a5c2b19400b763bc11739df24acec235dda7ea2cfd3d300057", - "https://deno.land/std@0.201.0/fmt/colors.ts": "87544aa2bc91087bb37f9c077970c85bfb041b48e4c37356129d7b450a415b6f", - "https://deno.land/x/discord_api_types@0.37.56/gateway/common.ts": "fb67003adda424df76c2726e0624d709c5a16e3694d6b75facd587d121fe121f", - "https://deno.land/x/discord_api_types@0.37.56/gateway/v10.ts": "1b0fa3d040825a7aab93d6acec63d457c79c445dcc934d4f511dc2c326988ddf", - "https://deno.land/x/discord_api_types@0.37.56/globals.ts": "7d8879654c4741ac071668ad52f2659bcdb66694cfe7da306c8437ec752807a7", - "https://deno.land/x/discord_api_types@0.37.56/payloads/common.ts": "7c42a7965f38d82d9a425cf732ff0d4d2b5370568168d5fa41f138fd8ab4c693", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/attachment.ts": "c66dccd54c1b84d073f2e1caa466e551b8045a84a2e8a88a1bfbc7e2c64a703d", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/base.ts": "f6a2556e14d489e1f0e5ddeb3a0303e2603e25330530dd9263e176013c5f51eb", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/boolean.ts": "65e29561b61785ca4ede4b1b4a88c5fc0696cfdf1fa74d5197588c196ee7ae98", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/channel.ts": "73c7fc49de242e1ce3be958375fd810750aed83553ef3860e3cddf858f9eb464", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/integer.ts": "a25d24a3e54d647c7039b99e3208fc0fc2228d174f6dcc421e93919b8154a011", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/mentionable.ts": "742e42857465866e0c08b587d7fb5ccd81d4705c61ce4cd6b97ad5692e88e969", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/number.ts": "be974ea68f5fdf55d7a7f5d3faf48a3193777d432b1aa9087afc204bcb916284", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/role.ts": "a57114d0f7eeee4ab7cf217a865dd9dbd9096d007c556aec6185d64257100f41", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/shared.ts": "9e2d3b3530280f6de5f9b6de1bb81e8a905998e058f784a9b041e48a96cd93d2", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/string.ts": "33ab12dab64544a70729b9b66b5a9790964ea779f05d4ab1a1e190e7c1b59e98", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommand.ts": "0013737da6d2b54e2f413fbf31cf9c84ea51bd9204b615cb4fd19b420f856cb2", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommandGroup.ts": "db5cc701bcb3d68c094de409da39c9a2b8834dc0d5038e5f963c96e5eaf412ac", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/_chatInput/user.ts": "ed2871693744298225ba53ddfb18d3e7afff20a34f413822d5b1193918aea27f", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/chatInput.ts": "46362da4e56c99cc69331481330d6e95c31d5e46f4cc36ec23f03cafbb687d52", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/contextMenu.ts": "89aed5f05f75d482e40259f55d0172143a90c1980d060d16545bdc14b68b29c4", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/internals.ts": "5eb5ea13a1247c73c0611886dea09ab8d632a9c5555ff0f33d44cd379fd75a08", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/_applicationCommands/permissions.ts": "ddca14b62e6afd418c1417117ffcc7cfb2ea5e5cc5353b4a0598435bdea45fb5", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/applicationCommands.ts": "b0646f2930d38113389bd1ecf8c605ac5af8fc40f93fafd17f968150419fac14", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/autocomplete.ts": "821ae50ff9845cac4b03169dbd4c4b187d8399765eb1f0d658d477c68e4c6136", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/base.ts": "c839d953bf7f7912aa7202e7586d65aafb7f90d22aedd21a13586295c62f2bdf", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/messageComponents.ts": "08faa77d1c1d9a33359a962b78b695304e27cb6435af319b41e9a9d3b395adb2", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/modalSubmit.ts": "3a02d2d7df5bcdb1ffcd089f15e0d82ab65dcc0cefa904c6e0621f46edac041e", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/ping.ts": "096ce582e9af373649fd5355cccd7424adceaffb73367b5301f1594ef5a3c264", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/_interactions/responses.ts": "0a607a78dd08a498c3b523a922c51ec9465021d98b3a21b7c7551524d49d70ce", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/application.ts": "69e8470289fcde7ec1483600540085e402a11b21cfeab2191db23dd387452ee7", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/auditLog.ts": "0a538dd3e4876ec741a6986afc1057589f43d3efd1684386346317801c53268d", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/autoModeration.ts": "9ccb4408f1c6392d9619fac159997e08e660080b3f9567a1619163a40329e3a2", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/channel.ts": "60ba55624295d4717db5330bd503cbd6c2da6dc68cb850505d4eca8ef6ac5c39", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/emoji.ts": "b9a30b16e1ec4dc15d6149e59aa48b02ad57a51335b7be5a7f5368db0491b3dd", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/gateway.ts": "4ce715fa94eadf5e2ba6adfc4a3bf99bac5d19c4787794ee1774b645a324db72", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/guild.ts": "536ce0d73d055290855febb556d6ffcc3308f5824887879d28f49d0e4a676fa6", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/guildScheduledEvent.ts": "bf506b7807501b71077751ec793e719c5515e1bb405dec5cc4371a61b03cf8b9", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/interactions.ts": "17dabe94016dad3d0d7fdc0aa812bf5b0b366465dd72cd0b01168880778cc60d", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/invite.ts": "92c09f549482a4e2ad5a3c1062debfb262c6fe4b6740581175a0b8108873ab01", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/mod.ts": "733090e3f67bb812dc0a3986d476e62483e88dfa0ce362d048a669b54b94c625", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/oauth2.ts": "dfb9f09fb44bf5faaa73ad4488ebe408905907d5fd46404895317f4e7c378489", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/permissions.ts": "5e7990e6dad3e35f8c130dc52de4bbf63afe5d6ad98e1c56b09da3ead94ad5a4", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/stageInstance.ts": "f0b9ee8c24c67298086fa32cb0595f6c29710d81b6fe85b958d48e6c549c4cb8", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/sticker.ts": "8511b5cdbe8289ce13fd51c2e96d24548345111b8d9f9c907dd3336f10e795bf", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/teams.ts": "101044d8c48a3cbabb60048eff9f69588bdd1ea84d1825de0372b4e23ad7ccbe", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/template.ts": "c6bee171ed0ce61fc8b59de42541a023bdcde62718deb42325397e5c82efdc27", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/user.ts": "59706244399f29512688c0813ec7dbaabd2fd5f583471564ff513e87213d2d13", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/voice.ts": "62d03a540f2e78e5f3989f71a0ee1ec682ef7306a4fa096f89118cbd82351d47", - "https://deno.land/x/discord_api_types@0.37.56/payloads/v10/webhook.ts": "7fc370f40a84f12a6e57ddda7cf2814f15039d6320b46979db0b49d5b91e303b", - "https://deno.land/x/discord_api_types@0.37.56/rest/common.ts": "0bfbcf97482fe86daeb68c57885bd04a7d6504ba9037f7e533ea466825afd051", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/application.ts": "5b44ce90cf739aac76e2f01c15a3d4a03724703695235aec4f800fdd08782ec0", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/auditLog.ts": "39a0914b6c51445023d82c3e3e66c9866cbb3cb6774d3e7eac63414ea9bcbfec", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/autoModeration.ts": "3d388fd9a91c34f04b5e3e1b6ffe12029fb48b511f37ff88042325ec6cbc6605", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/channel.ts": "76adde119c0f1fe0114269766c1f15db482d88911c2a1682090388a07a9c3209", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/emoji.ts": "9f694a1bd63886c62a87b4320f3bfa5d4f534b2d87c317d77d572d10522df3aa", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/gateway.ts": "747cb95c9a8bca4e52423c780d5fc492fb0dab2b6015cd7e51e890e8d51acf29", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/guild.ts": "89d448eb50f891d19034480b45bc7e038c0712a0cc9b102b524b5d1f254f5b70", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/guildScheduledEvent.ts": "29d361f395d8cd1ecb47550615d19e10793d513cc5ad8d32895da2cc9cd0fd89", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/interactions.ts": "2b6decdfff921b6aa8f0e6e5c61d38469a0178c5ecf1a18dd17ad6738143e662", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/invite.ts": "28f8e740bdaa782c9d9d504049323762b5c1180348019dc5f9e0a900ec11213e", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/mod.ts": "eb7e802fc0d134d862f9b5eef57a9cd7f536ce09c1ca5e8809f09ff9ef4e9eeb", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/oauth2.ts": "b659a35654c17767480d142c46c36f5fe2544346875745e3f654c5e7c3d9f3f9", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/stageInstance.ts": "d5d96446a0275860f136d40546bf0519d5d8d4171d03058598559df095dbf899", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/sticker.ts": "1f7f2729308a0ec1fe373b5df7ac71bafc200132f1a06df7e75bf5ce1d1069c1", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/template.ts": "0ad41c3c85571d3c5b0bec3914c678a21f376ec162ef0d3f1f7731a8d1d1009c", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/user.ts": "60cfa227426c791021e9e8f769287e997477e722db5a3c577a9ec54078aaffca", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/voice.ts": "cdbe9d6c39c8f44635d8632bf62a95b8c15877b92c56ddf69df2072bb1a74edc", - "https://deno.land/x/discord_api_types@0.37.56/rest/v10/webhook.ts": "f4d47265bc69f9fa32efd0fd620db8b1d1996d2184cf5ac624abd104862d6ae0", - "https://deno.land/x/discord_api_types@0.37.56/rpc/common.ts": "a693352ffd86ae9e995fb3fbfbfd2be30896257ecb83c5611050f060b08de4ef", - "https://deno.land/x/discord_api_types@0.37.56/rpc/v10.ts": "fbaad9f3d73fce88e76b0e52ad5345093f18077e4293937c9ec0ee24415b9a93", - "https://deno.land/x/discord_api_types@0.37.56/utils/internals.ts": "cb70895ba89f7947c38f7fa447b0190cb14b5585be323414cda53d2ccb19b16c", - "https://deno.land/x/discord_api_types@0.37.56/utils/v10.ts": "056bd036f8c65365ff28eb63ec6897811d51921cca6d068392dd1ca5b397ae62", - "https://deno.land/x/discord_api_types@0.37.56/v10.ts": "f3f23492c59e77859aba5b34431edf3668c37f722d7f70c2e1ef7ba4bcda3010", + "https://deno.land/std@0.203.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.203.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", + "https://deno.land/std@0.203.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.203.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.203.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.203.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.203.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.203.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", + "https://deno.land/std@0.203.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.203.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.203.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.203.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.203.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.203.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.203.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.203.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.203.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.203.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.203.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.203.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.203.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.203.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.203.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.203.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.203.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.203.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.203.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.203.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.203.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.203.0/datetime/constants.ts": "b63a6b702e06fa028fb2ffa25e0cf775e3b21cf7f38e53a6f219e9641894dfbb", + "https://deno.land/std@0.203.0/dotenv/mod.ts": "1da8c6d0e7f7d8a5c2b19400b763bc11739df24acec235dda7ea2cfd3d300057", + "https://deno.land/std@0.203.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", + "https://deno.land/x/discord_api_types@0.37.58/gateway/common.ts": "fb67003adda424df76c2726e0624d709c5a16e3694d6b75facd587d121fe121f", + "https://deno.land/x/discord_api_types@0.37.58/gateway/v10.ts": "1b0fa3d040825a7aab93d6acec63d457c79c445dcc934d4f511dc2c326988ddf", + "https://deno.land/x/discord_api_types@0.37.58/globals.ts": "7d8879654c4741ac071668ad52f2659bcdb66694cfe7da306c8437ec752807a7", + "https://deno.land/x/discord_api_types@0.37.58/payloads/common.ts": "7c42a7965f38d82d9a425cf732ff0d4d2b5370568168d5fa41f138fd8ab4c693", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/attachment.ts": "c66dccd54c1b84d073f2e1caa466e551b8045a84a2e8a88a1bfbc7e2c64a703d", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/base.ts": "f6a2556e14d489e1f0e5ddeb3a0303e2603e25330530dd9263e176013c5f51eb", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/boolean.ts": "65e29561b61785ca4ede4b1b4a88c5fc0696cfdf1fa74d5197588c196ee7ae98", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/channel.ts": "73c7fc49de242e1ce3be958375fd810750aed83553ef3860e3cddf858f9eb464", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/integer.ts": "a25d24a3e54d647c7039b99e3208fc0fc2228d174f6dcc421e93919b8154a011", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/mentionable.ts": "742e42857465866e0c08b587d7fb5ccd81d4705c61ce4cd6b97ad5692e88e969", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/number.ts": "be974ea68f5fdf55d7a7f5d3faf48a3193777d432b1aa9087afc204bcb916284", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/role.ts": "a57114d0f7eeee4ab7cf217a865dd9dbd9096d007c556aec6185d64257100f41", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/shared.ts": "9e2d3b3530280f6de5f9b6de1bb81e8a905998e058f784a9b041e48a96cd93d2", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/string.ts": "33ab12dab64544a70729b9b66b5a9790964ea779f05d4ab1a1e190e7c1b59e98", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommand.ts": "0013737da6d2b54e2f413fbf31cf9c84ea51bd9204b615cb4fd19b420f856cb2", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/subcommandGroup.ts": "db5cc701bcb3d68c094de409da39c9a2b8834dc0d5038e5f963c96e5eaf412ac", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/_chatInput/user.ts": "ed2871693744298225ba53ddfb18d3e7afff20a34f413822d5b1193918aea27f", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/chatInput.ts": "46362da4e56c99cc69331481330d6e95c31d5e46f4cc36ec23f03cafbb687d52", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/contextMenu.ts": "89aed5f05f75d482e40259f55d0172143a90c1980d060d16545bdc14b68b29c4", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/internals.ts": "5eb5ea13a1247c73c0611886dea09ab8d632a9c5555ff0f33d44cd379fd75a08", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/_applicationCommands/permissions.ts": "ddca14b62e6afd418c1417117ffcc7cfb2ea5e5cc5353b4a0598435bdea45fb5", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/applicationCommands.ts": "b0646f2930d38113389bd1ecf8c605ac5af8fc40f93fafd17f968150419fac14", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/autocomplete.ts": "821ae50ff9845cac4b03169dbd4c4b187d8399765eb1f0d658d477c68e4c6136", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/base.ts": "13b9399fae286ad7b80181f116c18c0bcefe30121d90a76a1d7ae0c7ac46e14b", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/messageComponents.ts": "08faa77d1c1d9a33359a962b78b695304e27cb6435af319b41e9a9d3b395adb2", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/modalSubmit.ts": "3a02d2d7df5bcdb1ffcd089f15e0d82ab65dcc0cefa904c6e0621f46edac041e", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/ping.ts": "096ce582e9af373649fd5355cccd7424adceaffb73367b5301f1594ef5a3c264", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/_interactions/responses.ts": "0a607a78dd08a498c3b523a922c51ec9465021d98b3a21b7c7551524d49d70ce", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/application.ts": "69e8470289fcde7ec1483600540085e402a11b21cfeab2191db23dd387452ee7", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/auditLog.ts": "ced9d28a20b2bb201761c37ef266ace325a808a405b7472d70ad6df3b56c4d87", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/autoModeration.ts": "9ccb4408f1c6392d9619fac159997e08e660080b3f9567a1619163a40329e3a2", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/channel.ts": "2d5168c85de11cb82503390aa6e49e20ea4bb806d010762cc3a4b1ece09b696a", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/emoji.ts": "b9a30b16e1ec4dc15d6149e59aa48b02ad57a51335b7be5a7f5368db0491b3dd", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/gateway.ts": "4ce715fa94eadf5e2ba6adfc4a3bf99bac5d19c4787794ee1774b645a324db72", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/guild.ts": "4c03e054fc07d8db0ee7fa2b6f645f57f7642de0d28a704aa15047a51544f710", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/guildScheduledEvent.ts": "bf506b7807501b71077751ec793e719c5515e1bb405dec5cc4371a61b03cf8b9", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/interactions.ts": "17dabe94016dad3d0d7fdc0aa812bf5b0b366465dd72cd0b01168880778cc60d", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/invite.ts": "92c09f549482a4e2ad5a3c1062debfb262c6fe4b6740581175a0b8108873ab01", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/mod.ts": "733090e3f67bb812dc0a3986d476e62483e88dfa0ce362d048a669b54b94c625", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/oauth2.ts": "dfb9f09fb44bf5faaa73ad4488ebe408905907d5fd46404895317f4e7c378489", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/permissions.ts": "5e7990e6dad3e35f8c130dc52de4bbf63afe5d6ad98e1c56b09da3ead94ad5a4", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/stageInstance.ts": "f0b9ee8c24c67298086fa32cb0595f6c29710d81b6fe85b958d48e6c549c4cb8", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/sticker.ts": "8511b5cdbe8289ce13fd51c2e96d24548345111b8d9f9c907dd3336f10e795bf", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/teams.ts": "101044d8c48a3cbabb60048eff9f69588bdd1ea84d1825de0372b4e23ad7ccbe", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/template.ts": "c6bee171ed0ce61fc8b59de42541a023bdcde62718deb42325397e5c82efdc27", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/user.ts": "361b93e3683fcf611c021c4f39c7501ca1482e999887d97a4b0a09398b4618c5", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/voice.ts": "62d03a540f2e78e5f3989f71a0ee1ec682ef7306a4fa096f89118cbd82351d47", + "https://deno.land/x/discord_api_types@0.37.58/payloads/v10/webhook.ts": "7fc370f40a84f12a6e57ddda7cf2814f15039d6320b46979db0b49d5b91e303b", + "https://deno.land/x/discord_api_types@0.37.58/rest/common.ts": "0bfbcf97482fe86daeb68c57885bd04a7d6504ba9037f7e533ea466825afd051", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/application.ts": "5b44ce90cf739aac76e2f01c15a3d4a03724703695235aec4f800fdd08782ec0", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/auditLog.ts": "39a0914b6c51445023d82c3e3e66c9866cbb3cb6774d3e7eac63414ea9bcbfec", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/autoModeration.ts": "3d388fd9a91c34f04b5e3e1b6ffe12029fb48b511f37ff88042325ec6cbc6605", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/channel.ts": "421ab1178e83765fe403c75b8e481823bd10a9288c1487163c4f1b5049d2ca7c", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/emoji.ts": "9f694a1bd63886c62a87b4320f3bfa5d4f534b2d87c317d77d572d10522df3aa", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/gateway.ts": "747cb95c9a8bca4e52423c780d5fc492fb0dab2b6015cd7e51e890e8d51acf29", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/guild.ts": "89d448eb50f891d19034480b45bc7e038c0712a0cc9b102b524b5d1f254f5b70", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/guildScheduledEvent.ts": "29d361f395d8cd1ecb47550615d19e10793d513cc5ad8d32895da2cc9cd0fd89", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/interactions.ts": "2b6decdfff921b6aa8f0e6e5c61d38469a0178c5ecf1a18dd17ad6738143e662", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/invite.ts": "28f8e740bdaa782c9d9d504049323762b5c1180348019dc5f9e0a900ec11213e", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/mod.ts": "eb7e802fc0d134d862f9b5eef57a9cd7f536ce09c1ca5e8809f09ff9ef4e9eeb", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/oauth2.ts": "b659a35654c17767480d142c46c36f5fe2544346875745e3f654c5e7c3d9f3f9", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/stageInstance.ts": "d5d96446a0275860f136d40546bf0519d5d8d4171d03058598559df095dbf899", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/sticker.ts": "1f7f2729308a0ec1fe373b5df7ac71bafc200132f1a06df7e75bf5ce1d1069c1", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/template.ts": "0ad41c3c85571d3c5b0bec3914c678a21f376ec162ef0d3f1f7731a8d1d1009c", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/user.ts": "60cfa227426c791021e9e8f769287e997477e722db5a3c577a9ec54078aaffca", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/voice.ts": "cdbe9d6c39c8f44635d8632bf62a95b8c15877b92c56ddf69df2072bb1a74edc", + "https://deno.land/x/discord_api_types@0.37.58/rest/v10/webhook.ts": "f4d47265bc69f9fa32efd0fd620db8b1d1996d2184cf5ac624abd104862d6ae0", + "https://deno.land/x/discord_api_types@0.37.58/rpc/common.ts": "a693352ffd86ae9e995fb3fbfbfd2be30896257ecb83c5611050f060b08de4ef", + "https://deno.land/x/discord_api_types@0.37.58/rpc/v10.ts": "fbaad9f3d73fce88e76b0e52ad5345093f18077e4293937c9ec0ee24415b9a93", + "https://deno.land/x/discord_api_types@0.37.58/utils/internals.ts": "cb70895ba89f7947c38f7fa447b0190cb14b5585be323414cda53d2ccb19b16c", + "https://deno.land/x/discord_api_types@0.37.58/utils/v10.ts": "056bd036f8c65365ff28eb63ec6897811d51921cca6d068392dd1ca5b397ae62", + "https://deno.land/x/discord_api_types@0.37.58/v10.ts": "f3f23492c59e77859aba5b34431edf3668c37f722d7f70c2e1ef7ba4bcda3010", "https://deno.land/x/ulid@v0.3.0/mod.ts": "f7ff065b66abd485051fc68af23becef6ccc7e81f7774d7fcfd894a4b2da1984" } } diff --git a/deps.ts b/deps.ts index 9862dd0..b1478e7 100644 --- a/deps.ts +++ b/deps.ts @@ -1,10 +1,10 @@ export { assertEquals, assertRejects, -} from "https://deno.land/std@0.201.0/assert/mod.ts"; +} from "https://deno.land/std@0.203.0/assert/mod.ts"; -export { load } from "https://deno.land/std@0.201.0/dotenv/mod.ts"; -export * from "https://deno.land/std@0.201.0/datetime/constants.ts"; +export { load } from "https://deno.land/std@0.203.0/dotenv/mod.ts"; +export * from "https://deno.land/std@0.203.0/datetime/constants.ts"; export { ulid } from "https://deno.land/x/ulid@v0.3.0/mod.ts"; export type { APIApplicationCommandInteractionDataOption, @@ -16,12 +16,12 @@ export type { APIUser, RESTPostAPIApplicationCommandsJSONBody, RESTPostAPIWebhookWithTokenJSONBody, -} from "https://deno.land/x/discord_api_types@0.37.56/v10.ts"; +} from "https://deno.land/x/discord_api_types@0.37.58/v10.ts"; export { ApplicationCommandOptionType, InteractionResponseType, InteractionType, MessageFlags, Utils, -} from "https://deno.land/x/discord_api_types@0.37.56/v10.ts"; +} from "https://deno.land/x/discord_api_types@0.37.58/v10.ts"; export { default as nacl } from "https://cdn.skypack.dev/tweetnacl@1.0.3"; From 186abd9c5005d861ae188bef586828a908986cb9 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 08:33:20 -0700 Subject: [PATCH 03/10] add `calculateSeasonScores` --- lib/leaderboard/leaderboard_client.ts | 89 +++++++++++++++++++- lib/leaderboard/leaderboard_client_test.ts | 94 ++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 lib/leaderboard/leaderboard_client_test.ts diff --git a/lib/leaderboard/leaderboard_client.ts b/lib/leaderboard/leaderboard_client.ts index 4ab679d..51ae07e 100644 --- a/lib/leaderboard/leaderboard_client.ts +++ b/lib/leaderboard/leaderboard_client.ts @@ -122,4 +122,91 @@ export interface LeaderboardClient { listSeasons(): Promise; } -// TODO: Calculate the top scorers of a given season to eventually be formatted in a leaderboard table. +/** + * CalculateScoresOptions is the options for calculateScores. + */ +export interface CalculateScoresOptions { + /** + * season is the season to calculate the score for. + */ + season: Season; + + /** + * possibleHighestScore is the highest possible score. + */ + possibleHighestScore: number; + + /** + * possibleLowestScore is the lowest possible score. + */ + possibleLowestScore: number; + + /** + * duration is the amount of milliseconds it takes to linearly interpolate + * between the highest and lowest possible scores. + */ + duration: number; +} + +/** + * calculateSubmissionScore calculates the score of a submission. + */ +export function calculateSubmissionScore( + submission: Submission, + question: lc.DailyQuestion, + options: CalculateScoresOptions, +): number { + const questionDate = new Date(question.date); + const submissionDate = new Date(submission.date); + const msElapsed = submissionDate.getTime() - questionDate.getTime(); + const ratio = Math.min(Math.max(msElapsed / options.duration, 0), 1); + const questionScore = + ((options.possibleHighestScore - options.possibleLowestScore) * + ratio) + options.possibleLowestScore; + return Math.ceil(questionScore); +} + +/** + * calculatePlayerScore calculates the score of a player in a season. + * + * See: + * https://github.com/acmcsufoss/lc-dailies/issues/32#issuecomment-1728904942 + */ +export function calculatePlayerScore( + playerID: string, + options: CalculateScoresOptions, +): number { + const submissions = options.season.submissions[playerID]; + if (!submissions) { + return 0; + } + + const scoreSum = Object.entries(submissions) + .reduce((score, [questionID, submission]) => { + const question = options.season.questions[questionID]; + if (!question) { + return score; + } + + return score + calculateSubmissionScore( + submission, + question, + options, + ); + }, 0); + + return scoreSum; +} + +/** + * calculateSeasonScores calculates the scores of all players in a season. + */ +export function calculateSeasonScores( + options: CalculateScoresOptions, +): Record { + return Object.keys(options.season.players) + .reduce((scores, playerID) => { + scores[playerID] = calculatePlayerScore(playerID, options); + return scores; + }, {} as Record); +} diff --git a/lib/leaderboard/leaderboard_client_test.ts b/lib/leaderboard/leaderboard_client_test.ts new file mode 100644 index 0000000..e4f5489 --- /dev/null +++ b/lib/leaderboard/leaderboard_client_test.ts @@ -0,0 +1,94 @@ +import { assertEquals } from "lc-dailies/deps.ts"; +import { + calculatePlayerScore, + calculateSeasonScores, +} from "./leaderboard_client.ts"; + +const FAKE_DURATION = 1e3 * 60 * 60 * 24; +const FAKE_POSSIBLE_HIGHEST_SCORE = 100; +const FAKE_POSSIBLE_LOWEST_SCORE = 50; +const FAKE_SEASON = { + "id": "01H8T4MM00BQHHK7VTTEJE1WAS", + "start_date": "Sun, 27 Aug 2023 00:00:00 GMT", + "players": { + "redacted_discord_id_00": { + "discord_user_id": "redacted_discord_id_00", + "lc_username": "EthanThatOneKid", + }, + "redacted_discord_id_01": { + "discord_user_id": "redacted_discord_id_01", + "lc_username": "PillowGit", + }, + }, + "questions": { + "implement-stack-using-queues": { + "name": "implement-stack-using-queues", + "date": "2023-08-28", + "title": "Implement Stack using Queues", + "difficulty": "Easy", + "url": "https://leetcode.com/problems/implement-stack-using-queues/", + }, + "counting-bits": { + "name": "counting-bits", + "date": "2023-09-01", + "title": "Counting Bits", + "difficulty": "Easy", + "url": "https://leetcode.com/problems/counting-bits/", + }, + }, + "submissions": { + "redacted_discord_id_00": { + "implement-stack-using-queues": { + "id": "1035629181", + "date": "Wed, 30 Aug 2023 04:10:39 GMT", + }, + "counting-bits": { + "id": "1037337123", + "date": "Fri, 01 Sep 2023 04:18:58 GMT", + }, + }, + "redacted_discord_id_01": { + "counting-bits": { + "id": "1037327504", + "date": "Fri, 01 Sep 2023 04:01:36 GMT", + }, + "implement-stack-using-queues": { + "id": "1034291152", + "date": "Mon, 28 Aug 2023 17:06:37 GMT", + }, + }, + }, +}; + +Deno.test("calculatePlayerScore calculates the score of a player", () => { + assertEquals( + calculatePlayerScore( + "redacted_discord_id_00", + { + season: FAKE_SEASON, + duration: FAKE_DURATION, + possibleHighestScore: FAKE_POSSIBLE_HIGHEST_SCORE, + possibleLowestScore: FAKE_POSSIBLE_LOWEST_SCORE, + }, + ), + 159, + ); +}); + +Deno.test("calculateSeasonScore calculates the scores of a season", () => { + const seasonScores = calculateSeasonScores({ + season: FAKE_SEASON, + duration: FAKE_DURATION, + possibleHighestScore: FAKE_POSSIBLE_HIGHEST_SCORE, + possibleLowestScore: FAKE_POSSIBLE_LOWEST_SCORE, + }); + + assertEquals( + seasonScores["redacted_discord_id_00"], + 159, + ); + assertEquals( + seasonScores["redacted_discord_id_01"], + 145, + ); +}); From 4827940a2de731296b3b38d0affdbfeda8338b30 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:37:34 -0700 Subject: [PATCH 04/10] add `makeDailyWebhookEmbeds` --- api/dailies.ts | 83 +++++++++++++++++++-------- cf/dailies/dailies.ts | 10 +++- deps.ts | 1 + lib/leaderboard/leaderboard_client.ts | 16 ++++++ 4 files changed, 85 insertions(+), 25 deletions(-) diff --git a/api/dailies.ts b/api/dailies.ts index c39e0e4..f595424 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -1,3 +1,4 @@ +import type { APIEmbed } from "lc-dailies/deps.ts"; import * as discord from "lc-dailies/lib/discord/mod.ts"; import * as router from "lc-dailies/lib/router/mod.ts"; import * as lc from "lc-dailies/lib/lc/mod.ts"; @@ -26,7 +27,7 @@ export function makeDailyWebhookPostHandler( } // Get the season ID if applicable. - const seasonID = request.url.searchParams.get("season_id") || undefined; + const seasonID = request.url.searchParams.get("season_id"); // Execute the webhook. return await executeDailyWebhook( @@ -42,7 +43,7 @@ async function executeDailyWebhook( lcClient: lc.LCClient, leaderboardClient: leaderboard.LeaderboardClient, webhookURL: string, - seasonID?: string, + seasonID: string | null, ): Promise { // Get the daily question. const question = await lcClient.getDailyQuestion(); @@ -55,13 +56,13 @@ async function executeDailyWebhook( ? await leaderboardClient.getCurrentSeason() : null; - // Format the webhook content. - const content = formatDailyWebhook({ question, season }); + // Format the webhook embed. + const embeds = makeDailyWebhookEmbeds({ question, season }); // Execute the webhook. await discord.executeWebhook({ url: webhookURL, - data: { content }, + data: { embeds }, }); // Acknowledge the request. @@ -69,9 +70,9 @@ async function executeDailyWebhook( } /** - * FormatDailyWebhookOptions are the options for formatDailyWebhook. + * DailyWebhookOptions are the options for makeDailyWebhookEmbeds. */ -export interface FormatDailyWebhookOptions { +export interface DailyWebhookOptions { /** * question is the daily question. */ @@ -84,25 +85,61 @@ export interface FormatDailyWebhookOptions { } /** - * formatDailyWebhook formats a daily webhook. + * makeDailyWebhookEmbeds formats a daily webhook. */ -export function formatDailyWebhook( - options: FormatDailyWebhookOptions, -): string { - const content = [ - `## Daily Leetcode Question for ${options.question.date}`, - `**Question**: ${options.question.title}`, - `**Difficulty**: ${options.question.difficulty}`, - `**Link**: <${options.question.url}>`, - `**Snack**: Here is a snack to get your brain working: ${snacks.pickRandom()}`, - "", - "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! ([more info](https://acmcsuf.com/lc-dailies-handbook))", - ].join("\n"); +export function makeDailyWebhookEmbeds( + options: DailyWebhookOptions, +): APIEmbed[] { + const questionEmbed: APIEmbed = { + title: `Daily Leetcode Question for ${options.question.date}`, + description: options.question.title, + url: options.question.url, + fields: [ + { + name: "Difficulty", + value: options.question.difficulty, + inline: true, + }, + { + name: "Snack", + value: snacks.pickRandom(), + inline: true, + }, + { + name: "Submit", + value: + "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! [More Info](https://acmcsuf.com/lc-dailies-handbook)", + }, + ], + }; if (!options.season) { - return content; + return [questionEmbed]; } - // TODO: Render the leaderboard. - return content; + const scores = leaderboard.calculateSeasonScores( + leaderboard.makeDefaultCalculateScoresOptions(options.season), + ); + const leaderboardContent = [ + "```", + "Rank | Name | Score", + "--- | --- | ---", + ...Object.entries(scores).map(([playerID, score], i) => { + const player = options.season!.players[playerID]; + return `${i + 1} | ${player.lc_username} | ${score}`; + }), + "```", + ].join("\n"); + + const leaderboardEmbed: APIEmbed = { + title: `Leaderboard for ${options.season.start_date}`, + fields: [ + { + name: "Leaderboard", + value: leaderboardContent, + }, + ], + }; + + return [questionEmbed, leaderboardEmbed]; } diff --git a/cf/dailies/dailies.ts b/cf/dailies/dailies.ts index 31a22b7..47616d3 100644 --- a/cf/dailies/dailies.ts +++ b/cf/dailies/dailies.ts @@ -38,7 +38,8 @@ export default { return new Response("Unexpected cron expression", { status: 400 }); } - return await execute(env.WEBHOOK_URL); + const seasonID = url.searchParams.get("season_id"); + return await execute(env.WEBHOOK_URL, seasonID); }, /** @@ -56,7 +57,12 @@ export default { }, }; -function execute(webhookURL: string) { +function execute(webhookURL: string | URL, seasonID?: string | null) { + if (seasonID) { + webhookURL = new URL(webhookURL); + webhookURL.searchParams.set("season_id", seasonID); + } + return fetch(webhookURL, { method: "POST" }); } diff --git a/deps.ts b/deps.ts index b1478e7..0720fd0 100644 --- a/deps.ts +++ b/deps.ts @@ -9,6 +9,7 @@ export { ulid } from "https://deno.land/x/ulid@v0.3.0/mod.ts"; export type { APIApplicationCommandInteractionDataOption, APIApplicationCommandOption, + APIEmbed, APIInteraction, APIInteractionResponse, APIInteractionResponseChannelMessageWithSource, diff --git a/lib/leaderboard/leaderboard_client.ts b/lib/leaderboard/leaderboard_client.ts index 51ae07e..361d4a2 100644 --- a/lib/leaderboard/leaderboard_client.ts +++ b/lib/leaderboard/leaderboard_client.ts @@ -200,6 +200,8 @@ export function calculatePlayerScore( /** * calculateSeasonScores calculates the scores of all players in a season. + * + * Returns a map of player ID to score. */ export function calculateSeasonScores( options: CalculateScoresOptions, @@ -210,3 +212,17 @@ export function calculateSeasonScores( return scores; }, {} as Record); } + +/** + * makeDefaultCalculateScoresOptions creates a default CalculateScoresOptions. + */ +export function makeDefaultCalculateScoresOptions( + season: Season, +): CalculateScoresOptions { + return { + season, + possibleHighestScore: 100, + possibleLowestScore: 50, + duration: 1e3 * 60 * 60 * 24 * 7, + }; +} From 1d34b2ab62d8884d32403a2fe4ab70e99dbcb856 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:38:55 -0700 Subject: [PATCH 05/10] override the webhook URL if applicable --- api/dailies.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/dailies.ts b/api/dailies.ts index f595424..5bb27e9 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -20,9 +20,15 @@ export function makeDailyWebhookPostHandler( return async function handlePostDailyWebhook( request: router.RouterRequest, ): Promise { + // Override the webhook URL if applicable. + const overrideWebhookURL = request.url.searchParams.get("webhook_url"); + if (overrideWebhookURL) { + webhookURL = overrideWebhookURL; + } + // Check the webhook token. const token = request.params["token"]; - if (webhookToken && token !== webhookToken) { + if (!overrideWebhookURL && webhookToken && token !== webhookToken) { return new Response("Invalid token", { status: 401 }); } From d99fd6bed4aeff0f686225ab3a6a123d382b1bde Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:07:37 -0700 Subject: [PATCH 06/10] add `makeManualDailyWebhookPostHandler` --- api/api.ts | 23 ++++- api/dailies.ts | 115 ++++++++++++++++----- env.ts | 5 + lib/lc/client.ts | 2 +- lib/leaderboard/leaderboard_client.ts | 6 +- lib/leaderboard/leaderboard_client_test.ts | 12 +-- main.ts | 2 +- 7 files changed, 129 insertions(+), 36 deletions(-) diff --git a/api/api.ts b/api/api.ts index e184331..c848a1e 100644 --- a/api/api.ts +++ b/api/api.ts @@ -3,7 +3,10 @@ import * as lc from "lc-dailies/lib/lc/mod.ts"; import * as leaderboard from "lc-dailies/lib/leaderboard/mod.ts"; import * as router from "lc-dailies/lib/router/mod.ts"; import * as discord_app from "./discord_app/mod.ts"; -import { makeDailyWebhookPostHandler } from "./dailies.ts"; +import { + makeDailyWebhookPostHandler, + makeManualDailyWebhookPostHandler, +} from "./dailies.ts"; import { makeSeasonGetHandler, makeSeasonsGetHandler } from "./seasons.ts"; /** @@ -30,6 +33,13 @@ export function makeAPIRouter( ), ), ) + .post( + new URLPattern({ pathname: "/webhook" }), + makeManualDailyWebhookPostHandler( + lcClient, + leaderboardClient, + ), + ) .post( new URLPattern({ pathname: "/webhook/:token" }), makeDailyWebhookPostHandler( @@ -86,11 +96,11 @@ export function makeOnListen( ); console.log( "- Invite LC-Dailies to your server:", - `http://127.0.0.1:${port}/invite/`, + `http://127.0.0.1:${port}/invite`, ); console.log( "- Latest season:", - `http://127.0.0.1:${port}/seasons/latest/`, + `http://127.0.0.1:${port}/seasons/latest`, ); }; } @@ -98,3 +108,10 @@ export function makeOnListen( function makeInviteURL(applicationID: string) { return `https://discord.com/api/oauth2/authorize?client_id=${applicationID}&scope=applications.commands`; } + +fetch( + "http://127.0.0.1:8080/webhook?season_id=01H8T4MM00BQHHK7VTTEJE1WAS&webhook_url=https://discord.com/api/webhooks/1128189891847667712/jUq5uK9dzM4V99fsj8Y6b8tBiZx_idMB5DAMKAPJQ4rTbEiqoy7ah2qUpORWyfaHGl4l", + { + method: "POST", + }, +).then((res) => res.json()).then(console.log).catch(console.error); diff --git a/api/dailies.ts b/api/dailies.ts index 5bb27e9..8a63971 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -1,4 +1,4 @@ -import type { APIEmbed } from "lc-dailies/deps.ts"; +import { type APIEmbed } from "lc-dailies/deps.ts"; import * as discord from "lc-dailies/lib/discord/mod.ts"; import * as router from "lc-dailies/lib/router/mod.ts"; import * as lc from "lc-dailies/lib/lc/mod.ts"; @@ -45,6 +45,32 @@ export function makeDailyWebhookPostHandler( }; } +/** + * makeManualDailyWebhookPostHandler creates a handler for any variable + * webhook URL POST requests. + */ +export function makeManualDailyWebhookPostHandler( + lcClient: lc.LCClient, + leaderboardClient: leaderboard.LeaderboardClient, +) { + return async function handleManualPostDailyWebhook( + request: router.RouterRequest, + ): Promise { + const seasonID = request.url.searchParams.get("season_id"); + const webhookURL = request.url.searchParams.get("webhook_url"); + if (!webhookURL) { + return new Response("Missing webhook_url", { status: 400 }); + } + + return await executeDailyWebhook( + lcClient, + leaderboardClient, + webhookURL, + seasonID, + ); + }; +} + async function executeDailyWebhook( lcClient: lc.LCClient, leaderboardClient: leaderboard.LeaderboardClient, @@ -97,9 +123,9 @@ export function makeDailyWebhookEmbeds( options: DailyWebhookOptions, ): APIEmbed[] { const questionEmbed: APIEmbed = { - title: `Daily Leetcode Question for ${options.question.date}`, - description: options.question.title, + title: options.question.title, url: options.question.url, + description: `Daily Leetcode Question for ${options.question.date}.`, fields: [ { name: "Difficulty", @@ -111,12 +137,11 @@ export function makeDailyWebhookEmbeds( value: snacks.pickRandom(), inline: true, }, - { - name: "Submit", - value: - "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! [More Info](https://acmcsuf.com/lc-dailies-handbook)", - }, ], + footer: { + text: + "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! [More Info](https://acmcsuf.com/lc-dailies-handbook)", + }, }; if (!options.season) { @@ -126,26 +151,70 @@ export function makeDailyWebhookEmbeds( const scores = leaderboard.calculateSeasonScores( leaderboard.makeDefaultCalculateScoresOptions(options.season), ); - const leaderboardContent = [ + const formattedLeaderboardTitle = + `Leaderboard for week of ${options.season.start_date}`; + const formattedLeaderboard = [ "```", - "Rank | Name | Score", - "--- | --- | ---", - ...Object.entries(scores).map(([playerID, score], i) => { - const player = options.season!.players[playerID]; - return `${i + 1} | ${player.lc_username} | ${score}`; - }), + ...Object.entries(scores) + .sort(({ 1: scoreA }, { 1: scoreB }) => scoreB - scoreA) + .map(([playerID, score], i) => { + const player = options.season!.players[playerID]; + const formattedScore = String(score).padStart(3, " "); + const formattedRank = formatRank(i + 1); + return `${formattedScore} ${player.lc_username} (${formattedRank})`; + }), "```", ].join("\n"); - const leaderboardEmbed: APIEmbed = { - title: `Leaderboard for ${options.season.start_date}`, - fields: [ - { - name: "Leaderboard", - value: leaderboardContent, - }, - ], + fields: [{ + name: formattedLeaderboardTitle, + value: formattedLeaderboard, + }], }; return [questionEmbed, leaderboardEmbed]; } + +/** + * formatRank formats the rank of a player in a season. + */ +export function formatRank(rank: number): string { + switch (rank) { + case 1: { + return "🥇"; + } + + case 2: { + return "🥈"; + } + + case 3: { + return "🥉"; + } + + case 11: + case 12: + case 13: { + return `${rank}th`; + } + } + + const lastDigit = rank % 10; + switch (lastDigit) { + case 1: { + return `${rank}st`; + } + + case 2: { + return `${rank}nd`; + } + + case 3: { + return `${rank}rd`; + } + + default: { + return `${rank}th`; + } + } +} diff --git a/env.ts b/env.ts index c4b4dfa..f6c84ef 100644 --- a/env.ts +++ b/env.ts @@ -38,3 +38,8 @@ export const DISCORD_WEBHOOK_URL = Deno.env.get("DISCORD_WEBHOOK_URL")!; * DISCORD_CHANNEL_ID is the channel ID specified for the application. */ export const DISCORD_CHANNEL_ID = Deno.env.get("DISCORD_CHANNEL_ID")!; + +/** + * KV_URL is the URL of the KV store. + */ +export const KV_URL = Deno.env.get("KV_URL"); diff --git a/lib/lc/client.ts b/lib/lc/client.ts index ccaa7fc..466d478 100644 --- a/lib/lc/client.ts +++ b/lib/lc/client.ts @@ -17,7 +17,7 @@ export interface DailyQuestion { name: string; /** - * date is the date the daily question was posted. + * date is the date the daily question was posted in the format of YYYY-MM-DD. */ date: string; diff --git a/lib/leaderboard/leaderboard_client.ts b/lib/leaderboard/leaderboard_client.ts index 361d4a2..8fe35d4 100644 --- a/lib/leaderboard/leaderboard_client.ts +++ b/lib/leaderboard/leaderboard_client.ts @@ -1,3 +1,4 @@ +import { DAY } from "lc-dailies/deps.ts"; import type * as lc from "lc-dailies/lib/lc/mod.ts"; /** @@ -156,7 +157,8 @@ export function calculateSubmissionScore( question: lc.DailyQuestion, options: CalculateScoresOptions, ): number { - const questionDate = new Date(question.date); + const [qYear, qMonth, qDay] = question.date.split("-").map(Number); + const questionDate = new Date(qYear, qMonth - 1, qDay); const submissionDate = new Date(submission.date); const msElapsed = submissionDate.getTime() - questionDate.getTime(); const ratio = Math.min(Math.max(msElapsed / options.duration, 0), 1); @@ -223,6 +225,6 @@ export function makeDefaultCalculateScoresOptions( season, possibleHighestScore: 100, possibleLowestScore: 50, - duration: 1e3 * 60 * 60 * 24 * 7, + duration: DAY, }; } diff --git a/lib/leaderboard/leaderboard_client_test.ts b/lib/leaderboard/leaderboard_client_test.ts index e4f5489..a962987 100644 --- a/lib/leaderboard/leaderboard_client_test.ts +++ b/lib/leaderboard/leaderboard_client_test.ts @@ -1,10 +1,10 @@ -import { assertEquals } from "lc-dailies/deps.ts"; +import { assertEquals, DAY } from "lc-dailies/deps.ts"; import { calculatePlayerScore, calculateSeasonScores, } from "./leaderboard_client.ts"; -const FAKE_DURATION = 1e3 * 60 * 60 * 24; +const FAKE_DURATION = DAY; const FAKE_POSSIBLE_HIGHEST_SCORE = 100; const FAKE_POSSIBLE_LOWEST_SCORE = 50; const FAKE_SEASON = { @@ -71,11 +71,11 @@ Deno.test("calculatePlayerScore calculates the score of a player", () => { possibleLowestScore: FAKE_POSSIBLE_LOWEST_SCORE, }, ), - 159, + 150, ); }); -Deno.test("calculateSeasonScore calculates the scores of a season", () => { +Deno.test("calculateSeasonScores calculates the scores of a season", () => { const seasonScores = calculateSeasonScores({ season: FAKE_SEASON, duration: FAKE_DURATION, @@ -85,10 +85,10 @@ Deno.test("calculateSeasonScore calculates the scores of a season", () => { assertEquals( seasonScores["redacted_discord_id_00"], - 159, + 150, ); assertEquals( seasonScores["redacted_discord_id_01"], - 145, + 122, ); }); diff --git a/main.ts b/main.ts index 69aa818..6a1f1a2 100644 --- a/main.ts +++ b/main.ts @@ -9,7 +9,7 @@ if (import.meta.main) { } async function main() { - const kv = await Deno.openKv(); + const kv = await Deno.openKv(env.KV_URL); const lcClient = new lc.LCClient(); const leaderboardClient = new DenoKvLeaderboardClient( kv, From d20288c2286a08ef1fbbd93a12e467e682069f7e Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:20:22 -0700 Subject: [PATCH 07/10] add `formatScores` --- api/dailies.ts | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/api/dailies.ts b/api/dailies.ts index 8a63971..aa92bb4 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -148,33 +148,33 @@ export function makeDailyWebhookEmbeds( return [questionEmbed]; } - const scores = leaderboard.calculateSeasonScores( - leaderboard.makeDefaultCalculateScoresOptions(options.season), - ); - const formattedLeaderboardTitle = - `Leaderboard for week of ${options.season.start_date}`; - const formattedLeaderboard = [ - "```", - ...Object.entries(scores) - .sort(({ 1: scoreA }, { 1: scoreB }) => scoreB - scoreA) - .map(([playerID, score], i) => { - const player = options.season!.players[playerID]; - const formattedScore = String(score).padStart(3, " "); - const formattedRank = formatRank(i + 1); - return `${formattedScore} ${player.lc_username} (${formattedRank})`; - }), - "```", - ].join("\n"); const leaderboardEmbed: APIEmbed = { fields: [{ - name: formattedLeaderboardTitle, - value: formattedLeaderboard, + name: `Leaderboard for week of ${options.season.start_date}`, + value: formatScores(options.season), }], }; return [questionEmbed, leaderboardEmbed]; } +/** + * formatScores formats the scores of all players in a season. + */ +export function formatScores(season: leaderboard.Season): string { + const scores = leaderboard.calculateSeasonScores( + leaderboard.makeDefaultCalculateScoresOptions(season), + ); + return Object.entries(scores) + .sort(({ 1: scoreA }, { 1: scoreB }) => scoreB - scoreA) + .map(([playerID, score], i) => { + const formattedScore = String(score).padStart(3, " "); + const formattedRank = formatRank(i + 1); + return `${formattedScore} ${playerID} (${formattedRank})`; + }) + .join("\n"); +} + /** * formatRank formats the rank of a player in a season. */ From 770e069dc21fcd8d931ae5b511b804b486600b43 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:34:57 -0700 Subject: [PATCH 08/10] combine leaderboard embed with question embed --- api/dailies.ts | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/api/dailies.ts b/api/dailies.ts index aa92bb4..fc10c83 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -122,10 +122,10 @@ export interface DailyWebhookOptions { export function makeDailyWebhookEmbeds( options: DailyWebhookOptions, ): APIEmbed[] { - const questionEmbed: APIEmbed = { + const embed: APIEmbed = { title: options.question.title, url: options.question.url, - description: `Daily Leetcode Question for ${options.question.date}.`, + description: `Daily Leetcode question for ${options.question.date}.`, fields: [ { name: "Difficulty", @@ -133,29 +133,26 @@ export function makeDailyWebhookEmbeds( inline: true, }, { - name: "Snack", + name: "Here is a snack to get your brain working!", value: snacks.pickRandom(), inline: true, }, + { + name: + "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below!", + value: "[More info](https://acmcsuf.com/lc-dailies-handbook)", + }, ], - footer: { - text: - "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below! [More Info](https://acmcsuf.com/lc-dailies-handbook)", - }, }; - if (!options.season) { - return [questionEmbed]; - } - - const leaderboardEmbed: APIEmbed = { - fields: [{ + if (options.season) { + embed.fields?.push({ name: `Leaderboard for week of ${options.season.start_date}`, value: formatScores(options.season), - }], - }; + }); + } - return [questionEmbed, leaderboardEmbed]; + return [embed]; } /** @@ -165,13 +162,18 @@ export function formatScores(season: leaderboard.Season): string { const scores = leaderboard.calculateSeasonScores( leaderboard.makeDefaultCalculateScoresOptions(season), ); - return Object.entries(scores) - .sort(({ 1: scoreA }, { 1: scoreB }) => scoreB - scoreA) - .map(([playerID, score], i) => { - const formattedScore = String(score).padStart(3, " "); - const formattedRank = formatRank(i + 1); - return `${formattedScore} ${playerID} (${formattedRank})`; - }) + return [ + "```", + ...Object.entries(scores) + .sort(({ 1: scoreA }, { 1: scoreB }) => scoreB - scoreA) + .map(([playerID, score], i) => { + const player = season.players[playerID]; + const formattedScore = String(score).padStart(3, " "); + const formattedRank = formatRank(i + 1); + return `${formattedScore} ${player.lc_username} (${formattedRank})`; + }), + "```", + ] .join("\n"); } From dd76295368f4635f9f860bf8106a3e5dae8e4651 Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 18:42:04 -0700 Subject: [PATCH 09/10] fix `calculateSubmissionScore` broken tests --- lib/leaderboard/leaderboard_client.ts | 3 +-- lib/leaderboard/leaderboard_client_test.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/leaderboard/leaderboard_client.ts b/lib/leaderboard/leaderboard_client.ts index 8fe35d4..01873f5 100644 --- a/lib/leaderboard/leaderboard_client.ts +++ b/lib/leaderboard/leaderboard_client.ts @@ -157,8 +157,7 @@ export function calculateSubmissionScore( question: lc.DailyQuestion, options: CalculateScoresOptions, ): number { - const [qYear, qMonth, qDay] = question.date.split("-").map(Number); - const questionDate = new Date(qYear, qMonth - 1, qDay); + const questionDate = new Date(`${question.date} GMT`); const submissionDate = new Date(submission.date); const msElapsed = submissionDate.getTime() - questionDate.getTime(); const ratio = Math.min(Math.max(msElapsed / options.duration, 0), 1); diff --git a/lib/leaderboard/leaderboard_client_test.ts b/lib/leaderboard/leaderboard_client_test.ts index a962987..261f0ca 100644 --- a/lib/leaderboard/leaderboard_client_test.ts +++ b/lib/leaderboard/leaderboard_client_test.ts @@ -71,7 +71,7 @@ Deno.test("calculatePlayerScore calculates the score of a player", () => { possibleLowestScore: FAKE_POSSIBLE_LOWEST_SCORE, }, ), - 150, + 159, ); }); @@ -85,10 +85,10 @@ Deno.test("calculateSeasonScores calculates the scores of a season", () => { assertEquals( seasonScores["redacted_discord_id_00"], - 150, + 159, ); assertEquals( seasonScores["redacted_discord_id_01"], - 122, + 145, ); }); From d2171a41ba3eb6fcbeb2d1e5ef4eec9801827d5d Mon Sep 17 00:00:00 2001 From: Ethan Davidson <31261035+EthanThatOneKid@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:17:22 -0700 Subject: [PATCH 10/10] Update dailies.ts --- api/dailies.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/dailies.ts b/api/dailies.ts index fc10c83..9dfa4ab 100644 --- a/api/dailies.ts +++ b/api/dailies.ts @@ -140,7 +140,7 @@ export function makeDailyWebhookEmbeds( { name: "Submit your solution by typing `/lc submit YOUR_SUBMISSION_URL` below!", - value: "[More info](https://acmcsuf.com/lc-dailies-handbook)", + value: "[See more…](https://acmcsuf.com/lc-dailies-handbook)", }, ], }; @@ -173,8 +173,7 @@ export function formatScores(season: leaderboard.Season): string { return `${formattedScore} ${player.lc_username} (${formattedRank})`; }), "```", - ] - .join("\n"); + ].join("\n"); } /**