Compare commits
115 Commits
v1.2.2-dev
...
main
Author | SHA1 | Date | |
---|---|---|---|
f9bb94a4a1 | |||
1575d0047c | |||
d173338d09 | |||
aedbbec6a2 | |||
fa41a5d61e | |||
ac6eca4827 | |||
88cc4eda3c | |||
7291216dcb | |||
36f44f4b41 | |||
825ec725d1 | |||
fb43d09d13 | |||
0463695323 | |||
314b793042 | |||
eca3207d4e | |||
e2658c41e5 | |||
dd313faa60 | |||
ba81f01fa0 | |||
e303651f53 | |||
40fc6838a0 | |||
67483697f0 | |||
1e1d0cddaa | |||
45418e2bd6 | |||
22005ac68f | |||
805c20953c | |||
ba0e4625c0 | |||
11465287dd | |||
36c59f83f4 | |||
4dda58f48e | |||
e85340245c | |||
252593a327 | |||
7999350f1e | |||
87a468b620 | |||
ae602d08de | |||
|
94c3244016 | ||
|
2f9126aa6e | ||
|
486a59715d | ||
|
25859d364d | ||
|
c0cba3e14e | ||
|
e3fb4da9f6 | ||
|
f2560a081e | ||
|
e8f16d8fea | ||
|
7c56381141 | ||
|
a1386dd688 | ||
|
9e5a89eabb | ||
|
d6bab1c2da | ||
|
d117a69893 | ||
|
87c7a8e9ec | ||
|
a27dc1923e | ||
|
c3967e0643 | ||
|
4144dde4d6 | ||
|
e05bd20f1f | ||
|
7003e12fa1 | ||
|
246848e94a | ||
|
a13888c6c1 | ||
|
49747d4b54 | ||
|
e94b8ce433 | ||
|
9a5a457123 | ||
|
c83e94f01d | ||
|
08f76fb996 | ||
|
d1ac674c6b | ||
|
e29a54b0d9 | ||
|
5b7c0c4225 | ||
|
4f0fa1e51f | ||
|
946f05d57b | ||
|
92c06529ff | ||
|
c4fc1a4ba6 | ||
|
b0d6bdcf8a | ||
|
2704cac98f | ||
|
a28957b956 | ||
|
105fb2827f | ||
|
9aa9511fd7 | ||
|
79821d7abc | ||
|
e075a3a5d6 | ||
|
3c01d37bd5 | ||
|
f7230ec0fb | ||
|
1927d3029a | ||
|
41b59f8b92 | ||
|
f3573eeaed | ||
|
44a4d572bb | ||
|
de6b9ec4e4 | ||
|
56d9cacfbe | ||
|
0c28ec437c | ||
|
8d64640193 | ||
|
1cb5e94145 | ||
|
428cbc1597 | ||
|
1c57cb70ae | ||
|
6f3710bc07 | ||
|
0bdca58d03 | ||
|
7121ab5390 | ||
|
2353486150 | ||
|
26b79327be | ||
|
9580a500fd | ||
|
10f8ed9438 | ||
|
024defbbfc | ||
|
27169afcf0 | ||
|
5f37a3e5a3 | ||
|
a44a83beeb | ||
|
d7bd6b5d41 | ||
|
0f8e9a3e10 | ||
|
9c6204b19b | ||
|
02201b2bfa | ||
|
1f115252ca | ||
|
e632dce612 | ||
|
cdb61cf602 | ||
|
b2e0b2b17d | ||
|
e7a76eee65 | ||
|
a70b27b112 | ||
|
1e358e582e | ||
|
d5254dc58a | ||
|
8bc80dfaa8 | ||
|
663ef2297b | ||
|
56be24b5cd | ||
|
ebc31b5a0c | ||
|
1367a90904 | ||
|
26b5728636 |
0
.github/workflows/docker-image-dev.yml → .gitea/workflows/docker-image-dev.yml
Executable file → Normal file
0
.github/workflows/docker-image-dev.yml → .gitea/workflows/docker-image-dev.yml
Executable file → Normal file
47
.gitea/workflows/docker-image-prod.yml
Normal file
47
.gitea/workflows/docker-image-prod.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
name: Silvanus Production Dockerization
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
env:
|
||||||
|
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||||
|
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Pull latest from Git
|
||||||
|
run: |
|
||||||
|
pwd
|
||||||
|
whoami
|
||||||
|
mkdir -p /var/lib/act_runner/
|
||||||
|
cd /var/lib/act_runner/
|
||||||
|
if [ ! -d "silvanus" ]; then
|
||||||
|
git clone https://git.vfsh.dev/voidf1sh/silvanus
|
||||||
|
cd silvanus
|
||||||
|
else
|
||||||
|
cd silvanus
|
||||||
|
git pull
|
||||||
|
fi
|
||||||
|
git checkout ${{ gitea.ref}}
|
||||||
|
- name: Build the Docker image
|
||||||
|
run: |
|
||||||
|
cd /var/lib/act_runner/silvanus
|
||||||
|
docker build . --file Dockerfile --tag v0idf1sh/silvanus
|
||||||
|
- name: Log into Docker Hub
|
||||||
|
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||||
|
- name: Push image to Docker Hub
|
||||||
|
run: |
|
||||||
|
cd /var/lib/act_runner/silvanus
|
||||||
|
docker push v0idf1sh/silvanus
|
||||||
|
- name: Restart the container
|
||||||
|
run: |
|
||||||
|
cd /srv/docker/silvanus
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
23
.github/workflows/docker-image-prod.yml
vendored
23
.github/workflows/docker-image-prod.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
name: Silvanus Production Dockerization
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
|
||||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Build the Docker image v0idf1sh/silvanus
|
|
||||||
run: docker build . --file Dockerfile --tag v0idf1sh/silvanus
|
|
||||||
- name: Log into Docker Hub
|
|
||||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
|
||||||
- name: Push image to Docker Hub v0idf1sh/silvanus
|
|
||||||
run: docker push v0idf1sh/silvanus
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,8 +2,7 @@
|
|||||||
.vscode
|
.vscode
|
||||||
package-lock.json
|
package-lock.json
|
||||||
.VSCodeCounter/
|
.VSCodeCounter/
|
||||||
env.dev
|
.env*
|
||||||
env.prod
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
data/guildInfo.json
|
data/guildInfo.json
|
||||||
data/rawstring.txt
|
data/rawstring.txt
|
||||||
|
@ -5,4 +5,4 @@ WORKDIR /usr/src/app
|
|||||||
COPY package.json ./
|
COPY package.json ./
|
||||||
RUN npm install
|
RUN npm install
|
||||||
COPY . .
|
COPY . .
|
||||||
CMD ["/bin/sh", "-c", "node main.js 2> /logs/error.log 1> /logs/silvanus.log"]
|
CMD ["/bin/sh", "-c", "node main.js 2>&1 > /logs/$(date +%Y-%m-%d_%H-%M-%S).txt"]
|
||||||
|
34
README.md
34
README.md
@ -1,36 +1,18 @@
|
|||||||
|
![Current Uptime Status](https://status.vfsh.dev/api/badge/1/status)
|
||||||
|
![Uptime Percentage](https://status.vfsh.dev/api/badge/1/uptime)
|
||||||
# Silvanus
|
# Silvanus
|
||||||
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!
|
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!
|
||||||
|
|
||||||
Important 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.
|
|
||||||
|
|
||||||
Silvanus is not affiliated with Grow A Tree or Limbo Labs.
|
Silvanus is not affiliated with Grow A Tree or Limbo Labs.
|
||||||
|
|
||||||
## Add Silvanus to your server
|
## Add Silvanus to your server
|
||||||
[Invite Silvanus to your Discord Server](https://discord.com/api/oauth2/authorize?client_id=521624335119810561&permissions=274877908992&scope=bot%20applications.commands)
|
[Invite Silvanus to your Discord Server](https://discord.com/api/oauth2/authorize?client_id=521624335119810561&permissions=275146475520&scope=applications.commands%20bot)
|
||||||
|
|
||||||
## voidf1sh Development Support Server
|
## Silvanus Support Server
|
||||||
[Join Discord Server](https://discord.gg/g5JRGn7PxU)
|
[Join Discord Server](https://discord.gg/g5JRGn7PxU)
|
||||||
|
|
||||||
## Setup
|
## Silvanus Support Wiki
|
||||||
|
Find the most up-to-date guides and information about Silvanus at the [Silvanus Support Wiki](https://silvanus.vfsh.dev/).
|
||||||
|
[Silvanus Setup Guide](https://silvanus.vfsh.dev/en/setup)
|
||||||
|
|
||||||
If your `/tree` and `/top trees` messages are in the same channel, simply run `/compare` in that channel and you're good to go!
|
Please find the most up-to-date guides and information at the Silvanus Support Discord Server!
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
* `/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
|
|
24
TODO.md
24
TODO.md
@ -1,10 +1,3 @@
|
|||||||
## In Progress
|
|
||||||
☑ Switch `/setup` to ask for the tree and leaderboard channels
|
|
||||||
* Switch `/compare` to check for newer trees and leaderboards when run **and** on every refresh
|
|
||||||
|
|
||||||
## Future Ideas
|
|
||||||
* Go through and comment the code
|
|
||||||
|
|
||||||
## Variable Structures
|
## Variable Structures
|
||||||
|
|
||||||
guildInfo = {
|
guildInfo = {
|
||||||
@ -21,7 +14,18 @@ guildInfo = {
|
|||||||
reminderOptIn: 0,
|
reminderOptIn: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
## Expected Behaviors
|
## New Table Planning
|
||||||
|
Table: `silvanus`.timers
|
||||||
|
|
||||||
* Run `/compare` before `/setup`: `/compare` will search the current channel for tree and leaderboard messages, then create a comparison embed. If it can't find `/tree` or `/top trees` messages, it'll return an error saying as much.
|
id | INT | NOT NULL | AUTO INCREMENT | PRIMARY KEY
|
||||||
* Run `/compare` after `/setup`: ``/compare` will search the current channel for tree and leaderboard messages, then create a comparison embed. If it can't find `/tree` or `/top trees` messages, it'll just use old data silently (odds are `/compare` is being run from another channel, that's fine)
|
status | VARCHAR(10) | NOT NULL | DEFAULT "WAITING"
|
||||||
|
dc_timecode | INT | NOT NULL | Discord timecode
|
||||||
|
guild_id | INT UNSIGNED | NOT NULL | Discord guild ID for referencing `silvanus`.guildInfo
|
||||||
|
|
||||||
|
|
||||||
|
Table: `silvanus`.auto_role_status
|
||||||
|
|
||||||
|
id | INT | NOT NULL | AUTO INCREMENT | PRIMARY KEY
|
||||||
|
user_id | INT UNSIGNED | NOT NULL
|
||||||
|
guild_id | INT UNSIGNED | NOT NULL
|
||||||
|
status | VARCHAR(10) | NOT NULL | DEFAULT "REMOVED" | OPTION: {"REMOVED", "ADDED"}
|
@ -5,5 +5,11 @@
|
|||||||
"treeHeight": 0,
|
"treeHeight": 0,
|
||||||
"validCommands": [],
|
"validCommands": [],
|
||||||
"rankMessageId": "",
|
"rankMessageId": "",
|
||||||
"rankings": []
|
"rankings": [],
|
||||||
|
"devTeamIds": [
|
||||||
|
"481933290912350209",
|
||||||
|
"269249959973355520",
|
||||||
|
"448606738669633536"
|
||||||
|
],
|
||||||
|
"ownerId": "481933290912350209"
|
||||||
}
|
}
|
@ -4,9 +4,12 @@
|
|||||||
},
|
},
|
||||||
"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.",
|
"aboutTitle": "About Silvanus",
|
||||||
"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.",
|
"info": "Silvanus is the ultimate companion bot designed for Grow A Tree, the popular Discord clicker game. With Silvanus by your side, you can take your tree-growing adventure to new heights! Enjoy customizable notifications, effortless tree height comparisons, and handy math features to optimize your gameplay.\n\n[Privacy Policy](https://assets.vfsh.dev/privacy.txt)",
|
||||||
"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.",
|
"topggTitle": "Want to support Silvanus?",
|
||||||
|
"topggBody": "Vote and leave a review for Silvanus [on Top.gg](https://top.gg/bot/521624335119810561)",
|
||||||
|
"setup": "For the most up to date guides and information, check out the Silvanus Support Wiki - https://silvanus.vfsh.dev/\n[Silvanus Setup Guide](https://silvanus.vfsh.dev/en/setup)\n\nNeed help and can't reach me on Discord? Send an email to SilvanusDev@gmail.com and I'll get back to you ASAP.",
|
||||||
|
"longDescription": "Silvanus, the ultimate companion bot for Grow A Tree, takes your tree-growing journey to the next level. Are you tired of cluttered channels filled with notifications? Silvanus has you covered! With its unique notification relay system, Silvanus listens for Grow A Tree's notifications in a hidden channel and sends customized, auto-deleting notifications to the channels of your choice. Customize your notifications and keep your server clean and organized.\n\nSilvanus simplifies leaderboard tree height comparisons with a simple command. No more manual calculations or guesswork. Silvanus shows you exactly how far you are from the next tree on the leaderboard, allowing you to gauge your progress effortlessly.",
|
||||||
"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"
|
"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": {
|
||||||
@ -19,11 +22,11 @@
|
|||||||
},
|
},
|
||||||
"embeds": {
|
"embeds": {
|
||||||
"footer": "Silvanus is not affiliated with Grow A Tree or Limbo Labs",
|
"footer": "Silvanus is not affiliated with Grow A Tree or Limbo Labs",
|
||||||
"color": "0x55FF55",
|
"color": 5635925,
|
||||||
"errorTitle": "Oops!",
|
"errorTitle": "Oops!",
|
||||||
"errorPrefix": "There seems to have been a problem.",
|
"errorPrefix": "There seems to have been a problem.",
|
||||||
"waterColor": "0x5555FF",
|
"waterColor": 5592575,
|
||||||
"fruitColor": "0xCC5555",
|
"fruitColor": 13391189,
|
||||||
"waterTitle": "Water Notification",
|
"waterTitle": "Water Notification",
|
||||||
"fruitTitle": "Fruit Notification",
|
"fruitTitle": "Fruit Notification",
|
||||||
"roleMenuTitle": "Role Menu",
|
"roleMenuTitle": "Role Menu",
|
||||||
@ -50,8 +53,16 @@
|
|||||||
"supportServer": "https://discord.gg/g5JRGn7PxU"
|
"supportServer": "https://discord.gg/g5JRGn7PxU"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"noGuild": "Setup has not been completed yet. Try running </setup:1065407649363005561> or </help setup:1065346941166297129>",
|
"noGuild": "I was unable to find an entry for your server in the database. Try using the `/setup rolemenu` or `/relay` commands to generate a new entry. If that fails, try `/setup reset`, then kick me and re-add me. If you continue receiving this error, join the Support Server or DM @vfsh",
|
||||||
"invalidSubcommand": "Invalid subcommand detected."
|
"invalidSubcommand": "Invalid subcommand detected.",
|
||||||
|
"noTreeMessage": "</tree:0> - Make sure you've sent or refreshed a Tree recently.",
|
||||||
|
"noLeaderboardMessage": "</top trees:0> - Make sure you've sent or refreshed the Tallest Trees leaderboard recently.",
|
||||||
|
"noCompareMessage": "</compare:0> - This is awkward, I've lost my own comparison message!",
|
||||||
|
"noFetchRole": "I was unable to find that role, please make sure it exists and I have access to it.",
|
||||||
|
"noGiveRole": "I was unable to give that role to you, please make sure I have permission to `Manage Roles` and that the role is below my role in the server settings role list.",
|
||||||
|
"noTakeRole": "I was unable to remove that role from you, please make sure I have permission to `Manage Roles` and that the role is below my role in the server settings role list.",
|
||||||
|
"yesGiveRole": "Successfully added the role to your profile!",
|
||||||
|
"yesTakeRole": "Successfully removed the role from your profile!"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"treeAndLeaderboard": "Tree and leaderboard messages were both found, setup is complete. Run </setupinfo:1065413032374706196> to verify. Run </compare:1065346941166297128> to get started!",
|
"treeAndLeaderboard": "Tree and leaderboard messages were both found, setup is complete. Run </setupinfo:1065413032374706196> to verify. Run </compare:1065346941166297128> to get started!",
|
||||||
@ -64,11 +75,14 @@
|
|||||||
"reset": "All guild configuration information has been removed from the database.",
|
"reset": "All guild configuration information has been removed from the database.",
|
||||||
"resetError": "There was a problem deleting your guild information, contact @voidf1sh#0420 for help.",
|
"resetError": "There was a problem deleting your guild information, contact @voidf1sh#0420 for help.",
|
||||||
"noRoleMenu": "A role menu has not been created for this guild yet. Run </setup rolemenu:0> to create a role menu.",
|
"noRoleMenu": "A role menu has not been created for this guild yet. Run </setup rolemenu:0> to create a role menu.",
|
||||||
"optout": "Notification relay has been disabled, to re-enable the relay use </notifications update:0> with no options."
|
"optout": "Notification relay has been disabled, to re-enable the relay use </relay update:0> with no options."
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"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": {}
|
||||||
}
|
}
|
18
dot-commands/_template
Normal file
18
dot-commands/_template
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
usage: "",
|
||||||
|
permission: "devTeam", // "devTeam" or "owner"
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
// Code Here
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
dot-commands/leave.js
Normal file
27
dot-commands/leave.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "leave",
|
||||||
|
description: "Leave a server",
|
||||||
|
usage: "<serverID> [<serverID>]".leave,
|
||||||
|
permission: "owner",
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
// Code Here
|
||||||
|
const serverIds = commandData.args.split(" ");
|
||||||
|
for (let i = 0; i < serverIds.length; i++) {
|
||||||
|
const id = serverIds[i];
|
||||||
|
const guild = await message.client.guilds.fetch(id).catch(e => {
|
||||||
|
if (!(e.status === 404)) throw e;
|
||||||
|
});
|
||||||
|
await guild.leave();
|
||||||
|
await message.channel.send("Left Guild: " + id);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command: " + err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
dot-commands/message.js
Normal file
23
dot-commands/message.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "message",
|
||||||
|
description: "Send a message to a server owner or server",
|
||||||
|
usage: "<serverID> <content>".message,
|
||||||
|
permission: "owner",
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
// Code Here
|
||||||
|
args = commandData.args.split(" ");
|
||||||
|
const guildOwnerId = args.shift();
|
||||||
|
const content = args.join(" ");
|
||||||
|
const dmChannel = await message.client.users.createDM(guildOwnerId);
|
||||||
|
await dmChannel.send(content);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command: " + err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
dot-commands/permissions.js
Normal file
36
dot-commands/permissions.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
const { PermissionsBitField } = require('discord.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "permissions",
|
||||||
|
description: "",
|
||||||
|
usage: ".permissions",
|
||||||
|
permission: "devTeam", // "devTeam" or "owner"
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
const me = message.guild.members.me;
|
||||||
|
const guildPerms = me.permissions;
|
||||||
|
const manageRoles = guildPerms.has(PermissionsBitField.Flags.ManageRoles);
|
||||||
|
const mentionEveryone = guildPerms.has(PermissionsBitField.Flags.MentionEveryone);
|
||||||
|
const channelPerms = me.permissionsIn(message.channel);
|
||||||
|
const viewChannel = channelPerms.has(PermissionsBitField.Flags.ViewChannel);
|
||||||
|
const sendMessages = channelPerms.has(PermissionsBitField.Flags.SendMessages);
|
||||||
|
const responseParts = [
|
||||||
|
`This is the status of my permissions in this server and this channel (<#${message.channel.id}>)`,
|
||||||
|
`**Guild Permissions**`,
|
||||||
|
`Manage Roles: ${manageRoles}`,
|
||||||
|
`Mention All Roles: ${mentionEveryone}`,
|
||||||
|
`**Channel Permissions**`,
|
||||||
|
`View Channel: ${viewChannel}`,
|
||||||
|
`Send Messages: ${sendMessages}`
|
||||||
|
];
|
||||||
|
const replyEmbed = fn.builders.embed(responseParts.join("\n"));
|
||||||
|
await message.reply(replyEmbed);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
dot-commands/ping.js
Normal file
18
dot-commands/ping.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "ping",
|
||||||
|
description: "pong",
|
||||||
|
usage: "ping pong",
|
||||||
|
permission: "everyone",
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
await message.reply("Pong!");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
dot-commands/servers.js
Normal file
22
dot-commands/servers.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "servers",
|
||||||
|
description: "Get a list of servers the bot is in",
|
||||||
|
usage: ".servers",
|
||||||
|
permission: "owner",
|
||||||
|
async execute(message, commandData) {
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
let servers = [];
|
||||||
|
const count = JSON.stringify(message.client.guilds.cache.size);
|
||||||
|
servers.push("I'm currently in " + count + " servers.");
|
||||||
|
const guilds = await message.client.guilds.cache;
|
||||||
|
await message.reply(servers.join("\n"));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
dot-commands/setupview.js
Normal file
24
dot-commands/setupview.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: "setupview",
|
||||||
|
description: "",
|
||||||
|
usage: "",
|
||||||
|
permission: "devTeam",
|
||||||
|
async execute(message, commandData) {
|
||||||
|
// Code here...
|
||||||
|
if (fn.dotCommands.checkPermissions(this.permission, message.author.id)) {
|
||||||
|
try {
|
||||||
|
if (message.client.guildInfos.has(message.guildId)) {
|
||||||
|
let guildInfo = message.client.guildInfos.get(message.guildId);
|
||||||
|
await message.reply(fn.builders.embed(guildInfo.generateSetupInfo()));
|
||||||
|
} else {
|
||||||
|
await message.reply(fn.builders.errorEmbed("Guild doesn't exist in database!"));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
await message.reply(fn.builders.errorEmbed("There was an error running the command."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
main.js
102
main.js
@ -5,6 +5,8 @@ const dotenv = require('dotenv');
|
|||||||
dotenv.config();
|
dotenv.config();
|
||||||
const token = process.env.TOKEN;
|
const token = process.env.TOKEN;
|
||||||
const statusChannelId = process.env.STATUSCHANNELID;
|
const statusChannelId = process.env.STATUSCHANNELID;
|
||||||
|
const heartbeatUrl = process.env.HEARTBEAT_URL;
|
||||||
|
const sendHeartbeat = typeof heartbeatUrl === 'string';
|
||||||
|
|
||||||
// Discord.JS
|
// Discord.JS
|
||||||
const { Client, GatewayIntentBits, Partials, ActivityType } = require('discord.js');
|
const { Client, GatewayIntentBits, Partials, ActivityType } = require('discord.js');
|
||||||
@ -25,27 +27,42 @@ const client = new Client({
|
|||||||
const fn = require('./modules/functions.js');
|
const fn = require('./modules/functions.js');
|
||||||
const strings = require('./data/strings.json');
|
const strings = require('./data/strings.json');
|
||||||
const dbfn = require('./modules/dbfn.js');
|
const dbfn = require('./modules/dbfn.js');
|
||||||
const isDev = process.env.DEBUG;
|
const { GuildInfo } = require('./modules/CustomClasses.js');
|
||||||
|
const isDev = process.env.DEBUG === "true";
|
||||||
|
let statusChannel;
|
||||||
|
|
||||||
client.once('ready', async () => {
|
client.once('ready', async () => {
|
||||||
fn.collectionBuilders.slashCommands(client);
|
fn.collectionBuilders.slashCommands(client);
|
||||||
|
fn.collectionBuilders.dotCommands(client);
|
||||||
|
fn.collectionBuilders.setvalidCommands(client);
|
||||||
await fn.collectionBuilders.guildInfos(client);
|
await fn.collectionBuilders.guildInfos(client);
|
||||||
await fn.collectionBuilders.messageCollectors(client);
|
await fn.collectionBuilders.messageCollectors(client);
|
||||||
|
const serverCount = client.guilds.cache.size;
|
||||||
// checkRateLimits();
|
// checkRateLimits();
|
||||||
console.log('Ready!');
|
console.log('Ready!');
|
||||||
client.user.setActivity({ name: strings.activity.name, type: ActivityType.Watching });
|
client.user.setActivity({ name: `${serverCount} trees grow.`, type: ActivityType.Watching });
|
||||||
|
statusChannel = await client.channels.fetch(statusChannelId);
|
||||||
if (isDev == 'false') {
|
if (isDev == 'false') {
|
||||||
client.channels.fetch(statusChannelId).then(channel => {
|
statusChannel.send(`${new Date().toISOString()} -- \nStartup Sequence Complete <@481933290912350209>`);
|
||||||
channel.send(`${new Date().toISOString()} -- \nStartup Sequence Complete <@481933290912350209>`);
|
}
|
||||||
});
|
|
||||||
|
// Heartbeat Timer
|
||||||
|
if (sendHeartbeat) {
|
||||||
|
setInterval(() => {
|
||||||
|
fn.sendHeartbeat(heartbeatUrl);
|
||||||
|
}, 30000);
|
||||||
|
if (isDev) console.log("Heartbeat interval set.");
|
||||||
|
} else {
|
||||||
|
if (isDev) console.log("No heartbeat URL set, will not send heartbeats for uptime monitoring.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// slash-commands
|
// slash-commands
|
||||||
client.on('interactionCreate', async interaction => {
|
client.on('interactionCreate', async interaction => {
|
||||||
|
try {
|
||||||
if (interaction.isCommand()) {
|
if (interaction.isCommand()) {
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
console.log(interaction);
|
// console.log(interaction);
|
||||||
}
|
}
|
||||||
const { commandName } = interaction;
|
const { commandName } = interaction;
|
||||||
|
|
||||||
@ -62,13 +79,13 @@ client.on('interactionCreate', async interaction => {
|
|||||||
case 'refresh':
|
case 'refresh':
|
||||||
// console.log(JSON.stringify(interaction));
|
// console.log(JSON.stringify(interaction));
|
||||||
await fn.refresh(interaction).catch(err => {
|
await fn.refresh(interaction).catch(err => {
|
||||||
interaction.channel.send(fn.builders.errorEmbed(err));
|
interaction.channel.send(fn.builders.errorEmbed("Oops! Something went wrong!"));
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'deleteping':
|
case 'deleteping':
|
||||||
if (interaction.message.deletable) {
|
if (interaction.message.deletable) {
|
||||||
await interaction.message.delete().catch(err => {
|
await interaction.message.delete().catch(err => {
|
||||||
console.error(err);
|
// console.error(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -84,6 +101,73 @@ client.on('interactionCreate', async interaction => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch(err) {
|
||||||
|
if (err === "Guild doesn't exist in database!") {
|
||||||
|
interaction.channel.send(fn.builders.errorEmbed(strings.error.noGuild));
|
||||||
|
console.error(err);
|
||||||
|
} else {
|
||||||
|
interaction.channel.send("Oops! An error occurred... Sorry about that, please contact my owner @vfsh if this keeps happening.");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('messageUpdate', async (oldMessage, message) => {
|
||||||
|
await fn.messages.updateHandler(message).catch(async e => {
|
||||||
|
switch (e) {
|
||||||
|
case strings.error.noCompareMessage:
|
||||||
|
await message.channel.send(strings.error.noCompareMessage);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('messageCreate', async message => {
|
||||||
|
await fn.messages.updateHandler(message).catch(e => console.error(e));
|
||||||
|
|
||||||
|
// Dot Command Handling
|
||||||
|
// Some basic checking to prevent running unnecessary code
|
||||||
|
if (message.author.bot) return;
|
||||||
|
|
||||||
|
// Break the message down into its components and analyze it
|
||||||
|
const commandData = fn.dotCommands.getCommandData(message);
|
||||||
|
// if (isDev) console.log(commandData);
|
||||||
|
|
||||||
|
if (commandData.isValid && commandData.isCommand) {
|
||||||
|
try {
|
||||||
|
client.dotCommands.get(commandData.command).execute(message, commandData);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
message.reply('There was an error trying to execute that command.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('guildCreate', async guild => {
|
||||||
|
const serverCount = client.guilds.cache.size;
|
||||||
|
client.user.setActivity({ name: `${serverCount} trees grow.`, type: ActivityType.Watching });
|
||||||
|
await statusChannel.send(`I've been added to a new guild: ${guild.name} (${guild.id})`);
|
||||||
|
const guildInfo = new GuildInfo()
|
||||||
|
.setIds(guild.id, guild.ownerId);
|
||||||
|
const setBasicQuery = guildInfo.queryBuilder("setBasic");
|
||||||
|
await dbfn.setGuildInfo(setBasicQuery).catch(e => console.error(e));
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('guildDelete', async guild => {
|
||||||
|
const serverCount = client.guilds.cache.size;
|
||||||
|
client.user.setActivity({ name: `${serverCount} trees grow.`, type: ActivityType.Watching });
|
||||||
|
await statusChannel.send(`I've been removed from a guild: ${guild.name} (${guild.id})`);
|
||||||
|
if (client.guildInfos.has(guild.id)) {
|
||||||
|
let guildInfo = client.guildInfos.get(guild.id);
|
||||||
|
guildInfo.setReminders(undefined, undefined, undefined, undefined, false);
|
||||||
|
const setRemindersQuery = guildInfo.queryBuilder("setReminders");
|
||||||
|
await dbfn.setGuildInfo(setRemindersQuery);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function checkRateLimits(hi) {
|
async function checkRateLimits(hi) {
|
||||||
@ -111,7 +195,7 @@ async function checkRateLimits(hi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process.on('unhandledRejection', error => {
|
process.on('unhandledRejection', error => {
|
||||||
console.error('Unhandled promise rejection:', error);
|
console.error('Unhandled promise rejection (pls dont break up with me):', error);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.login(token);
|
client.login(token);
|
@ -11,6 +11,7 @@ module.exports = {
|
|||||||
GuildInfo: class {
|
GuildInfo: class {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.guildId = "";
|
this.guildId = "";
|
||||||
|
this.ownerId = ""; // TODO Is ownerId fully implemented?
|
||||||
this.treeName = "";
|
this.treeName = "";
|
||||||
this.treeHeight = 0;
|
this.treeHeight = 0;
|
||||||
this.treeMessageId = "";
|
this.treeMessageId = "";
|
||||||
@ -24,10 +25,13 @@ module.exports = {
|
|||||||
this.reminderChannelId = "";
|
this.reminderChannelId = "";
|
||||||
this.watchChannelId = "";
|
this.watchChannelId = "";
|
||||||
this.notificationsEnabled = false;
|
this.notificationsEnabled = false;
|
||||||
|
this.compareChannelId = "";
|
||||||
|
this.compareMessageId = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
setId(id) {
|
setIds(guildId, ownerId) {
|
||||||
this.guildId = id;
|
this.guildId = guildId;
|
||||||
|
this.ownerId = ownerId === undefined ? this.ownerId : ownerId
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setName(name) {
|
setName(name) {
|
||||||
@ -43,17 +47,29 @@ module.exports = {
|
|||||||
this.treeChannelId = channelId;
|
this.treeChannelId = channelId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
setTreeInfo(name, height, channelId, messageId) {
|
||||||
|
this.treeName = name ? name : this.treeName;
|
||||||
|
this.treeHeight = height;
|
||||||
|
this.treeChannelId = channelId ? channelId : this.treeChannelId;
|
||||||
|
this.treeMessageId = messageId ? messageId : this.treeMessageId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
setLeaderboardMessage(messageId, channelId) {
|
setLeaderboardMessage(messageId, channelId) {
|
||||||
this.leaderboardMessageId = messageId ? messageId : this.leaderboardMessageId;
|
this.leaderboardMessageId = messageId ? messageId : this.leaderboardMessageId;
|
||||||
this.leaderboardChannelId = channelId;
|
this.leaderboardChannelId = channelId ? channelId : this.leaderboardChannelId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
setCompareMessage(channelId, messageId) {
|
||||||
|
this.compareChannelId = channelId;
|
||||||
|
this.compareMessageId = messageId;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setReminders(waterMessage, fruitMessage, reminderChannelId, watchChannelId, enabled) {
|
setReminders(waterMessage, fruitMessage, reminderChannelId, watchChannelId, enabled) {
|
||||||
if (waterMessage) this.waterMessage = waterMessage;
|
this.waterMessage = waterMessage === undefined ? this.waterMessage : waterMessage
|
||||||
if (fruitMessage) this.fruitMessage = fruitMessage;
|
this.fruitMessage = fruitMessage === undefined ? this.fruitMessage : fruitMessage;
|
||||||
if (reminderChannelId) this.reminderChannelId = reminderChannelId;
|
this.reminderChannelId = reminderChannelId === undefined ? this.reminderChannelId : reminderChannelId
|
||||||
if (watchChannelId) this.watchChannelId = watchChannelId;
|
this.watchChannelId = watchChannelId === undefined ? this.watchChannelId : watchChannelId;
|
||||||
if (enabled) this.notificationsEnabled = enabled;
|
this.notificationsEnabled = enabled === undefined ? this.notificationsEnabled : enabled;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setRoles(waterRoleId, fruitRoleId) {
|
setRoles(waterRoleId, fruitRoleId) {
|
||||||
@ -120,18 +136,28 @@ module.exports = {
|
|||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
break;
|
break;
|
||||||
case "setTreeMessage":
|
case "setTreeMessage":
|
||||||
|
// queryParts = [
|
||||||
|
// `UPDATE guild_info SET tree_message_id = ${db.escape(this.treeMessageId)}, `,
|
||||||
|
// `tree_channel_id = ${db.escape(this.treeChannelId)} `,
|
||||||
|
// `WHERE guild_id = ${db.escape(this.guildId)}`
|
||||||
|
// ];
|
||||||
queryParts = [
|
queryParts = [
|
||||||
`UPDATE guild_info SET tree_message_id = ${db.escape(this.treeMessageId)}, `,
|
`INSERT INTO guild_info (guild_id, tree_message_id, tree_channel_id)`,
|
||||||
`tree_channel_id = ${db.escape(this.treeChannelId)} `,
|
`VALUES (${db.escape(this.guildId)}, ${db.escape(this.treeMessageId)}, ${db.escape(this.treeChannelId)})`,
|
||||||
`WHERE guild_id = ${db.escape(this.guildId)}`
|
`ON DUPLICATE KEY UPDATE tree_message_id = ${db.escape(this.treeMessageId)}, tree_channel_id = ${db.escape(this.treeChannelId)}`
|
||||||
];
|
];
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
break;
|
break;
|
||||||
case "setLeaderboardMessage":
|
case "setLeaderboardMessage":
|
||||||
|
// queryParts = [
|
||||||
|
// `UPDATE guild_info SET leaderboard_message_id = ${db.escape(this.leaderboardMessageId)}, `,
|
||||||
|
// `leaderboard_channel_id = ${db.escape(this.leaderboardChannelId)} `,
|
||||||
|
// `WHERE guild_id = ${db.escape(this.guildId)}`
|
||||||
|
// ];
|
||||||
queryParts = [
|
queryParts = [
|
||||||
`UPDATE guild_info SET leaderboard_message_id = ${db.escape(this.leaderboardMessageId)}, `,
|
`INSERT INTO guild_info (guild_id, leaderboard_message_id, leaderboard_channel_id)`,
|
||||||
`leaderboard_channel_id = ${db.escape(this.leaderboardChannelId)} `,
|
`VALUES (${db.escape(this.guildId)}, ${db.escape(this.leaderboardMessageId)}, ${db.escape(this.leaderboardChannelId)})`,
|
||||||
`WHERE guild_id = ${db.escape(this.guildId)}`
|
`ON DUPLICATE KEY UPDATE leaderboard_message_id = ${db.escape(this.leaderboardMessageId)}, leaderboard_channel_id = ${db.escape(this.leaderboardChannelId)}`
|
||||||
];
|
];
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
break;
|
break;
|
||||||
@ -153,6 +179,44 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
return queryParts.join('');
|
return queryParts.join('');
|
||||||
break;
|
break;
|
||||||
|
case "setTreeInfo":
|
||||||
|
queryParts = [
|
||||||
|
`INSERT INTO guild_info (`,
|
||||||
|
`guild_id, tree_name, tree_height, tree_channel_id, tree_message_id`,
|
||||||
|
`) VALUES (`,
|
||||||
|
`${db.escape(this.guildId)}, ${db.escape(this.treeName)}, ${db.escape(this.treeHeight)}, ${db.escape(this.treeChannelId)}, ${db.escape(this.treeMessageId)}`,
|
||||||
|
`) ON DUPLICATE KEY UPDATE tree_name = ${db.escape(this.treeName)}, `,
|
||||||
|
`tree_height = ${db.escape(this.treeHeight)}, `,
|
||||||
|
`tree_channel_id = ${db.escape(this.treeChannelId)}, `,
|
||||||
|
`tree_message_id = ${db.escape(this.treeMessageId)}`
|
||||||
|
];
|
||||||
|
return queryParts.join('');
|
||||||
|
case "setCompareMessage":
|
||||||
|
queryParts = [
|
||||||
|
`INSERT INTO guild_info (`,
|
||||||
|
`guild_id, compare_channel_id, compare_message_id`,
|
||||||
|
`) VALUES (`,
|
||||||
|
`${db.escape(this.guildId)}, ${db.escape(this.compareChannelId)}, ${db.escape(this.compareMessageId)}`,
|
||||||
|
`) ON DUPLICATE KEY UPDATE compare_channel_id = ${db.escape(this.compareChannelId)}, compare_message_id = ${db.escape(this.compareMessageId)}`,
|
||||||
|
];
|
||||||
|
return queryParts.join('');
|
||||||
|
// TODO This is hacked in and needs to be implemented throughout the code
|
||||||
|
case "setIds":
|
||||||
|
queryParts = [
|
||||||
|
`UPDATE guild_info SET `,
|
||||||
|
`owner_id=${db.escape(this.ownerId)} `,
|
||||||
|
`WHERE guild_id=${db.escape(this.guildId)}`
|
||||||
|
];
|
||||||
|
return queryParts.join('');
|
||||||
|
case "setBasic":
|
||||||
|
queryParts = [
|
||||||
|
`INSERT INTO guild_info (`,
|
||||||
|
`guild_id, owner_id`,
|
||||||
|
`) VALUES (`,
|
||||||
|
`${db.escape(this.guildId)}, ${db.escape(this.ownerId)}`,
|
||||||
|
`) ON DUPLICATE KEY UPDATE owner_id=${db.escape(this.ownerId)}`
|
||||||
|
];
|
||||||
|
return queryParts.join('');
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -166,6 +230,7 @@ module.exports = {
|
|||||||
`[Tree Link](https://discord.com/channels/${this.guildId}/${this.treeChannelId}/${this.treeMessageId})`,
|
`[Tree Link](https://discord.com/channels/${this.guildId}/${this.treeChannelId}/${this.treeMessageId})`,
|
||||||
`Leaderboard Channel: <#${this.leaderboardChannelId}>`,
|
`Leaderboard Channel: <#${this.leaderboardChannelId}>`,
|
||||||
`[Leaderboard Link](https://discord.com/channels/${this.guildId}/${this.leaderboardChannelId}/${this.leaderboardMessageId})`,
|
`[Leaderboard Link](https://discord.com/channels/${this.guildId}/${this.leaderboardChannelId}/${this.leaderboardMessageId})`,
|
||||||
|
`Notification Relay Enabled: ${this.notificationsEnabled}`,
|
||||||
`Notification Watch Channel: <#${this.watchChannelId}>`,
|
`Notification Watch Channel: <#${this.watchChannelId}>`,
|
||||||
`Notification Relay Channel: <#${this.reminderChannelId}>`,
|
`Notification Relay Channel: <#${this.reminderChannelId}>`,
|
||||||
`Water Message: ${this.waterMessage}`,
|
`Water Message: ${this.waterMessage}`,
|
||||||
|
@ -56,20 +56,16 @@ module.exports = {
|
|||||||
db.end();
|
db.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (res.length == 0) {
|
|
||||||
reject("There is no database entry for your guild yet. Try running /setup");
|
|
||||||
db.end();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
row = res[0];
|
row = res[0];
|
||||||
const guildInfo = new GuildInfo()
|
const guildInfo = new GuildInfo()
|
||||||
.setId(row.guild_id)
|
.setIds(row.guild_id, row.owner_id)
|
||||||
.setName(row.tree_name)
|
.setName(row.tree_name)
|
||||||
.setHeight(row.tree_height)
|
.setHeight(row.tree_height)
|
||||||
.setTreeMessage(row.tree_message_id, row.tree_channel_id)
|
.setTreeMessage(row.tree_message_id, row.tree_channel_id)
|
||||||
.setLeaderboardMessage(row.leaderboard_message_id, row.leaderboard_channel_id)
|
.setLeaderboardMessage(row.leaderboard_message_id, row.leaderboard_channel_id)
|
||||||
.setReminders(row.water_message, row.fruit_message, row.reminder_channel_id, row.watch_channel_id, row.notifications_enabled)
|
.setReminders(row.water_message, row.fruit_message, row.reminder_channel_id, row.watch_channel_id, row.notifications_enabled)
|
||||||
.setRoles(row.water_role_id, row.fruit_role_id);
|
.setRoles(row.water_role_id, row.fruit_role_id)
|
||||||
|
.setCompareMessage(row.compare_channel_id, row.compare_message_id);
|
||||||
db.end();
|
db.end();
|
||||||
resolve(guildInfo);
|
resolve(guildInfo);
|
||||||
});
|
});
|
||||||
@ -106,15 +102,17 @@ module.exports = {
|
|||||||
for (let i = 0; i < res.length; i++) {
|
for (let i = 0; i < res.length; i++) {
|
||||||
let row = res[i];
|
let row = res[i];
|
||||||
guildInfos.push(new GuildInfo()
|
guildInfos.push(new GuildInfo()
|
||||||
.setId(row.guild_id)
|
.setIds(row.guild_id, row.owner_id)
|
||||||
.setName(row.tree_name)
|
.setName(row.tree_name)
|
||||||
.setHeight(row.tree_height)
|
.setHeight(row.tree_height)
|
||||||
.setTreeMessage(row.tree_message_id, row.tree_channel_id)
|
.setTreeMessage(row.tree_message_id, row.tree_channel_id)
|
||||||
.setLeaderboardMessage(row.leaderboard_message_id, row.leaderboard_channel_id)
|
.setLeaderboardMessage(row.leaderboard_message_id, row.leaderboard_channel_id)
|
||||||
.setReminders(row.water_message, row.fruit_message, row.reminder_channel_id, row.watch_channel_id, row.notifications_enabled)
|
.setReminders(row.water_message, row.fruit_message, row.reminder_channel_id, row.watch_channel_id, row.notifications_enabled)
|
||||||
.setRoles(row.water_role_id, row.fruit_role_id)
|
.setRoles(row.water_role_id, row.fruit_role_id)
|
||||||
|
.setCompareMessage(row.compare_channel_id, row.compare_message_id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// console.log(res.length + " // " + guildInfos.length);
|
||||||
|
|
||||||
db.end();
|
db.end();
|
||||||
resolve(guildInfos);
|
resolve(guildInfos);
|
||||||
@ -141,6 +139,7 @@ module.exports = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
db.end();
|
db.end();
|
||||||
|
// console.log("Updated the database");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -170,6 +169,7 @@ module.exports = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
db.end();
|
db.end();
|
||||||
|
console.log("Updated the database");
|
||||||
resolve({ "status": "Successfully set the guild information", "data": null });
|
resolve({ "status": "Successfully set the guild information", "data": null });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -298,46 +298,5 @@ module.exports = {
|
|||||||
resolve({ "status": "Successfully uploaded the leaderboard", "data": res });
|
resolve({ "status": "Successfully uploaded the leaderboard", "data": res });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
|
||||||
get24hTree(guildId, treeName) {
|
|
||||||
const db = mysql.createConnection({
|
|
||||||
host: process.env.DBHOST,
|
|
||||||
user: process.env.DBUSER,
|
|
||||||
password: process.env.DBPASS,
|
|
||||||
database: process.env.DBNAME,
|
|
||||||
port: process.env.DBPORT
|
|
||||||
});
|
|
||||||
db.connect((err) => {
|
|
||||||
if (err) throw `Error connecting to the database: ${err.message}`;
|
|
||||||
});
|
|
||||||
// Returns a Promise, resolve({ "status": "", "data": leaderboard })
|
|
||||||
const select24hTreeQuery = `SELECT id, tree_name, tree_rank, tree_height, has_pin FROM leaderboard WHERE guild_id = ${db.escape(guildId)} AND tree_name = ${db.escape(treeName)} AND timestamp > date_sub(now(), interval 1 day) ORDER BY id ASC LIMIT 1`;
|
|
||||||
// TODO run the query and return a promise then process the results. resolve with { "status": , "data": leaderboard }
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
db.query(select24hTreeQuery, (err, res) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
db.end();
|
|
||||||
reject("Error fetching the historic 24hr tree height: " + err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let hist24hTree = {};
|
|
||||||
if (res.length > 0) {
|
|
||||||
hist24hTree = {
|
|
||||||
"treeName": res[0].tree_name,
|
|
||||||
"treeRank": res[0].tree_rank,
|
|
||||||
"treeHeight": res[0].tree_height,
|
|
||||||
"hasPin": res[0].has_pin
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
hist24hTree = {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.end();
|
|
||||||
resolve({ "status": "Successfully fetched historic 24hr tree.", "data": hist24hTree });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -2,7 +2,7 @@
|
|||||||
// dotenv for handling environment variables
|
// dotenv for handling environment variables
|
||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const isDev = process.env.isDev;
|
const isDev = process.env.DEBUG === "true";
|
||||||
const package = require('../package.json');
|
const package = require('../package.json');
|
||||||
|
|
||||||
// filesystem
|
// filesystem
|
||||||
@ -17,8 +17,10 @@ const { GuildInfo } = require('./CustomClasses');
|
|||||||
const config = require('../data/config.json');
|
const config = require('../data/config.json');
|
||||||
const strings = require('../data/strings.json');
|
const strings = require('../data/strings.json');
|
||||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||||
|
const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js'));
|
||||||
const dbfn = require('./dbfn.js');
|
const dbfn = require('./dbfn.js');
|
||||||
const { finished } = require('stream');
|
const { finished } = require('stream');
|
||||||
|
const https = require('https');
|
||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
// Functions for managing and creating Collections
|
// Functions for managing and creating Collections
|
||||||
@ -35,6 +37,44 @@ const functions = {
|
|||||||
}
|
}
|
||||||
if (isDev) console.log('Slash Commands Collection Built');
|
if (isDev) console.log('Slash Commands Collection Built');
|
||||||
},
|
},
|
||||||
|
dotCommands(client) {
|
||||||
|
// If the dotCommands collection doesn't exist already, create it
|
||||||
|
if (!client.dotCommands) client.dotCommands = new Discord.Collection();
|
||||||
|
// Empty the dotcommands collection
|
||||||
|
client.dotCommands.clear();
|
||||||
|
// Iterate over each file within ./dot-commands that ends with .js
|
||||||
|
for (const file of dotCommandFiles) {
|
||||||
|
// Pull the file into a temporary variable
|
||||||
|
const dotCommand = require(`../dot-commands/${file}`);
|
||||||
|
// Create a new entry in the collection with the key of the command name, value of the command itself
|
||||||
|
client.dotCommands.set(dotCommand.name, dotCommand);
|
||||||
|
// Old code from NodBot to implement aliases for dot commands. Unused as of 5/16/23
|
||||||
|
// if (Array.isArray(dotCommand.alias)) {
|
||||||
|
// dotCommand.alias.forEach(element => {
|
||||||
|
// client.dotCommands.set(element, dotCommand);
|
||||||
|
// });
|
||||||
|
// } else if (dotCommand.alias != undefined) {
|
||||||
|
// client.dotCommands.set(dotCommand.alias, dotCommand);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if (isDev) console.log('Dot Commands Collection Built');
|
||||||
|
},
|
||||||
|
setvalidCommands(client) {
|
||||||
|
// Iterate over every entry in the dotCommands collection
|
||||||
|
for (const entry of client.dotCommands.map(command => command)) {
|
||||||
|
// Add the command's name to the valid commands list for validation later
|
||||||
|
config.validCommands.push(entry.name);
|
||||||
|
// Old code from NodBot to implement aliases for dot commands. Unused as of 5/16/23
|
||||||
|
// if (Array.isArray(entry.alias)) {
|
||||||
|
// entry.alias.forEach(element => {
|
||||||
|
// config.validCommands.push(element);
|
||||||
|
// });
|
||||||
|
// } else if (entry.alias != undefined) {
|
||||||
|
// config.validCommands.push(entry.alias);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if (isDev) console.log(`Valid Commands Added to Config\n${config.validCommands}`);
|
||||||
|
},
|
||||||
async guildInfos(client) {
|
async guildInfos(client) {
|
||||||
const guildInfos = await dbfn.getAllGuildInfos();
|
const guildInfos = await dbfn.getAllGuildInfos();
|
||||||
if (!client.guildInfos) client.guildInfos = new Discord.Collection();
|
if (!client.guildInfos) client.guildInfos = new Discord.Collection();
|
||||||
@ -52,7 +92,17 @@ const functions = {
|
|||||||
const { guildInfos, messageCollectors } = client;
|
const { guildInfos, messageCollectors } = client;
|
||||||
// Iterate over each guild info
|
// Iterate over each guild info
|
||||||
await guildInfos.forEach(async guildInfo => {
|
await guildInfos.forEach(async guildInfo => {
|
||||||
await functions.collectors.create(client, guildInfo);
|
await functions.collectors.create(client, guildInfo).catch(async e => {
|
||||||
|
if (e === "ERRNOGUILD") {
|
||||||
|
guildInfo.setReminders(undefined, undefined, undefined, undefined, false);
|
||||||
|
const query = guildInfo.queryBuilder("setReminders");
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
await functions.collectionBuilders.guildInfos(client);
|
||||||
|
console.log("Disabled notification relay for a guild I'm no longer in: " + guildInfo.guildId);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -123,6 +173,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) {
|
||||||
@ -165,6 +225,19 @@ const functions = {
|
|||||||
const messageContents = { embeds: [embed], ephemeral: privateBool };
|
const messageContents = { embeds: [embed], ephemeral: privateBool };
|
||||||
return messageContents;
|
return messageContents;
|
||||||
},
|
},
|
||||||
|
aboutEmbed(private) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor(strings.embeds.color)
|
||||||
|
.setTitle(strings.help.aboutTitle)
|
||||||
|
.setDescription(`${strings.help.info}\n\n${strings.help.longDescription}`)
|
||||||
|
.setFooter({ text: `v${package.version} - ${strings.embeds.footer}` });
|
||||||
|
embed.addFields([
|
||||||
|
{ name: strings.help.topggTitle, value: strings.help.topggBody}
|
||||||
|
]);
|
||||||
|
const privateBool = private == 'true';
|
||||||
|
const messageContents = { embeds: [embed], ephemeral: privateBool };
|
||||||
|
return messageContents;
|
||||||
|
},
|
||||||
errorEmbed(content) {
|
errorEmbed(content) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setColor(0xFF0000)
|
.setColor(0xFF0000)
|
||||||
@ -184,6 +257,55 @@ const functions = {
|
|||||||
return messageContents;
|
return messageContents;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dotCommands: {
|
||||||
|
getCommandData(message) {
|
||||||
|
const commandData = {};
|
||||||
|
// Split the message content at the final instance of a period
|
||||||
|
const finalPeriod = message.content.lastIndexOf('.');
|
||||||
|
// if(isDev) console.log(message.content);
|
||||||
|
// If the final period is the last character, or doesn't exist
|
||||||
|
if (finalPeriod < 0) {
|
||||||
|
// if (isDev) console.log(finalPeriod);
|
||||||
|
commandData.isCommand = false;
|
||||||
|
return commandData;
|
||||||
|
}
|
||||||
|
commandData.isCommand = true;
|
||||||
|
// Get the first part of the message, everything leading up to the final period
|
||||||
|
commandData.args = message.content.slice(0, finalPeriod);
|
||||||
|
// Get the last part of the message, everything after the final period
|
||||||
|
commandData.command = message.content.slice(finalPeriod).replace('.', '').toLowerCase();
|
||||||
|
commandData.author = `${message.author.username}#${message.author.discriminator}`;
|
||||||
|
return this.checkCommand(commandData);
|
||||||
|
},
|
||||||
|
checkCommand(commandData) {
|
||||||
|
if (commandData.isCommand) {
|
||||||
|
const validCommands = require('../data/config.json').validCommands;
|
||||||
|
commandData.isValid = validCommands.includes(commandData.command);
|
||||||
|
// Add exceptions for messages that contain only a link
|
||||||
|
if (commandData.args.startsWith('http')) commandData.isValid = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
commandData.isValid = false;
|
||||||
|
console.error('Somehow a non-command made it to checkCommands()');
|
||||||
|
}
|
||||||
|
return commandData;
|
||||||
|
},
|
||||||
|
checkPermissions(type, userId) {
|
||||||
|
if (type === "devTeam") {
|
||||||
|
const { devTeamIds } = config;
|
||||||
|
let matchFound = false;
|
||||||
|
for (let i = 0; i < devTeamIds.length; i++) {
|
||||||
|
if (userId === devTeamIds[i]) matchFound = true;
|
||||||
|
}
|
||||||
|
return matchFound;
|
||||||
|
} else if (type === "owner") {
|
||||||
|
return config.ownerId === userId;
|
||||||
|
} else if (type === "everyone") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
rankings: {
|
rankings: {
|
||||||
parse(interaction, guildInfo) {
|
parse(interaction, guildInfo) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -250,7 +372,7 @@ const functions = {
|
|||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(strings.status.missingLeaderboardMessage);
|
reject(strings.status.missingLeaderboardMessage);
|
||||||
console.error(err);
|
// console.error(err);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@ -265,9 +387,9 @@ const functions = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
async compare(interaction, guildInfo) {
|
async compare(guildInfo) {
|
||||||
try {
|
try {
|
||||||
const getLeaderboardResponse = await dbfn.getLeaderboard(interaction.guildId);
|
const getLeaderboardResponse = await dbfn.getLeaderboard(guildInfo.guildId);
|
||||||
const leaderboard = getLeaderboardResponse.data; // [ { treeName: "Name", treeHeight: 1234.5, treeRank: 67 }, {...}, {...} ]
|
const leaderboard = getLeaderboardResponse.data; // [ { treeName: "Name", treeHeight: 1234.5, treeRank: 67 }, {...}, {...} ]
|
||||||
|
|
||||||
// Prepare the beginning of the comparison message
|
// Prepare the beginning of the comparison message
|
||||||
@ -314,7 +436,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}]`);
|
||||||
@ -327,6 +448,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');
|
||||||
@ -337,12 +459,12 @@ const functions = {
|
|||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(strings.status.missingTreeMessage);
|
reject(strings.status.missingTreeMessage);
|
||||||
console.error(err);
|
// console.error(err);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(strings.status.missingTreeChannel);
|
reject(strings.status.missingTreeChannel);
|
||||||
console.error(err);
|
// console.error(err);
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -483,12 +605,184 @@ 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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Checks if a message is a leaderboard message, and returns the entries if it is
|
||||||
isLeaderboard(message) {
|
isLeaderboard(message) {
|
||||||
if (message.embeds.length > 0) {
|
if (message.embeds.length > 0) {
|
||||||
return message.embeds[0].data.title == "Tallest Trees";
|
// Grab the description and title from the embed
|
||||||
|
const { title, description } = message.embeds[0].data;
|
||||||
|
// Check that it's a Top Trees message
|
||||||
|
if (title == "Tallest Trees") {
|
||||||
|
// Break the description into an array of each line
|
||||||
|
const lines = description.split("\n");
|
||||||
|
const leaderboard = {
|
||||||
|
guildId: message.guildId,
|
||||||
|
entries: []
|
||||||
|
};
|
||||||
|
|
||||||
|
lines.forEach(line => {
|
||||||
|
// Skeleton entry:
|
||||||
|
const entry = {
|
||||||
|
treeRank: 0,
|
||||||
|
treeName: "",
|
||||||
|
treeHeight: 0,
|
||||||
|
hasPin: 0
|
||||||
|
}
|
||||||
|
// Break the line into parts separated by a hyphen -
|
||||||
|
// DO NOT USE this, it breaks when any tree name contains " - " which
|
||||||
|
// isn't uncommon apparently
|
||||||
|
// const parts = line.split(" - ");
|
||||||
|
|
||||||
|
// Instead, find the indices of the first and last instances of " - "
|
||||||
|
const hyphenIndices = [line.indexOf(" - "), line.lastIndexOf(" - ")];
|
||||||
|
const parts = [
|
||||||
|
line.slice(0, hyphenIndices[0]),
|
||||||
|
line.slice(hyphenIndices[0] + 3, hyphenIndices[1]),
|
||||||
|
line.slice(hyphenIndices[1] + 3, line.length)
|
||||||
|
];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Grab the rank
|
||||||
|
|
||||||
|
// Preset the indices to split the lines to get the data
|
||||||
|
const rankIndices = [parts[0].indexOf("#") + 1, parts[0].lastIndexOf("``")];
|
||||||
|
const nameIndices = [parts[1].indexOf("``") + 2, parts[1].lastIndexOf("``")];
|
||||||
|
const heightIndices = [0, parts[2].lastIndexOf("ft")];
|
||||||
|
|
||||||
|
// Set the data from the parts using the indices
|
||||||
|
entry.treeRank = parts[0].slice(...rankIndices);
|
||||||
|
entry.treeName = parts[1].slice(...nameIndices);
|
||||||
|
entry.treeHeight = parts[2].slice(...heightIndices);
|
||||||
|
|
||||||
|
// Go back and check for the first 3 as they use emojis instead of numbers, this will overwrite above
|
||||||
|
if (line.includes("🥇")) entry.treeRank = 1;
|
||||||
|
if (line.includes("🥈")) entry.treeRank = 2;
|
||||||
|
if (line.includes("🥉")) entry.treeRank = 3;
|
||||||
|
|
||||||
|
// Check for the pin showing ownership
|
||||||
|
if (line.includes("📍")) entry.hasPin = 1;
|
||||||
|
|
||||||
|
// Add the entry to the array
|
||||||
|
leaderboard.entries.push(entry);
|
||||||
|
});
|
||||||
|
return leaderboard;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateHandler(message) {
|
||||||
|
if (message.partial) {
|
||||||
|
message = await message.fetch().catch(e => {
|
||||||
|
throw 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) {
|
||||||
|
if (isDev) console.log(`LU: ${message.guild.name}`);
|
||||||
|
let guildInfo;
|
||||||
|
let doDbUpdate = false;
|
||||||
|
if (message.client.guildInfos.has(message.guildId)) {
|
||||||
|
guildInfo = message.client.guildInfos.get(message.guildId);
|
||||||
|
if ((guildInfo.leaderboardChannelId != message.channel.id) || (guildInfo.leaderboardMessageId != message.id)) {
|
||||||
|
guildInfo.setLeaderboardMessage(message.id, message.channel.id);
|
||||||
|
doDbUpdate = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
guildInfo = new GuildInfo().setIds(message.guildId, message.guild.ownerId)
|
||||||
|
.setLeaderboardMessage(message.id, message.channel.id);
|
||||||
|
doDbUpdate = true;
|
||||||
|
}
|
||||||
|
if (doDbUpdate) {
|
||||||
|
const query = guildInfo.queryBuilder("setLeaderboardMessage");
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
await functions.collectionBuilders.guildInfos(message.client);
|
||||||
|
}
|
||||||
|
await dbfn.uploadLeaderboard(isLeaderboard);
|
||||||
|
// Update the comparison message
|
||||||
|
// Check for valid message IDs
|
||||||
|
if (guildInfo.treeMessageId === "") throw strings.error.noTreeMessage;
|
||||||
|
if (guildInfo.leaderboardMessageId === "") throw strings.error.noLeaderboardMessage;
|
||||||
|
if (guildInfo.compareMessageId === "") throw strings.error.noCompareMessage;
|
||||||
|
|
||||||
|
// Fetch the messages
|
||||||
|
const { guild } = message;
|
||||||
|
// Tree
|
||||||
|
const treeChannel = await guild.channels.fetch(guildInfo.treeChannelId);
|
||||||
|
const treeMessage = await treeChannel.messages.fetch(guildInfo.treeMessageId);
|
||||||
|
// Leaderboard
|
||||||
|
const leaderboardChannel = await guild.channels.fetch(guildInfo.leaderboardChannelId);
|
||||||
|
const leaderboardMessage = await leaderboardChannel.messages.fetch(guildInfo.leaderboardMessageId);
|
||||||
|
// Comparison
|
||||||
|
const compareChannel = await guild.channels.fetch(guildInfo.compareChannelId);
|
||||||
|
const compareMessage = await compareChannel.messages.fetch(guildInfo.compareMessageId);
|
||||||
|
|
||||||
|
// Update the tree information
|
||||||
|
// Make sure we have a tree message and parse it
|
||||||
|
const isTree = this.isTree(treeMessage);
|
||||||
|
if (!isTree) throw "Tree message isn't actually a tree message!";
|
||||||
|
guildInfo.setTreeInfo(isTree.treeName, isTree.treeHeight);
|
||||||
|
|
||||||
|
// Grab the leaderboard
|
||||||
|
// Make sure it's a leaderboard and parse it
|
||||||
|
// const isLeaderboard = this.messages.isLeaderboard(leaderboardMessage);
|
||||||
|
// if (!isLeaderboard) throw "Leaderboard message isn't actually a leaderboard message!";
|
||||||
|
// Upload the leaderboard
|
||||||
|
// await dbfn.uploadLeaderboard(isLeaderboard);
|
||||||
|
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
||||||
|
const comparedRankings = await functions.rankings.compare(guildInfo);
|
||||||
|
const embed = functions.builders.comparisonEmbed(comparedRankings, guildInfo);
|
||||||
|
await compareMessage.edit(embed).catch(e => console.error(e));
|
||||||
|
} else if (isTree) {
|
||||||
|
// Check if the message is a tree
|
||||||
|
// if (isDev) console.log(`TU: ${isTree.treeName}: ${isTree.treeHeight}ft`);
|
||||||
|
let guildInfo;
|
||||||
|
let doDbUpdate = false;
|
||||||
|
if (message.client.guildInfos.has(message.guildId)) {
|
||||||
|
guildInfo = message.client.guildInfos.get(message.guildId);
|
||||||
|
if ((guildInfo.treeName != isTree.treeName) || (guildInfo.treeHeight != isTree.treeHeight)) {
|
||||||
|
guildInfo.setTreeInfo(isTree.treeName, isTree.treeHeight, message.channel.id, message.id);
|
||||||
|
doDbUpdate = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
guildInfo = new GuildInfo().setIds(message.guildId, message.guild.ownerId)
|
||||||
|
.setTreeInfo(isTree.treeName, isTree.treeHeight, message.channel.id, message.id);
|
||||||
|
doDbUpdate = true;
|
||||||
|
}
|
||||||
|
if (doDbUpdate) {
|
||||||
|
const query = guildInfo.queryBuilder("setTreeInfo");
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
await functions.collectionBuilders.guildInfos(message.client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -496,14 +790,24 @@ const functions = {
|
|||||||
async fruitPing(interaction) {
|
async fruitPing(interaction) {
|
||||||
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 role = await functions.roles.fetchRole(interaction.guild, guildInfo.fruitRoleId);
|
|
||||||
let status = "No Changes Made";
|
let status = "No Changes Made";
|
||||||
|
let errorFlag = false;
|
||||||
|
const role = await functions.roles.fetchRole(interaction.guild, guildInfo.fruitRoleId).catch(e => {
|
||||||
|
errorFlag = true;
|
||||||
|
status = strings.error.noFetchRole;
|
||||||
|
});
|
||||||
if (interaction.member.roles.cache.some(role => role.id == guildInfo.fruitRoleId)) {
|
if (interaction.member.roles.cache.some(role => role.id == guildInfo.fruitRoleId)) {
|
||||||
await functions.roles.takeRole(interaction.member, role);
|
await functions.roles.takeRole(interaction.member, role).catch(e => {
|
||||||
status = "Removed the fruit role.";
|
errorFlag = true;
|
||||||
|
status = strings.error.noTakeRole;
|
||||||
|
});
|
||||||
|
if (!errorFlag) status = strings.error.yesTakeRole;
|
||||||
} else {
|
} else {
|
||||||
await functions.roles.giveRole(interaction.member, role);
|
await functions.roles.giveRole(interaction.member, role).catch(e => {
|
||||||
status = "Added the fruit role.";
|
errorFlag = true;
|
||||||
|
status = strings.error.noGiveRole;
|
||||||
|
});
|
||||||
|
if (!errorFlag) status = strings.error.yesGiveRole;
|
||||||
}
|
}
|
||||||
return functions.builders.embed(status);
|
return functions.builders.embed(status);
|
||||||
} else {
|
} else {
|
||||||
@ -514,13 +818,23 @@ const functions = {
|
|||||||
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);
|
||||||
let status = "No Changes Made";
|
let status = "No Changes Made";
|
||||||
const role = await functions.roles.fetchRole(interaction.guild, guildInfo.waterRoleId);
|
let errorFlag = false;
|
||||||
|
const role = await functions.roles.fetchRole(interaction.guild, guildInfo.waterRoleId).catch(e => {
|
||||||
|
errorFlag = true;
|
||||||
|
status = strings.error.noFetchRole;
|
||||||
|
});
|
||||||
if (interaction.member.roles.cache.some(role => role.id == guildInfo.waterRoleId)) {
|
if (interaction.member.roles.cache.some(role => role.id == guildInfo.waterRoleId)) {
|
||||||
await functions.roles.takeRole(interaction.member, role);
|
await functions.roles.takeRole(interaction.member, role).catch(e => {
|
||||||
status = "Removed the water role.";
|
errorFlag = true;
|
||||||
|
status = strings.error.noTakeRole;
|
||||||
|
});
|
||||||
|
if (!errorFlag) status = strings.error.yesTakeRole;
|
||||||
} else {
|
} else {
|
||||||
await functions.roles.giveRole(interaction.member, role);
|
await functions.roles.giveRole(interaction.member, role).catch(e => {
|
||||||
status = "Added the water role.";
|
errorFlag = true;
|
||||||
|
status = strings.error.noGiveRole;
|
||||||
|
});
|
||||||
|
if (!errorFlag) status = strings.error.yesGiveRole;
|
||||||
}
|
}
|
||||||
return functions.builders.embed(status);
|
return functions.builders.embed(status);
|
||||||
} else {
|
} else {
|
||||||
@ -530,13 +844,13 @@ const functions = {
|
|||||||
},
|
},
|
||||||
roles: {
|
roles: {
|
||||||
async fetchRole(guild, roleId) {
|
async fetchRole(guild, roleId) {
|
||||||
return await guild.roles.fetch(roleId).catch(err => console.error("Error fetching the role: " + err + "\n" + roleId));
|
return await guild.roles.fetch(roleId);
|
||||||
},
|
},
|
||||||
async giveRole(member, role) {
|
async giveRole(member, role) {
|
||||||
await member.roles.add(role).catch(err => console.error("Error giving the role: " + err + "\n" + JSON.stringify(role)));
|
await member.roles.add(role);
|
||||||
},
|
},
|
||||||
async takeRole(member, role) {
|
async takeRole(member, role) {
|
||||||
await member.roles.remove(role).catch(err => console.error("Error taking the role: " + err + "\n" + JSON.stringify(role)));
|
await member.roles.remove(role);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
collectors: {
|
collectors: {
|
||||||
@ -547,9 +861,11 @@ const functions = {
|
|||||||
await this.end(client, guildInfo);
|
await this.end(client, guildInfo);
|
||||||
}
|
}
|
||||||
// Make sure guildInfo is what we expect, the watch channel isnt blank, and notifications are enabled
|
// Make sure guildInfo is what we expect, the watch channel isnt blank, and notifications are enabled
|
||||||
if (guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "" && guildInfo.notificationsEnabled) {
|
if ((guildInfo instanceof GuildInfo && guildInfo.watchChannelId != "") && (guildInfo.notificationsEnabled == true)) {
|
||||||
// Fetch the Guild
|
// Fetch the Guild
|
||||||
const guild = await client.guilds.fetch(guildInfo.guildId);
|
const guild = await client.guilds.fetch(guildInfo.guildId).catch(e => {
|
||||||
|
throw "ERRNOGUILD"
|
||||||
|
});
|
||||||
// Fetch the Channel
|
// Fetch the Channel
|
||||||
const channel = await guild.channels.fetch(guildInfo.watchChannelId);
|
const channel = await guild.channels.fetch(guildInfo.watchChannelId);
|
||||||
// Create the filter function
|
// Create the filter function
|
||||||
@ -561,23 +877,27 @@ const functions = {
|
|||||||
const collector = channel.createMessageCollector({ filter });
|
const collector = channel.createMessageCollector({ filter });
|
||||||
// Add the collector to the messageCollectors Collection
|
// Add the collector to the messageCollectors Collection
|
||||||
client.messageCollectors.set(guildInfo.guildId, collector);
|
client.messageCollectors.set(guildInfo.guildId, collector);
|
||||||
|
// if (isDev) console.log("Set up a collector in " + guildInfo.guildId);
|
||||||
collector.on('collect', message => {
|
collector.on('collect', message => {
|
||||||
|
// if (isDev) console.log("Collected a message in " + message.guild.id);
|
||||||
// Check for manual relay use with "water ping" and "fruit ping"
|
// Check for manual relay use with "water ping" and "fruit ping"
|
||||||
if (message.content.toLowerCase().includes("water ping")) {
|
if (message.content.toLowerCase().includes("water ping")) {
|
||||||
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
||||||
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If the message doesn't contain an embed, we can ignore it
|
// If the message doesn't contain an embed, we can ignore it
|
||||||
if (message.embeds == undefined) return;
|
if (message.embeds == undefined) return;
|
||||||
if (message.embeds.length == 0) return;
|
if (message.embeds.length == 0) return;
|
||||||
|
// console.log(JSON.stringify(message.embeds[0].data));
|
||||||
|
if (message.embeds[0].data.description == undefined) return;
|
||||||
// Check the description field of the embed to determine if it matches Grow A Tree's notification texts
|
// 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)) {
|
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
||||||
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
functions.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
||||||
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
functions.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -593,23 +913,45 @@ const functions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async refresh(interaction) {
|
async refresh(interaction) {
|
||||||
// const getGuildInfoResponse = await dbfn.getGuildInfo(interaction.guildId);
|
|
||||||
// let guildInfo = getGuildInfoResponse.data;
|
|
||||||
let guildInfo = interaction.client.guildInfos.get(interaction.guild.id);
|
|
||||||
const findMessagesResponse = await this.messages.find(interaction, guildInfo);
|
|
||||||
if (findMessagesResponse.code == 1) {
|
|
||||||
guildInfo = findMessagesResponse.data;
|
|
||||||
// Parse the tree
|
|
||||||
await this.tree.parse(interaction, guildInfo);
|
|
||||||
// Parse the leaderboard
|
|
||||||
await this.rankings.parse(interaction, guildInfo);
|
|
||||||
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
|
||||||
const comparedRankings = await this.rankings.compare(interaction, guildInfo);
|
|
||||||
|
|
||||||
|
if (interaction.client.guildInfos.has(interaction.guildId)) {
|
||||||
|
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
||||||
|
// Check for valid message IDs
|
||||||
|
if (guildInfo.treeMessageId === "") throw strings.error.noTreeMessage;
|
||||||
|
if (guildInfo.leaderboardMessageId === "") throw strings.error.noLeaderboardMessage;
|
||||||
|
|
||||||
|
// Fetch the messages
|
||||||
|
const { guild } = interaction;
|
||||||
|
// Tree
|
||||||
|
const treeChannel = await guild.channels.fetch(guildInfo.treeChannelId);
|
||||||
|
const treeMessage = await treeChannel.messages.fetch(guildInfo.treeMessageId);
|
||||||
|
// Leaderboard
|
||||||
|
const leaderboardChannel = await guild.channels.fetch(guildInfo.leaderboardChannelId);
|
||||||
|
const leaderboardMessage = await leaderboardChannel.messages.fetch(guildInfo.leaderboardMessageId);
|
||||||
|
|
||||||
|
// Update the comparison message information
|
||||||
|
guildInfo.setCompareMessage(interaction.channel.id, interaction.message.id);
|
||||||
|
const query = guildInfo.queryBuilder("setCompareMessage");
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
|
||||||
|
// Update the tree information
|
||||||
|
// Make sure we have a tree message and parse it
|
||||||
|
const isTree = this.messages.isTree(treeMessage);
|
||||||
|
if (!isTree) throw "Tree message isn't actually a tree message!";
|
||||||
|
guildInfo.setTreeInfo(isTree.treeName, isTree.treeHeight);
|
||||||
|
|
||||||
|
// Grab the leaderboard
|
||||||
|
// Make sure it's a leaderboard and parse it
|
||||||
|
const isLeaderboard = this.messages.isLeaderboard(leaderboardMessage);
|
||||||
|
if (!isLeaderboard) throw "Leaderboard message isn't actually a leaderboard message!";
|
||||||
|
// Upload the leaderboard
|
||||||
|
await dbfn.uploadLeaderboard(isLeaderboard);
|
||||||
|
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
||||||
|
const comparedRankings = await this.rankings.compare(guildInfo);
|
||||||
const embed = this.builders.comparisonEmbed(comparedRankings, guildInfo);
|
const embed = this.builders.comparisonEmbed(comparedRankings, guildInfo);
|
||||||
await interaction.update(embed).catch(e => console.error(e));
|
await interaction.update(embed).catch(e => console.error(e));
|
||||||
} else {
|
} else {
|
||||||
await interaction.update(this.builders.errorEmbed(findMessagesResponse.status));
|
throw "Guild doesn't exist in database!";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reset(interaction) {
|
reset(interaction) {
|
||||||
@ -652,6 +994,9 @@ const functions = {
|
|||||||
timeToHeight(beginHeight, destHeight, efficiency, quality) {
|
timeToHeight(beginHeight, destHeight, efficiency, quality) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let time = 0;
|
let time = 0;
|
||||||
|
let oldTime = 0;
|
||||||
|
let compostAppliedCount = 0;
|
||||||
|
let totalWaterCount = 0;
|
||||||
if ((efficiency) && (quality)) {
|
if ((efficiency) && (quality)) {
|
||||||
for (let i = beginHeight; i < destHeight; i++) {
|
for (let i = beginHeight; i < destHeight; i++) {
|
||||||
const randNum = Math.floor(Math.random() * 100);
|
const randNum = Math.floor(Math.random() * 100);
|
||||||
@ -661,9 +1006,15 @@ const functions = {
|
|||||||
let waterTime = functions.getWaterTime(i);
|
let waterTime = functions.getWaterTime(i);
|
||||||
let reductionTime = waterTime * qualityPercent;
|
let reductionTime = waterTime * qualityPercent;
|
||||||
let finalTime = waterTime - reductionTime;
|
let finalTime = waterTime - reductionTime;
|
||||||
|
compostAppliedCount++;
|
||||||
|
totalWaterCount++;
|
||||||
time += parseFloat(finalTime);
|
time += parseFloat(finalTime);
|
||||||
|
oldTime += waterTime;
|
||||||
} else {
|
} else {
|
||||||
time += parseFloat(functions.getWaterTime(i));
|
totalWaterCount++;
|
||||||
|
let waterTime = parseFloat(functions.getWaterTime(i));
|
||||||
|
time += waterTime;
|
||||||
|
oldTime += waterTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -673,8 +1024,15 @@ const functions = {
|
|||||||
time += waterTime;
|
time += waterTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const readableWaterTime = this.parseWaterTime(time);
|
||||||
resolve(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) {
|
sleep(ms) {
|
||||||
@ -688,28 +1046,28 @@ 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);
|
||||||
console.log(`Water Relay: ${guild.name}: ${guildInfo.treeName}`);
|
if (isDev) console.log(`WR: ${guild.name}: ${guildInfo.treeName}`);
|
||||||
await reminderChannel.send(reminderEmbed).then(async m => {
|
await reminderChannel.send(message).then(async m => {
|
||||||
if (!m.deletable) return;
|
if (!m.deletable) return;
|
||||||
await this.sleep(500).then(async () => {
|
await this.sleep(500).then(async () => {
|
||||||
await m.delete().catch(e => console.error(e));
|
await m.delete().catch(e => console.error(e));
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(err);
|
console.error(`[${err.code}]: ${err.message} - ${err.url}`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
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);
|
||||||
console.log(`Fruit Relay: ${guild.name}: ${guildInfo.treeName}`);
|
if (isDev) console.log(`FR: ${guild.name}: ${guildInfo.treeName}`);
|
||||||
await reminderChannel.send(reminderEmbed).then(async m => {
|
await reminderChannel.send(message).then(async m => {
|
||||||
if (!m.deletable) return;
|
if (!m.deletable) return;
|
||||||
await this.sleep(500).then(async () => {
|
await this.sleep(500).then(async () => {
|
||||||
await m.delete().catch(e => console.error(e));
|
await m.delete().catch(e => console.error(e));
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error(err);
|
console.error(`[${err.code}]: ${err.message} - ${err.url}`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async setupCollectors(client) {
|
async setupCollectors(client) {
|
||||||
@ -726,19 +1084,19 @@ const functions = {
|
|||||||
const collector = channel.createMessageCollector({ filter });
|
const collector = channel.createMessageCollector({ filter });
|
||||||
collector.on('collect', message => {
|
collector.on('collect', message => {
|
||||||
if (message.content.toLowerCase().includes("water ping")) {
|
if (message.content.toLowerCase().includes("water ping")) {
|
||||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
||||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.embeds == undefined) return;
|
if (message.embeds == undefined) return;
|
||||||
if (message.embeds.length == 0) return;
|
if (message.embeds.length == 0) return;
|
||||||
guildInfo = client.guildInfos.get(guild.id);
|
guildInfo = client.guildInfos.get(guild.id);
|
||||||
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
||||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
||||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -756,23 +1114,49 @@ const functions = {
|
|||||||
collectors.push(collector);
|
collectors.push(collector);
|
||||||
collector.on('collect', message => {
|
collector.on('collect', message => {
|
||||||
if (message.content.toLowerCase().includes("water ping")) {
|
if (message.content.toLowerCase().includes("water ping")) {
|
||||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
} else if (message.content.toLowerCase().includes("fruit ping")) {
|
||||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (message.embeds == undefined) return;
|
if (message.embeds == undefined) return;
|
||||||
if (message.embeds.length == 0) return;
|
if (message.embeds.length == 0) return;
|
||||||
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
if (message.embeds[0].data.description.includes(strings.notifications.water)) {
|
||||||
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild);
|
this.sendWaterReminder(guildInfo, guildInfo.waterMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
} else if (message.embeds[0].data.description.includes(strings.notifications.fruit)) {
|
||||||
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild);
|
this.sendFruitReminder(guildInfo, guildInfo.fruitMessage, guildInfo.reminderChannelId, guild).catch(e => console.error(`[${e.code}]: ${e.message} - ${e.url}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw "Guild doesn't exist in database!";
|
throw "Guild doesn't exist in database!";
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
generateErrorId() {
|
||||||
|
const digitCount = 10;
|
||||||
|
const digits = [];
|
||||||
|
for (let i = 0; i < digitCount; i++) {
|
||||||
|
const randBase = Math.random();
|
||||||
|
const randNumRaw = randBase * 10;
|
||||||
|
const randNumRound = Math.floor(randNumRaw);
|
||||||
|
digits.push(randNumRound);
|
||||||
|
}
|
||||||
|
const errorId = digits.join("");
|
||||||
|
return errorId;
|
||||||
|
},
|
||||||
|
async sendHeartbeat(url) {
|
||||||
|
https.get(url, async (response) => {
|
||||||
|
if (isDev) console.log("Sent Heartbeat Request: " + url);
|
||||||
|
let data = '';
|
||||||
|
|
||||||
|
response.on('data', (chunk) => data += chunk);
|
||||||
|
|
||||||
|
response.on('end', () => {
|
||||||
|
if (isDev) console.log("Received Heartbeat Response: " + data);
|
||||||
|
parsedData = JSON.parse(data);
|
||||||
|
if ( !(parsedData.ok) ) console.error("Heartbeat failed");
|
||||||
|
});
|
||||||
|
}).on("error", (error) => console.error(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
98
modules/utils.js
Normal file
98
modules/utils.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* eslint-disable no-case-declarations */
|
||||||
|
/* eslint-disable indent */
|
||||||
|
// dotenv for handling environment variables
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
dotenv.config();
|
||||||
|
const token = process.env.TOKEN;
|
||||||
|
const dbfn = require('./dbfn.js');
|
||||||
|
|
||||||
|
// Discord.JS
|
||||||
|
const { Client, GatewayIntentBits, Partials } = require('discord.js');
|
||||||
|
const client = new Client({
|
||||||
|
intents: [
|
||||||
|
GatewayIntentBits.Guilds,
|
||||||
|
GatewayIntentBits.GuildMessages,
|
||||||
|
GatewayIntentBits.GuildMessageReactions,
|
||||||
|
GatewayIntentBits.MessageContent
|
||||||
|
],
|
||||||
|
partials: [
|
||||||
|
Partials.Channel,
|
||||||
|
Partials.Message
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Various imports
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
client.once('ready', async () => {
|
||||||
|
// watchRequestRates();
|
||||||
|
await fn.collectionBuilders.guildInfos(client);
|
||||||
|
const guilds = client.guilds.cache;
|
||||||
|
console.log("I'm in " + guilds.size + " guilds with " + client.guildInfos.size + " guildInfos");
|
||||||
|
// guilds.each(g => {
|
||||||
|
// console.log(g.name + "," + g.id + "," + g.ownerId);
|
||||||
|
// });
|
||||||
|
await setAllGuildOwners();
|
||||||
|
process.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.login(token);
|
||||||
|
|
||||||
|
|
||||||
|
async function watchRequestRates() {
|
||||||
|
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 watchRequestRates();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setAllGuildOwners() {
|
||||||
|
try {
|
||||||
|
let guildInfosArray = new Array();
|
||||||
|
let guildUpdateCount = 0;
|
||||||
|
let guildMissingCount = 0;
|
||||||
|
client.guildInfos.forEach((guildInfo) => {
|
||||||
|
guildInfosArray.push(guildInfo);
|
||||||
|
});
|
||||||
|
// console.log(guildInfosArray);
|
||||||
|
for (let i = 0; i < guildInfosArray.length; i++) {
|
||||||
|
const guildInfo = guildInfosArray[i];
|
||||||
|
let eFlag = 0;
|
||||||
|
const guild = await client.guilds.fetch(guildInfo.guildId).catch(e => {
|
||||||
|
eFlag = 1;
|
||||||
|
if (e.status === 404) {
|
||||||
|
console.log("Missing guild: " + guildInfo.guildId);
|
||||||
|
guildMissingCount++;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (eFlag === 1) continue;
|
||||||
|
guildInfo.setIds(guildInfo.guildId, guild.ownerId);
|
||||||
|
const query = guildInfo.queryBuilder("setIds");
|
||||||
|
console.log(query);
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
guildUpdateCount++;
|
||||||
|
}
|
||||||
|
console.log(`Updated ${guildUpdateCount} guilds with ${guildMissingCount} missing guilds.`);
|
||||||
|
} catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "silvanus",
|
"name": "silvanus",
|
||||||
"version": "1.2.2",
|
"version": "1.2.9",
|
||||||
"description": "Grow A Tree Companion Bot",
|
"description": "Grow A Tree Companion Bot",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -17,8 +17,8 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/voidf1sh/silvanus#readme",
|
"homepage": "https://github.com/voidf1sh/silvanus#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.4.0",
|
||||||
"discord.js": "^14.7.1",
|
"discord.js": "^14.11.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"string-replace-all": "^2.0.0",
|
"string-replace-all": "^2.0.0",
|
||||||
|
39
privacy
Normal file
39
privacy
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Privacy Policy for Silvanus Discord Bot
|
||||||
|
|
||||||
|
This Privacy Policy describes how Silvanus ("the Bot," "we," or "us") collects, uses, and stores your personal information when you interact with the bot. Please read this policy carefully to understand our practices regarding your personal data and how we will treat it.
|
||||||
|
|
||||||
|
Data Collection and Usage:
|
||||||
|
We collect and store the following data when you use the Silvanus Discord Bot:
|
||||||
|
|
||||||
|
User IDs: We collect user IDs to provide personalized services and ensure smooth bot functionality.
|
||||||
|
Guild IDs: We collect guild IDs to enable the bot to operate within specific servers and provide server-specific features.
|
||||||
|
Channel IDs: We collect channel IDs to facilitate bot functionality within specific channels.
|
||||||
|
Message IDs: We collect message IDs to support certain features and improve the user experience.
|
||||||
|
Role IDs: We collect role IDs to provide role-based functionality within Discord servers.
|
||||||
|
Tree Names, Ranks, and Heights: We collect tree-related data for in-game purposes and to enhance user interactions.
|
||||||
|
|
||||||
|
Data Storage and Security:
|
||||||
|
All collected data is stored securely using industry-standard practices. User IDs, guild IDs, channel IDs, message IDs, role IDs, tree names, tree ranks, and tree heights are stored in an encrypted MariaDB database. Additionally, the database is stored on a LUKS encrypted drive, providing an extra layer of security.
|
||||||
|
|
||||||
|
Data Deletion Requests:
|
||||||
|
If you would like to request the deletion of your data, please follow one of the methods below:
|
||||||
|
|
||||||
|
Discord Message: You can message @voidf1sh on Discord to request data deletion.
|
||||||
|
Email: You can send an email to SilvanusDev@gmail.com to request data deletion.
|
||||||
|
Support Server: You can join our support server at https://discord.gg/g5JRGn7PxU and submit a request for data deletion.
|
||||||
|
|
||||||
|
Please note that we will make reasonable efforts to respond to and process your data deletion requests in a timely manner, subject to any legal obligations or legitimate interests that may prevent us from complying with such requests.
|
||||||
|
|
||||||
|
Data Sharing:
|
||||||
|
We do not sell or share your personal information with third parties, except in the following circumstances:
|
||||||
|
|
||||||
|
Compliance with Laws: We may share your personal information if required to do so by applicable laws, regulations, legal processes, or governmental requests.
|
||||||
|
Protection of Rights: We may share your personal information to protect the rights, property, or safety of Silvanus, its users, or others.
|
||||||
|
|
||||||
|
Changes to the Privacy Policy:
|
||||||
|
We reserve the right to modify or update this Privacy Policy at any time. We will notify users of any material changes through the bot or other means. Your continued use of Silvanus after such modifications or updates signifies your acceptance of the revised Privacy Policy.
|
||||||
|
|
||||||
|
Contact Information:
|
||||||
|
If you have any questions, concerns, or requests regarding this Privacy Policy or our data practices, please contact us at SilvanusDev@gmail.com.
|
||||||
|
|
||||||
|
Last updated: [22 May 2023]
|
20
slash-commands/about.js
Executable file
20
slash-commands/about.js
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
const { SlashCommandBuilder, messageLink } = require('discord.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
const strings = require('../data/strings.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('about')
|
||||||
|
.setDescription('Get info about the bot')
|
||||||
|
.addStringOption(o =>
|
||||||
|
o.setName('private')
|
||||||
|
.setDescription('Should the response be sent privately?')
|
||||||
|
.setRequired(true)
|
||||||
|
.addChoices(
|
||||||
|
{ name: "True", value: "true" },
|
||||||
|
{ name: "False", value: "false" })),
|
||||||
|
async execute(interaction) {
|
||||||
|
const aboutEmbed = fn.builders.aboutEmbed(interaction.options.getString('private'));
|
||||||
|
await interaction.reply(aboutEmbed);
|
||||||
|
},
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
const { SlashCommandBuilder, Guild } = require('discord.js');
|
const { SlashCommandBuilder, Guild } = require('discord.js');
|
||||||
const dbfn = require('../modules/dbfn.js');
|
const dbfn = require('../modules/dbfn.js');
|
||||||
const fn = require('../modules/functions.js');
|
const fn = require('../modules/functions.js');
|
||||||
|
const strings = require('../data/strings.json');
|
||||||
const { GuildInfo } = require('../modules/CustomClasses.js');
|
const { GuildInfo } = require('../modules/CustomClasses.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -14,36 +15,33 @@ module.exports = {
|
|||||||
|
|
||||||
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);
|
// Handle a missing tree or leaderboard
|
||||||
if (findMessagesResponse.code == 1) {
|
let errors = [];
|
||||||
let guildInfo = interaction.client.guildInfos.get(interaction.guildId);
|
if (guildInfo.treeMessageId === "") {
|
||||||
// Parse the leaderboard message
|
errors.push(strings.error.noTreeMessage);
|
||||||
await fn.rankings.parse(interaction, guildInfo);
|
} else if (guildInfo.leaderboardMessageId === "") {
|
||||||
|
errors.push(strings.error.noLeaderboardMessage);
|
||||||
|
}
|
||||||
|
if (errors.length > 0) {
|
||||||
|
const embed = fn.builders.errorEmbed(`Unable to complete comparison, unable to find message(s):\n${errors.join("\n")}`);
|
||||||
|
await interaction.editReply(embed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
||||||
const comparedRankings = await fn.rankings.compare(interaction, guildInfo);
|
const comparedRankings = await fn.rankings.compare(guildInfo);
|
||||||
|
|
||||||
const embed = fn.builders.comparisonEmbed(comparedRankings, guildInfo);
|
const embed = fn.builders.comparisonEmbed(comparedRankings, guildInfo);
|
||||||
await interaction.editReply(embed).catch(e => console.error(e));
|
await interaction.editReply(embed).then(async m => {
|
||||||
|
guildInfo.setCompareMessage(m.channel.id, m.id);
|
||||||
|
const query = guildInfo.queryBuilder("setCompareMessage");
|
||||||
|
await dbfn.setGuildInfo(query);
|
||||||
|
}).catch(e => console.error(e));
|
||||||
} else {
|
} else {
|
||||||
await interaction.editReply(fn.builders.errorEmbed(findMessagesResponse.status));
|
let errors = [];
|
||||||
}
|
errors.push(strings.error.noTreeMessage);
|
||||||
} else {
|
errors.push(strings.error.noLeaderboardMessage);
|
||||||
// Create a basic guildInfo with blank data
|
const embed = fn.builders.errorEmbed(`Unable to complete comparison, unable to find message(s):\n${errors.join("\n")}`);
|
||||||
let guildInfo = new GuildInfo()
|
await interaction.editReply(embed);
|
||||||
.setId(interaction.guildId)
|
|
||||||
.setTreeMessage("", interaction.channelId)
|
|
||||||
.setLeaderboardMessage("", interaction.channelId)
|
|
||||||
// Using the above guildInfo, try to find the Grow A Tree messages
|
|
||||||
const findMessagesResponse = await fn.messages.find(interaction, guildInfo);
|
|
||||||
guildInfo = findMessagesResponse.data;
|
|
||||||
if (findMessagesResponse.code == 1) {
|
|
||||||
// Build the string that shows the comparison // TODO Move the string building section to fn.builders?
|
|
||||||
const comparedRankings = await fn.rankings.compare(interaction, guildInfo);
|
|
||||||
const embed = fn.builders.comparisonEmbed(comparedRankings, guildInfo);
|
|
||||||
await interaction.editReply(embed).catch(e => console.error(e));
|
|
||||||
} else {
|
|
||||||
await interaction.editReply(fn.builders.errorEmbed(findMessagesResponse.status));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
interaction.editReply(fn.builders.errorEmbed(err)).catch(err => {
|
interaction.editReply(fn.builders.errorEmbed(err)).catch(err => {
|
||||||
|
30
slash-commands/permissions.js
Executable file
30
slash-commands/permissions.js
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
const { SlashCommandBuilder, PermissionsBitField, PermissionFlagsBits } = require('discord.js');
|
||||||
|
const fn = require('../modules/functions.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
data: new SlashCommandBuilder()
|
||||||
|
.setName('permissions')
|
||||||
|
.setDescription('Check my permissions here')
|
||||||
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||||
|
async execute(interaction) {
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
|
const me = interaction.guild.members.me;
|
||||||
|
const guildPerms = me.permissions;
|
||||||
|
const manageRoles = guildPerms.has(PermissionsBitField.Flags.ManageRoles);
|
||||||
|
const mentionEveryone = guildPerms.has(PermissionsBitField.Flags.MentionEveryone);
|
||||||
|
const channelPerms = me.permissionsIn(interaction.channel);
|
||||||
|
const viewChannel = channelPerms.has(PermissionsBitField.Flags.ViewChannel);
|
||||||
|
const sendMessages = channelPerms.has(PermissionsBitField.Flags.SendMessages);
|
||||||
|
const responseParts = [
|
||||||
|
`This is the status of my permissions in this server and this channel (<#${interaction.channel.id}>)`,
|
||||||
|
`**Guild Permissions**`,
|
||||||
|
`Manage Roles: ${manageRoles}`,
|
||||||
|
`Mention All Roles: ${mentionEveryone}`,
|
||||||
|
`**Channel Permissions**`,
|
||||||
|
`View Channel: ${viewChannel}`,
|
||||||
|
`Send Messages: ${sendMessages}`
|
||||||
|
];
|
||||||
|
const replyEmbed = fn.builders.embed(responseParts.join("\n"));
|
||||||
|
await interaction.editReply(replyEmbed);
|
||||||
|
}
|
||||||
|
};
|
@ -55,6 +55,11 @@ module.exports = {
|
|||||||
.setDescription("Message to send for fruit reminders")
|
.setDescription("Message to send for fruit reminders")
|
||||||
.setRequired(false)
|
.setRequired(false)
|
||||||
)
|
)
|
||||||
|
.addBooleanOption(o =>
|
||||||
|
o.setName('enabled')
|
||||||
|
.setDescription("Enable the relay?")
|
||||||
|
.setRequired(false)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.addSubcommand(sc =>
|
.addSubcommand(sc =>
|
||||||
sc.setName('disable')
|
sc.setName('disable')
|
||||||
@ -105,7 +110,7 @@ module.exports = {
|
|||||||
const reminderChannel = interaction.options.getChannel('pingchannel');
|
const reminderChannel = interaction.options.getChannel('pingchannel');
|
||||||
// Create a new GuildInfo object
|
// Create a new GuildInfo object
|
||||||
let guildInfo = new GuildInfo()
|
let guildInfo = new GuildInfo()
|
||||||
.setId(interaction.guildId)
|
.setIds(interaction.guildId, interaction.guild.ownerId)
|
||||||
// Set the reminder configuration
|
// Set the reminder configuration
|
||||||
.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
.setReminders(waterMessage, fruitMessage, reminderChannel.id, watchChannel.id, true);
|
||||||
// Update the guildInfos Collection
|
// Update the guildInfos Collection
|
||||||
@ -114,6 +119,8 @@ module.exports = {
|
|||||||
let query = guildInfo.queryBuilder("setReminders");
|
let query = guildInfo.queryBuilder("setReminders");
|
||||||
// Run the query
|
// Run the query
|
||||||
await dbfn.setGuildInfo(query);
|
await dbfn.setGuildInfo(query);
|
||||||
|
// Refresh the collection
|
||||||
|
await fn.collectionBuilders.guildInfos(interaction.client);
|
||||||
// Create a messageCollector on the watch channel
|
// Create a messageCollector on the watch channel
|
||||||
fn.collectors.create(interaction.client, guildInfo);
|
fn.collectors.create(interaction.client, guildInfo);
|
||||||
// Compose a reply
|
// Compose a reply
|
||||||
@ -135,21 +142,25 @@ module.exports = {
|
|||||||
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');
|
||||||
|
const inEnabled = interaction.options.getBoolean('enabled');
|
||||||
|
|
||||||
// Check if each option is set, if it is, use it. Otherwise use what was already set
|
// 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;
|
||||||
|
const outEnabled = inEnabled ? inEnabled : guildInfo.notificationsEnabled;
|
||||||
|
|
||||||
// Update the relay configuration
|
// Update the relay configuration
|
||||||
guildInfo.setReminders(outWaterMessage, outFruitMessage, outReminderChannelId, outWatchChannelId, true);
|
guildInfo.setReminders(outWaterMessage, outFruitMessage, outReminderChannelId, outWatchChannelId, outEnabled);
|
||||||
// Update the guildInfos Collection
|
// Update the guildInfos Collection
|
||||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||||
// Build a query to update the database
|
// Build a query to update the database
|
||||||
let query = guildInfo.queryBuilder("setReminders");
|
let query = guildInfo.queryBuilder("setReminders");
|
||||||
// Run the query
|
// Run the query
|
||||||
await dbfn.setGuildInfo(query);
|
await dbfn.setGuildInfo(query);
|
||||||
|
// Refresh the collection
|
||||||
|
await fn.collectionBuilders.guildInfos(interaction.client);
|
||||||
// Create a messageCollector on the watch channel
|
// Create a messageCollector on the watch channel
|
||||||
fn.collectors.create(interaction.client, guildInfo);
|
fn.collectors.create(interaction.client, guildInfo);
|
||||||
// Compose a reply
|
// Compose a reply
|
||||||
@ -173,6 +184,8 @@ module.exports = {
|
|||||||
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
interaction.client.guildInfos.set(interaction.guildId, guildInfo);
|
||||||
// Update the database
|
// 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));
|
||||||
|
// Refresh the collection
|
||||||
|
await fn.collectionBuilders.guildInfos(interaction.client);
|
||||||
// Close the collector
|
// Close the collector
|
||||||
await fn.collectors.end(interaction.client, guildInfo).catch(e => console.error(e));
|
await fn.collectors.end(interaction.client, guildInfo).catch(e => console.error(e));
|
||||||
// Reply confirming disabling of relay
|
// Reply confirming disabling of relay
|
@ -7,21 +7,7 @@ const { GuildInfo } = require('../modules/CustomClasses.js');
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
.setName('setup')
|
.setName('setup')
|
||||||
.setDescription('Attempt automatic configuration of the bot.')
|
.setDescription('Configure some feature settings.')
|
||||||
.addSubcommand(sc =>
|
|
||||||
sc.setName('compare')
|
|
||||||
.setDescription('Set up the channels to be used with /compare')
|
|
||||||
.addChannelOption(o =>
|
|
||||||
o.setName('treechannel')
|
|
||||||
.setDescription('What channel is your tree in?')
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addChannelOption(o =>
|
|
||||||
o.setName('leaderboardchannel')
|
|
||||||
.setDescription('What channel is your leaderboard in?')
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.addSubcommand(sc =>
|
.addSubcommand(sc =>
|
||||||
sc.setName('rolemenu')
|
sc.setName('rolemenu')
|
||||||
.setDescription('Setup the roles to be used with /rolemenu')
|
.setDescription('Setup the roles to be used with /rolemenu')
|
||||||
@ -53,30 +39,6 @@ module.exports = {
|
|||||||
await interaction.deferReply({ ephemeral: true });
|
await interaction.deferReply({ ephemeral: true });
|
||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
switch (subcommand) {
|
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);
|
|
||||||
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)
|
|
||||||
.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":
|
case "rolemenu":
|
||||||
let waterRoleId = interaction.options.getRole('waterrole').id;
|
let waterRoleId = interaction.options.getRole('waterrole').id;
|
||||||
let fruitRoleId = interaction.options.getRole('fruitrole') ? interaction.options.getRole('fruitrole').id : undefined;
|
let fruitRoleId = interaction.options.getRole('fruitrole') ? interaction.options.getRole('fruitrole').id : undefined;
|
||||||
@ -88,7 +50,7 @@ module.exports = {
|
|||||||
await interaction.editReply(fn.builders.embeds.treeRoleMenu(guildInfo)).catch(e => console.error(e));
|
await interaction.editReply(fn.builders.embeds.treeRoleMenu(guildInfo)).catch(e => console.error(e));
|
||||||
} else {
|
} else {
|
||||||
let guildInfo = new GuildInfo()
|
let guildInfo = new GuildInfo()
|
||||||
.setId(interaction.guildId);
|
.setIds(interaction.guildId, interaction.guild.ownerId);
|
||||||
guildInfo.setRoles(waterRoleId, fruitRoleId);
|
guildInfo.setRoles(waterRoleId, fruitRoleId);
|
||||||
await dbfn.setGuildInfo(guildInfo.queryBuilder("setRoles"));
|
await dbfn.setGuildInfo(guildInfo.queryBuilder("setRoles"));
|
||||||
await fn.collectionBuilders.guildInfos(interaction.client);
|
await fn.collectionBuilders.guildInfos(interaction.client);
|
||||||
|
@ -36,16 +36,10 @@ module.exports = {
|
|||||||
await interaction.deferReply({ ephemeral: private });
|
await interaction.deferReply({ ephemeral: private });
|
||||||
const inBeginHeight = interaction.options.getInteger('beginheight');
|
const inBeginHeight = interaction.options.getInteger('beginheight');
|
||||||
const endHeight = interaction.options.getInteger('endheight');
|
const endHeight = interaction.options.getInteger('endheight');
|
||||||
const efficiency = interaction.options.getInteger('efficiency');
|
const efficiency = interaction.options.getInteger('efficiency') != undefined ? interaction.options.getInteger('efficiency') : 10;
|
||||||
const quality = interaction.options.getInteger('quality');
|
const quality = interaction.options.getInteger('quality') != undefined ? interaction.options.getInteger('quality') : 5;
|
||||||
let beginHeight, replyContent;
|
let beginHeight, replyContent;
|
||||||
|
|
||||||
if ((efficiency && !quality) || (quality && !efficiency)) {
|
|
||||||
const reply = fn.builders.embed("You must include **both** efficiency *and* quality, I only received one.");
|
|
||||||
await interaction.editReply(reply).catch(e => console.error(e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inBeginHeight) {
|
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;
|
||||||
@ -53,14 +47,23 @@ module.exports = {
|
|||||||
beginHeight = inBeginHeight;
|
beginHeight = inBeginHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeString = await fn.timeToHeight(beginHeight, endHeight, efficiency, quality);
|
const timeToHeightResults = await fn.timeToHeight(beginHeight, endHeight, efficiency, quality);
|
||||||
|
const { time, totalWaterCount, compostAppliedCount, average, savedTime } = timeToHeightResults;
|
||||||
|
let replyFields = undefined;
|
||||||
if (efficiency && quality) {
|
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 ${timeString}`;
|
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 {
|
} else {
|
||||||
replyContent = `I estimate that a tree growing from ${beginHeight}ft to ${endHeight}ft will take ${timeString}`;
|
replyContent = `I estimate that a tree growing from ${beginHeight}ft to ${endHeight}ft will take ${time}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reply = fn.builders.embed(replyContent)
|
const reply = fn.builders.embeds.information(replyContent, replyFields)
|
||||||
await interaction.editReply(reply);
|
await interaction.editReply(reply);
|
||||||
}
|
}
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user