Hosting Silvanus Yourself
This guide is a work in progress but a rough overview is below. Silvanus can be run on baremetal or within a Docker container. I use a Docker container for production and run on baremetal for development. Silvanus is written in JavaScript with the Node.js runtime and Discord.js library. Currently Silvanus uses D.js v14. Silvanus requires the use of a MySQL database - specifically MariaDB, however I don't think there are any features used that would prevent the use of a vanilla MySQL server.
Database Structure
Here is the table structure for the two tables required by Silvanus
guild_info
*guild_id | VARCHAR(50) | NOT NULL
owner_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
tree_name | VARCHAR(100) | NOT NULL DEFAULT "Unknown"
tree_height | FLOAT | NOT NULL DEFAULT 0.0
tree_message_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
tree_channel_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
leaderboard_message_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
leaderboard_channel_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
water_message | VARCHAR(1500) | NOT NULL DEFAULT "Not Set"
water_role_id | VARCHAR(50) | NOT NULL DEFAULT "Not Set"
fruit_message | VARCHAR(1500) | NOT NULL DEFAULT "Not Set"
fruit_role_id | VARCHAR(50) | NOT NULL DEFAULT "Not Set"
reminder_channel_id | VARCHAR(50) | NOT NULL DEFAULT "Not Set"
watch_channel_id | VARCHAR(50) | NOT NULL DEFAULT "Not Set"
notifications_enabled | VARCHAR(5) | NOT NULL DEFAULT "false"
compare_channel_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
compare_message_id | VARCHAR(50) | NOT NULL DEFAULT "Unknown"
* = PRIMARY KEY
leaderboard
*id | INT(10) | NOT NULL | AUTO_INCREMENT
guild_id | VARCHAR(50) | NOT NULL |
tree_name | VARCHAR(100) | NOT NULL |
tree_rank | INT(10) | NOT NULL |
tree_height | FLOAT | NOT NULL | DEFAULT 1
has_pin | TINYINT(1) | NOT NULL | DEFAULT 0
timestamp | DATETIME | | DEFAULT current_timestamp()
* = PRIMARY KEY
Baremetal
Running Silvanus on bare metal is preferred for testing development versions, but you can run the production version on bare metal too if desired.
Secrets
To avoid leaking secrets in the code and images, Silvanus uses environment variables and the dotenv
module to handle secrets like bot tokens and keys. When running on bare metal, simply create a file named .env
in the root of the project directory with the following structure:
TOKEN=
BOTID=
DEBUG=false
DBHOST=
DBPORT=
DBUSER=
DBPASS=
DBNAME=
STATUSCHANNELID=
ownerId=
Alternatively you can pass these variables from the command line if you're a masochist.
Running the Bot
$ git clone https://github.com/voidf1sh/silvanus
$ cd silvanus
$ npm i
$ node main.js
$ ???
$ profit
You can also use PM2
to manage the node process.
Docker
Running Silvanus in a Docker container is preferred when deployed to production use. This will allow for easy restarts if and when the bot crashes due to my lazy code. --restart unless-stopped
is your friend here.
Docker Compose
Create a folder to store the docker-compose.yml
and logs, for example ~/silvanus
:
$ mkdir -p ~/silvanus/logs
$ cd ~/silvanus
Create a docker-compose.yml
based on this template:
~/silvanus $ vim docker-compose.yml
version: "3"
services:
web:
image: v0idf1sh/silvanus
container_name: silvanus
restart: unless-stopped
environment:
TOKEN: "<YOUR_TOKEN_HERE>"
BOTID: "<YOUR_BOTS_USER_ID_HERE>"
DEBUG: "<true OR false>"
DBHOST: "<DATABASE_HOSTNAME_OR_IP>"
DBPORT: "<DATABASE_PORT>"
DBUSER: "<DATABASE_USERNAME>"
DBPASS: "<DATABASE_PASSWORD>"
DBNAME: "<DATABASE_NAME>"
STATUSCHANNELID: "<CHANNEL_ID>"
ownerId: "<YOUR_USER_ID>"
HEARTBEAT_URL: "<OPTIONAL_HEARTBEAT_URL_FOR_UPTIME_MONITORING>"
volumes:
- ./logs:/logs
~
Start the container:
~/silvanus $ sudo docker-compose up -d
Stop the container:
~/silvanus $ sudo docker-compose stop
~/silvanus $ sudo docker-compose down
Logs
By default logs are stored in the logs
folder where your docker-compose.yml
file is. This can be changed by modifying the volumes
entry in the Compose file. Unique logs are created on every startup, with a timestamp for a title. Silvanus does not implement any log rotation at the moment, so these may need to be periodically cleared out. Alternatively you could change how log files are generated in the Dockerfile
.
Table creation syntax
CREATE TABLE `guild_info` (
`guild_id` varchar(50) NOT NULL,
`owner_id` varchar(50) NOT NULL DEFAULT 'Unknown',
`tree_name` varchar(100) NOT NULL DEFAULT 'Unknown',
`tree_height` float NOT NULL DEFAULT 0,
`tree_message_id` varchar(50) NOT NULL DEFAULT 'Unknown',
`tree_channel_id` varchar(50) NOT NULL DEFAULT 'Unknown',
`leaderboard_message_id` varchar(50) NOT NULL DEFAULT 'Unknown',
`leaderboard_channel_id` varchar(50) NOT NULL DEFAULT 'Unknown',
`water_message` varchar(1500) DEFAULT 'Not Set',
`water_role_id` varchar(50) DEFAULT 'Not Set',
`fruit_message` varchar(1500) DEFAULT 'Not Set',
`fruit_role_id` varchar(50) DEFAULT 'Not Set',
`reminder_channel_id` varchar(50) DEFAULT 'Not Set',
`watch_channel_id` varchar(50) DEFAULT 'Not Set',
`notifications_enabled` varchar(5) DEFAULT 'false',
`compare_channel_id` varchar(50) DEFAULT 'Unknown',
`compare_message_id` varchar(50) DEFAULT 'Unknown',
PRIMARY KEY (`guild_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `leaderboard` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(50) NOT NULL,
`tree_name` varchar(100) NOT NULL,
`tree_rank` int(10) NOT NULL,
`tree_height` float NOT NULL DEFAULT 1,
`has_pin` tinyint(1) NOT NULL DEFAULT 0,
`timestamp` datetime DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;