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 |
8
.github/workflows/docker-image-dev.yml
vendored
8
.github/workflows/docker-image-dev.yml
vendored
@ -20,10 +20,4 @@ jobs:
|
|||||||
- name: Log into Docker Hub
|
- name: Log into Docker Hub
|
||||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||||
- name: Push image to Docker Hub v0idf1sh/silvanus-dev
|
- name: Push image to Docker Hub v0idf1sh/silvanus-dev
|
||||||
run: docker push 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
|
|
8
.github/workflows/docker-image-prod.yml
vendored
8
.github/workflows/docker-image-prod.yml
vendored
@ -20,10 +20,4 @@ jobs:
|
|||||||
- name: Log into Docker Hub
|
- name: Log into Docker Hub
|
||||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||||
- name: Push image to Docker Hub v0idf1sh/silvanus
|
- name: Push image to Docker Hub v0idf1sh/silvanus
|
||||||
run: docker push 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
|
## 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.
|
Use `/commands` to view a description of all my commands.
|
||||||
|
|
||||||
## Permissions
|
## 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
|
## Commands
|
||||||
* `/setup` - You only need to run this command if your server has its `/tree` and `/top trees` messages in separate channels.
|
* `/compare` - Compare your tree to others on the leaderboard
|
||||||
* `/setupinfo` - Displays your server's configuration information.
|
* `/relay set` - Setup a Notification Relay for the first time
|
||||||
* `/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 (`[💧]`).
|
* `/relay update` - Update an already configured Notification Relay
|
||||||
* `/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.
|
* `/relay disable` - Disable the Notification Relay
|
||||||
* This feature relies on Grow A Tree's built-in Notification system. Refer to Grow A Tree for instructions on setting them up.
|
* `/rolemenu` - Send a self-assignable role menu for relay pings
|
||||||
* `watchchannel`: Select the channel you've configured Grow A Tree to send notifications in.
|
* `/watertime` - Calculates the time between waters for a tree of a given height
|
||||||
* `watermessage`: This option sets the message to send when the tree is ready to be watered. This can include `@pings`, links, etc.
|
* `/timetoheight` - Calculates how long it would take a tree to grow to a given height
|
||||||
* `pingchannel`: Select the channel you want Silvanus to forward the notifications to.
|
* `/setup compare` - Set the channels to use with `/compare`
|
||||||
* `fruitmessage`: Optional: This sets the message to send when the tree is dropping fruit. If not set, the `watermessage` will be used instead.
|
* `/setup view` - View your server's configuration
|
||||||
* `/rolemenu` - Creates a menu for users to give themselves Water and Fruit pingable roles.
|
* `/setup reset` - Delete your server's configuration
|
||||||
* Requires `Manage Roles` permission to run.
|
* `/help` - Displays the bot's help page
|
||||||
* `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.
|
|
@ -5,9 +5,9 @@
|
|||||||
"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!\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.",
|
"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.",
|
"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": {
|
"commands": {
|
||||||
"compare": "</compare:1065346941166297128>",
|
"compare": "</compare:1065346941166297128>",
|
||||||
@ -70,5 +70,8 @@
|
|||||||
"water": "is ready to be watered again!",
|
"water": "is ready to be watered again!",
|
||||||
"fruit": "Fruit is appearing!"
|
"fruit": "Fruit is appearing!"
|
||||||
},
|
},
|
||||||
|
"ids": {
|
||||||
|
"growATree": "972637072991068220"
|
||||||
|
},
|
||||||
"temp": {}
|
"temp": {}
|
||||||
}
|
}
|
33
main.js
33
main.js
@ -28,9 +28,10 @@ const dbfn = require('./modules/dbfn.js');
|
|||||||
const isDev = process.env.DEBUG;
|
const isDev = process.env.DEBUG;
|
||||||
|
|
||||||
client.once('ready', async () => {
|
client.once('ready', async () => {
|
||||||
await fn.collectionBuilders.slashCommands(client);
|
fn.collectionBuilders.slashCommands(client);
|
||||||
await fn.collectionBuilders.guildInfos(client);
|
await fn.collectionBuilders.guildInfos(client);
|
||||||
await fn.setupCollectors(client);
|
await fn.collectionBuilders.messageCollectors(client);
|
||||||
|
// checkRateLimits();
|
||||||
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 });
|
||||||
if (isDev == 'false') {
|
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 => {
|
process.on('unhandledRejection', error => {
|
||||||
console.error('Unhandled promise rejection:', error);
|
console.error('Unhandled promise rejection:', error);
|
||||||
});
|
});
|
||||||
|
@ -39,12 +39,12 @@ module.exports = {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setTreeMessage(messageId, channelId) {
|
setTreeMessage(messageId, channelId) {
|
||||||
this.treeMessageId = messageId;
|
this.treeMessageId = messageId ? messageId : this.treeMessageId;
|
||||||
this.treeChannelId = channelId;
|
this.treeChannelId = channelId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setLeaderboardMessage(messageId, channelId) {
|
setLeaderboardMessage(messageId, channelId) {
|
||||||
this.leaderboardMessageId = messageId;
|
this.leaderboardMessageId = messageId ? messageId : this.leaderboardMessageId;
|
||||||
this.leaderboardChannelId = channelId;
|
this.leaderboardChannelId = channelId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ module.exports = {
|
|||||||
case "setTreeMessage":
|
case "setTreeMessage":
|
||||||
queryParts = [
|
queryParts = [
|
||||||
`UPDATE guild_info SET tree_message_id = ${db.escape(this.treeMessageId)}, `,
|
`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)}`
|
`WHERE guild_id = ${db.escape(this.guildId)}`
|
||||||
];
|
];
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
@ -130,7 +130,7 @@ module.exports = {
|
|||||||
case "setLeaderboardMessage":
|
case "setLeaderboardMessage":
|
||||||
queryParts = [
|
queryParts = [
|
||||||
`UPDATE guild_info SET leaderboard_message_id = ${db.escape(this.leaderboardMessageId)}, `,
|
`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)}`
|
`WHERE guild_id = ${db.escape(this.guildId)}`
|
||||||
];
|
];
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
@ -153,7 +153,17 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,6 @@ async function uploadCommands() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await deleteCommands();
|
// await deleteCommands();
|
||||||
await uploadCommands();
|
await uploadCommands();
|
||||||
})();
|
})();
|
@ -1,34 +1,29 @@
|
|||||||
/*
|
const commands = [
|
||||||
</setup:1065407649363005561>
|
{raw: "`/compare`", clickable: "</compare:1077058896469966889>"},
|
||||||
</setupinfo:1065413032374706196>
|
{raw: "`/relay set`", clickable: "</relay set:1077322799032578152>"},
|
||||||
</compare:1065346941166297128>
|
{raw: "`/relay update`", clickable: "</relay update:1077322799032578152>"},
|
||||||
</setping:1068373237995683902>
|
{raw: "`/relay disable`", clickable: "</relay disable:1077322799032578152>"},
|
||||||
</optout:1068753032801693758>
|
{raw: "`/rolemenu`", clickable: "</rolemenu:1077058896469966892>"},
|
||||||
</watertime:1066970330029113444>
|
{raw: "`/watertime`", clickable: "</watertime:1077058896469966895>"},
|
||||||
</timetoheight:1067727254634889227>
|
{raw: "`/timetoheight`", clickable: "</timetoheight:1077058896469966894>"},
|
||||||
</reset:1065412317052944476>
|
{raw: "`/setup compare`", clickable: "</setup compare:1077058896469966893>"},
|
||||||
</help:1065346941166297129>
|
{raw: "`/setup view`", clickable: "</setup view:1077058896469966893>"},
|
||||||
</tree:972648557796524032>
|
{raw: "`/setup reset`", clickable: "</setup reset:1077058896469966893>"},
|
||||||
</top trees:1051840665362894950>
|
{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 fs = require('fs');
|
||||||
const replaceAll = require('string.prototype.replaceall');
|
const replaceAll = require('string.prototype.replaceall');
|
||||||
const path = "./modules/input.txt";
|
const path = "./modules/input.txt";
|
||||||
const string = fs.readFileSync(path).toString();
|
const string = fs.readFileSync(path).toString();
|
||||||
let newString = replaceAll(string, '\* ', '');
|
let newString = replaceAll(string, '\* ', '');
|
||||||
newString = replaceAll(newString, '\r\n', '\\n');
|
newString = replaceAll(newString, '\n', '\\n');
|
||||||
newString = replaceAll(newString, '\t', ' - ');
|
newString = replaceAll(newString, '\t', ' - ');
|
||||||
newString = replaceAll(newString, '`/setup`', '</setup:1065407649363005561>');
|
commands.forEach(command => {
|
||||||
newString = replaceAll(newString, '`/setupinfo`', '</setupinfo:1065413032374706196>');
|
newString = replaceAll(newString, command.raw, command.clickable);
|
||||||
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>');
|
|
||||||
newString = replaceAll(newString, '`', '``');
|
newString = replaceAll(newString, '`', '``');
|
||||||
fs.writeFileSync(path, newString);
|
fs.writeFileSync(path, newString);
|
||||||
return "Done";
|
return "Done";
|
@ -43,6 +43,17 @@ const functions = {
|
|||||||
client.guildInfos.set(guildInfo.guildId, guildInfo);
|
client.guildInfos.set(guildInfo.guildId, guildInfo);
|
||||||
}
|
}
|
||||||
return 'guildInfos Collection Built';
|
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: {
|
builders: {
|
||||||
@ -112,6 +123,16 @@ const functions = {
|
|||||||
.setDescription(description)
|
.setDescription(description)
|
||||||
.setFooter({ text: strings.embeds.roleMenuFooter });
|
.setFooter({ text: strings.embeds.roleMenuFooter });
|
||||||
return { embeds: [embed], components: [actionRow] };
|
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) {
|
comparisonEmbed(content, guildInfo) {
|
||||||
@ -303,7 +324,6 @@ const functions = {
|
|||||||
},
|
},
|
||||||
tree: {
|
tree: {
|
||||||
parse(interaction, guildInfo) {
|
parse(interaction, guildInfo) {
|
||||||
let input;
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (guildInfo == undefined) {
|
if (guildInfo == undefined) {
|
||||||
reject(`The guild entry hasn't been created yet. [${interaction.guildId || interaction.commandGuildId}]`);
|
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.");
|
reject("This doesn't appear to be a valid ``/tree`` message.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let input;
|
||||||
input = m.embeds[0].data.description;
|
input = m.embeds[0].data.description;
|
||||||
let treeName = m.embeds[0].data.title;
|
let treeName = m.embeds[0].data.title;
|
||||||
let lines = input.split('\n');
|
let lines = input.split('\n');
|
||||||
@ -472,12 +493,61 @@ const functions = {
|
|||||||
},
|
},
|
||||||
isTree(message) {
|
isTree(message) {
|
||||||
if (message.embeds.length > 0) {
|
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) {
|
isLeaderboard(message) {
|
||||||
if (message.embeds.length > 0) {
|
if (message.embeds.length > 0) {
|
||||||
return message.embeds[0].data.title == "Tallest Trees";
|
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)));
|
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) {
|
async refresh(interaction) {
|
||||||
// const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
// const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
||||||
// let guildInfo = getGuildInfoResponse.data;
|
// let guildInfo = getGuildInfoResponse.data;
|
||||||
@ -585,31 +708,48 @@ const functions = {
|
|||||||
}
|
}
|
||||||
return `${waterParts.value} ${waterParts.units}`;
|
return `${waterParts.value} ${waterParts.units}`;
|
||||||
},
|
},
|
||||||
timeToHeight(beginHeight, destHeight) {
|
timeToHeight(beginHeight, destHeight, efficiency, quality) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let time = 0;
|
let time = 0;
|
||||||
for (let i = beginHeight; i < destHeight; i++) {
|
let oldTime = 0;
|
||||||
const waterTime = parseFloat(functions.getWaterTime(i));
|
let compostAppliedCount = 0;
|
||||||
// console.log("Height: " + i + "Time: " + waterTime);
|
let totalWaterCount = 0;
|
||||||
time += waterTime;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
const readableWaterTime = this.parseWaterTime(time);
|
||||||
// 60 secs in min
|
const savedTime = this.parseWaterTime(oldTime - time);
|
||||||
// 3600 secs in hr
|
resolve({
|
||||||
// 86400 sec in day
|
time: readableWaterTime,
|
||||||
|
totalWaterCount: totalWaterCount ? totalWaterCount : undefined,
|
||||||
let units = " secs";
|
compostAppliedCount: compostAppliedCount ? compostAppliedCount : undefined,
|
||||||
if (60 < time && time <= 3600) { // Minutes
|
average: totalWaterCount ? parseFloat((compostAppliedCount / totalWaterCount) * 100).toFixed(1) : undefined,
|
||||||
time = parseFloat(time / 60).toFixed(1);
|
savedTime: savedTime
|
||||||
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) {
|
sleep(ms) {
|
||||||
@ -624,22 +764,34 @@ const functions = {
|
|||||||
async sendWaterReminder(guildInfo, message, channelId, guild) {
|
async sendWaterReminder(guildInfo, message, channelId, guild) {
|
||||||
const reminderChannel = await guild.channels.fetch(channelId);
|
const reminderChannel = await guild.channels.fetch(channelId);
|
||||||
const reminderEmbed = functions.builders.waterReminderEmbed(message, guildInfo);
|
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);
|
console.error(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async sendFruitReminder(guildInfo, message, channelId, guild) {
|
async sendFruitReminder(guildInfo, message, channelId, guild) {
|
||||||
const reminderChannel = await guild.channels.fetch(channelId);
|
const reminderChannel = await guild.channels.fetch(channelId);
|
||||||
const reminderEmbed = functions.builders.fruitReminderEmbed(message, guildInfo);
|
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);
|
console.error(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async setupCollectors(client) {
|
async setupCollectors(client) {
|
||||||
let guildInfos = client.guildInfos;
|
let guildInfos = client.guildInfos;
|
||||||
guildInfos.set("collectors", []);
|
let collectorsArray = [];
|
||||||
await guildInfos.forEach(async guildInfo => {
|
await guildInfos.forEach(async guildInfo => {
|
||||||
if ( guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "" && guildInfo.notificationsEnabled) {
|
if (guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "" && guildInfo.notificationsEnabled) {
|
||||||
const guild = await client.guilds.fetch(guildInfo.guildId);
|
const guild = await client.guilds.fetch(guildInfo.guildId);
|
||||||
// console.log(guildInfo instanceof GuildInfo);
|
// console.log(guildInfo instanceof GuildInfo);
|
||||||
const channel = await guild.channels.fetch(guildInfo.watchChannelId);
|
const channel = await guild.channels.fetch(guildInfo.watchChannelId);
|
||||||
@ -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",
|
"name": "silvanus",
|
||||||
"version": "1.2.1",
|
"version": "1.2.3",
|
||||||
"description": "Grow A Tree Companion Bot",
|
"description": "Grow A Tree Companion Bot",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/voidf1sh/silvanus#readme",
|
"homepage": "https://github.com/voidf1sh/silvanus#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.3.3",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.7.1",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
|
@ -14,7 +14,7 @@ module.exports = {
|
|||||||
{ name: "True", value: "true" },
|
{ name: "True", value: "true" },
|
||||||
{ name: "False", value: "false" })),
|
{ name: "False", value: "false" })),
|
||||||
execute(interaction) {
|
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);
|
interaction.reply(helpEmbed);
|
||||||
},
|
},
|
||||||
};
|
};
|
@ -6,7 +6,7 @@ const strings = require('../data/strings.json');
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('notifications')
|
.setName('relay')
|
||||||
.setDescription('A notification relay for improved water and fruit notifications')
|
.setDescription('A notification relay for improved water and fruit notifications')
|
||||||
.addSubcommand(sc =>
|
.addSubcommand(sc =>
|
||||||
sc.setName('set')
|
sc.setName('set')
|
||||||
@ -67,75 +67,115 @@ module.exports = {
|
|||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
// if (process.env.DEBUG) console.log(`${typeof subcommand}: ${subcommand}`);
|
// if (process.env.DEBUG) console.log(`${typeof subcommand}: ${subcommand}`);
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
|
// Set all components for the first time
|
||||||
case "set":
|
case "set":
|
||||||
|
// If there is already a guildInfo object for this server
|
||||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
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);
|
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||||
guildInfo.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
// Get options from the interaction
|
||||||
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 {
|
|
||||||
const watchChannel = interaction.options.getChannel('watchchannel');
|
const watchChannel = interaction.options.getChannel('watchchannel');
|
||||||
const waterMessage = interaction.options.getString('watermessage');
|
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 fruitMessage = interaction.options.getString('fruitmessage') ? interaction.options.getString('fruitmessage') : interaction.options.getString('watermessage');
|
||||||
const reminderChannel = interaction.options.getChannel('pingchannel');
|
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()
|
let guildInfo = new GuildInfo()
|
||||||
.setId(interaction.guildId)
|
.setId(interaction.guildId)
|
||||||
|
// Set the reminder configuration
|
||||||
.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
.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");
|
let query = guildInfo.queryBuilder("setReminders");
|
||||||
|
// Run the query
|
||||||
await dbfn.setGuildInfo(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}>.`,
|
`I'll watch <#${watchChannel.id}> for Grow A Tree Notifications and relay them to <#${reminderChannel.id}>.`,
|
||||||
`Water Message: ${waterMessage}`
|
`Water Message: ${waterMessage}`,
|
||||||
];
|
`Fruit Message: ${fruitMessage}`
|
||||||
if (fruitMessage != "") replyParts.push(`Fruit Message: ${fruitMessage}`);
|
].join("\n");
|
||||||
await interaction.editReply(replyParts.join("\n")).catch(e => console.error(e));
|
// Send the reply
|
||||||
fn.collectionBuilders.guildInfos(interaction.client);
|
await interaction.editReply(reply).catch(e => console.error(e));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "update":
|
case "update": // Update the relay configuration piecemeal
|
||||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||||
|
|
||||||
|
// Get all possible options from the interaction
|
||||||
const inWatchChannel = interaction.options.getChannel('watchchannel');
|
const inWatchChannel = interaction.options.getChannel('watchchannel');
|
||||||
const inWaterMessage = interaction.options.getString('watermessage');
|
const inWaterMessage = interaction.options.getString('watermessage');
|
||||||
const inFruitMessage = interaction.options.getString('fruitmessage');
|
const inFruitMessage = interaction.options.getString('fruitmessage');
|
||||||
const inReminderChannel = interaction.options.getChannel('pingchannel');
|
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 outWatchChannelId = inWatchChannel ? inWatchChannel.id : guildInfo.watchChannelId;
|
||||||
const outWaterMessage = inWaterMessage ? inWaterMessage : guildInfo.waterMessage;
|
const outWaterMessage = inWaterMessage ? inWaterMessage : guildInfo.waterMessage;
|
||||||
const outFruitMessage = inFruitMessage ? inFruitMessage : guildInfo.fruitMessage;
|
const outFruitMessage = inFruitMessage ? inFruitMessage : guildInfo.fruitMessage;
|
||||||
const outReminderChannelId = inReminderChannel ? inReminderChannel.id : guildInfo.reminderChannelId;
|
const outReminderChannelId = inReminderChannel ? inReminderChannel.id : guildInfo.reminderChannelId;
|
||||||
|
|
||||||
|
// Update the relay configuration
|
||||||
guildInfo.setReminders(outWaterMessage, outFruitMessage, outReminderChannelId, outWatchChannelId, true);
|
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");
|
let query = guildInfo.queryBuilder("setReminders");
|
||||||
|
// Run the query
|
||||||
await dbfn.setGuildInfo(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}>.`,
|
`I'll watch <#${outWatchChannelId}> for Grow A Tree Notifications and relay them to <#${outReminderChannelId}>.`,
|
||||||
`Water Message: ${outWaterMessage}`
|
`Water Message: ${outWaterMessage}`,
|
||||||
];
|
`Fruit Message: ${outFruitMessage}`
|
||||||
if (outFruitMessage != "") replyParts.push(`Fruit Message: ${outFruitMessage}`);
|
].join("\n");
|
||||||
await interaction.editReply(replyParts.join("\n")).catch(e => console.error(e));
|
// Send the reply
|
||||||
fn.collectionBuilders.guildInfos(interaction.client);
|
await interaction.editReply(reply).catch(e => console.error(e));
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply(fn.builders.errorEmbed("There is no existing notification relay to update!")).catch(e => console.error(e));
|
await interaction.editReply(fn.builders.errorEmbed("There is no existing notification relay to update!")).catch(e => console.error(e));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'disable':
|
case 'disable': // Disable the relay
|
||||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||||
let guildInfo = interaction.client.guildInfos.get(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);
|
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 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));
|
await interaction.editReply(fn.builders.embed(strings.status.optout)).catch(e => console.error(e));
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply(fn.builders.errorEmbed("A notification relay has not been set up yet!")).catch(e => console.error(e));
|
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 =>
|
.addChannelOption(o =>
|
||||||
o.setName('leaderboardchannel')
|
o.setName('leaderboardchannel')
|
||||||
.setDescription('If your leaderboard isn\'t in the same channel, where is it?')
|
.setDescription('What channel is your leaderboard in?')
|
||||||
.setRequired(false)
|
.setRequired(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.addSubcommand(sc =>
|
.addSubcommand(sc =>
|
||||||
@ -41,12 +41,12 @@ module.exports = {
|
|||||||
.setDescription('View your server\'s configuration'))
|
.setDescription('View your server\'s configuration'))
|
||||||
.addSubcommand(sc =>
|
.addSubcommand(sc =>
|
||||||
sc.setName('reset')
|
sc.setName('reset')
|
||||||
.setDescription('Remove all server configuration from the database')
|
.setDescription('Remove all server configuration from the database')
|
||||||
.addBooleanOption(o =>
|
.addBooleanOption(o =>
|
||||||
o.setName('confirm')
|
o.setName('confirm')
|
||||||
.setDescription('WARNING THIS IS IRREVERSIBLE')
|
.setDescription('WARNING THIS IS IRREVERSIBLE')
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
@ -54,15 +54,27 @@ module.exports = {
|
|||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
switch (subcommand) {
|
switch (subcommand) {
|
||||||
case "compare":
|
case "compare":
|
||||||
|
const treeChannel = interaction.options.getChannel('treechannel');
|
||||||
|
const leaderboardChannel = interaction.options.getChannel('leaderboardchannel');
|
||||||
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||||
const findMessagesResponse = await fn.messages.find(interaction, guildInfo);
|
guildInfo.setTreeMessage(undefined, treeChannel.id);
|
||||||
await interaction.editReply(findMessagesResponse.status).catch(e => console.error(e));
|
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 {
|
} else {
|
||||||
let guildInfo = new GuildInfo()
|
let guildInfo = new GuildInfo()
|
||||||
.setId(interaction.guildId);
|
.setId(interaction.guildId)
|
||||||
const findMessagesResponse = await fn.messages.find(interaction, guildInfo);
|
.setTreeMessage(undefined, treeChannel.id)
|
||||||
await interaction.editReply(findMessagesResponse.status).catch(e => console.error(e));
|
.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;
|
break;
|
||||||
case "rolemenu":
|
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));
|
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 {
|
} 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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -6,28 +6,64 @@ module.exports = {
|
|||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.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')
|
||||||
.addIntegerOption(o =>
|
.addIntegerOption(o =>
|
||||||
o.setName('endheight')
|
o.setName('endheight')
|
||||||
.setDescription('Ending tree height in feet')
|
.setDescription('Ending tree height in feet')
|
||||||
.setRequired(true))
|
.setRequired(true)
|
||||||
.addIntegerOption(o =>
|
)
|
||||||
|
.addIntegerOption(o =>
|
||||||
o.setName('beginheight')
|
o.setName('beginheight')
|
||||||
.setDescription('Beginning tree height in feet')
|
.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) {
|
async execute(interaction) {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
const private = interaction.options.getBoolean('private') != undefined ? interaction.options.getBoolean('private') : true;
|
||||||
let beginHeight = interaction.options.getInteger('beginheight');
|
await interaction.deferReply({ ephemeral: private });
|
||||||
|
const inBeginHeight = interaction.options.getInteger('beginheight');
|
||||||
const endHeight = interaction.options.getInteger('endheight');
|
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);
|
const guildInfo = interaction.client.guildInfos.get(interaction.guild.id);
|
||||||
beginHeight = guildInfo.treeHeight;
|
beginHeight = guildInfo.treeHeight;
|
||||||
|
} else {
|
||||||
|
beginHeight = inBeginHeight;
|
||||||
}
|
}
|
||||||
fn.timeToHeight(beginHeight, endHeight).then(res => {
|
|
||||||
interaction.editReply(`It will take a tree that is ${beginHeight}ft tall ${res} to reach ${endHeight}ft.`);
|
const timeToHeightResults = await fn.timeToHeight(beginHeight, endHeight, efficiency, quality);
|
||||||
}).catch(err => {
|
const { time, totalWaterCount, compostAppliedCount, average, savedTime } = timeToHeightResults;
|
||||||
interaction.editReply("Error: " + err);
|
let replyFields = undefined;
|
||||||
console.error(err);
|
if (efficiency && quality) {
|
||||||
return;
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user