Compare commits

..

No commits in common. "main" and "0.1.5" have entirely different histories.
main ... 0.1.5

18 changed files with 61 additions and 112 deletions

View file

@ -5,26 +5,27 @@ It posts the server name, password, time, and player count. Written in Node.js,
discord.js library to interact with Discord and fetches server stats via the XML feed
(accessible through the server's web interface). The update interval is configurable.
## Screenshots
<details>
<summary>Discord embed in english</summary>
![discord_en.png](misc%2Fimages%2Freadme%2Fdiscord_en.png)
![discord_en.png](misc%2Fimages%2Fdiscord_en.png)
</details>
<details>
<summary>Discord embed in german</summary>
![discord_de.png](misc%2Fimages%2Freadme%2Fdiscord_de.png)
![discord_de.png](misc%2Fimages%2Fdiscord_de.png)
</details>
<details>
<summary>Terminal output (NodeJS)</summary>
![bot_terminal.png](misc%2Fimages%2Freadme%2Fbot_terminal.png)
![bot_terminal.png](misc%2Fimages%2Fbot_terminal.png)
</details>
@ -72,27 +73,21 @@ discord.js library to interact with Discord and fetches server stats via the XML
1. Navigate to the root directory of the cloned repository.
2. Build and start the container:
```bash
docker compose up -d --build
docker-compose up -d --build
```
3. The bot should now be running and posting server stats to the specified Discord channel.
### Option 2: Run Without Docker (Using Node.js)
1. Navigate to the root directory of the cloned repository.
2. Install dependencies:
```bash
npm install
```
3. Start the bot:
```bash
npm start
```
4. The bot should now be running and posting server stats to the specified Discord channel.
- Note: Closing the terminal will stop the bot. Use a process manager like [PM2](https://pm2.io/) to keep it running.

View file

@ -1,8 +1,8 @@
{
"application": {
"serverPassword": "TypeMyServerPasswordHere",
"serverStatsUrl": "http://127.0.0.1:8080/feed/dedicated-server-stats.xml",
"serverMapUrl": "http://127.0.0.1:8080/feed/dedicated-server-stats-map.jpg",
"serverStatsUrl": "http://1.1.1.1:8080/feed/dedicated-server-stats.xml",
"serverMapUrl": "http://1.1.1.1:8080/feed/dedicated-server-stats-map.jpg",
"updateIntervalSeconds": 30
},
"discord": {

View file

@ -1,8 +1,8 @@
{
"application": {
"serverPassword": "TypeMyServerPasswordHere",
"serverStatsUrl": "http://127.0.0.1:8080/feed/dedicated-server-stats.xml",
"serverMapUrl": "http://127.0.0.1:8080/feed/dedicated-server-stats-map.jpg",
"serverStatsUrl": "http://1.1.1.1:8080/feed/dedicated-server-stats.xml",
"serverMapUrl": "http://1.1.1.1:8080/feed/dedicated-server-stats-map.jpg",
"updateIntervalSeconds": 30
},
"discord": {

View file

@ -1,3 +1,5 @@
version: "2"
services:
ls25bot:
build:
@ -23,13 +25,3 @@ services:
echo "Restarting ls25bot container at $(date)"
docker restart ls25bot
done
# This is used for development purposes only
webserver:
image: nginx:alpine
container_name: ls25bot-webserver
restart: always
volumes:
- ./misc/files:/usr/share/nginx/html
ports:
- "8080:80"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View file

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

View file

@ -1,6 +1,6 @@
{
"name": "ls25-discord-bot",
"version": "0.1.8",
"version": "0.1.5",
"description": "A simple discord bot for farming simulator 25",
"main": "source/Main.ts",
"scripts": {

View file

@ -2,7 +2,6 @@ import {Client, IntentsBitField} from 'discord.js';
import Configuration from "./Services/Configuration";
import Logging from "./Services/Logging";
import DiscordService from "./Services/DiscordEmbed";
import VersionChecker from './Services/VersionChecker';
// Create a new logger instance and configuration instance
const appLogger = Logging.getLogger();
@ -21,24 +20,6 @@ if(!appConfig.isConfigurationValid()) {
process.exit(1);
}
/**
* Check the version of the bot and log if it is up to date
*/
const versionChecker = new VersionChecker();
versionChecker.checkVersionIsUpdated().then((isUpToDate: boolean): void => {
if (!isUpToDate) {
appLogger.warn(`====================================================`);
appLogger.warn(`====================================================`);
appLogger.warn(`The bot is not up to date. Please update it soon.`);
appLogger.warn(`Use the command 'git pull && docker compose up -d --build' to update the bot.`);
appLogger.warn(`====================================================`);
appLogger.warn(`====================================================`);
} else {
appLogger.info(`The bot is up to date. No update needed.`);
}
});
/**
* Create a new discord client instance
*/

View file

@ -92,16 +92,6 @@ export default class DiscordEmbed {
return true;
}
/**
* Truncates a string at a given length
* @param text The input text to truncate
* @param maxLength The allowed characters until truncation
* @returns The truncated string
*/
private async truncateText(text: string, maxLength = 1024): Promise<string> {
return text.length > maxLength ? text.slice(0, maxLength - 3) + '...' : text;
}
/**
* Send server stats embed in a channel
* @param serverStats
@ -110,6 +100,8 @@ export default class DiscordEmbed {
let embed = new EmbedBuilder();
let config = this.appConfiguration;
serverStats.getServerMonth();
embed.setTitle(config.translation.discordEmbed.title);
if (!serverStats.isOnline()) {
embed.setColor(0xCA0000);
@ -123,8 +115,6 @@ export default class DiscordEmbed {
embed.setThumbnail(config.application.serverMapUrl);
let playerListString: string;
let playerListTitleString = `${config.translation.discordEmbed.titlePlayerCount} (${serverStats.getPlayerCount()??0}/${serverStats.getMaxPlayerCount()??0}):`;
if(serverStats.getPlayerList().length === 0) {
playerListString = config.translation.discordEmbed.noPlayersOnline;
} else {
@ -139,7 +129,7 @@ export default class DiscordEmbed {
let serverMods = serverStats.getServerMods();
let serverModsText = "-/-";
if(serverMods.length > 0) {
serverModsText = await this.truncateText(serverMods.map(mod => `${mod.name}`).join(', '));
serverModsText = serverMods.map(mod => `${mod.name}`).join(', ');
}
// @ts-ignore
@ -150,11 +140,12 @@ export default class DiscordEmbed {
{name: config.translation.discordEmbed.titleServerMap, value: serverStats.getServerMap()},
{name: config.translation.discordEmbed.titleServerMods, value: serverModsText},
{
name: playerListTitleString,
name: `${config.translation.discordEmbed.titlePlayerCount} (${serverStats.getPlayerCount()}/${serverStats.getMaxPlayerCount()}):`,
value: playerListString
},
);
}
this.appLogger.debug(embed);
return embed;
}
}

View file

@ -137,6 +137,37 @@ export default class ServerStatusFeed {
});
}
/**
* Returns the server month as a string
* @returns {string} The server month as a string
*/
public getServerMonth(): string {
let config: IConfiguration = Configuration.getConfiguration();
let dayTime = this.getServerStats()?.Server.dayTime;
if (dayTime === undefined) {
return "Error";
}
let month = dayTime / (60 * 60 * 1000 * 24);
month = month % 1;
month = month * 12;
month = Math.floor(month);
let months = [
config.translation.common.monthJanuary,
config.translation.common.monthFebruary,
config.translation.common.monthMarch,
config.translation.common.monthApril,
config.translation.common.monthMay,
config.translation.common.monthJune,
config.translation.common.monthJuly,
config.translation.common.monthAugust,
config.translation.common.monthSeptember,
config.translation.common.monthOctober,
config.translation.common.monthNovember,
config.translation.common.monthDecember
]
return months[month-1];
}
/**
* Returns the server time in the format HH:MM
* @returns {string} The server time in the format HH:MM
@ -156,23 +187,23 @@ export default class ServerStatusFeed {
if(minutesString.length === 1) {
minutesString = `0${minutesString}`;
}
return `${hoursString}:${minutesString}`;
return `${hoursString}:${minutesString} (${this.getServerMonth()})`;
}
/**
* Returns the server player count
* @returns {number | null | undefined} The server player count
* @returns {number} The server player count
*/
public getPlayerCount(): number | null | undefined {
return <number>this.getServerStats()?.Server?.Slots?.numUsed;
public getPlayerCount(): number {
return <number>this.getServerStats()?.Server.Slots.numUsed;
}
/**
* Returns the server player count
* @returns {number | null | undefined} The server player count
* @returns {number} The server player count
*/
public getMaxPlayerCount(): number | null | undefined {
return <number>this.getServerStats()?.Server?.Slots?.capacity;
public getMaxPlayerCount(): number {
return <number>this.getServerStats()?.Server.Slots.capacity;
}
/**

View file

@ -1,41 +0,0 @@
export default class VersionChecker {
private readonly localPackageVersion: string;
private readonly versionUrl: string = "https://raw.githubusercontent.com/cloudmaker97/FS25-Discord-Bot/refs/heads/main/package.json";
constructor() {
this.localPackageVersion = require('../../package.json').version;
}
/**
* Check if the version of the bot is up to date
*/
public async checkVersionIsUpdated(): Promise<boolean> {
const latestVersion = await this.getLatestReleasedVersion();
return this.isNewerVersion(latestVersion, this.localPackageVersion);
}
/**
* Get the latest released version of the bot from the github repository
*/
public async getLatestReleasedVersion(): Promise<string> {
const response = await fetch(this.versionUrl);
const latestPackage = await response.text();
const latestVersion = JSON.parse(latestPackage)?.version;
return latestVersion;
}
/**
* Check if the latest version is newer than the current version
*/
public isNewerVersion(latestVersion: string, currentVersion: string) {
const v1Parts: number[] = latestVersion.split('.').map(Number);
const v2Parts: number[] = currentVersion.split('.').map(Number);
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
const part1 = v1Parts[i] || 0;
const part2 = v2Parts[i] || 0;
if (part1 > part2) return false;
if (part1 < part2) return true;
}
return true;
}
}