Commit-Overflow-Counting/commands/commit-count.js

284 lines
11 KiB
JavaScript

/*
* 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().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((a, b) => {
if (a[1] == b[1]) {
if (b > a) {
return -1;
}
else if (a < b) {
return 1;
}
return 0;
}
else if (a[1] < b[1]) {
return -1;
}
else {
return 1;
}
}));
// 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) {
// Complicated... But TLDR gives priority to Commit URL, then Attachment, then Reaction
if (verifyReason == '' || verifyReason != 'Attachment' || verifyReason != 'Reaction') {
await interaction.channel.messages
.fetch(messageId)
.then(async (message) => {
if (
/http.:\/\/.*\/(commit|compare|pull)\/.*/g.test(message.content)
) {
dateVerified = true;
verifyReason = 'Commit URL';
}
else if ((message.attachments.size > 0) && verifyReason != 'Commit URL') {
dateVerified = true;
verifyReason = 'Attachment';
}
else if ((message.reactions.cache.size > 0) && verifyReason != 'Commit URL' && verifyReason != 'Attachment') {
for (const reaction of message.reactions.cache.values()) {
const emojiName = reaction._emoji.name;
if (emojiName === emojiCheck) {
dateVerified = true;
verifyReason = 'Reaction';
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') ??
new Date(),
);
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 if (formattedDay != (monthName + ' ' + (new Date()).getDate().toString())) {
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 });
}
},
};