From 52f2206b55f87d7fe9357de97340a7c655b8b92b Mon Sep 17 00:00:00 2001 From: Skylar Grant Date: Sat, 21 Jan 2023 09:58:32 -0500 Subject: [PATCH] Revert "Pull from treeanalyzer" This reverts commit d3ac2fc86981534c38690106a38feb37e23763d8. --- .DS_Store | Bin 0 -> 6148 bytes README.md | 49 +++++++++++- data/config.json | 9 ++- data/guildInfo.json | 1 + data/strings.json | 11 +-- main.js | 7 +- modules/functions.js | 149 ++++++++++++++++++++++++++++++++++++ package.json | 23 ++++++ slash-commands/compare.js | 12 +++ slash-commands/help.js | 49 ++++++++++++ slash-commands/reset.js | 12 +++ slash-commands/setup.js | 47 ++++++++++++ slash-commands/setupinfo.js | 12 +++ 13 files changed, 370 insertions(+), 11 deletions(-) create mode 100644 .DS_Store create mode 100644 data/guildInfo.json create mode 100644 package.json create mode 100644 slash-commands/compare.js create mode 100644 slash-commands/help.js create mode 100644 slash-commands/reset.js create mode 100644 slash-commands/setup.js create mode 100644 slash-commands/setupinfo.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3eeca27b58624472d7e02ec43c9755997cfea4ca GIT binary patch literal 6148 zcmeHKJ5Iw;5S)b+k&vR4lNXSoE$q7e{0EBSd5&($Jt;Yj@sT z?>x&>c)b8@_txJ7TL5diBi=nM&F{M}?5Z+Gr1Okt+~I@~FT5@@=WZ}zz>)Dg|A<%g znEL57JdNXU*>S3p0#ZNKq<|FoQoz3tjqcbL&WZ8qV2BZb zxMDht>zE~o%@f3~a86`~W=SO`)oR4Bq%+^Dt}C1qlMbul!|KUa6N<&td47v>SeK|M z1*E{a0@u0SdjEf*|1kfbleCipQs7@HV5|LMzvC-aZ(Y2c_u58(q message and a single message that everyone uses. If everyone creates their own and , the reference messages will have to be updated every time, or old data will be displayed.", + "setup": "To begin analyzing your Tree, first you must set up the reference messages.\n\n1. Run in the channel(s) that contain your server's tree and leaderboard messages.\n2. Now simply run where you want your analysis to be visible.", + "permissions": "At a minimum, Grow A Tree Analyzer requires permissions to `Send Messages` and `Send Messages in Threads` if applicable. If Analyzer is given permission to `Manage Messages`, the bot will delete the `.settree` and `.setranks` messages to reduce spam." }, "embeds": { - "footer": "", + "footer": "Grow A Tree Analyzer is not affiliated with Grow A Tree or Limbo Labs", "color": "0x55FF55" }, "emoji": { + "joint": "<:joint:862082955902976000>", "next": "⏭️", "previous": "⏮️", "confirm": "☑️", "cancel": "❌" }, "urls": { - "avatar": "" + "avatar": "https://cdn.discordapp.com/avatars/513184762073055252/12227aa23a06d5178853e59b72c7487b.webp?size=128" }, "temp": {} } \ No newline at end of file diff --git a/main.js b/main.js index dc55716..f1ab4d0 100644 --- a/main.js +++ b/main.js @@ -30,20 +30,23 @@ client.once('ready', () => { fn.collections.slashCommands(client); console.log('Ready!'); client.channels.fetch(statusChannelId).then(channel => { - channel.send(`${new Date().toISOString()} -- Ready`); + channel.send(`${new Date().toISOString()} -- \nStartup Sequence Complete`); }); }); // slash-commands client.on('interactionCreate', async interaction => { if (interaction.isCommand()) { + // if (isDev) { + // console.log(interaction); + // } const { commandName } = interaction; if (client.slashCommands.has(commandName)) { client.slashCommands.get(commandName).execute(interaction); } else { interaction.reply('Sorry, I don\'t have access to that command.'); - console.error('Slash command attempted to run but not found: /' + commandName); + console.error('Slash command attempted to run but not found: ' + commandName); } } diff --git a/modules/functions.js b/modules/functions.js index f3edb7f..8761aaa 100644 --- a/modules/functions.js +++ b/modules/functions.js @@ -47,6 +47,16 @@ const functions = { ); return refreshActionRow; }, + comparisonEmbed(content, refreshActionRow) { + // Create the embed using the content passed to this function + const embed = new EmbedBuilder() + .setColor(strings.embeds.color) + .setTitle('Tree Growth Comparison') + .setDescription(content) + .setFooter({text: strings.embeds.footer}); + const messageContents = { embeds: [embed], components: [refreshActionRow] }; + return messageContents; + }, helpEmbed(content, private) { const embed = new EmbedBuilder() .setColor(strings.embeds.color) @@ -76,6 +86,115 @@ const functions = { return messageContents; } }, + rankings: { + parse(interaction) { + return new Promise ((resolve, reject) => { + if (guildInfo[interaction.guildId] == undefined) { + reject("The guild entry hasn't been created yet."); + return; + } + if (guildInfo[interaction.guildId].rankMessageId != undefined) { + interaction.guild.channels.fetch(guildInfo[interaction.guildId].rankChannelId).then(c => { + c.messages.fetch(guildInfo[interaction.guildId].rankMessageId).then(rankMessage => { + if ((rankMessage.embeds.length == 0) || (rankMessage.embeds[0].data.title != 'Tallest Trees' )) { + reject("This doesn't appear to be a valid ``/top trees`` message."); + return; + } + let lines = rankMessage.embeds[0].data.description.split('\n'); + let rankings = []; + for (let i = 0; i < 10; i++) { + let breakdown = lines[i].split(' - '); + if (breakdown[0].includes('🥇')) { + breakdown[0] = '``#1 ``' + } else if (breakdown[0].includes('🥈')) { + breakdown[0] = '``#2 ``' + } else if (breakdown[0].includes('🥉')) { + breakdown[0] = '``#3 ``' + } + + let trimmedRank = breakdown[0].slice(breakdown[0].indexOf('#') + 1, breakdown[0].lastIndexOf('``')); + + let trimmedName = breakdown[1].slice(breakdown[1].indexOf('``') + 2); + trimmedName = trimmedName.slice(0, trimmedName.indexOf('``')); + + let trimmedHeight = parseFloat(breakdown[2].slice(0, breakdown[2].indexOf('ft'))).toFixed(1); + + rankings.push({ + rank: trimmedRank, + name: trimmedName, + height: trimmedHeight + }); + } + + guildInfo[interaction.guildId].rankings = rankings; + fs.writeFileSync('../data/guildInfo.json', JSON.stringify(guildInfo)); + guildInfo = require('../data/guildInfo.json'); + resolve(rankings); + }); + }); + } else { + reject("The rankMessageId is undefined somehow"); + return; + } + }); + + }, + compare(interaction) { + if (guildInfo[interaction.guildId] == undefined) { + return `Please reset the reference messages! (${interaction.guildId})`; + } + let treeHeight = parseFloat(guildInfo[interaction.guildId].treeHeight).toFixed(1); + if ((guildInfo[interaction.guildId].rankings.length > 0) && (treeHeight > 0)) { + let replyString = 'Current Tree Height: ' + treeHeight + 'ft\n\n'; + guildInfo[interaction.guildId].rankings.forEach(e => { + let difference = parseFloat(e.height).toFixed(1) - treeHeight; + const absDifference = parseFloat(Math.abs(difference)).toFixed(1); + if (difference > 0) { + replyString += `${absDifference}ft shorter than rank #${e.rank}\n`; + } else if (difference < 0) { + replyString += `${absDifference}ft taller than rank #${e.rank}\n`; + } else if (difference == 0) { + replyString += `Same height as rank #${e.rank}\n`; + } + }); + return 'Here\'s how your tree compares: \n' + replyString; + } else { + console.error('Not configured correctly\n' + 'Guild ID: ' + interaction.guildId + '\nGuild Info: ' + JSON.stringify(guildInfo[interaction.guildId])); + return 'Not configured correctly'; + } + } + }, + tree: { + parse(interaction) { + let input; + return new Promise((resolve, reject) => { + if (guildInfo[interaction.guildId] == undefined) { + reject(`The guild entry hasn't been created yet. [${interaction.guildId || interaction.commandGuildId}]`); + return; + } + if (guildInfo[interaction.guildId].treeMessageId != "") { + interaction.guild.channels.fetch(guildInfo[interaction.guildId].treeChannelId).then(c => { + c.messages.fetch(guildInfo[interaction.guildId].treeMessageId).then(m => { + if ( (m.embeds.length == 0) || !(m.embeds[0].data.description.includes('Your tree is')) ) { + reject("This doesn't appear to be a valid ``/tree`` message."); + return; + } + input = m.embeds[0].data.description; + let lines = input.split('\n'); + guildInfo[interaction.guildId].treeHeight = parseFloat(lines[0].slice(lines[0].indexOf('is') + 3, lines[0].indexOf('ft'))).toFixed(1); + fs.writeFileSync('../data/guildInfo.json', JSON.stringify(guildInfo)); + guildInfo = require('../data/guildInfo.json'); + resolve("The reference tree message has been saved/updated."); + }); + }) + } else { + console.error('treeMessageId undefined'); + reject("There was an unknown error while setting the tree message."); + return; + } + }); + } + }, refresh(interaction) { functions.rankings.parse(interaction).then(r1 => { functions.tree.parse(interaction).then(r2 => { @@ -87,6 +206,36 @@ const functions = { }).catch(e => { interaction.reply(functions.builders.errorEmbed(e)); }); + }, + reset(guildId) { + delete guildInfo[guildId]; + fs.writeFileSync('../data/guildInfo.json', JSON.stringify(guildInfo)); + guildInfo = require('../data/guildInfo.json'); + return; + }, + getInfo(guildId) { + const guildInfo = guildInfo[guildId]; + if (guildInfo != undefined) { + let guildInfoString = ""; + if (guildInfo.treeMessageId != "") { + guildInfoString += `Tree Message ID: ${guildInfo.treeMessageId}\n`; + } + if (guildInfo.treeChannelId != "") { + guildInfoString += `Tree Channel ID: ${guildInfo.treeChannelId}\n`; + } + if (guildInfo.rankMessageId != "") { + guildInfoString += `Rank Message ID: ${guildInfo.rankMessageId}\n`; + } + if (guildInfo.rankChannelId != "") { + guildInfoString += `Rank Channel ID: ${guildInfo.rankChannelId}\n`; + } + if (guildInfo.treeHeight != "") { + guildInfoString += `Tree Height: ${guildInfo.treeHeight}\n`; + } + return `Here if your servers setup info:\n${guildInfoString}`; + } else { + return "Your guild hasn't been set up yet."; + } } }; diff --git a/package.json b/package.json new file mode 100644 index 0000000..03adfd0 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "treeanalyzer", + "version": "1.0.0", + "description": "Analyze Grow A Tree", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/voidf1sh/treeanalyzer.git" + }, + "author": "Skylar Grant", + "license": "ISC", + "bugs": { + "url": "https://github.com/voidf1sh/treeanalyzer/issues" + }, + "homepage": "https://github.com/voidf1sh/treeanalyzer#readme", + "dependencies": { + "discord.js": "^14.7.1", + "dotenv": "^16.0.3" + } +} diff --git a/slash-commands/compare.js b/slash-commands/compare.js new file mode 100644 index 0000000..cf0ba38 --- /dev/null +++ b/slash-commands/compare.js @@ -0,0 +1,12 @@ +const { SlashCommandBuilder } = require('discord.js'); +const fn = require('../modules/functions.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('compare') + .setDescription('See how your tree compares to other trees!'), + async execute(interaction) { + const embed = fn.builders.comparisonEmbed(fn.rankings.compare(interaction), fn.builders.refreshAction()); + interaction.reply(embed); + }, +}; \ No newline at end of file diff --git a/slash-commands/help.js b/slash-commands/help.js new file mode 100644 index 0000000..3ad4e16 --- /dev/null +++ b/slash-commands/help.js @@ -0,0 +1,49 @@ +const { SlashCommandBuilder, messageLink } = require('discord.js'); +const fn = require('../modules/functions.js'); +const strings = require('../data/strings.json'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('help') + .setDescription('Get help using the bot') + .addSubcommand(subcommand => + subcommand + .setName('info') + .setDescription('Learn about the bot') + .addStringOption(o => + o.setName('private') + .setDescription('Should the response be sent privately?') + .setRequired(true) + .addChoices( + { name: "True", value: "true" }, + { name: "False", value: "false" } + ))) + .addSubcommand(subcommand => + subcommand + .setName('setup') + .setDescription('Learn how to setup the bot') + .addStringOption(o => + o.setName('private') + .setDescription('Should the response be sent privately?') + .setRequired(true) + .addChoices( + { name: "True", value: "true" }, + { name: "False", value: "false" } + ))) + .addSubcommand(subcommand => + subcommand + .setName('permissions') + .setDescription('Learn about the bot\'s permissions') + .addStringOption(o => + o.setName('private') + .setDescription('Should the response be sent privately?') + .setRequired(true) + .addChoices( + { name: "True", value: "true" }, + { name: "False", value: "false" } + ))), + execute(interaction) { + const helpEmbed = fn.builders.helpEmbed(strings.help[interaction.options.getSubcommand()], interaction.options.getString('private')); + interaction.reply(helpEmbed); + }, +}; \ No newline at end of file diff --git a/slash-commands/reset.js b/slash-commands/reset.js new file mode 100644 index 0000000..cecb3cd --- /dev/null +++ b/slash-commands/reset.js @@ -0,0 +1,12 @@ +const { SlashCommandBuilder } = require('discord.js'); +const fn = require('../modules/functions.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('reset') + .setDescription('Reset all message assignments in your server'), + execute(interaction) { + fn.reset(interaction.guildId); + interaction.reply(fn.builders.embed("Assignments Reset")); + }, +}; \ No newline at end of file diff --git a/slash-commands/setup.js b/slash-commands/setup.js new file mode 100644 index 0000000..05e327d --- /dev/null +++ b/slash-commands/setup.js @@ -0,0 +1,47 @@ +const { SlashCommandBuilder } = require('discord.js'); +const fn = require('../modules/functions.js'); +const guildInfo = require('../data/guildInfo.json'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('setup') + .setDescription('Attempt automatic configuration of the bot.'), + execute(interaction) { + if (guildInfo[interaction.guildId] == undefined) { + guildInfo[interaction.guildId] = { + "treeMessageId": "", + "treeChannelId": "", + "rankMessageId": "", + "rankChannelId": "", + "rankings": [], + "treeHeight": 0 + }; + } + interaction.channel.messages.fetch({ limit: 20 }).then(msgs => { + let treeFound = false; + let rankFound = false; + msgs.forEach(msg => { + if (msg.embeds.length > 0) { + if (msg.embeds[0].data.description.includes("Your tree is")) { + treeFound = true; + guildInfo[interaction.guildId].treeChannelId = msg.channelId; + guildInfo[interaction.guildId].treeMessageId = msg.id; + fn.tree.parse(msg); + } else if (msg.embeds[0].data.title == "Tallest Trees") { + rankFound = true; + guildInfo[interaction.guildId].rankChannelId = msg.channelId; + guildInfo[interaction.guildId].rankMessageId = msg.id; + fn.rankings.parse(msg); + } + } + }); + if (treeFound && !(rankFound)) { + interaction.reply(fn.builders.embed("A tree message was found, but a leaderboard message was not. Please run this command again in the channel containing the leaderboard if you haven't done so already. Run ``/setupinfo`` to see if the message is set.")); + } else if (!(treeFound) && rankFound) { + interaction.reply(fn.builders.embed("A leaderboard message was found, but a tree message was not. Please run this command again in the channel containing the tree if you haven't done so already. Run ``/setupinfo`` to see if the message is set.")); + } else if (treeFound && rankFound) { + interaction.reply(fn.builders.embed("Tree and leaderboard messages were both found, setup is complete. Run ``/setupinfo`` to verify.")); + } + }); + }, +}; \ No newline at end of file diff --git a/slash-commands/setupinfo.js b/slash-commands/setupinfo.js new file mode 100644 index 0000000..b30871a --- /dev/null +++ b/slash-commands/setupinfo.js @@ -0,0 +1,12 @@ +const { SlashCommandBuilder } = require('discord.js'); +const fn = require('../modules/functions.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('setupinfo') + .setDescription('View information about how the bot is set up in your server'), + execute(interaction) { + const embed = fn.builders.embed(fn.getInfo(interaction.guildId)); + interaction.reply(embed); + }, +}; \ No newline at end of file