Compare commits
22 Commits
main
...
v1.2.4-hot
Author | SHA1 | Date | |
---|---|---|---|
|
51dbff6f28 | ||
|
dbd66c248c | ||
|
3eaa7db561 | ||
|
a0822cb002 | ||
|
03202db21d | ||
|
15a1093aa3 | ||
|
b880503059 | ||
|
915ca4bf7c | ||
|
f4ecbf9ba3 | ||
|
291db33692 | ||
|
1b0269c304 | ||
|
b2ff3030f9 | ||
|
555d0436f8 | ||
|
c2fe7bfa9a | ||
|
d3a04921d6 | ||
|
3127305c03 | ||
|
989d26af83 | ||
|
30c43ee0cf | ||
|
6fe4456513 | ||
|
3912ccf40e | ||
|
38a70d5457 | ||
|
53ccfa31d2 |
6
.github/workflows/docker-image-dev.yml
vendored
6
.github/workflows/docker-image-dev.yml
vendored
@ -21,9 +21,3 @@ jobs:
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub v0idf1sh/silvanus-dev
|
||||
run: docker push v0idf1sh/silvanus-dev
|
||||
- name: Set up a skeleton .env file
|
||||
run: echo "TOKEN=${{secrets.DEVTOKEN}}" > .env && echo "BOTID=${{ secrets.CLIENTID }}" >> .env
|
||||
- name: Install modules
|
||||
run: npm i
|
||||
- name: Refresh commands with Discord
|
||||
run: node modules/_deploy-global.js
|
6
.github/workflows/docker-image-prod.yml
vendored
6
.github/workflows/docker-image-prod.yml
vendored
@ -21,9 +21,3 @@ jobs:
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub v0idf1sh/silvanus
|
||||
run: docker push v0idf1sh/silvanus
|
||||
- name: Set up a skeleton .env file
|
||||
run: echo "TOKEN=${{secrets.PRODTOKEN}}" > .env && echo "clientId=${{ secrets.PRODCLIENTID }}" >> .env
|
||||
- name: Install modules
|
||||
run: npm i
|
||||
- name: Refresh commands with Discord
|
||||
run: node modules/_deploy-global.js
|
38
README.md
38
README.md
@ -13,34 +13,24 @@ Silvanus is not affiliated with Grow A Tree or Limbo Labs.
|
||||
|
||||
## Setup
|
||||
|
||||
If your `/tree` and `/top trees` messages are in the same channel, simple run `/compare` in that channel and you're good to go!
|
||||
If your `/tree` and `/top trees` messages are in the same channel, simply run `/compare` in that channel and you're good to go!
|
||||
|
||||
Otherwise, run `/setup` to set the proper channels for the bot to look in for the `/tree` and `/top trees` messages.
|
||||
Otherwise, run `/setup compare` to set the proper channels for the bot to look in for the `/tree` and `/top trees` messages.
|
||||
|
||||
Use `/commands` to view a description of all my commands.
|
||||
|
||||
## Permissions
|
||||
Silvanus requires permissions to `Send Messages` and `Send Messages in Threads` if applicable. If you plan to use the Role Menu Silvanus will also need permission to `Manage Roles`
|
||||
Silvanus requires permissions to `Send Messages` and `Send Messages in Threads` if applicable. If you plan to use the Role Menu Silvanus will also need permission to `Manage Roles`.
|
||||
|
||||
## Commands
|
||||
* `/setup` - You only need to run this command if your server has its `/tree` and `/top trees` messages in separate channels.
|
||||
* `/setupinfo` - Displays your server's configuration information.
|
||||
* `/compare` - Sends a refreshable embed that calculcates the height difference between your tree and the trees currently displayed on your Tallest Trees message. There is also an Active Growth Indicator (`[💧]`).
|
||||
* `/notifications` - Guild members with the `Manage Roles` permission can run this command to set up automatic reminders when your tree is ready to be watered or when fruit is dropping.
|
||||
* This feature relies on Grow A Tree's built-in Notification system. Refer to Grow A Tree for instructions on setting them up.
|
||||
* `watchchannel`: Select the channel you've configured Grow A Tree to send notifications in.
|
||||
* `watermessage`: This option sets the message to send when the tree is ready to be watered. This can include `@pings`, links, etc.
|
||||
* `pingchannel`: Select the channel you want Silvanus to forward the notifications to.
|
||||
* `fruitmessage`: Optional: This sets the message to send when the tree is dropping fruit. If not set, the `watermessage` will be used instead.
|
||||
* `/rolemenu` - Creates a menu for users to give themselves Water and Fruit pingable roles.
|
||||
* Requires `Manage Roles` permission to run.
|
||||
* `waterrole`: Select the role to give users when they select the Water button
|
||||
* `fruitrole`: Optional: Select the role to give users when they select the Fruit button
|
||||
* If this option isn't set, no Fruit Role will be available for self-assignment.
|
||||
* `/watertime` - Calculates the wait time between waters for a tree of a given height.
|
||||
* `height`: The height in feet to calculate for.
|
||||
* `/timetoheight` - Calculates how long it would take to grow to a height
|
||||
* `endheight`: The destination height, in feet.
|
||||
* `beginheight`: Optional: The starting height, in feet. If this option isn't set, the current height of your tree will be used insead.
|
||||
* `/reset` - Removes your server's configuration from the database.
|
||||
* `/help` - Displays the bot's help page and links to each command.
|
||||
* `/compare` - Compare your tree to others on the leaderboard
|
||||
* `/relay set` - Setup a Notification Relay for the first time
|
||||
* `/relay update` - Update an already configured Notification Relay
|
||||
* `/relay disable` - Disable the Notification Relay
|
||||
* `/rolemenu` - Send a self-assignable role menu for relay pings
|
||||
* `/watertime` - Calculates the time between waters for a tree of a given height
|
||||
* `/timetoheight` - Calculates how long it would take a tree to grow to a given height
|
||||
* `/setup compare` - Set the channels to use with `/compare`
|
||||
* `/setup view` - View your server's configuration
|
||||
* `/setup reset` - Delete your server's configuration
|
||||
* `/help` - Displays the bot's help page
|
@ -5,9 +5,9 @@
|
||||
"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!\n\nImportant Note: Silvanus is only as up-to-date as your server's newest Tree and Tallest Trees messages. Make sure to refresh them before refreshing Silvanus' Compare message.",
|
||||
"setup": "If your ``/tree`` and ``/top trees`` messages are in the same channel, simple run </compare:1065346941166297128> in that channel and you're good to go!\n\nOtherwise, run </setup:1065407649363005561> to set the proper channels for the bot to look in for the ``/tree`` and ``/top trees`` messages.\n\nUse </commands:1069501270454456331> to view a description of all my commands.",
|
||||
"setup": "If your </tree:972648557796524032> and </top trees:1051840665362894950> messages are in the same channel, simply run </compare:1065346941166297128> in that channel and you're good to go!\n\nOtherwise, run </setup compare:1065407649363005561> to set the proper channels for the bot to look in for the </tree:972648557796524032> and </top trees:1051840665362894950> messages.\n\nUse </commands:1077058896469966888> to view a description of all my commands.",
|
||||
"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": "</setup:1065407649363005561> - You only need to run this command if your server has its ``/tree`` and ``/top trees`` messages in separate channels.\n</setupinfo:1065413032374706196> - Displays your server's configuration information.\n</compare:1065346941166297128> - Sends a refreshable embed that calculcates the height difference between your tree and the trees currently displayed on your Tallest Trees message. There is also an Active Growth Indicator (``[💧]``).\n</notifications:0> - Guild members with the ``Manage Roles`` permission can run this command to set up automatic reminders when your tree is ready to be watered or when fruit is dropping.\n This feature relies on Grow A Tree's built-in Notification system. Refer to Grow A Tree for instructions on setting them up.\n ``watchchannel``: Select the channel you've configured Grow A Tree to send notifications in.\n ``watermessage``: This option sets the message to send when the tree is ready to be watered. This can include ``@pings``, links, etc.\n ``pingchannel``: Select the channel you want Silvanus to forward the notifications to.\n ``fruitmessage``: Optional: This sets the message to send when the tree is dropping fruit. If not set, the ``watermessage`` will be used instead.\n</rolemenu:0> - Creates a menu for users to give themselves Water and Fruit pingable roles.\n Requires ``Manage Roles`` permission to run.\n ``waterrole``: Select the role to give users when they select the Water button\n ``fruitrole``: Optional: Select the role to give users when they select the Fruit button\n If this option isn't set, no Fruit Role will be available for self-assignment.\n</watertime:1066970330029113444> - Calculates the wait time between waters for a tree of a given height.\n ``height``: The height in feet to calculate for.\n</timetoheight:1067727254634889227> - Calculates how long it would take to grow to a height\n ``endheight``: The destination height, in feet.\n ``beginheight``: Optional: The starting height, in feet. If this option isn't set, the current height of your tree will be used insead.\n</reset:1065412317052944476> - Removes your server's configuration from the database.\n</help:1065346941166297129> - Displays the bot's help page and links to each command."
|
||||
"allCommands": "</compare:1077058896469966889> - Compare your tree to others on the leaderboard\n</relay set:1077322799032578152> - Setup a Notification Relay for the first time\n</relay update:1077322799032578152> - Update an already configured Notification Relay\n</relay disable:1077322799032578152> - Disable the Notification Relay\n</rolemenu:1077058896469966892> - Send a self-assignable role menu for relay pings\n</watertime:1077058896469966895> - Calculates the time between waters for a tree of a given height\n</timetoheight:1077058896469966894> - Calculates how long it would take a tree to grow to a given height\n</setup compare:1077058896469966893> - Set the channels to use with </compare:1077058896469966889>\n</setup view:1077058896469966893> - View your server's configuration\n</setup reset:1077058896469966893> - Delete your server's configuration\n</help:1077058896469966890> - Displays the bot's help page"
|
||||
},
|
||||
"commands": {
|
||||
"compare": "</compare:1065346941166297128>",
|
||||
@ -70,5 +70,8 @@
|
||||
"water": "is ready to be watered again!",
|
||||
"fruit": "Fruit is appearing!"
|
||||
},
|
||||
"ids": {
|
||||
"growATree": "972637072991068220"
|
||||
},
|
||||
"temp": {}
|
||||
}
|
33
main.js
33
main.js
@ -28,9 +28,10 @@ const dbfn = require('./modules/dbfn.js');
|
||||
const isDev = process.env.DEBUG;
|
||||
|
||||
client.once('ready', async () => {
|
||||
await fn.collectionBuilders.slashCommands(client);
|
||||
fn.collectionBuilders.slashCommands(client);
|
||||
await fn.collectionBuilders.guildInfos(client);
|
||||
await fn.setupCollectors(client);
|
||||
await fn.collectionBuilders.messageCollectors(client);
|
||||
// checkRateLimits();
|
||||
console.log('Ready!');
|
||||
client.user.setActivity({ name: strings.activity.name, type: ActivityType.Watching });
|
||||
if (isDev == 'false') {
|
||||
@ -85,6 +86,34 @@ client.on('interactionCreate', async interaction => {
|
||||
}
|
||||
});
|
||||
|
||||
client.on('messageUpdate', async message => {
|
||||
await fn.messages.updateHandler(message).catch(e => console.error(e));
|
||||
});
|
||||
|
||||
async function checkRateLimits(hi) {
|
||||
const axios = require('axios');
|
||||
|
||||
// Make a GET request to the Discord API
|
||||
await axios.get('https://discord.com/api/v10/users/@me', {
|
||||
headers: {
|
||||
'Authorization': `Bot ${token}`
|
||||
}
|
||||
}).then(response => {
|
||||
// Get the rate limit headers
|
||||
const remaining = response.headers['x-ratelimit-remaining'];
|
||||
const reset = response.headers['x-ratelimit-reset'];
|
||||
|
||||
// Log the rate limit headers
|
||||
console.log(`Remaining requests: ${remaining}`);
|
||||
console.log(`Reset time (Unix epoch seconds): ${reset}`);
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
await fn.sleep(500).then(async () =>{
|
||||
await checkRateLimits();
|
||||
})
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', error => {
|
||||
console.error('Unhandled promise rejection:', error);
|
||||
});
|
||||
|
@ -39,12 +39,12 @@ module.exports = {
|
||||
return this;
|
||||
}
|
||||
setTreeMessage(messageId, channelId) {
|
||||
this.treeMessageId = messageId;
|
||||
this.treeMessageId = messageId ? messageId : this.treeMessageId;
|
||||
this.treeChannelId = channelId;
|
||||
return this;
|
||||
}
|
||||
setLeaderboardMessage(messageId, channelId) {
|
||||
this.leaderboardMessageId = messageId;
|
||||
this.leaderboardMessageId = messageId ? messageId : this.leaderboardMessageId;
|
||||
this.leaderboardChannelId = channelId;
|
||||
return this;
|
||||
}
|
||||
@ -122,7 +122,7 @@ module.exports = {
|
||||
case "setTreeMessage":
|
||||
queryParts = [
|
||||
`UPDATE guild_info SET tree_message_id = ${db.escape(this.treeMessageId)}, `,
|
||||
`tree_channel_id = ${db.escape(this.treeChannelId)}, `,
|
||||
`tree_channel_id = ${db.escape(this.treeChannelId)} `,
|
||||
`WHERE guild_id = ${db.escape(this.guildId)}`
|
||||
];
|
||||
return queryParts.join('');
|
||||
@ -130,7 +130,7 @@ module.exports = {
|
||||
case "setLeaderboardMessage":
|
||||
queryParts = [
|
||||
`UPDATE guild_info SET leaderboard_message_id = ${db.escape(this.leaderboardMessageId)}, `,
|
||||
`leaderboard_channel_id = ${db.escape(this.leaderboardChannelId)}, `,
|
||||
`leaderboard_channel_id = ${db.escape(this.leaderboardChannelId)} `,
|
||||
`WHERE guild_id = ${db.escape(this.guildId)}`
|
||||
];
|
||||
return queryParts.join('');
|
||||
@ -153,6 +153,16 @@ module.exports = {
|
||||
}
|
||||
return queryParts.join('');
|
||||
break;
|
||||
case "setTreeInfo":
|
||||
queryParts = [
|
||||
`INSERT INTO guild_info (`,
|
||||
`guild_id, tree_name, tree_height`,
|
||||
`) VALUES (`,
|
||||
`${db.escape(this.guildId)}, ${db.escape(this.treeName)}, ${db.escape(this.treeHeight)}`,
|
||||
`) ON DUPLICATE KEY UPDATE tree_name = ${db.escape(this.treeName)}, `,
|
||||
`tree_height = ${db.escape(this.treeHeight)}`
|
||||
];
|
||||
return queryParts.join('');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -54,6 +54,6 @@ async function uploadCommands() {
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await deleteCommands();
|
||||
// await deleteCommands();
|
||||
await uploadCommands();
|
||||
})();
|
@ -1,34 +1,29 @@
|
||||
/*
|
||||
</setup:1065407649363005561>
|
||||
</setupinfo:1065413032374706196>
|
||||
</compare:1065346941166297128>
|
||||
</setping:1068373237995683902>
|
||||
</optout:1068753032801693758>
|
||||
</watertime:1066970330029113444>
|
||||
</timetoheight:1067727254634889227>
|
||||
</reset:1065412317052944476>
|
||||
</help:1065346941166297129>
|
||||
</tree:972648557796524032>
|
||||
</top trees:1051840665362894950>
|
||||
*/
|
||||
|
||||
const commands = [
|
||||
{raw: "`/compare`", clickable: "</compare:1077058896469966889>"},
|
||||
{raw: "`/relay set`", clickable: "</relay set:1077322799032578152>"},
|
||||
{raw: "`/relay update`", clickable: "</relay update:1077322799032578152>"},
|
||||
{raw: "`/relay disable`", clickable: "</relay disable:1077322799032578152>"},
|
||||
{raw: "`/rolemenu`", clickable: "</rolemenu:1077058896469966892>"},
|
||||
{raw: "`/watertime`", clickable: "</watertime:1077058896469966895>"},
|
||||
{raw: "`/timetoheight`", clickable: "</timetoheight:1077058896469966894>"},
|
||||
{raw: "`/setup compare`", clickable: "</setup compare:1077058896469966893>"},
|
||||
{raw: "`/setup view`", clickable: "</setup view:1077058896469966893>"},
|
||||
{raw: "`/setup reset`", clickable: "</setup reset:1077058896469966893>"},
|
||||
{raw: "`/help`", clickable: "</help:1077058896469966890>"},
|
||||
{raw: "`/tree`", clickable: "</tree:972648557796524032>"},
|
||||
{raw: "`/top trees`", clickable: "</top trees:1051840665362894950>"},
|
||||
{raw: "`/commands`", clickable: "</commands:1077058896469966888>"}
|
||||
];
|
||||
const fs = require('fs');
|
||||
const replaceAll = require('string.prototype.replaceall');
|
||||
const path = "./modules/input.txt";
|
||||
const string = fs.readFileSync(path).toString();
|
||||
let newString = replaceAll(string, '\* ', '');
|
||||
newString = replaceAll(newString, '\r\n', '\\n');
|
||||
newString = replaceAll(newString, '\n', '\\n');
|
||||
newString = replaceAll(newString, '\t', ' - ');
|
||||
newString = replaceAll(newString, '`/setup`', '</setup:1065407649363005561>');
|
||||
newString = replaceAll(newString, '`/setupinfo`', '</setupinfo:1065413032374706196>');
|
||||
newString = replaceAll(newString, '`/compare`', '</compare:1065346941166297128>');
|
||||
newString = replaceAll(newString, '`/watertime`', '</watertime:1066970330029113444>');
|
||||
newString = replaceAll(newString, '`/timetoheight`', '</timetoheight:1067727254634889227>');
|
||||
newString = replaceAll(newString, '`/reset`', '</reset:1065412317052944476>');
|
||||
newString = replaceAll(newString, '`/help`', '</help:1065346941166297129>');
|
||||
newString = replaceAll(newString, '`/commands`', '</commands:1069501270454456331>');
|
||||
newString = replaceAll(newString, '`/notifications`', '</notifications:0>');
|
||||
newString = replaceAll(newString, '`/rolemenu`', '</rolemenu:0>');
|
||||
commands.forEach(command => {
|
||||
newString = replaceAll(newString, command.raw, command.clickable);
|
||||
});
|
||||
newString = replaceAll(newString, '`', '``');
|
||||
fs.writeFileSync(path, newString);
|
||||
return "Done";
|
@ -43,6 +43,17 @@ const functions = {
|
||||
client.guildInfos.set(guildInfo.guildId, guildInfo);
|
||||
}
|
||||
return 'guildInfos Collection Built';
|
||||
},
|
||||
async messageCollectors(client) {
|
||||
// Create an empty collection for MessageCollectors
|
||||
if (!client.messageCollectors) client.messageCollectors = new Discord.Collection();
|
||||
client.messageCollectors.clear();
|
||||
// Get all of the guild infos from the client
|
||||
const { guildInfos, messageCollectors } = client;
|
||||
// Iterate over each guild info
|
||||
await guildInfos.forEach(async guildInfo => {
|
||||
await functions.collectors.create(client, guildInfo);
|
||||
});
|
||||
}
|
||||
},
|
||||
builders: {
|
||||
@ -112,6 +123,16 @@ const functions = {
|
||||
.setDescription(description)
|
||||
.setFooter({ text: strings.embeds.roleMenuFooter });
|
||||
return { embeds: [embed], components: [actionRow] };
|
||||
},
|
||||
information(content, fields) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(strings.embeds.color)
|
||||
.setTitle('Information')
|
||||
.setDescription(content)
|
||||
.setFooter({ text: `v${package.version} - ${strings.embeds.footer}` });
|
||||
if (fields) embed.addFields(fields);
|
||||
const messageContents = { embeds: [embed], ephemeral: true };
|
||||
return messageContents;
|
||||
}
|
||||
},
|
||||
comparisonEmbed(content, guildInfo) {
|
||||
@ -303,7 +324,6 @@ const functions = {
|
||||
},
|
||||
tree: {
|
||||
parse(interaction, guildInfo) {
|
||||
let input;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (guildInfo == undefined) {
|
||||
reject(`The guild entry hasn't been created yet. [${interaction.guildId || interaction.commandGuildId}]`);
|
||||
@ -316,6 +336,7 @@ const functions = {
|
||||
reject("This doesn't appear to be a valid ``/tree`` message.");
|
||||
return;
|
||||
}
|
||||
let input;
|
||||
input = m.embeds[0].data.description;
|
||||
let treeName = m.embeds[0].data.title;
|
||||
let lines = input.split('\n');
|
||||
@ -472,12 +493,61 @@ const functions = {
|
||||
},
|
||||
isTree(message) {
|
||||
if (message.embeds.length > 0) {
|
||||
return message.embeds[0].data.description.includes("Your tree is");
|
||||
// Grab the description and title
|
||||
const {description, title} = message.embeds[0].data;
|
||||
// Make sure it's a tree message
|
||||
if (description.includes("Your tree is")) {
|
||||
// Grab the name
|
||||
const treeName = title;
|
||||
// Grab the tree's height
|
||||
const indices = [description.indexOf("Your tree is ") + 13, description.indexOf("ft")];
|
||||
const treeHeightStr = description.slice(indices[0], indices[1]);
|
||||
const treeHeightFloat = parseFloat(treeHeightStr).toFixed(1);
|
||||
|
||||
// Return the info gathered
|
||||
return {
|
||||
treeName: treeName,
|
||||
treeHeight: treeHeightFloat
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isLeaderboard(message) {
|
||||
if (message.embeds.length > 0) {
|
||||
return message.embeds[0].data.title == "Tallest Trees";
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async updateHandler(message) {
|
||||
if (message.partial) {
|
||||
message = await message.fetch().catch(e => console.error(e));
|
||||
}
|
||||
// Make sure the message is from Grow A Tree
|
||||
if (message.author.id != strings.ids.growATree) return;
|
||||
// Check and store the message types
|
||||
const isLeaderboard = this.isLeaderboard(message);
|
||||
const isTree = this.isTree(message);
|
||||
// Check if the message is a leaderboard
|
||||
if (isLeaderboard) {
|
||||
// Need to actually handle this later
|
||||
// console.log("I've seen a leaderboard update.");
|
||||
} else if (isTree) { // Check if the message is a tree
|
||||
// console.log(`I've seen a tree update: ${isTree.treeName}: ${isTree.treeHeight}ft`);
|
||||
let guildInfo;
|
||||
if (message.client.guildInfos.has(message.guildId)) {
|
||||
guildInfo = message.client.guildInfos.get(message.guildId);
|
||||
guildInfo.setName(isTree.treeName)
|
||||
.setHeight(isTree.treeHeight);
|
||||
} else {
|
||||
guildInfo = new GuildInfo().setId(message.guildId)
|
||||
.setName(isTree.treeName)
|
||||
.setHeight(isTree.treeHeight);
|
||||
}
|
||||
const query = guildInfo.queryBuilder("setTreeInfo");
|
||||
await dbfn.setGuildInfo(query);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -528,6 +598,59 @@ const functions = {
|
||||
await member.roles.remove(role).catch(err => console.error("Error taking the role: " + err + "\n" + JSON.stringify(role)));
|
||||
}
|
||||
},
|
||||
collectors: {
|
||||
async create(client, guildInfo) {
|
||||
// If a collector is already setup
|
||||
if (client.messageCollectors.has(guildInfo.guildId)) {
|
||||
// Close the collector
|
||||
await this.end(client, guildInfo);
|
||||
}
|
||||
// Make sure guildInfo is what we expect, the watch channel isnt blank, and notifications are enabled
|
||||
if (guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "" && guildInfo.notificationsEnabled) {
|
||||
// Fetch the Guild
|
||||
const guild = await client.guilds.fetch(guildInfo.guildId);
|
||||
// Fetch the Channel
|
||||
const channel = await guild.channels.fetch(guildInfo.watchChannelId);
|
||||
// Create the filter function
|
||||
const filter = message => {
|
||||
// Discard any messages sent by Silvanus
|
||||
return message.author.id != process.env.BOTID;
|
||||
}
|
||||
// Create the collector
|
||||
const collector = channel.createMessageCollector({ filter });
|
||||
// Add the collector to the messageCollectors Collection
|
||||
client.messageCollectors.set(guildInfo.guildId, collector);
|
||||
collector.on('collect', message => {
|
||||
// Check for manual relay use with "water ping" and "fruit ping"
|
||||
if (message.content.toLowerCase().includes("water ping")) {
|
||||
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
||||
return;
|
||||
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
||||
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
||||
return;
|
||||
}
|
||||
// If the message doesn't contain an embed, we can ignore it
|
||||
if (message.embeds == undefined) return;
|
||||
if (message.embeds.length == 0) return;
|
||||
// Check the description field of the embed to determine if it matches Grow A Tree's notification texts
|
||||
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
||||
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
||||
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
||||
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
async end(client, guildInfo) {
|
||||
if (!client.messageCollectors) throw "No Message Collectors";
|
||||
if (!client.messageCollectors.has(guildInfo.guildId)) throw "Guild doesn't have a Message Collector";
|
||||
const collector = client.messageCollectors.get(guildInfo.guildId);
|
||||
// Close the collector
|
||||
await collector.stop();
|
||||
// Remove the collector from the messageCollectors Collection
|
||||
client.messageCollectors.delete(guildInfo.guildId);
|
||||
}
|
||||
},
|
||||
async refresh(interaction) {
|
||||
// const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
||||
// let guildInfo = getGuildInfoResponse.data;
|
||||
@ -585,31 +708,48 @@ const functions = {
|
||||
}
|
||||
return `${waterParts.value} ${waterParts.units}`;
|
||||
},
|
||||
timeToHeight(beginHeight, destHeight) {
|
||||
timeToHeight(beginHeight, destHeight, efficiency, quality) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let time = 0;
|
||||
let oldTime = 0;
|
||||
let compostAppliedCount = 0;
|
||||
let totalWaterCount = 0;
|
||||
if ((efficiency) && (quality)) {
|
||||
for (let i = beginHeight; i < destHeight; i++) {
|
||||
const randNum = Math.floor(Math.random() * 100);
|
||||
const compostApplied = randNum <= efficiency;
|
||||
if (compostApplied) {
|
||||
let qualityPercent = quality / 100;
|
||||
let waterTime = functions.getWaterTime(i);
|
||||
let reductionTime = waterTime * qualityPercent;
|
||||
let finalTime = waterTime - reductionTime;
|
||||
compostAppliedCount++;
|
||||
totalWaterCount++;
|
||||
time += parseFloat(finalTime);
|
||||
oldTime += waterTime;
|
||||
} else {
|
||||
totalWaterCount++;
|
||||
let waterTime = parseFloat(functions.getWaterTime(i));
|
||||
time += waterTime;
|
||||
oldTime += waterTime;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = beginHeight; i < destHeight; i++) {
|
||||
const waterTime = parseFloat(functions.getWaterTime(i));
|
||||
// console.log("Height: " + i + "Time: " + waterTime);
|
||||
time += waterTime;
|
||||
}
|
||||
|
||||
// 60 secs in min
|
||||
// 3600 secs in hr
|
||||
// 86400 sec in day
|
||||
|
||||
let units = " secs";
|
||||
if (60 < time && time <= 3600) { // Minutes
|
||||
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);
|
||||
const readableWaterTime = this.parseWaterTime(time);
|
||||
const savedTime = this.parseWaterTime(oldTime - time);
|
||||
resolve({
|
||||
time: readableWaterTime,
|
||||
totalWaterCount: totalWaterCount ? totalWaterCount : undefined,
|
||||
compostAppliedCount: compostAppliedCount ? compostAppliedCount : undefined,
|
||||
average: totalWaterCount ? parseFloat((compostAppliedCount / totalWaterCount) * 100).toFixed(1) : undefined,
|
||||
savedTime: savedTime
|
||||
});
|
||||
});
|
||||
},
|
||||
sleep(ms) {
|
||||
@ -624,20 +764,32 @@ const functions = {
|
||||
async sendWaterReminder(guildInfo, message, channelId, guild) {
|
||||
const reminderChannel = await guild.channels.fetch(channelId);
|
||||
const reminderEmbed = functions.builders.waterReminderEmbed(message, guildInfo);
|
||||
await reminderChannel.send(reminderEmbed).catch(err => {
|
||||
console.log(`Water Relay: ${guild.name}: ${guildInfo.treeName}`);
|
||||
await reminderChannel.send(reminderEmbed).then(async m => {
|
||||
if (!m.deletable) return;
|
||||
await this.sleep(500).then(async () => {
|
||||
await m.delete().catch(e => console.error(e));
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
async sendFruitReminder(guildInfo, message, channelId, guild) {
|
||||
const reminderChannel = await guild.channels.fetch(channelId);
|
||||
const reminderEmbed = functions.builders.fruitReminderEmbed(message, guildInfo);
|
||||
await reminderChannel.send(reminderEmbed).catch(err => {
|
||||
console.log(`Fruit Relay: ${guild.name}: ${guildInfo.treeName}`);
|
||||
await reminderChannel.send(reminderEmbed).then(async m => {
|
||||
if (!m.deletable) return;
|
||||
await this.sleep(500).then(async () => {
|
||||
await m.delete().catch(e => console.error(e));
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
async setupCollectors(client) {
|
||||
let guildInfos = client.guildInfos;
|
||||
guildInfos.set("collectors", []);
|
||||
let collectorsArray = [];
|
||||
await guildInfos.forEach(async guildInfo => {
|
||||
if (guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "" && guildInfo.notificationsEnabled) {
|
||||
const guild = await client.guilds.fetch(guildInfo.guildId);
|
||||
@ -666,6 +818,36 @@ const functions = {
|
||||
});
|
||||
}
|
||||
});
|
||||
guildInfos.set("collectors", collectorsArray);
|
||||
},
|
||||
async setupCollector(channel, interaction) {
|
||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||
let collectors = interaction.client.guildInfos.get('collectors');
|
||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||
const filter = message => {
|
||||
return message.author.id != process.env.BOTID;
|
||||
}
|
||||
const collector = channel.createMessageCollector({ filter });
|
||||
collectors.push(collector);
|
||||
collector.on('collect', message => {
|
||||
if (message.content.toLowerCase().includes("water ping")) {
|
||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
||||
return;
|
||||
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
||||
return;
|
||||
}
|
||||
if (message.embeds == undefined) return;
|
||||
if (message.embeds.length == 0) return;
|
||||
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
||||
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw "Guild doesn't exist in database!";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
information(content, fields) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(strings.embeds.color)
|
||||
.setTitle('Information')
|
||||
.setDescription(content)
|
||||
.setFooter({ text: `v${package.version} - ${strings.embeds.footer}` });
|
||||
if (fields) embed.addFields(fields);
|
||||
const messageContents = { embeds: [embed], ephemeral: true };
|
||||
return messageContents;
|
||||
}
|
||||
|
||||
replyContent = `I estimate that a tree with ${efficiency}% Composter Efficiency and ${quality}% Compost Quality growing from ${beginHeight}ft to ${endHeight}ft will take ${time}`;
|
||||
replyFields = [
|
||||
{ name: `Start Height:`, value: `**${beginHeight}ft**`, inline: true },
|
||||
{ name: `End Height:`, value: `**${endHeight}**`, inline: true },
|
||||
{ name: `Efficiency:`, value: `**${efficiency}%**`, inline: true },
|
||||
{ name: `Quality:`, value: `**${quality}%**`, inline: true },
|
||||
{ name: `Summary`, value: `Compost Applied **${compostAppliedCount}** times, out of **${totalWaterCount}** waterings, for an average of **${average}%**` }
|
||||
];
|
||||
|
||||
const reply = information(replyContent, replyFields);
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "silvanus",
|
||||
"version": "1.2.1",
|
||||
"version": "1.2.3",
|
||||
"description": "Grow A Tree Companion Bot",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@ -17,6 +17,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/voidf1sh/silvanus#readme",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.3",
|
||||
"discord.js": "^14.7.1",
|
||||
"dotenv": "^16.0.3",
|
||||
"mysql": "^2.18.1",
|
||||
|
@ -14,7 +14,7 @@ module.exports = {
|
||||
{ name: "True", value: "true" },
|
||||
{ name: "False", value: "false" })),
|
||||
execute(interaction) {
|
||||
const helpEmbed = fn.builders.helpEmbed(`${strings.help.info}\n\n**Setup**\n${strings.help.setup}\n\nSee </commands:0> for a list of all my commands\n\n**Support Server**\n${strings.urls.supportServer}`, interaction.options.getString('private'));
|
||||
const helpEmbed = fn.builders.helpEmbed(`${strings.help.info}\n\n**Setup**\n${strings.help.setup}\n\n**Support Server**\n${strings.urls.supportServer}`, interaction.options.getString('private'));
|
||||
interaction.reply(helpEmbed);
|
||||
},
|
||||
};
|
@ -6,7 +6,7 @@ const strings = require('../data/strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('notifications')
|
||||
.setName('relay')
|
||||
.setDescription('A notification relay for improved water and fruit notifications')
|
||||
.addSubcommand(sc =>
|
||||
sc.setName('set')
|
||||
@ -67,75 +67,115 @@ module.exports = {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
// if (process.env.DEBUG) console.log(`${typeof subcommand}: ${subcommand}`);
|
||||
switch (subcommand) {
|
||||
// Set all components for the first time
|
||||
case "set":
|
||||
// If there is already a guildInfo object for this server
|
||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||
const watchChannel = interaction.options.getChannel('watchchannel');
|
||||
const waterMessage = interaction.options.getString('watermessage');
|
||||
const fruitMessage = interaction.options.getString('fruitmessage') ? interaction.options.getString('fruitmessage') : interaction.options.getString('watermessage');
|
||||
const reminderChannel = interaction.options.getChannel('pingchannel');
|
||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||
guildInfo.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
||||
let query = guildInfo.queryBuilder("setReminders");
|
||||
await dbfn.setGuildInfo(query);
|
||||
const replyParts = [
|
||||
`I'll watch <#${watchChannel.id}> for Grow A Tree Notifications and relay them to <#${reminderChannel.id}>.`,
|
||||
`Water Message: ${waterMessage}`
|
||||
];
|
||||
if (fruitMessage != "") replyParts.push(`Fruit Message: ${fruitMessage}`);
|
||||
await interaction.editReply(replyParts.join("\n")).catch(e => console.error(e));
|
||||
fn.collectionBuilders.guildInfos(interaction.client);
|
||||
} else {
|
||||
// Get options from the interaction
|
||||
const watchChannel = interaction.options.getChannel('watchchannel');
|
||||
const waterMessage = interaction.options.getString('watermessage');
|
||||
// If the fruit message is set, use it, otherwise default to the water message.
|
||||
const fruitMessage = interaction.options.getString('fruitmessage') ? interaction.options.getString('fruitmessage') : interaction.options.getString('watermessage');
|
||||
const reminderChannel = interaction.options.getChannel('pingchannel');
|
||||
// Set the reminder configuration in the GuildInfo object
|
||||
guildInfo.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
||||
// Update the guildInfos Collection
|
||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||
// Build a query to update the database
|
||||
let query = guildInfo.queryBuilder("setReminders");
|
||||
// Run the query
|
||||
await dbfn.setGuildInfo(query);
|
||||
// Set up a collector on the watch channel
|
||||
fn.collectors.create(interaction.client, guildInfo);
|
||||
// Compose a reply
|
||||
const reply = [
|
||||
`I'll watch <#${watchChannel.id}> for Grow A Tree Notifications and relay them to <#${reminderChannel.id}>.`,
|
||||
`Water Message: ${waterMessage}`,
|
||||
`Fruit Message: ${fruitMessage}`
|
||||
].join("\n");
|
||||
// Send the reply
|
||||
await interaction.editReply(fn.builders.embed(reply)).catch(e => console.error(e));
|
||||
} else {
|
||||
// Get options from the interaction
|
||||
const watchChannel = interaction.options.getChannel('watchchannel');
|
||||
const waterMessage = interaction.options.getString('watermessage');
|
||||
// If the fruit message is set, use it. Otherwise default to the water message
|
||||
const fruitMessage = interaction.options.getString('fruitmessage') ? interaction.options.getString('fruitmessage') : interaction.options.getString('watermessage');
|
||||
const reminderChannel = interaction.options.getChannel('pingchannel');
|
||||
// Create a new GuildInfo object
|
||||
let guildInfo = new GuildInfo()
|
||||
.setId(interaction.guildId)
|
||||
// Set the reminder configuration
|
||||
.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
||||
// Update the guildInfos Collection
|
||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||
// Build a query to update the database
|
||||
let query = guildInfo.queryBuilder("setReminders");
|
||||
// Run the query
|
||||
await dbfn.setGuildInfo(query);
|
||||
const replyParts = [
|
||||
// Create a messageCollector on the watch channel
|
||||
fn.collectors.create(interaction.client, guildInfo);
|
||||
// Compose a reply
|
||||
const reply = [
|
||||
`I'll watch <#${watchChannel.id}> for Grow A Tree Notifications and relay them to <#${reminderChannel.id}>.`,
|
||||
`Water Message: ${waterMessage}`
|
||||
];
|
||||
if (fruitMessage != "") replyParts.push(`Fruit Message: ${fruitMessage}`);
|
||||
await interaction.editReply(replyParts.join("\n")).catch(e => console.error(e));
|
||||
fn.collectionBuilders.guildInfos(interaction.client);
|
||||
`Water Message: ${waterMessage}`,
|
||||
`Fruit Message: ${fruitMessage}`
|
||||
].join("\n");
|
||||
// Send the reply
|
||||
await interaction.editReply(reply).catch(e => console.error(e));
|
||||
}
|
||||
break;
|
||||
case "update":
|
||||
case "update": // Update the relay configuration piecemeal
|
||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||
|
||||
// Get all possible options from the interaction
|
||||
const inWatchChannel = interaction.options.getChannel('watchchannel');
|
||||
const inWaterMessage = interaction.options.getString('watermessage');
|
||||
const inFruitMessage = interaction.options.getString('fruitmessage');
|
||||
const inReminderChannel = interaction.options.getChannel('pingchannel');
|
||||
|
||||
// Check if each option is set, if it is, use it. Otherwise use what was already set
|
||||
const outWatchChannelId = inWatchChannel ? inWatchChannel.id : guildInfo.watchChannelId;
|
||||
const outWaterMessage = inWaterMessage ? inWaterMessage : guildInfo.waterMessage;
|
||||
const outFruitMessage = inFruitMessage ? inFruitMessage : guildInfo.fruitMessage;
|
||||
const outReminderChannelId = inReminderChannel ? inReminderChannel.id : guildInfo.reminderChannelId;
|
||||
|
||||
// Update the relay configuration
|
||||
guildInfo.setReminders(outWaterMessage, outFruitMessage, outReminderChannelId, outWatchChannelId, true);
|
||||
// Update the guildInfos Collection
|
||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||
// Build a query to update the database
|
||||
let query = guildInfo.queryBuilder("setReminders");
|
||||
// Run the query
|
||||
await dbfn.setGuildInfo(query);
|
||||
const replyParts = [
|
||||
// Create a messageCollector on the watch channel
|
||||
fn.collectors.create(interaction.client, guildInfo);
|
||||
// Compose a reply
|
||||
const reply = [
|
||||
`I'll watch <#${outWatchChannelId}> for Grow A Tree Notifications and relay them to <#${outReminderChannelId}>.`,
|
||||
`Water Message: ${outWaterMessage}`
|
||||
];
|
||||
if (outFruitMessage != "") replyParts.push(`Fruit Message: ${outFruitMessage}`);
|
||||
await interaction.editReply(replyParts.join("\n")).catch(e => console.error(e));
|
||||
fn.collectionBuilders.guildInfos(interaction.client);
|
||||
`Water Message: ${outWaterMessage}`,
|
||||
`Fruit Message: ${outFruitMessage}`
|
||||
].join("\n");
|
||||
// Send the reply
|
||||
await interaction.editReply(reply).catch(e => console.error(e));
|
||||
} else {
|
||||
await interaction.editReply(fn.builders.errorEmbed("There is no existing notification relay to update!")).catch(e => console.error(e));
|
||||
}
|
||||
break;
|
||||
case 'disable':
|
||||
case 'disable': // Disable the relay
|
||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||
// Update the relay config with all undefined except for `enabled` which is false
|
||||
guildInfo.setReminders(undefined, undefined, undefined, undefined, false);
|
||||
// Update the guildInfos Collection
|
||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||
// Update the database
|
||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setReminders")).catch(e => console.error(e));
|
||||
await fn.collectionBuilders.guildInfos(interaction.client);
|
||||
// Close the collector
|
||||
await fn.collectors.end(interaction.client, guildInfo).catch(e => console.error(e));
|
||||
// Reply confirming disabling of relay
|
||||
await interaction.editReply(fn.builders.embed(strings.status.optout)).catch(e => console.error(e));
|
||||
} else {
|
||||
await interaction.editReply(fn.builders.errorEmbed("A notification relay has not been set up yet!")).catch(e => console.error(e));
|
||||
|
@ -18,8 +18,8 @@ module.exports = {
|
||||
)
|
||||
.addChannelOption(o =>
|
||||
o.setName('leaderboardchannel')
|
||||
.setDescription('If your leaderboard isn\'t in the same channel, where is it?')
|
||||
.setRequired(false)
|
||||
.setDescription('What channel is your leaderboard in?')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
.addSubcommand(sc =>
|
||||
@ -54,15 +54,27 @@ module.exports = {
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
switch (subcommand) {
|
||||
case "compare":
|
||||
const treeChannel = interaction.options.getChannel('treechannel');
|
||||
const leaderboardChannel = interaction.options.getChannel('leaderboardchannel');
|
||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||
const findMessagesResponse = await fn.messages.find(interaction, guildInfo);
|
||||
await interaction.editReply(findMessagesResponse.status).catch(e => console.error(e));
|
||||
guildInfo.setTreeMessage(undefined, treeChannel.id);
|
||||
guildInfo.setLeaderboardMessage(undefined, leaderboardChannel.id);
|
||||
// Update the database
|
||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setTreeMessage")).catch(e => console.error(e));
|
||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setLeaderboardMessage")).catch(e => console.error(e));
|
||||
const reply = `Tree Channel: <#${treeChannel.id}> | Leaderboard Channel: <#${leaderboardChannel.id}>`;
|
||||
await interaction.editReply(fn.builders.embed(reply)).catch(e => console.error(e));
|
||||
} else {
|
||||
let guildInfo = new GuildInfo()
|
||||
.setId(interaction.guildId);
|
||||
const findMessagesResponse = await fn.messages.find(interaction, guildInfo);
|
||||
await interaction.editReply(findMessagesResponse.status).catch(e => console.error(e));
|
||||
.setId(interaction.guildId)
|
||||
.setTreeMessage(undefined, treeChannel.id)
|
||||
.setLeaderboardMessage(undefined, leaderboardChannel.id);
|
||||
// Update the database
|
||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setTreeMessage")).catch(e => console.error(e));
|
||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setLeaderboardMessage")).catch(e => console.error(e));
|
||||
const reply = `Tree Channel: <#${treeChannel.id}> | Leaderboard Channel: <#${leaderboardChannel.id}>`;
|
||||
await interaction.editReply(fn.builders.embed(reply)).catch(e => console.error(e));
|
||||
}
|
||||
break;
|
||||
case "rolemenu":
|
||||
@ -114,7 +126,7 @@ module.exports = {
|
||||
await interaction.editReply(fn.builders.embed("You must select 'true' to confirm setup reset. No changes have been made.")).catch(e => console.error(e));
|
||||
}
|
||||
} else {
|
||||
throw "Guild doesn't exist in database!";
|
||||
await interaction.editReply(fn.builders.errorEmbed("There is no configuration to delete.")).catch(e => console.error(e));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -9,25 +9,61 @@ module.exports = {
|
||||
.addIntegerOption(o =>
|
||||
o.setName('endheight')
|
||||
.setDescription('Ending tree height in feet')
|
||||
.setRequired(true))
|
||||
.setRequired(true)
|
||||
)
|
||||
.addIntegerOption(o =>
|
||||
o.setName('beginheight')
|
||||
.setDescription('Beginning tree height in feet')
|
||||
.setRequired(false)),
|
||||
.setRequired(false)
|
||||
)
|
||||
.addIntegerOption(o =>
|
||||
o.setName('efficiency')
|
||||
.setDescription('Composter efficiency percentage, rounded')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addIntegerOption(o =>
|
||||
o.setName('quality')
|
||||
.setDescription('Compost quality percentage, rounded')
|
||||
.setRequired(false)
|
||||
)
|
||||
.addBooleanOption(o =>
|
||||
o.setName('private')
|
||||
.setDescription('Should the reply be visible only to you?')
|
||||
.setRequired(false)
|
||||
),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
let beginHeight = interaction.options.getInteger('beginheight');
|
||||
const private = interaction.options.getBoolean('private') != undefined ? interaction.options.getBoolean('private') : true;
|
||||
await interaction.deferReply({ ephemeral: private });
|
||||
const inBeginHeight = interaction.options.getInteger('beginheight');
|
||||
const endHeight = interaction.options.getInteger('endheight');
|
||||
if (!beginHeight) {
|
||||
const efficiency = interaction.options.getInteger('efficiency') != undefined ? interaction.options.getInteger('efficiency') : 10;
|
||||
const quality = interaction.options.getInteger('quality') != undefined ? interaction.options.getInteger('quality') : 5;
|
||||
let beginHeight, replyContent;
|
||||
|
||||
if (!inBeginHeight) {
|
||||
const guildInfo = interaction.client.guildInfos.get(interaction.guild.id);
|
||||
beginHeight = guildInfo.treeHeight;
|
||||
} else {
|
||||
beginHeight = inBeginHeight;
|
||||
}
|
||||
|
||||
const timeToHeightResults = await fn.timeToHeight(beginHeight, endHeight, efficiency, quality);
|
||||
const { time, totalWaterCount, compostAppliedCount, average, savedTime } = timeToHeightResults;
|
||||
let replyFields = undefined;
|
||||
if (efficiency && quality) {
|
||||
replyContent = `I estimate that a tree with ${efficiency}% Composter Efficiency and ${quality}% Compost Quality growing from ${beginHeight}ft to ${endHeight}ft will take ${time}`;
|
||||
replyFields = [
|
||||
{ name: `Height Gained:`, value: `${endHeight - beginHeight}ft`, inline: true},
|
||||
{ name: `E/Q:`, value: `${efficiency}%/${quality}%`, inline: true},
|
||||
{ name: `Compost Applied`, value: `${compostAppliedCount} times`, inline: true },
|
||||
{ name: `Compost Average`, value: `${average}%`, inline: true },
|
||||
{ name: `Saved Time`, value: savedTime, inline: true }
|
||||
];
|
||||
} else {
|
||||
replyContent = `I estimate that a tree growing from ${beginHeight}ft to ${endHeight}ft will take ${time}`;
|
||||
}
|
||||
|
||||
const reply = fn.builders.embeds.information(replyContent, replyFields)
|
||||
await interaction.editReply(reply);
|
||||
}
|
||||
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 => {
|
||||
interaction.editReply("Error: " + err);
|
||||
console.error(err);
|
||||
return;
|
||||
});
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user