Compare commits
9 Commits
main
...
dev/v1.1.4
Author | SHA1 | Date |
---|---|---|
Skylar Grant | a8ceecff9a | |
Skylar Grant | 9b2ad681b8 | |
Skylar Grant | 112c671435 | |
Skylar Grant | 74b65a1978 | |
Skylar Grant | a69bca9259 | |
Skylar Grant | f5daa186ac | |
Skylar Grant | d6eac632e8 | |
Skylar Grant | 2627cc24b1 | |
Skylar Grant | 82c20dda68 |
|
@ -4,7 +4,7 @@
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"title": "Silvanus Help",
|
"title": "Silvanus Help",
|
||||||
"info": "Silvanus is the ultimate Grow A Tree companion bot! Quickly compare your server's tree to others on the leaderboard with automatic calculation of tree height differences, active growth detection, watering time calculations, and more! Get started with </help:1065346941166297129> and </setup:1065407649363005561>, then check out </compare:1065346941166297128>.\n\nImportant Note: Silvanus is only as up-to-date as your server's Tree and Tallest Trees messages. Make sure to refresh them before refreshing Silvanus' Compare message.\n\nFor the best experience we recommend the use of a single </tree:972648557796524032> and </top trees:1051840665362894950> message, otherwise make sure to run </setup:1065407649363005561> each time you run </compare:1065346941166297128>.",
|
"info": "Silvanus is the ultimate Grow A Tree companion bot! Quickly compare your server's tree to others on the leaderboard with automatic calculation of tree height differences, active growth detection, watering time calculations, and more! Get started with </help:1065346941166297129> and </setup:1065407649363005561>, then check out </compare:1065346941166297128>.\n\nImportant Note: Silvanus is only as up-to-date as your server's Tree and Tallest Trees messages. Make sure to refresh them before refreshing Silvanus' Compare message.\n\nFor the best experience we recommend the use of a single </tree:972648557796524032> and </top trees:1051840665362894950> message, otherwise make sure to run </setup:1065407649363005561> each time you run </compare:1065346941166297128>.\nStatus Indicator: ``[ Active Growth Indicator: 💧 | 24 Hour Observed Growth | Wait Time Between Waters ]``",
|
||||||
"setup": "To begin analyzing your Tree, first you must set up the reference messages.\n\n1. Run </setup:1065407649363005561> in the channel(s) that contain your server's tree and leaderboard messages.\n2. Now simply run </compare:1065346941166297128> where you want your analysis to be visible.",
|
"setup": "To begin analyzing your Tree, first you must set up the reference messages.\n\n1. Run </setup:1065407649363005561> in the channel(s) that contain your server's tree and leaderboard messages.\n2. Now simply run </compare:1065346941166297128> where you want your analysis to be visible.",
|
||||||
"permissions": "At a minimum, Silvanus 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.",
|
"permissions": "At a minimum, Silvanus 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.",
|
||||||
"allCommands": "</compare:1065346941166297128> | </setup:1065407649363005561> | </watertime:1066970330029113444> | </setupinfo:1065413032374706196> | </reset:1065412317052944476> | </help:1065346941166297129>"
|
"allCommands": "</compare:1065346941166297128> | </setup:1065407649363005561> | </watertime:1066970330029113444> | </setupinfo:1065413032374706196> | </reset:1065412317052944476> | </help:1065346941166297129>"
|
||||||
|
|
14
main.js
14
main.js
|
@ -30,9 +30,14 @@ client.once('ready', () => {
|
||||||
fn.collections.slashCommands(client);
|
fn.collections.slashCommands(client);
|
||||||
console.log('Ready!');
|
console.log('Ready!');
|
||||||
client.user.setActivity({ name: strings.activity.name, type: ActivityType.Watching });
|
client.user.setActivity({ name: strings.activity.name, type: ActivityType.Watching });
|
||||||
client.channels.fetch(statusChannelId).then(channel => {
|
if (isDev == 'false') {
|
||||||
channel.send(`${new Date().toISOString()} -- \nStartup Sequence Complete`);
|
client.channels.fetch(statusChannelId).then(channel => {
|
||||||
});
|
channel.send(`${new Date().toISOString()} -- \nStartup Sequence Complete <@481933290912350209>`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Dev shit
|
||||||
|
fn.checkReady(client);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// slash-commands
|
// slash-commands
|
||||||
|
@ -53,6 +58,9 @@ client.on('interactionCreate', async interaction => {
|
||||||
|
|
||||||
if (interaction.isButton() && interaction.component.customId == 'refresh') {
|
if (interaction.isButton() && interaction.component.customId == 'refresh') {
|
||||||
fn.refresh(interaction);
|
fn.refresh(interaction);
|
||||||
|
} else if (interaction.isButton() && interaction.component.customId == 'resetping') {
|
||||||
|
fn.resetPing(interaction);
|
||||||
|
interaction.reply({ content: "Reset water readiness detection system.", ephemeral: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
185
modules/dbfn.js
185
modules/dbfn.js
|
@ -81,7 +81,7 @@ module.exports = {
|
||||||
if (err) throw `Error connecting to the database: ${err.message}`;
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
});
|
});
|
||||||
// Get a server's tree information from the database
|
// Get a server's tree information from the database
|
||||||
const selectGuildInfoQuery = `SELECT tree_name, tree_height, tree_message_id, tree_channel_id, leaderboard_message_id, leaderboard_channel_id FROM guild_info WHERE guild_id = ${db.escape(guildId)}`;
|
const selectGuildInfoQuery = `SELECT tree_name, tree_height, tree_message_id, tree_channel_id, leaderboard_message_id, leaderboard_channel_id, ping_role_id, ping_channel_id, reminded_status FROM guild_info WHERE guild_id = ${db.escape(guildId)}`;
|
||||||
// TODO run this query and return a promise then structure the output into a GuildInfo object. resolve with { "status": , "data": guildInfo }
|
// TODO run this query and return a promise then structure the output into a GuildInfo object. resolve with { "status": , "data": guildInfo }
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
db.query(selectGuildInfoQuery, (err, res) => {
|
db.query(selectGuildInfoQuery, (err, res) => {
|
||||||
|
@ -97,7 +97,10 @@ module.exports = {
|
||||||
"treeMessageId": "123",
|
"treeMessageId": "123",
|
||||||
"treeChannelId": "123",
|
"treeChannelId": "123",
|
||||||
"leaderboardMessageId": "123",
|
"leaderboardMessageId": "123",
|
||||||
"leaderboardChannelId": "123"
|
"leaderboardChannelId": "123",
|
||||||
|
"reminderMessage": "Abc",
|
||||||
|
"reminderChannelId": "123",
|
||||||
|
"remindedStatus": 0
|
||||||
};*/
|
};*/
|
||||||
if (res.length == 0) {
|
if (res.length == 0) {
|
||||||
reject("There is no database entry for your guild yet. Try running /setup");
|
reject("There is no database entry for your guild yet. Try running /setup");
|
||||||
|
@ -111,7 +114,10 @@ module.exports = {
|
||||||
"treeMessageId": row.tree_message_id,
|
"treeMessageId": row.tree_message_id,
|
||||||
"treeChannelId": row.tree_channel_id,
|
"treeChannelId": row.tree_channel_id,
|
||||||
"leaderboardMessageId": row.leaderboard_message_id,
|
"leaderboardMessageId": row.leaderboard_message_id,
|
||||||
"leaderboardChannelId": row.leaderboard_channel_id
|
"leaderboardChannelId": row.leaderboard_channel_id,
|
||||||
|
"pingRoleId": row.ping_role_id,
|
||||||
|
"pingChannelId": row.ping_channel_id,
|
||||||
|
"remindedStatus": row.reminded_status
|
||||||
};
|
};
|
||||||
db.end();
|
db.end();
|
||||||
resolve({ "status": "Successfully fetched guild information", "data": guildInfo });
|
resolve({ "status": "Successfully fetched guild information", "data": guildInfo });
|
||||||
|
@ -300,5 +306,178 @@ module.exports = {
|
||||||
resolve({ "status": "Successfully uploaded the leaderboard", "data": res });
|
resolve({ "status": "Successfully uploaded the leaderboard", "data": res });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
get24hTree(guildId, treeName) {
|
||||||
|
const db = mysql.createConnection({
|
||||||
|
host : process.env.DBHOST,
|
||||||
|
user : process.env.DBUSER,
|
||||||
|
password : process.env.DBPASS,
|
||||||
|
database : process.env.DBNAME,
|
||||||
|
port : process.env.DBPORT
|
||||||
|
});
|
||||||
|
db.connect((err) => {
|
||||||
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
|
});
|
||||||
|
// Returns a Promise, resolve({ "status": "", "data": leaderboard })
|
||||||
|
const select24hTreeQuery = `SELECT id, tree_name, tree_rank, tree_height, has_pin FROM leaderboard WHERE guild_id = ${db.escape(guildId)} AND tree_name = ${db.escape(treeName)} AND timestamp > date_sub(now(), interval 1 day) ORDER BY id ASC LIMIT 1`;
|
||||||
|
// TODO run the query and return a promise then process the results. resolve with { "status": , "data": leaderboard }
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(select24hTreeQuery, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
db.end();
|
||||||
|
reject("Error fetching the historic 24hr tree height: " + err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hist24hTree = {
|
||||||
|
"treeName": res[0].tree_name,
|
||||||
|
"treeRank": res[0].tree_rank,
|
||||||
|
"treeHeight": res[0].tree_height,
|
||||||
|
"hasPin": res[0].has_pin
|
||||||
|
}
|
||||||
|
db.end();
|
||||||
|
resolve({ "status": "Successfully fetched historic 24hr tree.", "data": hist24hTree });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setReminderInfo(guildId, reminderMessage, reminderChannelId) {
|
||||||
|
const db = mysql.createConnection({
|
||||||
|
host : process.env.DBHOST,
|
||||||
|
user : process.env.DBUSER,
|
||||||
|
password : process.env.DBPASS,
|
||||||
|
database : process.env.DBNAME,
|
||||||
|
port : process.env.DBPORT
|
||||||
|
});
|
||||||
|
db.connect((err) => {
|
||||||
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
|
});
|
||||||
|
// Returns a Promise, resolve({ "status": "", "data": leaderboard })
|
||||||
|
const insertReminderInfoQuery = `UPDATE guild_info SET ping_role_id = ${db.escape(reminderMessage)}, ping_channel_id = ${db.escape(reminderChannelId)} WHERE guild_id = ${db.escape(guildId)}`;
|
||||||
|
// TODO run the query and return a promise then process the results. resolve with { "status": , "data": leaderboard }
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(insertReminderInfoQuery, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
db.end();
|
||||||
|
reject("Error updating the reminder info: " + err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.end();
|
||||||
|
resolve({ "status": `Successfully set the reminder message to "${reminderMessage}" in <#${reminderChannelId}>`, "data": res });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setRemindedStatus(guildId, remindedStatus) {
|
||||||
|
const db = mysql.createConnection({
|
||||||
|
host : process.env.DBHOST,
|
||||||
|
user : process.env.DBUSER,
|
||||||
|
password : process.env.DBPASS,
|
||||||
|
database : process.env.DBNAME,
|
||||||
|
port : process.env.DBPORT
|
||||||
|
});
|
||||||
|
db.connect((err) => {
|
||||||
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
|
});
|
||||||
|
// Returns a Promise, resolve({ "status": "", "data": leaderboard })
|
||||||
|
const setRemindedStatusQuery = `UPDATE guild_info SET reminded_status = ${db.escape(remindedStatus)} WHERE guild_id = ${db.escape(guildId)}`;
|
||||||
|
// TODO run the query and return a promise then process the results. resolve with { "status": , "data": leaderboard }
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(setRemindedStatusQuery, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
db.end();
|
||||||
|
reject("Error updating the reminded status: " + err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.end();
|
||||||
|
resolve({ "status": `Successfully set the reminded status to ${remindedStatus}`, "data": res });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
setReminderOptIn(guildId, optIn) {
|
||||||
|
const db = mysql.createConnection({
|
||||||
|
host : process.env.DBHOST,
|
||||||
|
user : process.env.DBUSER,
|
||||||
|
password : process.env.DBPASS,
|
||||||
|
database : process.env.DBNAME,
|
||||||
|
port : process.env.DBPORT
|
||||||
|
});
|
||||||
|
db.connect((err) => {
|
||||||
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
|
});
|
||||||
|
// Returns a Promise, resolve({ "status": "", "data": leaderboard })
|
||||||
|
const setReminderOptInQuery = `UPDATE guild_info SET reminder_optin = ${db.escape(optIn)} WHERE guild_id = ${db.escape(guildId)}`;
|
||||||
|
// TODO run the query and return a promise then process the results. resolve with { "status": , "data": leaderboard }
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(setReminderOptInQuery, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
db.end();
|
||||||
|
reject("Error updating the reminder opt-in status: " + err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
db.end();
|
||||||
|
resolve({ "status": `Successfully set the reminder opt-in status to ${optIn}`, "data": res });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getOptedInGuilds() {
|
||||||
|
const db = mysql.createConnection({
|
||||||
|
host : process.env.DBHOST,
|
||||||
|
user : process.env.DBUSER,
|
||||||
|
password : process.env.DBPASS,
|
||||||
|
database : process.env.DBNAME,
|
||||||
|
port : process.env.DBPORT
|
||||||
|
});
|
||||||
|
db.connect((err) => {
|
||||||
|
if (err) throw `Error connecting to the database: ${err.message}`;
|
||||||
|
});
|
||||||
|
// Get a server's tree information from the database
|
||||||
|
const getOptedInGuildsQuery = `SELECT guild_id, tree_name, tree_height, tree_message_id, tree_channel_id, leaderboard_message_id, leaderboard_channel_id, ping_role_id, ping_channel_id, reminded_status FROM guild_info WHERE reminder_optin = 1 AND reminded_status = 0`;
|
||||||
|
// TODO run this query and return a promise then structure the output into a GuildInfo object. resolve with { "status": , "data": guildInfo }
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.query(getOptedInGuildsQuery, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
reject("Error fetching guild information: " + err.message);
|
||||||
|
db.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*const guildInfo = { "guildId": "123",
|
||||||
|
"treeName": "name",
|
||||||
|
"treeHeight": 123,
|
||||||
|
"treeMessageId": "123",
|
||||||
|
"treeChannelId": "123",
|
||||||
|
"leaderboardMessageId": "123",
|
||||||
|
"leaderboardChannelId": "123",
|
||||||
|
"reminderMessage": "Abc",
|
||||||
|
"reminderChannelId": "123",
|
||||||
|
"remindedStatus": 0
|
||||||
|
};*/
|
||||||
|
if (res.length == 0) {
|
||||||
|
resolve({"status": "No servers have opted in yet"});
|
||||||
|
db.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row = res[0];
|
||||||
|
let guilds = [];
|
||||||
|
res.forEach(row => {
|
||||||
|
guilds.push({
|
||||||
|
"guildId": row.guild_id,
|
||||||
|
"treeName": row.tree_name,
|
||||||
|
"treeHeight": row.tree_height,
|
||||||
|
"treeMessageId": row.tree_message_id,
|
||||||
|
"treeChannelId": row.tree_channel_id,
|
||||||
|
"leaderboardMessageId": row.leaderboard_message_id,
|
||||||
|
"leaderboardChannelId": row.leaderboard_channel_id,
|
||||||
|
"reminderMessage": row.ping_role_id,
|
||||||
|
"reminderChannelId": row.ping_channel_id,
|
||||||
|
"remindedStatus": row.reminded_status
|
||||||
|
});
|
||||||
|
});
|
||||||
|
db.end();
|
||||||
|
resolve({ "status": "Successfully fetched guild information", "data": guilds });
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -17,6 +17,7 @@ const config = require('../data/config.json');
|
||||||
const strings = require('../data/strings.json');
|
const strings = require('../data/strings.json');
|
||||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||||
const dbfn = require('./dbfn.js');
|
const dbfn = require('./dbfn.js');
|
||||||
|
const { finished } = require('stream');
|
||||||
|
|
||||||
dbfn.createGuildTables().then(res => {
|
dbfn.createGuildTables().then(res => {
|
||||||
console.log(res.status);
|
console.log(res.status);
|
||||||
|
@ -41,17 +42,26 @@ const functions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builders: {
|
builders: {
|
||||||
refreshAction() {
|
async refreshAction(guildId) {
|
||||||
// Create the button to go in the Action Row
|
// Create the button to go in the Action Row
|
||||||
const refreshButton = new ButtonBuilder()
|
const refreshButton = new ButtonBuilder()
|
||||||
.setCustomId('refresh')
|
.setCustomId('refresh')
|
||||||
.setLabel('Refresh')
|
.setLabel('Refresh')
|
||||||
.setStyle(ButtonStyle.Primary);
|
.setStyle(ButtonStyle.Primary);
|
||||||
|
const resetPingButton = new ButtonBuilder()
|
||||||
|
.setCustomId('resetping')
|
||||||
|
.setLabel('Reset Ping')
|
||||||
|
.setStyle(ButtonStyle.Secondary);
|
||||||
// Create the Action Row with the Button in it, to be sent with the Embed
|
// Create the Action Row with the Button in it, to be sent with the Embed
|
||||||
const refreshActionRow = new ActionRowBuilder()
|
let refreshActionRow = new ActionRowBuilder()
|
||||||
.addComponents(
|
.addComponents(
|
||||||
refreshButton
|
refreshButton
|
||||||
);
|
);
|
||||||
|
const getGuildInfoResponse = await dbfn.getGuildInfo(guildId);
|
||||||
|
const guildInfo = getGuildInfoResponse.data;
|
||||||
|
if (guildInfo.reminderMessage != "" && guildInfo.reminderChannelId != "") {
|
||||||
|
refreshActionRow.addComponents(resetPingButton);
|
||||||
|
}
|
||||||
return refreshActionRow;
|
return refreshActionRow;
|
||||||
},
|
},
|
||||||
comparisonEmbed(content, refreshActionRow) {
|
comparisonEmbed(content, refreshActionRow) {
|
||||||
|
@ -64,6 +74,16 @@ const functions = {
|
||||||
const messageContents = { embeds: [embed], components: [refreshActionRow] };
|
const messageContents = { embeds: [embed], components: [refreshActionRow] };
|
||||||
return messageContents;
|
return messageContents;
|
||||||
},
|
},
|
||||||
|
reminderEmbed(content, guildInfo) {
|
||||||
|
// Create the embed using the content passed to this function
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(strings.embeds.color)
|
||||||
|
.setTitle('Water Reminder')
|
||||||
|
.setDescription(`${content}\n[Your Tree](https://discord.com/channels/${guildInfo.guildId}/${guildInfo.treeChannelId}/${guildInfo.treeMessageId})`)
|
||||||
|
.setFooter({text: `This message will self-destruct in 60 seconds...`});
|
||||||
|
const messageContents = { embeds: [embed] };
|
||||||
|
return messageContents;
|
||||||
|
},
|
||||||
helpEmbed(content, private) {
|
helpEmbed(content, private) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setColor(strings.embeds.color)
|
.setColor(strings.embeds.color)
|
||||||
|
@ -173,49 +193,56 @@ const functions = {
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
compare(interaction) {
|
async compare(interaction) {
|
||||||
return new Promise((resolve, reject) => {
|
try {
|
||||||
dbfn.getGuildInfo(interaction.guildId).then(res => {
|
// fetch the guild's settings from the database
|
||||||
const guildInfo = res.data;
|
const guildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
||||||
guildInfo.guildId = interaction.guildId;
|
const guildInfo = guildInfoResponse.data; // { "guildId": "123","treeName": "name","treeHeight": 123,"treeMessageId": "123","treeChannelId": "123","leaderboardMessageId": "123","leaderboardChannelId": "123"};
|
||||||
|
// Get the most recent leaderboard listing from the database
|
||||||
let treeHeight = parseFloat(guildInfo.treeHeight).toFixed(1);
|
const getLeaderboardResponse = await dbfn.getLeaderboard(interaction.guildId);
|
||||||
dbfn.getLeaderboard(interaction.guildId).then(res => {
|
const leaderboard = getLeaderboardResponse.data; // [ { treeName: "Name", treeHeight: 1234.5, treeRank: 67 }, {...}, {...} ]
|
||||||
const leaderboard = res.data;
|
|
||||||
|
// Prepare the beginning of the comparison message
|
||||||
let replyString = 'Current Tree Height: ' + treeHeight + 'ft\n\n';
|
let comparisonReplyString = `Here\'s how your tree compares: \nCurrent Tree Height: ${guildInfo.treeHeight}ft\n\n`;
|
||||||
leaderboard.reverse().forEach(treeRanking => {
|
// Iterate over the leaderboard entries, backwards
|
||||||
let difference = parseFloat(treeRanking.treeHeight).toFixed(1) - treeHeight;
|
for (let i = leaderboard.length - 1; i >= 0; i--) {
|
||||||
let decimal = (treeRanking.treeHeight % 1).toFixed(1);
|
const leaderboardEntry = leaderboard[i];
|
||||||
let growthIndicator = "";
|
// Setup the status indicator, default to blank, we'll change it later
|
||||||
if (decimal > 0) {
|
let statusIndicator = "``[";
|
||||||
growthIndicator += "[+]";
|
if ((leaderboardEntry.treeHeight % 1).toFixed(1) > 0) statusIndicator += "💧|";
|
||||||
}
|
|
||||||
const absDifference = parseFloat(Math.abs(difference)).toFixed(1);
|
// Get the data for this tree from 24 hours ago
|
||||||
if (treeRanking.hasPin) {
|
const get24hTreeResponse = await dbfn.get24hTree(interaction.guildId, leaderboardEntry.treeName);
|
||||||
replyString += "This is your tree. ";
|
const dayAgoTree = get24hTreeResponse.data;
|
||||||
} else if ((treeRanking.treeHeight == treeHeight) && (treeRanking.treeName == guildInfo.treeName)) {
|
const hist24hDifference = (leaderboardEntry.treeHeight - dayAgoTree.treeHeight).toFixed(1);
|
||||||
replyString += "This might be your tree. Same height, same name. ";
|
statusIndicator += `+${hist24hDifference}ft|`
|
||||||
} else {
|
|
||||||
if (difference > 0) {
|
// Get the 24h watering time for this tree
|
||||||
replyString += `#${treeRanking.treeRank} - ${absDifference}ft${growthIndicator} shorter `;
|
const totalWaterTime = await functions.timeToHeight(dayAgoTree.treeHeight, leaderboardEntry.treeHeight);
|
||||||
} else if (difference < 0) {
|
statusIndicator += `${totalWaterTime}]\`\``;
|
||||||
replyString += `#${treeRanking.treeRank} - ${absDifference}ft${growthIndicator} taller `;
|
|
||||||
} else if (difference == 0) {
|
// Determine if this tree is the guild's tree
|
||||||
replyString += `#${treeRanking.treeRank} - Same Height${growthIndicator} `;
|
if (leaderboardEntry.hasPin) {
|
||||||
}
|
comparisonReplyString += `#${leaderboardEntry.treeRank} - This is your tree`;
|
||||||
}
|
} else { // If it's another guild's tree
|
||||||
replyString += `[${functions.getWaterTime(treeRanking.treeHeight)} mins]\n`;
|
// Calculate the current height difference
|
||||||
});
|
const currentHeightDifference = guildInfo.treeHeight - leaderboardEntry.treeHeight;
|
||||||
resolve('Here\'s how your tree compares: \n' + replyString);
|
|
||||||
}).catch(err => {
|
if (currentHeightDifference > 0) { // Guild Tree is taller than the leaderboard tree
|
||||||
console.error(err);
|
comparisonReplyString += `#${leaderboardEntry.treeRank} - ${currentHeightDifference.toFixed(1)}ft taller`;
|
||||||
});
|
} else {
|
||||||
}).catch(err => {
|
comparisonReplyString += `#${leaderboardEntry.treeRank} - ${Math.abs(currentHeightDifference).toFixed(1)}ft shorter`;
|
||||||
reject(err);
|
}
|
||||||
return;
|
}
|
||||||
});
|
// Build a string using the current leaderboard entry and the historic entry from 24 hours ago
|
||||||
});
|
comparisonReplyString += `\n${statusIndicator}\n`;
|
||||||
|
// if (process.env.isDev == 'true') comparisonReplyString += `Current Height: ${leaderboardEntry.treeHeight} 24h Ago Height: ${dayAgoTree.treeHeight}\n`;
|
||||||
|
}
|
||||||
|
return comparisonReplyString;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return 'An error occurred while comparing the trees.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tree: {
|
tree: {
|
||||||
|
@ -262,8 +289,9 @@ const functions = {
|
||||||
refresh(interaction) {
|
refresh(interaction) {
|
||||||
functions.rankings.parse(interaction).then(r1 => {
|
functions.rankings.parse(interaction).then(r1 => {
|
||||||
functions.tree.parse(interaction).then(r2 => {
|
functions.tree.parse(interaction).then(r2 => {
|
||||||
functions.rankings.compare(interaction).then(res => {
|
functions.rankings.compare(interaction).then(async res => {
|
||||||
const embed = functions.builders.comparisonEmbed(res, functions.builders.refreshAction())
|
const refreshActionRow = await functions.builders.refreshAction(interaction.guildId);
|
||||||
|
const embed = functions.builders.comparisonEmbed(res, refreshActionRow)
|
||||||
interaction.update(embed);
|
interaction.update(embed);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -304,27 +332,100 @@ const functions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getWaterTime(size) {
|
getWaterTime(size) {
|
||||||
const seconds = Math.floor(Math.pow(size * 0.07 + 5, 1.1));
|
return Math.floor(Math.pow(size * 0.07 + 5, 1.1)); // Seconds
|
||||||
return (Math.floor((Math.pow(size * 0.07 + 5, 1.1))) / 60).toFixed(2);
|
|
||||||
},
|
},
|
||||||
timeToHeight(interaction, destHeight) {
|
timeToHeight(beginHeight, destHeight) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
dbfn.getGuildInfo(interaction.guildId).then(res => {
|
let time = 0;
|
||||||
let guildInfo = res.data;
|
for (let i = beginHeight; i < destHeight; i++) {
|
||||||
let currentHeight = parseInt(guildInfo.treeHeight);
|
const waterTime = parseFloat(functions.getWaterTime(i));
|
||||||
let time = 0;
|
// console.log("Height: " + i + "Time: " + waterTime);
|
||||||
for (let i = currentHeight; i < destHeight; i++) {
|
time += waterTime;
|
||||||
const waterTime = parseFloat(functions.getWaterTime(i));
|
}
|
||||||
console.log("Height: " + i + "Time: " + waterTime);
|
|
||||||
time += waterTime;
|
// 60 secs in min
|
||||||
}
|
// 3600 secs in hr
|
||||||
resolve(time.toFixed(2));
|
// 86400 sec in day
|
||||||
}).catch(err => {
|
|
||||||
console.error(err);
|
let units = " secs";
|
||||||
reject(err);
|
if ( 60 < time && time <= 3600 ) { // Minutes
|
||||||
return;
|
time = parseFloat(time / 60).toFixed(1);
|
||||||
})
|
units = " mins";
|
||||||
|
} else if ( 3600 < time && time <= 86400 ) {
|
||||||
|
time = parseFloat(time / 3600).toFixed(1);
|
||||||
|
units = " hrs";
|
||||||
|
} else if ( 86400 < time ) {
|
||||||
|
time = parseFloat(time / 86400).toFixed(1);
|
||||||
|
units = " days";
|
||||||
|
}
|
||||||
|
resolve(time + units);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
},
|
||||||
|
async sendReminder(guildInfo, guild) {
|
||||||
|
const { guildId, reminderChannelId, reminderMessage } = guildInfo;
|
||||||
|
const reminderChannel = await guild.channels.fetch(reminderChannelId);
|
||||||
|
const reminderEmbed = functions.builders.reminderEmbed(reminderMessage, guildInfo);
|
||||||
|
reminderChannel.send(reminderEmbed).then(async m => {
|
||||||
|
await dbfn.setRemindedStatus(guildId, 1);
|
||||||
|
if (m.deletable) {
|
||||||
|
setTimeout(function() {
|
||||||
|
m.delete();
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async setReminder(interaction, ms) {
|
||||||
|
setTimeout(this.sendReminder(interaction), ms);
|
||||||
|
},
|
||||||
|
async checkReady(client) { // Check if the guilds trees are ready to water
|
||||||
|
// TODO This is hard coded for the dev server, need to change it to lookup each server and iterate over them
|
||||||
|
// Would also be helpful to have an opt-in or opt-out ability for water checks
|
||||||
|
try {
|
||||||
|
const getOptedInGuildsResponse = await dbfn.getOptedInGuilds();
|
||||||
|
if (getOptedInGuildsResponse.status != "No servers have opted in yet") {
|
||||||
|
const guilds = getOptedInGuildsResponse.data;
|
||||||
|
guilds.forEach(async guildInfo => {
|
||||||
|
const { guildId, treeChannelId, treeMessageId, remindedStatus } = guildInfo;
|
||||||
|
|
||||||
|
if (remindedStatus == 0) {
|
||||||
|
const guild = await client.guilds.fetch(guildId);
|
||||||
|
const treeChannel = await guild.channels.fetch(treeChannelId);
|
||||||
|
const treeMessage = await treeChannel.messages.fetch(treeMessageId);
|
||||||
|
const readyToWater = treeMessage.embeds[0].description.includes('Ready to be watered');
|
||||||
|
if (readyToWater) {
|
||||||
|
console.log("Ready to water");
|
||||||
|
this.sendReminder(guildInfo, guild);
|
||||||
|
this.sleep(5000).then(() => {
|
||||||
|
this.checkReady(client);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Not ready to water");
|
||||||
|
this.sleep(5000).then(() => {
|
||||||
|
this.checkReady(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// console.log(getOptedInGuildsResponse.status);
|
||||||
|
this.sleep(5000).then(() => {
|
||||||
|
this.checkReady(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
this.sleep(30000).then(() => {
|
||||||
|
this.checkReady(client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetPing(interaction) {
|
||||||
|
dbfn.setRemindedStatus(interaction.guildId, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "silvanus",
|
"name": "silvanus",
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"description": "Grow A Tree Comparison Tool",
|
"description": "Grow A Tree Comparison Tool",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const dbfn = require('../modules/dbfn.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('dumptree')
|
||||||
|
.setDescription('Dump the contents of the tree message to console'),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
||||||
|
const { treeMessageId, treeChannelId } = getGuildInfoResponse.data;
|
||||||
|
interaction.guild.channels.fetch(treeChannelId).then(treeChannel => {
|
||||||
|
treeChannel.messages.fetch(treeMessageId).then(treeMessage => {
|
||||||
|
interaction.editReply("done");
|
||||||
|
console.log(JSON.stringify(treeMessage.embeds[0]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
|
@ -7,8 +7,9 @@ module.exports = {
|
||||||
.setDescription('See how your tree compares to other trees!'),
|
.setDescription('See how your tree compares to other trees!'),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
interaction.deferReply().then(() => {
|
interaction.deferReply().then(() => {
|
||||||
fn.rankings.compare(interaction).then(res => {
|
fn.rankings.compare(interaction).then(async res => {
|
||||||
const embed = fn.builders.comparisonEmbed(res, fn.builders.refreshAction());
|
const refreshActionRow = await fn.builders.refreshAction(interaction.guildId);
|
||||||
|
const embed = fn.builders.comparisonEmbed(res, refreshActionRow);
|
||||||
interaction.editReply(embed).catch(err => {
|
interaction.editReply(embed).catch(err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||||
|
const dbfn = require('../modules/dbfn.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('optout')
|
||||||
|
.setDescription('Opt-out of automatic water reminders')
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles),
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
const setReminderOptInResponse = await dbfn.setReminderOptIn(interaction.guildId, 0);
|
||||||
|
interaction.editReply(setReminderOptInResponse.status);
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
await interaction.editReply(fn.builders.errorEmbed(err));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
|
||||||
|
const dbfn = require('../modules/dbfn.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('setping')
|
||||||
|
.setDescription('Opt-in to automatic water reminders')
|
||||||
|
.addStringOption(o =>
|
||||||
|
o.setName('pingmsg')
|
||||||
|
.setDescription('The message to send for a water reminder')
|
||||||
|
.setRequired(true))
|
||||||
|
.addChannelOption(o =>
|
||||||
|
o.setName('pingchannel')
|
||||||
|
.setDescription('The channel to send the water reminder in')
|
||||||
|
.setRequired(true))
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.ManageRoles),
|
||||||
|
async execute(interaction) {
|
||||||
|
try {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
const reminderMessage = interaction.options.getString('pingmsg');
|
||||||
|
const reminderChannel = interaction.options.getChannel('pingchannel');
|
||||||
|
const setPingRoleResponse = await dbfn.setReminderInfo(interaction.guildId, reminderMessage, reminderChannel.id);
|
||||||
|
await dbfn.setReminderOptIn(interaction.guildId, 1);
|
||||||
|
interaction.editReply(setPingRoleResponse.status);
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
await interaction.editReply(fn.builders.errorEmbed(err));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const dbfn = require('../modules/dbfn.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('setping')
|
||||||
|
.setDescription('Run this command when you water your tree to have a reminder sent.'),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
||||||
|
const guildInfo = getGuildInfoResponse.data;
|
||||||
|
const reminderTimeS = fn.getWaterTime(guildInfo.treeHeight);
|
||||||
|
const reminderTimeMs = reminderTimeS * 1000;
|
||||||
|
fn.setReminder(interaction, reminderTimeMs, guildInfo.pingRoleId);
|
||||||
|
interaction.editReply("A reminder has been set.");
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,4 +1,5 @@
|
||||||
const { SlashCommandBuilder } = require('discord.js');
|
const { SlashCommandBuilder } = require('discord.js');
|
||||||
|
const dbfn = require('../modules/dbfn.js');
|
||||||
const fn = require('../modules/functions.js');
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -6,14 +7,19 @@ module.exports = {
|
||||||
.setName('timetoheight')
|
.setName('timetoheight')
|
||||||
.setDescription('Calculate how long it would take to reach a given height')
|
.setDescription('Calculate how long it would take to reach a given height')
|
||||||
.addStringOption(o =>
|
.addStringOption(o =>
|
||||||
o.setName('height')
|
o.setName('beginheight')
|
||||||
.setDescription('Tree height in feet, numbers ONLY')
|
.setDescription('Begining tree height in feet, numbers ONLY')
|
||||||
|
.setRequired(true))
|
||||||
|
.addStringOption(o =>
|
||||||
|
o.setName('endheight')
|
||||||
|
.setDescription('Ending tree height in feet, numbers ONLY')
|
||||||
.setRequired(true)),
|
.setRequired(true)),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
const destTreeHeight = interaction.options.getString('height');
|
const beginHeight = interaction.options.getString('beginheight');
|
||||||
fn.timeToHeight(interaction, destTreeHeight).then(res => {
|
const endHeight = interaction.options.getString('endheight');
|
||||||
interaction.editReply(`It will take you ${res} minutes to reach ${destTreeHeight}ft.`);
|
fn.timeToHeight(beginHeight, endHeight).then(res => {
|
||||||
|
interaction.editReply(`It will take a tree that is ${beginHeight}ft tall ${res} to reach ${endHeight}ft.`);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
interaction.editReply("Error: " + err);
|
interaction.editReply("Error: " + err);
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
Loading…
Reference in New Issue