Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the barebone code necessary to get a Discord bot running #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ cdk.out
cdk.context.json

# Configuration
**/*config.ts
**/*config.ts

# Exception for JS files under the discord-bot folder
!server-hosting/discord-bot/**/*.js
30 changes: 30 additions & 0 deletions server-hosting/discord-bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# How to setup a discord bot to start your satisfactory server remotely

# Requirements

You will need:

* A discord account
* A discord server that you manage (meaning you can add apps and bots to it)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have to be part of this MR, but it would be nice as a future feature to integrate the creation of the micro instance that manages the bot into the cdk code. Eventually we could have a config variable boolean that could control whether you want the discord bot to be provisioned and deployed along with the other services. (this is just a note, nothing actionable here)

* A machine with internet access (or whatever access needed to start your satisfactory server remotely) where your bot will be running
* NodeJS 16.6+ installed on that machine

# Stage 1 : Setup the discord app and the code

* Copy the code contained in this folder on your machine and install it by running `npm install`
* Get yourself a discord application with a bot and add the bot to your Discord server. I suggest you follow the very good instructions here: https://discordjs.guide/preparations/setting-up-a-bot-application.html
* Copy the important information (Bot Token, App Client Id and GuildId) in the `config.json` file
* Create your custom code to start your Satisfactory server in the `commands/ficsit.js` file (You will need to modify this file so it calls Lambda or any other service you wish to use to start your bot)

# Stage 2 : Deploy your code

* From the machine where your bot will run, deploy your commands with `node deploy-commands.js` (This registers the commands with your bot application so they can be used). This need to be done everytime you add a new command
* From the machine where your bot will run, start your bot with `node index.js` (This will login the bot in your server and he will be available to respond to commands and NEED to be running for your bot to stay online)
* Test out your commands : `/ping`, `/user` and `/server` are example commands included to test that your bot work with the most basic of commands.
* The `/ficsit` command is to be used to start your Satisfactory server.

# Optional : If you want to run your bot as a linux service
* Modify the `discord-bot.service` with your appropriate PATHS, USER and GROUP and copy it to `/etc/systemd/system/discord-bot.service`
* Add `#!/usr/bin/env node` as the very first line of the `index.js` file
* Make the `index.js` file executable by running `chmod +x index.js`
* Use `sudo systemctl daemon-reload` followed by `sudo systemctl enable discord-bot.service` followed by `sudo systemctl start discord-bot.service` to register/enable/start your discord bot service
15 changes: 15 additions & 0 deletions server-hosting/discord-bot/commands/ficsit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { SlashCommandBuilder } = require('@discordjs/builders');


module.exports = {
data: new SlashCommandBuilder()
.setName('ficsit')
.setDescription('Start the satisfactory server!'),
async execute(interaction) {
await interaction.reply("Sending message to start Satisfactory Server!");
///////// Your code to start the Satisfactory Server here ////////
Copy link
Owner

@feydan feydan Jan 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is in the repo that contains a way to start the server by hitting an http endpoint, I wonder if it would be good to have a config.ts setting for the lambda/api gateway url -- then this command could just hit that url with an http client to start the server. Maybe have it as an optional implementation if the config is set for the start url. What do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I'm using GCP I removed all of my GCP-related code such as the centralised GCP config (And also because I use PubSub as the trigger for my GCP Cloud Functions so that's extra layer I have to deal with) from this file as this would have been pretty useless in your case since your whole setup is on AWS.

But you are correct that config should be centralised for easier access.

And in your particular case and having this command hit the URL is probably the simplest way to make it work.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a small hiccup with this step. It's that the api url is generated after the resources have been deployed and the deployment does not affect local files. The deployment can output the api url once the deployment has completed and the user can then copy this value into the config file.

},
};



10 changes: 10 additions & 0 deletions server-hosting/discord-bot/commands/ping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { SlashCommandBuilder } = require('@discordjs/builders');

module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
await interaction.reply('Pong!');
},
};
8 changes: 8 additions & 0 deletions server-hosting/discord-bot/commands/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { SlashCommandBuilder } = require('@discordjs/builders');

module.exports = {
data: new SlashCommandBuilder().setName('server').setDescription('Replies with server info!'),
async execute(interaction) {
await interaction.reply(`Server name: ${interaction.guild.name}\nTotal members: ${interaction.guild.memberCount}`);
},
};
8 changes: 8 additions & 0 deletions server-hosting/discord-bot/commands/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { SlashCommandBuilder } = require('@discordjs/builders');

module.exports = {
data: new SlashCommandBuilder().setName('user').setDescription('Replies with user info!'),
async execute(interaction) {
await interaction.reply(`Your tag: ${interaction.user.tag}\nYour id: ${interaction.user.id}`);
},
};
5 changes: 5 additions & 0 deletions server-hosting/discord-bot/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"token": "Your Bot Token",
"clientId" : "Your App Oauth2 Client Id",
"guildId" : "Your Server (Guild) Id"
}
19 changes: 19 additions & 0 deletions server-hosting/discord-bot/deploy-commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const fs = require('fs');
const { SlashCommandBuilder } = require('@discordjs/builders');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const { clientId, guildId, token } = require('./config.json');

const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));

for (const file of commandFiles) {
const command = require(`./commands/${file}`);
commands.push(command.data.toJSON());
}

const rest = new REST({ version: '9' }).setToken(token);

rest.put(Routes.applicationGuildCommands(clientId,guildId), { body: commands })
.then(() => console.log('Successfully registered application commands.'))
.catch(console.error);
15 changes: 15 additions & 0 deletions server-hosting/discord-bot/discord-bot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[Unit]
Description=Discord Bot

[Service]
ExecStart=/path/to/folder/discord-bot/index.js
Restart=on-failure
StandardOutput=journal
User=YOUR_USER
Group=YOUR_GROUP
Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV=production
WorkingDirectory=/path/to/folder/discord-bot

[Install]
WantedBy=multi-user.target
6 changes: 6 additions & 0 deletions server-hosting/discord-bot/events/interactionCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
name: 'interactionCreate',
execute(interaction) {
console.log(`${interaction.user.tag} in #${interaction.channel.name} triggered an interaction.`);
},
};
7 changes: 7 additions & 0 deletions server-hosting/discord-bot/events/ready.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
name: 'ready',
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
},
};
46 changes: 46 additions & 0 deletions server-hosting/discord-bot/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Require the necessary discord.js classes
const fs = require('fs');
const { Client, Collection, Intents } = require('discord.js');
const { token } = require('./config.json');

// Create a new client instance
const client = new Client({ intents: [Intents.FLAGS.GUILDS] });

client.commands = new Collection();

const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
const eventFiles = fs.readdirSync('./events').filter(file => file.endsWith('.js'));

for (const file of commandFiles) {
const command = require(`./commands/${file}`);
// Set a new item in the Collection
// With the key as the command name and the value as the exported module
client.commands.set(command.data.name, command);
}

for (const file of eventFiles) {
const event = require(`./events/${file}`);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}

client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;

const command = client.commands.get(interaction.commandName);

if (!command) return;

try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});

// Login to Discord with your client's token
client.login(token);