7 Hosting Silvanus Yourself
Skylar Grant edited this page 2023-09-02 16:27:21 -07:00

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;