# What is WildBeast?

WildBeast is a wheels-attached Discord bot - meaning the heavy lifting is already done for you, making WildBeast easy to modify and extend to perfectly fit your use case.

This documentation focusses on self-hosting and extending WildBeast. If you don't feel like hosting your own instance, we host a publicly available version of WildBeast known as [WildBot](https://invite.thesharks.xyz/) which you can invite to your own server.

## Getting Started

Ready to get your hands dirty? Pick one of these guides to get started with WildBeast.

{% content-ref url="/pages/GZl1FGmj444CJYsd96je" %}
[Linux guides](/guides/linux-guides)
{% endcontent-ref %}

### Fundamentals: Dive a little deeper

Looking to write your own commands, or other custom features? Look below for our advanced guides.

{% content-ref url="/pages/UuVbQuGUkbb4hldF4G5h" %}
[Commands](/fundamentals/commands)
{% endcontent-ref %}

{% content-ref url="/pages/v8Bau9AiNO19svzlkLIs" %}
[Buttons](/fundamentals/commands/slash-commands/buttons)
{% endcontent-ref %}

{% content-ref url="/pages/eHMhOqB25WXL20vV1ypI" %}
[Broken mention](broken://pages/eHMhOqB25WXL20vV1ypI)
{% endcontent-ref %}

{% content-ref url="/pages/tkDXeFOZD8kBsxozH3yr" %}
[Broken mention](broken://pages/tkDXeFOZD8kBsxozH3yr)
{% endcontent-ref %}


# Linux guides


# Setup

Instructions to run WildBeast on Linux

## The basics

{% hint style="info" %}
**Good to know:** For the purpose of this guide, we'll use [Ubuntu 20.04](https://ubuntu.com).&#x20;

Any modern Linux distribution will work, however the commands described here only work on Debian based distributions.
{% endhint %}

{% hint style="warning" %}
For safety reasons, please **don't run WildBeast with the `root` account.**

If you don't know how to create a new user on Linux, please see [DigitalOcean's guide on making new sudo-enabled users](https://www.digitalocean.com/community/tutorials/how-to-create-a-new-sudo-enabled-user-on-ubuntu-20-04-quickstart)
{% endhint %}

We need a few prerequisites:

* A computer running any [supported version of Ubuntu](https://github.com/nodesource/distributions#debian-and-ubuntu-based-distributions)
  * You need `sudo` or `doas` privileges, or access to the `root` account
  * If you want to run WildBeast 24/7, you should get a [VPS](/extras/vps-recommendations)
* [Git](https://git-scm.com/download/linux)
* A text editor. We're going to use `nano`, but you can use anything you'd like.

## Installation

### Installing Node.js

1. Run the following code in your terminal:

   ```
   curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
   ```

   (Not a fan of `curl <url> | bash -`? You can do this [manually](https://github.com/nodesource/distributions#manual-installation) too.)
2. Install Node.js with the following command:

   ```
   sudo apt-get install -y nodejs
   ```

### Installing Postgres

Postgres is available to install by default on Ubuntu, just run the following code in your terminal:

```bash
sudo apt install postgresql postgresql-contrib
```

We need to do some extra steps to prepare Postgres for use:

1. Create a new user for WildBeast to use:

   ```bash
   sudo -u postgres createuser --interactive
   ```

   The new user does **not** need to be a superuser, and can be called whatever you want.
2. Finally, create a new database:

   ```bash
   sudo -u postgres createdb wildbeast
   ```

   (We used `wildbeast` here as the database user, change it if you used something else in the previous step)

## Setting up

### Getting the code

Clone the code and install the required modules:

```
git clone https://github.com/TheSharks/WildBeast.git
cd ./WildBeast
npm install
```

### Setting the options

Open the example configuration file in `nano` and enter your details:

```
nano .env.example
```

When you're done, **save the file as `.env`**

### **Starting the database**

Before we can start, we need to initialize the database. Run the following code:

```
npm run-script migrations:up
```

### **Testing it out!**

Now for the fun part, testing to see if it worked!

Start WildBeast for the first time with the following command:

```
npm start
```

You should see something similar to the following if everything went well

`19:11:41 [info] Gateway: Client ready`&#x20;

`19:11:41 [info] Gateway - shard 0: Gateway ready`

Test if your bot works by running the `/ping` command

![](/files/RKuNWjEMRMxGzf9bfWLf)

{% hint style="info" %}
Slash commands can take a while to appear.

Don't have your bot in your server yet? Check [this guide.](/extras/adding-your-bot-to-your-server)
{% endhint %}

### Next steps

* [Running as a service for autostart](/guides/linux-guides/running-as-a-service)


# Running as a service

Using systemd, run WildBeast as a service for automatic (re)starting

{% hint style="info" %}
**Good to know:** This guide uses `systemd`, we're assuming this is present on your system.

If you're using Ubuntu 20.04, you're already using `systemd`.
{% endhint %}

## Making a new service

Start creating a new service:

```
sudo nano /etc/systemd/system/wildbeast.service
```

Copy and paste the following example:

```ini
[Unit]
After=network.target network-online.target
Requires=network.target network-online.target
StartLimitBurst=3
StartLimitIntervalSec=0

[Service]
WorkingDirectory=/REPLACE THIS
Type=simple
Restart=on-failure
RestartSec=1
User=REPLACE THIS
ExecStart=/usr/bin/env npm start

[Install]
WantedBy=multi-user.target
```

{% hint style="warning" %}
Pay attention to the `REPLACE THIS`, as you might've guessed you need to replace these values.

* `WorkingDirectory` - The folder where you saved WildBeast

  For example: `/home/wildbeast/WildBeast`
* `User` - The user that's going to run WildBeast

  Do **NOT** use `root`, [create a new user](https://www.digitalocean.com/community/tutorials/how-to-create-a-new-sudo-enabled-user-on-ubuntu-20-04-quickstart) for safety.
  {% endhint %}

## Controlling the service

### Starting

```
sudo systemctl start wildbeast.service
```

### Stopping

```
sudo systemctl stop wildbeast.service
```

### Start on boot

```
sudo systemctl enable wildbeast.service
```

To undo:

```
sudo systemctl disable wildbeast.service
```

### Restarting

```
sudo systemctl restart wildbeast.service
```


# Commands

Write your own custom commands

{% hint style="danger" %}
Custom commands is an advanced feature and requires knowledge of programming.

**Support for doing this will not be provided in addition to what is listed on these pages.**
{% endhint %}

{% hint style="info" %}
Under the hood, WildBeast's command system is an extension of the system from [Detritus](https://github.com/detritusjs).

We won't go into some of the more advanced ways to control command flow that's included with Detritus' system, such as `onBeforeRun`, built-in ratelimiting, and permission checks.

For more info on this, please see the [Detritus Documentation](https://detritusjs.com/classes/interaction_command.interactioncommand), and the [Detritus Examples](https://github.com/detritusjs/client/tree/master/examples/ts/example-bots/commands/interactions/commands) instead.
{% endhint %}

There are 3 types of commands, slash commands and 2 types of context menu commands. Documentation for each type is provided below.

{% content-ref url="/pages/oYT7k9vIzboyOn17em9z" %}
[Slash commands](/fundamentals/commands/slash-commands)
{% endcontent-ref %}

{% content-ref url="/pages/72iKkkCwC5wAHOJculn5" %}
[Context menu actions](/fundamentals/commands/context-menu-actions)
{% endcontent-ref %}


# Slash commands

Slash commands are the primary way to interact with bots

![](/files/iIPgxnqaktaNAp8XZG6X)

## Creating commands

{% hint style="info" %}
**Reminder:** Check the [Detritus Documentation](https://detritusjs.com/classes/interaction_command.interactioncommand) for more advanced settings.
{% endhint %}

A finished command looks like this:

```typescript
import { Interaction } from 'detritus-client'
import { BaseSlashCommand } from '../base'

export default class PingCommand extends BaseSlashCommand {
  constructor () {
    super({
      description: 'Ping',
      name: 'ping'
    })
  }

  async run (context: Interaction.InteractionContext): Promise<void> {
    await context.editOrRespond(context, 'Pong!')
  }
}

```

There are a few things to note with regard to how commands are constructed:

* All commands are new classes that extend a base class, in this example the command is a plain slash command.
* The resulting command is exported as the default export.
* The constructor with a super call is used to set properties of the class instead of directly assigning them, this avoids incompatibilities with Detritus.

## In practice

When restarting WildBeast, your newly created command will automatically be registered as a global command, and will be available within a few hours.

Rather want a guild-based command instead of a global one? Add guild IDs to the constructor of your command:

```typescript
constructor () {
  super({
    description: 'Ping',
    name: 'ping',
    guildIds: ['110462143152803840']
  })
}
```

## What's next?

There are more things you can do with commands, like adding options, creating subcommands, and adding buttons and select menus.

{% content-ref url="/pages/01qlimUVFDJsFgeQj8uU" %}
[Subcommands](/fundamentals/commands/slash-commands/subcommands)
{% endcontent-ref %}

{% content-ref url="/pages/AeFUMVhrJ4rrZNnZwYHV" %}
[Options](/fundamentals/commands/slash-commands/options)
{% endcontent-ref %}

{% content-ref url="/pages/v8Bau9AiNO19svzlkLIs" %}
[Buttons](/fundamentals/commands/slash-commands/buttons)
{% endcontent-ref %}


# Subcommands

Nest multiple separate commands under one base command

![](/files/FTfRBt8zvbmu0iXfmHNT)

Subcommands are a great way to nest seperate commands under one base command, each subcommand is their own seperate command and can be called as such.

## In practise

{% hint style="info" %}
We're continuing off the example from the [Slash commands](/fundamentals/commands/slash-commands) page
{% endhint %}

```typescript
import { Interaction } from 'detritus-client'
import { BaseSlashCommand, BaseCommandOption } from '../base'

export default class PingCommand extends BaseSlashCommand {
  constructor () {
    super({
      name: 'ping',
      // With subcommands, the description doesn't matter,
      // since it doesn't get shown to end users.
      description: '',
      options: [
        new PongCommand()
      ]
    })
  }
  // The base command should not have a run(), it never gets called
}

export class PongCommand extends BaseCommandOption {
  constructor () {
    super({
      name: 'pong'
      description: 'Pong!'
    })
  }
  
  async run (context: Interaction.InteractionContext): Promise<void> {
    await context.editOrRespond(context, 'Pong!')
  }
}
```


# Options

Add selectable options to your commands

![](/files/5X1cFzBtgBBrvKe2En4L)

Options are the primary way to take user input for slash commands, they're type-checked on Discord's side, so type safety is guaranteed!

## In practise

```typescript
import { Interaction } from 'detritus-client'
import { ApplicationCommandOptionTypes } from 'detritus-client/lib/constants'

import { BaseSlashCommand } from '../base'

export interface CommandArgs {
  dice?: number
  sides?: number
}

export default class DiceCommand extends BaseSlashCommand {
  description = 'Roll some dice'
  name = 'dice'

  constructor () {
    super({
      options: [
        {
          type: ApplicationCommandOptionTypes.INTEGER,
          name: 'dice',
          description: 'The number of dice to roll (default: 1)',
          required: false
        },
        {
          type: ApplicationCommandOptionTypes.INTEGER,
          name: 'sides',
          description: 'The number of sides on the dice (default: 6)',
          required: false
        }
      ]
    })
  }

  async run (context: Interaction.InteractionContext, args: CommandArgs): Promise<void> {
    const { dice, sides } = args
    const diceCount = dice ?? 1
    const diceSides = sides ?? 6

    let total = 0
    for (let i = 0; i < diceCount; i++) {
      total += Math.floor(Math.random() * diceSides) + 1
    }

    await context.editOrRespond(`${context.user.username} rolled ${diceCount}d${diceSides} and got ${total}`)
  }
}
```


# Buttons

Add buttons for easy follow ups

![](/files/t2scgrGXJquUqQN4AXFx)

Buttons are a simple way to create follow ups **after** a slash command has finished.

## In practise

```typescript
import { Interaction } from 'detritus-client'
import { ComponentContext, Components, Embed } from 'detritus-client/lib/utils'
import fetch from 'node-fetch'

import { BaseSlashCommand } from '../base'

export default class RandomCatCommand extends BaseSlashCommand {
  name = 'cat'
  description = 'Sends a random cat image'

  async run (context: Interaction.InteractionContext | ComponentContext): Promise<void> {
    const components = new Components({
      timeout: 5 * (60 * 1000),
      onTimeout: async () => await context.editOrRespond({ components: [] })
    })
    components.addButton({
      emoji: '🔃',
      run: async (componentContext: ComponentContext) => {
        await this.run(componentContext)
      }
    })
    const { fact } = await (await fetch('https://catfact.ninja/fact')).json()
    const embed = new Embed()
      .setDescription(fact)
      .setImage(`https://cataas.com/cat?${context.interaction.id}`) // cache busting
      .setFooter('Powered by cataas.com')
    await context.editOrRespond({
      embed,
      components
    })
  }
}

```


# Context menu actions

Add commands to context menus shown on right-click for users and messages

There are 2 types of context menu actions, depending on what the end user is opening the context menu on.

{% tabs %}
{% tab title="User context" %}
![](/files/173iEpt1xFOlhOznIsVS)

```typescript
import { Interaction } from 'detritus-client'
import { MessageFlags } from 'detritus-client/lib/constants'

import { BaseContextMenuUserCommand, ContextMenuUserArgs } from '../../base'

export default class InformationCommand extends BaseContextMenuUserCommand {
  name = 'Avatar'

  async run (context: Interaction.InteractionContext, args: ContextMenuUserArgs): Promise<void> {
    await context.editOrRespond({
      embed: {
        description: `${args.user.mention}'s avatar`,
        image: {
          url: `${args.user.avatarUrl}?size=512`
        }
      },
      flags: MessageFlags.EPHEMERAL
    })
  }
}

```

{% endtab %}

{% tab title="Message context" %}
![](/files/0i8tPD5dREFxnR86vAj2)

```typescript
import { Interaction } from 'detritus-client'
import { MessageFlags } from 'detritus-client/lib/constants'

import { BaseContextMenuMessageCommand, ContextMenuMessageArgs } from '../../base'

export default class InformationCommand extends BaseContextMenuMessageCommand {
  name = 'Message ID'

  async run (context: Interaction.InteractionContext, args: ContextMenuMessageArgs): Promise<void> {
    await context.editOrRespond({
      embed: {
        description: `Message ID: ${args.message.id}`
      },
      flags: MessageFlags.EPHEMERAL
    })
  }
}

```

{% endtab %}
{% endtabs %}


# Jobs

Jobs are a simple way to run code asynchronously on a set schedule, or whenever you need to.

## Basic setup

A job either extends the `Job` class, or the `ScheduledJob` class, a scheduled job will run on a set time schedule and a normal job runs when called

Jobs are indexed and required based on their name either in context of the client, or in context of the cluster, a cluster job is called only once and a client job is called for each client that the cluster holds.&#x20;

Cluster jobs should be named `job-name.cluster.ts`\
Client jobs should be named `job-name.client.ts`

## In practise

Scheduled jobs work like this:

```typescript
import { ScheduledJob } from './base'

const job = new ScheduledJob('a_scheduled_job', 1000, async () => {
  // do some work every second
})

job.start()

export default job
```

While regular jobs work like this:

```typescript
import { Job } from './base'

const job = new Job('a_job', async () => {
  // do some work
})

export default job

// elsewhere in the code:
import { jobs } from '../cache'
const job = jobs.get('a_job')

job?.run()
```


# VPS recommendations

Need to run WildBeast 24/7? Get a cheap VPS.

Need to run WildBeast 24/7? Get a cheap VPS. We recommend any of the below.

### [Hetzner](https://hetzner.cloud/?ref=TrwA0pf5HM06)

Cheap Finland and Germany-based VPSes.

### [DigitalOcean](https://m.do.co/c/3ead1675d3af)

The gold standard. Locations available worldwide.

### [Scaleway](https://scaleway.com)

Incredibly cheap but powerful VPSes, owned by online.net, based in France and The Netherlands.

### [Xenyth](https://xenyth.net/)

A hosting solution made by Discord bot developers, aimed at a lower price range, starting from $2.49.

### [OVH](https://ovh.com)

Cheap VPSes, used by many people. France and Canadian locations available.

### [Linode](https://linode.com)

US-based VPS provider, locations available worldwide.

### [Vultr](https://www.vultr.com/?ref=8379976)

US-based VPS provider, locations available worldwide.


# Creating a bot account

This page will describe, in detail, how to obtain a token for your bot.

1. Navigate to your [Applications Page](https://discord.com/developers/applications) (you might need to login for this)
2. Click "New Application" on the top right of the page

   ![](/files/EfiK1JquyC388vSmVfah)
3. Think of a name for your bot, and click "Create"

   ![](/files/XNqxT79hmhgw1aaNlX1C)
4. Navigate to the "Bot" tab

   ![](/files/GjgQXLJydjBnhSh5e8y7)
5. Click "Add Bot" on the left of the page

   ![](/files/71clX9I6z0oV4ljyUU3l)
6. Click "Yes, do it!"

   ![](/files/Iw8xb6o5vrBmxVK2zaXd)
7. Optionally, set an avatar and change the username, and click "Save".
8. Select the "Copy" button to copy your bot's token.

   ![](/files/ry1UuL33POr7bzn3AvBW)


# Adding your bot to your server

This page will show you how to invite your bot to your servers.

{% hint style="info" %}
Don’t have a bot account yet? Check out [this page](/extras/creating-a-bot-account) for instructions how to make one.
{% endhint %}

1. Once you have created a bot account, navigate to the OAuth2 page of your application![](/files/EbvEH3T2TROEMKD7kVa4)
2. Scroll down to the OAuth2 URL Generator and check the "applications.commands" and "bot" scopes

   ![](/files/15GANYHvXVpSD8hZaGtX)
3. Copy the link it generates and open it in a new tab, choosing what server you want to add it to, and hit "Authorize"

   ![](/files/twxsVTIFTbbhJDbIRZOf)

{% hint style="warning" %}
Don’t see the server you want to add the bot to? You need to have the ”Manage Server” permission in the server for it to show up.
{% endhint %}


