/* * Commit Overflow Counting - Slash Command Definition File * commit-count.js - Gets an initial commit count for Commit Overflow */ const { SlashCommandBuilder, EmbedBuilder } = require('discord.js'); const { convertSnowflakeToDate, convertDateToSnowflake, } = require('../utils/convert.js'); module.exports = { data: new SlashCommandBuilder() .setName('commit-count') .setDescription('Counts Commits') // .addStringOption(option => // option.setName('emoji') // .setDescription('Optionally choose an emoji for verified messages')) // .addMentionableOption(option => // option.setName('role') // .setDescription('Choose a role for those that can react to verify')) .addStringOption((option) => option .setName('start') .setDescription( 'Optionally specify a start date (MM/DD/YYYY preferred)', ), ) .addStringOption((option) => option .setName('end') .setDescription('Optionally specify a end date (MM/DD/YYYY preferred)'), ) .addStringOption((option) => option .setName('ephemeral') .setDescription('Post the avatar in the current channel') .addChoices( { name: 'Send to me only', value: 'true' }, { name: 'Send in channel', value: 'false' }, ), ), async execute(interaction) { if (interaction.inGuild() && interaction.channel.isThread()) { const threadStarterMessage = await interaction.channel.fetchStarterMessage(); const emojiCheck = interaction.options.getString('emoji') ?? 'gh_green_square'; const startDate = new Date( interaction.options.getString('start-date') ?? threadStarterMessage.createdTimestamp, ); // let endDate = new Date( // interaction.options.getString('end-date') ?? // interaction.createdTimestamp, // ); const isEphemeral = interaction.options.getString('ephemeral') ?? 'true'; await interaction.deferReply({ ephemeral: (isEphemeral === 'true') }); // Fetch Messages const sum_messages = []; let first_id = convertDateToSnowflake(startDate); let messageMap = new Map(); let continueFetching = true; while (continueFetching) { const options = { limit: 100 }; options.after = first_id; try { const messages = await interaction.channel.messages.fetch(options); sum_messages.push(...messages.values()); for (const [snowflake, message] of messages) { if (message.author.id === threadStarterMessage.author.id) { const messageDate = convertSnowflakeToDate(snowflake); let monthName = ''; // Assuming only Dec, Jan for now, can be expanded later if (messageDate.getMonth() === 11) { monthName = 'Dec.'; } else if (messageDate.getMonth() === 0) { monthName = 'Jan.'; } const mapKey = monthName + ' ' + (messageDate.getDate() - 1).toString(); if (messageMap.has(mapKey)) { const mapValue = messageMap.get(mapKey); mapValue.push(snowflake); messageMap.set(mapKey, mapValue); } else { const mapValue = [snowflake]; messageMap.set(mapKey, mapValue); } } } first_id = messages.first().id; if (messages.size < 100) { continueFetching = false; } } catch (error) { console.error('Error fetching messages: ', error); continueFetching = false; } } messageMap = new Map([...messageMap.entries()].sort()); // Start Commit Checking let validString = ''; let invalidString = ''; const validDays = []; for (const [date] of messageMap) { let dateVerified = false; let verifyReason = ''; const messageList = messageMap.get(date); for (const messageId of messageList) { if (verifyReason == '') { await interaction.channel.messages .fetch(messageId) .then(async (message) => { if ( /http.:\/\/.*\/(commit|compare|pull)\/.*/g.test(message.content) ) { dateVerified = true; verifyReason = `Commit URL - ${message.id}`; } else if ((message.attachments.size > 0)) { dateVerified = true; verifyReason = `Attachment - ${message.id}`; } else if ((message.reactions.cache.size > 0)) { for (const reaction of message.reactions.cache.values()) { const emojiName = reaction._emoji.name; if (emojiName === emojiCheck) { dateVerified = true; verifyReason = `Reaction - ${message.id}`; break; } } } }) .catch(console.error); } } if (dateVerified) { validString += `${date}: ${verifyReason}\n`; validDays.push(date); } } const resultsEmbed = { title: 'Commits', fields: [ { name: 'Valid Commits', value: validString, }, ], }; const allDays = []; const dayMap = new Map(); const currentDate = new Date( interaction.options.getString('start-date') ?? threadStarterMessage.createdTimestamp, ); const endDate = new Date( interaction.options.getString('end-date') ?? interaction.createdTimestamp, ); endDate.setDate(endDate.getDate() + 1); while (currentDate <= endDate) { allDays.push(new Date(currentDate)); currentDate.setDate(currentDate.getDate() + 1); } for (const day of allDays) { let monthName = ''; // Assuming only Dec, Jan for ease at the moment. Can be changed later if (day.getMonth() == 11) { monthName = 'Dec.'; } else if (day.getMonth() == 0) { monthName = 'Jan.'; } const formattedDay = monthName + ' ' + day.getDate().toString(); if (validDays.includes(formattedDay)) { dayMap.set(formattedDay, true); } else { invalidString += `${formattedDay}\n`; dayMap.set(formattedDay, false); } } let streakCount = 0; const streaksArray = []; for (const [, hasCommit] of dayMap) { if (hasCommit) { streakCount += 1; } else { streaksArray.push(streakCount); streakCount = 0; } } streaksArray.push(streakCount); streakCount = 0; if (invalidString != '') { resultsEmbed.fields.push({ name: 'Missed Days', value: invalidString, }); resultsEmbed.footer = { text: `Organizers: Please check the above dates and react with ${emojiCheck} on any missed commits!`, }; } // Largest Array Value One Liner from https://stackoverflow.com/a/30850912 resultsEmbed.fields.push( { name: 'Total Commit Count', value: validDays.length, }, { name: 'Largest Streak', value: streaksArray[ streaksArray.reduce( (iMax, x, i, arr) => (x > arr[iMax] ? i : iMax), 0, ) ] ?? 0, }, ); await interaction.editReply({ embeds: [resultsEmbed], ephemeral: (isEphemeral === 'true') }); } else { const notInThread = new EmbedBuilder() .setColor(interaction.member.displayHexColor) .setTitle('Not in a Thread') .setDescription('You must run this command in a Thread!'); await interaction.reply({ embeds: [notInThread], ephemeral: true }); } }, };