add article

This commit is contained in:
Raven Scott 2024-10-02 01:40:18 -04:00
parent ba61bbd438
commit 977917b2e7

View File

@ -0,0 +1,252 @@
<!-- lead -->
Enabling users to use My-MC.Link within any Channel on Discord.
The My-MC.Link Discord bot offers a comprehensive and user-friendly interface for Minecraft server management, enabling users to control server operations from any Discord server. This deep dive explores the technical aspects of the bot, including its architecture, command handling, token management, API integration, and the strategies employed to deliver seamless user interactions. By combining Discords API with the powerful features of the My-MC.Link service, this bot provides an extensive range of server functionalities in a highly accessible and easily deployable format.
### Project Structure and Key Dependencies
The bot leverages several essential libraries and APIs to deliver its functionality:
```javascript
import { Client, GatewayIntentBits, SlashCommandBuilder, REST, Routes, EmbedBuilder } from 'discord.js';
import jsonfile from 'jsonfile';
import MyMCLib from 'mymc-lib';
import unirest from 'unirest';
import { readFileSync } from 'fs';
import cmd from 'cmd-promise';
```
#### Breakdown of Key Dependencies:
- **Discord.js**: This is the backbone of the bot, providing classes like `Client`, `SlashCommandBuilder`, `REST`, and `EmbedBuilder`. These components enable interaction with Discord's API, handling everything from registering commands to managing user interactions and generating rich embeds for responses.
- **jsonfile**: This package manages reading and writing user-specific tokens in JSON format. Storing tokens in a file allows the bot to persist authentication information between sessions, making it unnecessary for users to re-authenticate repeatedly.
- **MyMCLib**: A custom library that acts as a wrapper around the My-MC.Link API, simplifying the process of interacting with the services various endpoints, such as starting or stopping servers, fetching logs, and managing mods.
- **unirest**: Used to make HTTP requests to the My-MC.Link API, specifically to handle token generation and validation.
- **cmd-promise**: A library that facilitates the execution of shell commands in a promise-based format, used for running server checks and other operational commands, ensuring that server status can be verified before certain actions are performed.
### Discord Client Initialization
The bot initializes the Discord client with a focus on `Guilds`, which makes it a server-centric bot that exclusively handles commands and interactions within Discord servers (as opposed to direct messages):
```javascript
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
```
By limiting the bots scope to `Guilds`, it ensures that the bot can manage interactions specific to Minecraft server administration in a controlled environment, reducing unnecessary overhead from other Discord intents.
### Token Management and API Authentication
#### Loading and Saving Tokens
One of the most critical aspects of the bots design is token management, which authenticates user interactions with the My-MC.Link API. The bot stores tokens in a JSON file (`tokens.json`) and retrieves or refreshes these tokens as necessary.
**Token Loading**:
```javascript
function loadTokens() {
try {
return jsonfile.readFileSync(tokensFile);
} catch (error) {
console.error('Error reading tokens file:', error);
return {};
}
}
```
The `loadTokens()` function reads the `tokens.json` file and returns an object containing user tokens. If the file cannot be read (e.g., it doesnt exist or has been corrupted), an empty object is returned, and the bot can request a new token.
**Token Saving**:
```javascript
function saveTokens(tokens) {
jsonfile.writeFileSync(tokensFile, tokens, { spaces: 2 });
}
```
The `saveTokens()` function writes the token data back to the `tokens.json` file, ensuring that any new or refreshed tokens are persisted for future use.
#### Automatic Token Retrieval
If a user doesnt have a valid token or their token has expired, the bot automatically requests a new one from the My-MC.Link service using the `fetchAndSaveToken()` function:
```javascript
async function fetchAndSaveToken(userId, interaction) {
return unirest
.post(config.endpoint.toString())
.headers({ 'Accept': 'application/json', 'Content-Type': 'application/json' })
.send({ "username": `mc_${userId}`, "password": config.password.toString()})
.then((tokenInfo) => {
const tokens = loadTokens();
tokens[userId] = tokenInfo.body.token; // Save the new token
saveTokens(tokens);
return tokenInfo.body.token;
})
.catch((error) => {
console.error('Error fetching token:', error);
sendSexyEmbed("Error", "An error occurred while fetching your API token.", interaction);
throw error;
});
}
```
This function performs several key steps:
1. It makes a POST request to the My-MC.Link API, sending the users credentials to request a new token.
2. On success, it loads the existing tokens, updates the token for the user, and saves the updated tokens to the `tokens.json` file.
3. If theres an error (e.g., the API is down or the request fails), it logs the error and provides feedback to the user via a rich embed.
#### Token Re-Validation and Re-Fetching
Once a token is stored, the bot checks its validity and, if necessary, automatically fetches a new token when making API calls:
```javascript
async function getToken(userId, interaction) {
const tokens = loadTokens();
if (!tokens[userId]) {
return await fetchAndSaveToken(userId, interaction);
}
return tokens[userId];
}
async function handleApiCall(apiCall, userId, interaction) {
try {
return await apiCall();
} catch (error) {
console.error('Token error, re-fetching token...');
await fetchAndSaveToken(userId, interaction);
return await apiCall();
}
}
```
Heres what happens:
1. **getToken**: This function checks if a token exists for the user in `tokens.json`. If no token is found, it calls `fetchAndSaveToken()` to retrieve and save a new one.
2. **handleApiCall**: Wraps any API call to handle invalid tokens by retrying the request after fetching a new token. If a token has expired or there is any issue with authentication, the bot fetches a fresh token and retries the request.
### Command Registration with Discord
The bot uses Discords `SlashCommandBuilder` to define a series of commands that allow users to interact with their Minecraft servers. These commands are registered with Discord using the REST API:
```javascript
const commands = [
new SlashCommandBuilder().setName('server-stats').setDescription('Get the server statistics'),
new SlashCommandBuilder().setName('server-log').setDescription('Get the server log'),
new SlashCommandBuilder().setName('start-server').setDescription('Start the Minecraft server'),
new SlashCommandBuilder().setName('stop-server').setDescription('Stop the Minecraft server'),
new SlashCommandBuilder().setName('restart-server').setDescription('Restart the Minecraft server'),
// Additional commands...
];
// Register commands with Discord
const rest = new REST({ version: '10' }).setToken(config.token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(config.clientId), { body: JSONCommands });
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
```
Each command is defined with a name and description using `SlashCommandBuilder`, which simplifies the process of adding new commands. These commands are then registered with Discord's API, ensuring they are available for use within the server.
### Handling User Interactions
When users invoke commands, the bot listens for interaction events and routes the request to the appropriate function based on the command name:
```javascript
client.on('interactionCreate', async interaction => {
if (!interaction.isCommand()) return;
const userId = interaction.user.id;
const apiToken = await getToken(userId, interaction);
const MyMC = new MyMCLib(apiToken);
switch (interaction.commandName) {
case 'server-stats':
const stats = await handleApiCall(() => MyMC.getStats(), userId, interaction);
handleResponse(stats, interaction);
break;
case 'start-server':
const startResult = await handleApiCall(() => MyMC.startServer(), userId, interaction);
handleResponse(startResult, interaction);
break;
case 'stop-server':
const stopResult = await handleApiCall(() => MyMC.stopServer(), userId, interaction);
handleResponse(stopResult, interaction);
break;
// Other commands...
}
});
```
Each command is mapped to an API call using the `MyMCLib` library. The bot interacts with the Minecraft server via authenticated requests, and responses are processed and displayed back to the user.
### Sending Responses and Embeds
One of the standout features of this bot is its use of rich embeds for displaying information to users. These embeds provide a visually appealing way to present data such as server statistics, logs, or mod information.
#### Simple Embeds
For single-field responses, the bot sends a simple embed with a title and description:
```javascript
function sendSexyEmbed(title, description, interaction, ephemeral = false) {
const embed = new EmbedBuilder()
.setColor("#3498DB")
.setTitle(title)
.setDescription(description)
.setTimestamp()
.setFooter({
text: `Requested by ${interaction.user.username}`,
iconURL: `${interaction.user.displayAvatarURL()}`
});
interaction
.reply({
embeds: [embed],
ephemeral: ephemeral
});
}
```
This function ensures that every response is styled with consistent colors, timestamps, and user information.
#### Complex Embeds with Multiple Fields
For more complex responses (such as server stats or mod lists), the bot generates an embed with multiple fields:
```javascript
function sendSexyEmbedWithFields(title, description, fields, interaction, ephemeral = false) {
const embed = new EmbedBuilder()
.setColor("#3498DB")
.setTitle(title)
.setDescription(description !== "N/A" ? description : undefined)
.addFields(fields)
.setTimestamp()
.setFooter({
text: `Requested by ${interaction.user.username}`,
iconURL: `${interaction.user.displayAvatarURL()}`
});
interaction.reply({
embeds: [embed],
ephemeral: ephemeral
});
}
```
This method allows the bot to handle more detailed responses, such as server resource usage, mod lists, and player data.
### Error Handling and Resilience
A critical aspect of the bots design is its resilience in the face of errors, particularly around token validation and API requests. The bot gracefully handles errors by attempting to fetch a new token and retrying the request. Additionally, the bot provides users with feedback through embeds when something goes wrong, keeping them informed without the need for manual intervention.
### Final Thoughts: A Comprehensive Minecraft Server Management Bot
The My-MC.Link Discord bot is a sophisticated and powerful tool for Minecraft server management, offering a seamless integration with Discord that enables users to perform server tasks through a familiar interface. Its use of modern technologies such as `discord.js`, `MyMCLib`, and JSON-based token management ensures that the bot is both scalable and user-friendly. The automatic token handling, rich embeds, and wide range of server management commands make the bot an invaluable tool for any My-MC.Link user.
From a technical standpoint, the bot demonstrates how effective integration of Discord with external services can lead to a highly functional and interactive user experience. With the ability to automate token management, handle complex API interactions, and deliver visually appealing feedback, this bot sets a high standard for user-installable applications within the Discord ecosystem.