From c98903a638acca0202a757942a8f0198abf0c46e Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Tue, 4 Jun 2024 13:48:17 +1000 Subject: [PATCH 1/4] Fix for submitting old scores, and recognising mobile link for The Mini --- src/discord/commands/guild/d.nyt.ts | 4 --- src/discord/utils/messageCommand.ts | 2 +- src/discord/utils/nytUtils.ts | 45 +++++++++++++++++++---------- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/discord/commands/guild/d.nyt.ts b/src/discord/commands/guild/d.nyt.ts index 444ec92b8..6dda25af7 100644 --- a/src/discord/commands/guild/d.nyt.ts +++ b/src/discord/commands/guild/d.nyt.ts @@ -258,10 +258,6 @@ export const dNYT: SlashCommand = { **๐Ÿ“… Submission Streak:** ${results.stats.submissionStreak} - **๐Ÿ”ฅ Win Streak:** ${results.stats.currentStreak} - - **โค๏ธโ€๐Ÿ”ฅ Best Win Streak:** ${results.stats.bestStreak} - **๐Ÿ† Best Time:** ${formatTime(results.stats.bestTime)} **โฑ๏ธ Average Time:** ${formatTime(results.stats.averageTime)} diff --git a/src/discord/utils/messageCommand.ts b/src/discord/utils/messageCommand.ts index 0ddb3ff73..241c92764 100644 --- a/src/discord/utils/messageCommand.ts +++ b/src/discord/utils/messageCommand.ts @@ -113,7 +113,7 @@ async function isTheMini(message: Message): Promise { const userId = message.author.id; // Extract userId from message // Regular expression to check if the message possibly mentions a The Mini score - const theMiniScorePattern = /https:\/\/www\.nytimes\.com\/badges\/games\/mini\.html\?d=\d{4}-\d{2}-\d{2}&t=\d+&c=[a-f0-9]+&smid=url-share/; + const theMiniScorePattern = /(https:\/\/www\.nytimes\.com\/badges\/games\/mini\.html\?d=\d{4}-\d{2}-\d{2}&t=\d+&c=[a-f0-9]+&smid=url-share)|(https:\/\/www\.nytimes\.com\/crosswords\/game\/mini)/; const match = messageContent.match(theMiniScorePattern); // If a match is found, send the message content for further processing diff --git a/src/discord/utils/nytUtils.ts b/src/discord/utils/nytUtils.ts index 8d6c49fec..cace54890 100644 --- a/src/discord/utils/nytUtils.ts +++ b/src/discord/utils/nytUtils.ts @@ -79,6 +79,7 @@ export namespace Wordle { stats.gamesPlayed = scores.length; const wins = scores.filter(score => score.score > 0).length; + // TODO: Round the win rate to whole percentage stats.winRate = wins / stats.gamesPlayed; // Find the frequency of each score const scoreFrequency = scores.reduce((acc: { [key: number]: number }, score) => { @@ -248,7 +249,7 @@ export namespace Wordle { // Prevent users from submitting puzzles from the future const puzzleNumber = parseInt(match[2].replace(',', ''), 10); const validPuzzleNumbers = await Wordle.todaysPuzzles(); - if (!validPuzzleNumbers.includes(puzzleNumber)) { + if (puzzleNumber > Math.max(...validPuzzleNumbers)) { log.debug(F, `Invalid Wordle score found (Puzzle number is from the future): ${match[1]}`); return false; } @@ -519,8 +520,8 @@ export namespace Connections { if (match) { const puzzleNumber = parseInt(match[1].match(/\d+/)?.[0] ?? 'NaN', 10); const validPuzzleNumbers = await Connections.todaysPuzzles(); - if (!validPuzzleNumbers.includes(puzzleNumber)) { - log.debug(F, `Invalid Connections puzzle found (Puzzle number is from the future): ${match[1]}`); + if (puzzleNumber > Math.max(...validPuzzleNumbers)) { + log.debug(F, `Invalid Wordle score found (Puzzle number is from the future): ${match[1]}`); return false; } @@ -784,26 +785,40 @@ export namespace TheMini { } export async function process(userId: string, messageContent: string): Promise { - const theMiniScorePattern = /https:\/\/www\.nytimes\.com\/badges\/games\/mini\.html\?d=\d{4}-\d{2}-\d{2}&t=\d+&c=[a-f0-9]+&smid=url-share/; + const theMiniScorePattern = /(https:\/\/www\.nytimes\.com\/badges\/games\/mini\.html\?d=\d{4}-\d{2}-\d{2}&t=\d+&c=[a-f0-9]+&smid=url-share)|(https:\/\/www\.nytimes\.com\/crosswords\/game\/mini)/; + log.debug(F, `Processing message for The Mini score: ${messageContent}`); const match = messageContent.match(theMiniScorePattern); if (match) { + log.debug(F, `The Mini puzzle found: ${match[0]}`); const url = match[0]; - const urlParts = url.split('&'); - const dateString = urlParts[0].split('=')[1]; - const timeString = urlParts[1].split('=')[1]; - const date = dateString; - log.debug(F, `dateString: ${dateString}, timeString: ${timeString}`); + let dateString; let + timeString; + if (url.includes('badges')) { + const urlParts = url.split('&'); + dateString = urlParts[0].split('=')[1]; + timeString = urlParts[1].split('=')[1]; + } else { + const dateMatch = messageContent.match(/(\d{1,2}\/\d{1,2}\/\d{4})/); + if (dateMatch) { + const dateParts = dateMatch[1].split('/'); + dateString = `${dateParts[2]}-${dateParts[0].padStart(2, '0')}-${dateParts[1].padStart(2, '0')}`; + } + const timeMatch = messageContent.match(/in (\d+):(\d+)/); + if (timeMatch) { + timeString = (parseInt(timeMatch[1]) * 60 + parseInt(timeMatch[2])).toString(); + } + } - // Get the date from the date string and make it into a (YYYY, MM, DD) tuple + // Check if dateString and timeString are defined before parsing them + if (dateString === undefined || timeString === undefined) { + return false; + } + + const date = dateString; // Convert time string to integer with radix parameter const time = parseInt(timeString, 10); - // const validDates = await todaysMiniDates(); - // if (!validDates.includes(date.toISOString().substring(0, 10))) { - // log.debug(F, `Invalid Mini puzzle found (Date is from the future): ${url}`); - // return false; - // } log.debug(F, `The Mini puzzle found: ${url}, Date: ${date}, Time: ${time}`); await TheMini.updateStats(userId, { score: time, puzzle: date }); return true; From 0a603a34815c24f328250accb14656284ef09d51 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Fri, 7 Jun 2024 23:02:59 +1000 Subject: [PATCH 2/4] Bugfixes for NYT tracking, remove /poll --- src/discord/commands/global/d.poll.ts | 189 -------------------- src/discord/commands/guild/d.levels.ts | 4 +- src/discord/commands/guild/d.profile.ts | 6 +- src/discord/events/messageReactionAdd.ts | 2 - src/discord/events/messageReactionRemove.ts | 2 - src/discord/utils/announcements.ts | 8 +- 6 files changed, 10 insertions(+), 201 deletions(-) delete mode 100644 src/discord/commands/global/d.poll.ts diff --git a/src/discord/commands/global/d.poll.ts b/src/discord/commands/global/d.poll.ts deleted file mode 100644 index 05d07d0d0..000000000 --- a/src/discord/commands/global/d.poll.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { - SlashCommandBuilder, - GuildMember, - Message, - ChannelType, - PermissionResolvable, - MessageReaction, -} from 'discord.js'; -import { stripIndents } from 'common-tags'; -import { SlashCommand } from '../../@types/commandDef'; -import commandContext from '../../utils/context'; -import { embedTemplate } from '../../utils/embedTemplate'; // eslint-disable-line -import { checkChannelPermissions } from '../../utils/checkPermissions'; - -const F = f(__filename); - -const emojiDict = { - 1: '1๏ธโƒฃ', - 2: '2๏ธโƒฃ', - 3: '3๏ธโƒฃ', - 4: '4๏ธโƒฃ', - 5: '5๏ธโƒฃ', - 6: '6๏ธโƒฃ', - 7: '7๏ธโƒฃ', - 8: '8๏ธโƒฃ', - 9: '9๏ธโƒฃ', -}; - -export const dPoll: SlashCommand = { - data: new SlashCommandBuilder() - .setName('poll') - .setDescription('Creates a poll!') - .addStringOption(option => option - .setName('question') - .setDescription('What do you want to ask?') - .setRequired(true)) - .addStringOption(option => option - .setName('choices') - .setDescription('List of up to 9 options separated by commas, EG: Red, Blue, Green') - .setRequired(true)), - async execute(interaction) { - log.info(F, await commandContext(interaction)); - await interaction.deferReply({ ephemeral: true }); - - if (!interaction.channel) { - await interaction.editReply('You need to be in a channel to use this command!'); - return false; - } - - if (interaction.channel.type === ChannelType.DM) { - await interaction.editReply('You can\'t poll yourself!'); - return false; - } - - if (interaction.channel.type === ChannelType.GuildVoice) { - await interaction.editReply('You can\'t poll a voice channel!'); - return false; - } - - const perms = await checkChannelPermissions(interaction.channel, [ - 'ViewChannel' as PermissionResolvable, - 'SendMessages' as PermissionResolvable, - 'AddReactions' as PermissionResolvable, - ]); - - if (!perms.hasPermission) { - await interaction.editReply({ content: `Please make sure I can ${perms.permission} here!` }); - return false; - } - - // await interaction.deferReply({ephemeral: true}); - // interaction.editReply({ content: 'Creating poll...': true }); - let question = interaction.options.getString('question'); - // log.debug(F, `question: ${question}`); - const optionsString = interaction.options.getString('choices'); - // log.debug(F, `optionsString: ${optionsString}`); - if (!question || !optionsString) { - await interaction.editReply('You need to provide a question and options!'); - return false; - } - const optionsArray = optionsString.split(',').map(option => option.trim()); - - if (optionsArray.length > 9) { - await interaction.editReply('You can only have 9 options max!'); - return false; - } - - let body = ''; - for (let i = 0; i < optionsArray.length; i += 1) { - body += `\n${i + 1}. ${optionsArray[i].trim()}`; - } - - const pollEmbed = embedTemplate() - .setAuthor(null) - .setDescription(stripIndents`${body}`) - .setFooter({ text: `A poll by ${(interaction.member as GuildMember).displayName}` }); - - // Check if the question has any mentions. - const mentions = question.match(/<.\d+>/g); - if (mentions) { - // log.debug(F, `mentions: ${mentions}`); - // Loop through each mention and replace it. - for (const mention of mentions) { // eslint-disable-line - // log.debug(F, `mention: ${mention}`); - const fullId = mention.replace(/[<>]/g, ''); - // log.debug(F, `fullId: ${fullId}`); - // Check to see what kind of prefix the mention has. - const prefix = fullId[0]; - // log.debug(F, `prefix: ${prefix}`); - const id = fullId.slice(1); - // log.debug(F, `id: ${id}`); - - let targetString = 'Unknown User' as string; - if (prefix === '@') { - const target = await interaction.guild?.members.fetch(id); // eslint-disable-line - if (target) { - targetString = target.displayName; - } - } else if (prefix === '#') { - const target = await interaction.guild?.channels.fetch(id); // eslint-disable-line - if (target) { - targetString = target.name; - } - } else if (prefix === '&') { - const target = await interaction.guild?.roles.fetch(id); // eslint-disable-line - if (target) { - targetString = target.name; - } - } - - // log.debug(F, `targetString: ${targetString}`); - - question = question.replace(mention, targetString); - } - } - // log.debug(F, `question: ${question}`); - pollEmbed.setTitle(`**${question}**`); - - await interaction.channel.send({ embeds: [pollEmbed] }) - .then(async (msg:Message) => { - for (let i = 0; i < optionsArray.length; i += 1) { - /* eslint-disable no-await-in-loop */ - await msg.react(emojiDict[i + 1 as keyof typeof emojiDict]); - } - }); - - await interaction.editReply({ content: 'Done!' }); - return true; - }, -}; - -export default dPoll; - -export async function updatePollEmbed({ message, emoji: { name: emojiName } }: MessageReaction) { - log.debug(F, 'updatePollEmbed triggered'); - if (emojiName && message.embeds[0] && message.embeds[0]?.footer?.text?.includes('A poll by')) { - const { description, title, footer } = message.embeds[0]; - if (description) { - const totalVotes = message.reactions.cache.reduce((total, reaction) => total + reaction.count - 1, 0); // subtract one from the total votes to account for the bot's vote - const descriptionArray = description.split('\n'); - const newDescriptionArray = descriptionArray.map((line, index) => { - const emoji = Object.values(emojiDict)[index]; - const reaction = message.reactions.cache.get(emoji); - if (reaction) { - const percentageRegex = / - (\d+(\.\d+)?%)?$/; // matches a percentage number at the end of the line after a dash - if (totalVotes === 0) { - return line.replace(percentageRegex, ''); // remove the percentage numbers - } - const percentage = (((reaction.count - 1) / totalVotes) * 100).toFixed(0); // subtract one from the count of each reaction - if (percentageRegex.test(line)) { - return line.replace(percentageRegex, ` - ${percentage}%`); // replace the existing percentage with the new percentage - } - return `${line} - ${percentage}%`; // append the new percentage to the line - } - return line; - }); - const newDescription = newDescriptionArray.join('\n'); - // Create a new embed with the updated description, title and footer - const pollEmbed = embedTemplate() - .setAuthor(null) - .setTitle(title) - .setDescription(newDescription) - .setFooter({ text: footer?.text, iconURL: footer?.iconURL }); - // Edit the message to use the new embed - await message.edit({ embeds: [pollEmbed] }); - // log.debug(F, `Updated poll embed`); - } - } -} diff --git a/src/discord/commands/guild/d.levels.ts b/src/discord/commands/guild/d.levels.ts index eb605fbcc..d253b0285 100644 --- a/src/discord/commands/guild/d.levels.ts +++ b/src/discord/commands/guild/d.levels.ts @@ -334,8 +334,8 @@ export const dLevels: SlashCommand = { // Generate the colors for the card based on the user's role color const roleColor = `#${(target.roles.color?.color || 0x99aab5).toString(16).padStart(6, '0')}`; - const cardLightColor = generateColors(roleColor, 0, -72, -67); - const cardDarkColor = generateColors(roleColor, 0, -72, -82); + const cardLightColor = generateColors(roleColor, 0, -75, -67); + const cardDarkColor = generateColors(roleColor, 0, -75, -80); const chipColor = generateColors(roleColor, 0, -50, -50); const barColor = generateColors(roleColor, 0, -20, -10); const textColor = generateColors(roleColor, 0, 0, 0); diff --git a/src/discord/commands/guild/d.profile.ts b/src/discord/commands/guild/d.profile.ts index dfaf28c48..2dc089356 100644 --- a/src/discord/commands/guild/d.profile.ts +++ b/src/discord/commands/guild/d.profile.ts @@ -169,7 +169,7 @@ export const dProfile: SlashCommand = { const roleColor = `#${(target.roles.color?.color || 0x99aab5).toString(16).padStart(6, '0')}`; log.debug(F, `roleColor: ${roleColor}`); - const cardLightColor = generateColors(roleColor, 0, -75, -70); + const cardLightColor = generateColors(roleColor, 0, -75, -67); const cardDarkColor = generateColors(roleColor, 0, -75, -80); const chipColor = generateColors(roleColor, 0, -50, -50); const barColor = generateColors(roleColor, 0, -20, -10); @@ -529,8 +529,8 @@ export async function getProfilePreview(target: GuildMember, option: string, ima // Generate the colors for the card based on the user's role color const roleColor = `#${(target.roles.color?.color || 0x99aab5).toString(16).padStart(6, '0')}`; - const cardLightColor = generateColors(roleColor, 0, -72, -67); - const cardDarkColor = generateColors(roleColor, 0, -72, -82); + const cardLightColor = generateColors(roleColor, 0, -75, -67); + const cardDarkColor = generateColors(roleColor, 0, -75, -80); const chipColor = generateColors(roleColor, 0, -50, -50); const barColor = generateColors(roleColor, 0, -20, -10); const textColor = generateColors(roleColor, 0, 0, 0); diff --git a/src/discord/events/messageReactionAdd.ts b/src/discord/events/messageReactionAdd.ts index b3a1c6061..679c52f3b 100644 --- a/src/discord/events/messageReactionAdd.ts +++ b/src/discord/events/messageReactionAdd.ts @@ -7,7 +7,6 @@ import { } from '../@types/eventDef'; import { chitragupta } from '../utils/chitragupta'; import { bestOf } from '../utils/bestOfTripsit'; -import { updatePollEmbed } from '../commands/global/d.poll'; import { aiReaction } from '../commands/global/d.ai'; // import log from '../../global/utils/log'; // import {parse} from 'path'; @@ -47,7 +46,6 @@ export const messageReactionAdd: MessageReactionAddEvent = { chitragupta(messageReaction, user, 1); bestOf(messageReaction); - updatePollEmbed(messageReaction); // await communityMod(reaction, user); }, }; diff --git a/src/discord/events/messageReactionRemove.ts b/src/discord/events/messageReactionRemove.ts index 2f19f8caf..e3df34fa0 100644 --- a/src/discord/events/messageReactionRemove.ts +++ b/src/discord/events/messageReactionRemove.ts @@ -6,7 +6,6 @@ import { MessageReactionRemoveEvent, } from '../@types/eventDef'; import { chitragupta } from '../utils/chitragupta'; -import { updatePollEmbed } from '../commands/global/d.poll'; const F = f(__filename); @@ -46,7 +45,6 @@ export const messageReactionRemove: MessageReactionRemoveEvent = { // return; // } chitragupta(reaction, user, -1); - updatePollEmbed(reaction); }, }; diff --git a/src/discord/utils/announcements.ts b/src/discord/utils/announcements.ts index c838528ec..1f7427baf 100644 --- a/src/discord/utils/announcements.ts +++ b/src/discord/utils/announcements.ts @@ -129,6 +129,7 @@ export async function announcements(message:Message) { '**Reminder:** Always research any substances you plan to take. Understanding the effects and potential risks can help you make safer choices.', '**Reminder:** Remember to take regular breaks when using screens for a long period of time to avoid eye strain.', '**Reminder:** TripSit strives to be a safe place for everyone. Please be kind and report any inappropriate behavior.', + '**Reminder:** Spending time outdoors is proven to have a positive impact on your mental health. Try to get some fresh air every day!', ]; const tipAnnouncements = [ @@ -136,12 +137,12 @@ export async function announcements(message:Message) { '**Tip:** Report a specific user or message with Right Click > Apps > Report Message.', '**Tip:** Use `/help` to learn more about the bot and its commands.', '**Tip:** Check out the for more tips and server info!', - '**Tip:** Go to to change your name color and mindset!', + '**Tip:** Go to to change your name color and mindset role icon!', '**Tip:** Use the "ephemeral" option in TripBot commands to use them privately.', - `**Tip:** Find a message especially fantastic? React with ${emojiGet('karma_upvote')} to reward karma!`, '**Tip:** Earn TripTokens in `/rpg` to buy customization items for your `/profile`!', '**Tip:** Head to the Activities Corner in the for small games and activities!', '**Tip:** Curious about a specific server function like levelling or karma? Head to Server Tips in the !', + '**Tip:** Keen to help with tripsitting? Head to the for info on how to become a Helper!', ]; const funAnnouncements = [ @@ -154,7 +155,8 @@ export async function announcements(message:Message) { '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/fastest-time-to-eat-a-bowl-of-pasta) The fastest time to eat a bowl of pasta is 17.03 seconds.', '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/most-ice-cream-scoops-balanced-on-a-cone) The most ice cream scoops balanced on a cone is 125.', '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/largest-collection-of-pokemon-memorabilia) The largest collection of Pokรฉmon memorabilia is 17,127 items.', - '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/most-people-making-heart-shaped-hand-gestures) The most people making heart-shaped hand gestures at once is 7,232.', + '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/69605-longest-conga-line) The record for the longest conga line is 119,986 people.', + '[**Did you know?**](https://www.guinnessworldrecords.com/world-records/longest-cake) The longest cake ever made was 5300m (17,388 ft) long, and was eaten in 10 minutes!', ]; // const chanAnnouncements = [ From 3050cd796c012845871bbd9f2734e70e87538be3 Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Fri, 7 Jun 2024 23:13:06 +1000 Subject: [PATCH 3/4] Whoops --- src/discord/utils/nytUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/discord/utils/nytUtils.ts b/src/discord/utils/nytUtils.ts index cace54890..889abedb2 100644 --- a/src/discord/utils/nytUtils.ts +++ b/src/discord/utils/nytUtils.ts @@ -795,8 +795,8 @@ export namespace TheMini { timeString; if (url.includes('badges')) { const urlParts = url.split('&'); - dateString = urlParts[0].split('=')[1]; - timeString = urlParts[1].split('=')[1]; + [dateString] = urlParts[0].split('='); + [, timeString] = urlParts[1].split('='); } else { const dateMatch = messageContent.match(/(\d{1,2}\/\d{1,2}\/\d{4})/); if (dateMatch) { @@ -805,7 +805,7 @@ export namespace TheMini { } const timeMatch = messageContent.match(/in (\d+):(\d+)/); if (timeMatch) { - timeString = (parseInt(timeMatch[1]) * 60 + parseInt(timeMatch[2])).toString(); + timeString = ((parseInt(timeMatch[1], 10) * 60) + parseInt(timeMatch[2], 10)).toString(); } } From 5644c6652861bfe1f93cfc88f58401646253719b Mon Sep 17 00:00:00 2001 From: Hipperooni Date: Mon, 10 Jun 2024 21:35:40 +1000 Subject: [PATCH 4/4] Add more rounding --- src/discord/commands/guild/d.nyt.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/discord/commands/guild/d.nyt.ts b/src/discord/commands/guild/d.nyt.ts index 6dda25af7..f64dbd136 100644 --- a/src/discord/commands/guild/d.nyt.ts +++ b/src/discord/commands/guild/d.nyt.ts @@ -170,7 +170,7 @@ export const dNYT: SlashCommand = { embed.setDescription(stripIndents` **๐ŸŽฎ Games Played:** ${results.stats.gamesPlayed} - **๐Ÿ† Win Rate:** ${(results.stats.winRate * 100)}% + **๐Ÿ† Win Rate:** ${Math.round(results.stats.winRate * 100)}% **๐Ÿ“… Submission Streak:** ${results.stats.submissionStreak} @@ -218,7 +218,7 @@ export const dNYT: SlashCommand = { embed.setDescription(stripIndents` **๐ŸŽฎ Games Played:** ${results.stats.gamesPlayed} - **๐Ÿ† Win Rate:** ${(results.stats.winRate * 100)}% + **๐Ÿ† Win Rate:** ${Math.round(results.stats.winRate * 100)}% **๐Ÿ“… Submission Streak:** ${results.stats.submissionStreak} @@ -319,7 +319,7 @@ export const dNYT: SlashCommand = { embed.setDescription(stripIndents` **๐ŸŽฎ Games Played:** ${results.stats.gamesPlayed} - **๐Ÿ† Win Rate:** ${(results.stats.winRate * 100)}% + **๐Ÿ† Win Rate:** ${Math.round(results.stats.winRate * 100)}% **๐Ÿ“Š Guess Distribution:** ${frequencyGraph} @@ -376,7 +376,7 @@ export const dNYT: SlashCommand = { embed.setDescription(stripIndents` **๐ŸŽฎ Games Played:** ${results.stats.gamesPlayed} - **๐Ÿ† Win Rate:** ${(results.stats.winRate * 100)}% + **๐Ÿ† Win Rate:** ${Math.round(results.stats.winRate * 100)}% **๐Ÿ“Š Mistakes Distribution:** ${frequencyGraph}