Compare commits

...

42 commits
0.1.1 ... main

Author SHA1 Message Date
ed492e9045
Use newer docker compose command
Some checks failed
Docker Build / build-and-push (push) Has been cancelled
2025-10-31 23:54:01 +01:00
24f65956ab Local development with webserver and demo files
Some checks failed
Docker Build / build-and-push (push) Has been cancelled
2025-07-10 17:25:29 +02:00
4e6668c2e5 Increased bot version to 0.1.8 2024-12-12 12:39:05 +01:00
696e8f1749 Added truncate for servers with long list of mods (discord max length 1024 chars) and feed parse optimization 2024-12-12 12:37:32 +01:00
a28796751a Updated the README and uploaded the social banner for this repository (Affinity Photo) 2024-12-11 03:17:38 +00:00
58451f6fd0
Removed the header image of the repository 2024-12-11 04:13:16 +01:00
87a3cb158c
Titelbild aktualisiert 2024-12-11 04:12:14 +01:00
9337105ca7
Update README.md 2024-12-11 04:01:34 +01:00
1e35792419 Changed the image names for correct language association 2024-12-08 02:44:47 +01:00
bcc98514e6
Merge pull request #15 from cloudmaker97/change-time-display 2024-12-08 02:32:00 +01:00
885461f316 Changed version to 0.1.7 (temporarily bugfix) 2024-12-08 02:29:52 +01:00
f4a872546a Removed the display of the game server month due to incorrect values, this must me reimplemented when more details about this is available #2 2024-12-08 02:29:17 +01:00
9fa2db35be
Merge pull request #14 from cloudmaker97/update-info 2024-12-01 12:35:30 +01:00
c2380609b2 Implemented version checking for the discord bot and increased version to 0.1.6 2024-12-01 12:34:50 +01:00
174f13a999
Merge pull request #13 from cloudmaker97/6-after-some-time-the-feed-isnt-in-sync-with-the-discord-embed 2024-12-01 12:11:19 +01:00
4b1c558d32 Increased version to 0.1.5 2024-12-01 12:10:43 +01:00
4d97478266
Merge pull request #12 from cloudmaker97/6-after-some-time-the-feed-isnt-in-sync-with-the-discord-embed 2024-12-01 12:06:55 +01:00
6bf6280a70 Made the discord service starter async for the node engine and added regularly restarts within docker 2024-12-01 12:06:19 +01:00
e2da4df733
Merge pull request #11 from cloudmaker97/10-bot-wont-start-cannot-read-properties-of-undefined-reading-mod 2024-11-28 09:51:39 +01:00
1dc67110b6 Switched the package manager from npm to pnpm for faster dependency resolving 2024-11-28 09:50:31 +01:00
c672838f1d Receiving of mods may fail, so return then an empty array as fallback 2024-11-28 09:44:31 +01:00
bdb80dbc4e
Merge pull request #9 from cloudmaker97/documentation/readme 2024-11-26 13:21:47 +01:00
b56c177d18 Rewrite of the README, outsourced the settings in SETTINGS.md 2024-11-26 13:20:51 +01:00
d3061d4178
Merge pull request #8 from cloudmaker97/feature/docker-registry 2024-11-26 13:07:34 +01:00
a65a00614d Changed package name to match the repository url 2024-11-26 13:02:57 +01:00
683362a18c Changed branch to main 2024-11-26 12:57:30 +01:00
bed9a719e3
Merge pull request #7 from cloudmaker97/feature/docker-registry 2024-11-26 12:56:55 +01:00
e9c0d47588 Changed build configuration 2024-11-26 12:55:38 +01:00
502ce76271 Added docker-build.yml for pushing to ghcr.io 2024-11-26 12:53:44 +01:00
cf82562dfa Changed README for expandable screenshots (in different languages) 2024-11-26 10:53:36 +01:00
32aae35ed3 Added demo configuration for en/ger language 2024-11-26 10:44:34 +01:00
df2e35df0f Set version on 0.1.3 2024-11-26 10:36:26 +01:00
7ded2598b5 Logging with timestamp, obtaining more details for #6 2024-11-26 10:35:56 +01:00
50d463ac3b Storage size of docker image minimized 2024-11-26 10:07:23 +01:00
4689de586e Changed version to 0.1.2 2024-11-26 01:15:58 +01:00
5081cebf73 Updated discord embed image 2024-11-26 01:07:09 +01:00
ae1413db60 Showing server mods in discord embed #5 2024-11-26 01:05:36 +01:00
e4f485217f Added configuration description for #2 2024-11-26 01:02:29 +01:00
c8c0e1018a Added current game month in discord embed #2 2024-11-26 00:48:02 +01:00
ef8deb53b5 Added validation for existence of fields #3 2024-11-26 00:09:50 +01:00
2e5dc546d7 Show no server password when none is given 2024-11-26 00:01:52 +01:00
883406c3de Added validation for user configuration #3 2024-11-25 23:58:44 +01:00
31 changed files with 1114 additions and 922 deletions

44
.github/workflows/docker-build.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Docker Build
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View file

@ -5,7 +5,12 @@ LABEL authors="Dennis Heinrich"
# Copy the source files
WORKDIR /app
COPY . /app
RUN npm install
RUN npm install pnpm -g
RUN pnpm install
RUN pnpm run build
CMD ["npm", "start"]
ENTRYPOINT ["npm", "start"]
## Simplyfy the rm commands
RUN rm -rf .ddev/ source/ misc/ .git .gitignore config.example-en.json Dockerfile docker-compose.yml README.md
CMD ["npm", "run", "start-only"]
ENTRYPOINT ["npm", "run", "start-only"]

136
README.md
View file

@ -1,74 +1,98 @@
# Farming Simulator 25 - Discord Bot
This bot will post the server stats of a Farming Simulator 25 server to a Discord channel. The bot will post the server
name, server password, server time, and the player count. The bot will update the server stats every x seconds (
configurable).
This bot periodically updates a Discord channel with stats from a Farming Simulator 25 server.
It posts the server name, password, time, and player count. Written in Node.js, it uses the
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.
![discord_embed.png](misc%2Fimages%2Fdiscord_embed.png)
## Screenshots
The bot is written in Node.js and uses the [discord.js](https://discord.js.org/) library to interact with the Discord
API. It
uses the XML-Feed from the Farming Simulator 25 server to get the server stats (you can find the feed URL in the web
interface of the server).
<details>
<summary>Discord embed in english</summary>
![bot_terminal.png](misc%2Fimages%2Fbot_terminal.png)
![discord_en.png](misc%2Fimages%2Freadme%2Fdiscord_en.png)
# Requirements
</details>
- Node.js - if you want to run the bot without Docker
- NPM - if you want to run the bot without Docker
- Docker (optional) - if you want to run the bot in a Docker container
<details>
<summary>Discord embed in german</summary>
## Install the bot
![discord_de.png](misc%2Fimages%2Freadme%2Fdiscord_de.png)
## Create a Discord bot
</details>
1. Open the [Discord Developer Portal](https://discord.com/developers/applications)
2. Click on `New Application`
3. Name your application
4. Click on `Bot` in the left menu
5. Click on `Copy` to copy the bot token
6. Click on `Installation` in the left menu
7. Add the Permission 'Administrator' to the bot guild installation (bottom of the page)
8. Click on `Copy` to copy the URL to install the bot to a guild
9. Your installation link url should look like this: `https://discord.com/oauth2/authorize?client_id=CLIENT_ID`
<details>
<summary>Terminal output (NodeJS)</summary>
## Configure the bot
![bot_terminal.png](misc%2Fimages%2Freadme%2Fbot_terminal.png)
</details>
## Requirements
- **Node.js**: Required if you want to run the bot without Docker.
- **NPM**: Required if you want to run the bot without Docker.
- **Docker (optional)**: Use Docker if you prefer running the bot in a containerized environment.
---
## Installation Guide
### Step 1: Create a Discord Bot
1. Open the [Discord Developer Portal](https://discord.com/developers/applications).
2. Click on `New Application` and give your application a name.
3. Navigate to the `Bot` section in the left menu and click on `Add Bot`.
4. Copy the bot token by clicking `Copy` (you'll need this later).
5. Go to the `OAuth2` > `URL Generator` section in the left menu.
6. Under "Scopes," select `bot`, and under "Bot Permissions," select `Administrator`.
7. Copy the generated URL to invite the bot to your Discord server.
- The URL should look like this:
`https://discord.com/oauth2/authorize?client_id=CLIENT_ID&scope=bot&permissions=8`
---
### Step 2: Configure the Bot
1. Clone the repository to your server
2. Move the `config.example.json` to `config.json`
3. Fill in the required fields in the `config.json` file, do not delete fields, just leave them empty for default values
4. All fields with a `(*)` are required fields for the bot to work. The other fields are optional and can be left by
default
2. Locate the configuration files:
- Use either
- `config.example-de.json` (for German)
- `config.example-en.json` (for English)
- Rename the chosen file to `config.json`.
3. Open `config.json` and fill in the required fields:
- Refer to `SETTINGS.md` for detailed descriptions of each field.
- Fields marked with `(*)` are important to check; other fields can be left empty for default values.
| **- Key -** | **- Description -** |
|----------------------------------------------|---------------------------------------------------------------------------|
| (*) application.serverPassword | The password to join the server (or leave empty) |
| (*) application.serverStatsUrl | The feed URL to the server stats (from the web interface from the server) |
| (*) application.serverMapUrl | The feed URL to the server map (from the web interface from the server) |
| (*) application.updateIntervalSeconds | The interval in seconds to update the server stats |
| (*) discord.guildId | The guild id where the bot should be installed |
| (*) discord.channelId | The channel id where the bot should post the server stats |
| (*) discord.botToken | The bot token from the Discord Developer Portal |
| translation.discordEmbed.title | The title of the Discord embed |
| translation.discordEmbed.descriptionOnline | The description when the server is online |
| translation.discordEmbed.descriptionOffline | The description when the server is offline |
| translation.discordEmbed.descriptionUnknown | The description when the server status is unknown |
| translation.discordEmbed.titleServerName | The title of the server name |
| translation.discordEmbed.titleServerPassword | The title of the server password |
| translation.discordEmbed.titleServerTime | The title of the server time |
| translation.discordEmbed.titlePlayerCount | The title of the player count |
| translation.discordEmbed.noPlayersOnline | The message when no players are online |
---
## Run inside a Docker container
## Running the Bot
1. Run `docker-compose up -d --build` in the root directory of the repository
2. The bot should be running now and posting the server stats to the Discord channel
### Option 1: Run Inside a Docker Container (Recommended)
### Run without Docker (Node.js)
1. Navigate to the root directory of the cloned repository.
2. Build and start the container:
1. Clone the repository to your server
2. Run `npm install`
3. Run `npm start`
4. The bot should be running now and posting the server stats to the Discord channel
5. If you close the terminal, the bot will stop running
```bash
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.

36
SETTINGS.md Normal file
View file

@ -0,0 +1,36 @@
# Settings and configuration
These are the settings that can be configured in the `config.json` file. The file is located in the root directory of the project. All
fields marked with `(*)` are required to be checked, or leave empty for default values.
| **- Key -** | **- Description -** |
|----------------------------------------------|---------------------------------------------------------------------------|
| (*) application.serverPassword | The password to join the server (or leave empty) |
| (*) application.serverStatsUrl | The feed URL to the server stats (from the web interface from the server) |
| (*) application.serverMapUrl | The feed URL to the server map (from the web interface from the server) |
| (*) application.updateIntervalSeconds | The interval in seconds to update the server stats |
| (*) discord.channelId | The channel id where the bot should post the server stats |
| (*) discord.botToken | The bot token from the Discord Developer Portal |
| translation.discordEmbed.title | The title of the Discord embed |
| translation.discordEmbed.descriptionOnline | The description when the server is online |
| translation.discordEmbed.descriptionOffline | The description when the server is offline |
| translation.discordEmbed.descriptionUnknown | The description when the server status is unknown |
| translation.discordEmbed.titleServerName | The title of the server name |
| translation.discordEmbed.titleServerPassword | The title of the server password |
| translation.discordEmbed.titleServerTime | The title of the server time |
| translation.discordEmbed.titlePlayerCount | The title of the player count |
| translation.discordEmbed.noPlayersOnline | The message when no players are online |
| translation.discordEmbed.titleServerMap | The title of the server map |
| translation.discordEmbed.titleServerMods | The title of the server mods |
| translation.common.monthJanuary | The month January in the language of the server |
| translation.common.monthFebruary | The month February in the language of the server |
| translation.common.monthMarch | The month March in the language of the server |
| translation.common.monthApril | The month April in the language of the server |
| translation.common.monthMay | The month May in the language of the server |
| translation.common.monthJune | The month June in the language of the server |
| translation.common.monthJuly | The month July in the language of the server |
| translation.common.monthAugust | The month August in the language of the server |
| translation.common.monthSeptember | The month September in the language of the server |
| translation.common.monthOctober | The month October in the language of the server |
| translation.common.monthNovember | The month November in the language of the server |
| translation.common.monthDecember | The month December in the language of the server |

41
config.example-de.json Normal file
View file

@ -0,0 +1,41 @@
{
"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",
"updateIntervalSeconds": 30
},
"discord": {
"channelId": "DiscordChannelId_12345",
"botToken": "DiscordSecretBotToken_XYZ"
},
"translation": {
"discordEmbed": {
"title": "LS25 Server Status",
"descriptionOnline": "Der Server ist online",
"descriptionOffline": "Der Server ist offline",
"descriptionUnknown": "Serverdaten werden abgerufen",
"titleServerName": "Server-Name:",
"titleServerMap": "Server-Karte:",
"titleServerMods": "Server-Mods:",
"titleServerPassword": "Server-Passwort:",
"titleServerTime": "Server-Zeit:",
"titlePlayerCount": "Spieler online:",
"noPlayersOnline": "Keine Spieler online"
},
"common": {
"monthJanuary": "Januar",
"monthFebruary": "Februar",
"monthMarch": "März",
"monthApril": "April",
"monthMay": "Mai",
"monthJune": "Juni",
"monthJuly": "Juli",
"monthAugust": "August",
"monthSeptember": "September",
"monthOctober": "Oktober",
"monthNovember": "November",
"monthDecember": "Dezember"
}
}
}

View file

@ -1,12 +1,11 @@
{
"application": {
"serverPassword": "TypeMyServerPasswordHere",
"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",
"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",
"updateIntervalSeconds": 30
},
"discord": {
"guildId": "DiscordServerId_12345",
"channelId": "DiscordChannelId_12345",
"botToken": "DiscordSecretBotToken_XYZ"
},
@ -17,10 +16,26 @@
"descriptionOffline": "Server is offline",
"descriptionUnknown": "Server status fetching",
"titleServerName": "Server name",
"titleServerMap": "Server map",
"titleServerMods": "Server mods",
"titleServerPassword": "Server password",
"titleServerTime": "Server time",
"titlePlayerCount": "Players online",
"noPlayersOnline": "No players online"
},
"common": {
"monthJanuary": "January",
"monthFebruary": "February",
"monthMarch": "March",
"monthApril": "April",
"monthMay": "May",
"monthJune": "June",
"monthJuly": "July",
"monthAugust": "August",
"monthSeptember": "September",
"monthOctober": "October",
"monthNovember": "November",
"monthDecember": "December"
}
}
}

View file

@ -1,5 +1,3 @@
version: "2"
services:
ls25bot:
build:
@ -9,3 +7,29 @@ services:
restart: always
volumes:
- ./config.json:/app/config.json
restart:
image: docker:cli
restart: unless-stopped
container_name: ls25bot-restart
volumes:
- /var/run/docker.sock:/var/run/docker.sock
entrypoint: [ "/bin/sh","-c" ]
command:
- |
echo "Restarting ls25bot container is running"
while true; do
sleep 7200 # Each 2 hours
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.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

839
package-lock.json generated
View file

@ -1,839 +0,0 @@
{
"name": "ls25discord",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ls25discord",
"version": "0.1.0",
"license": "proprietary",
"dependencies": {
"discord.js": "^14.16.3",
"fast-xml-parser": "^4.5.0",
"winston": "^3.17.0"
},
"devDependencies": {
"@types/node": "^22.9.0",
"json-schema-to-typescript": "^15.0.3",
"typescript": "^5.6.3"
}
},
"node_modules/@apidevtools/json-schema-ref-parser": {
"version": "11.7.2",
"resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz",
"integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jsdevtools/ono": "^7.1.3",
"@types/json-schema": "^7.0.15",
"js-yaml": "^4.1.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/philsturgeon"
}
},
"node_modules/@colors/colors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
"license": "MIT",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"license": "MIT",
"dependencies": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
"kuler": "^2.0.0"
}
},
"node_modules/@discordjs/builders": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.9.0.tgz",
"integrity": "sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/formatters": "^0.5.0",
"@discordjs/util": "^1.1.1",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "0.37.97",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/builders/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.5.0.tgz",
"integrity": "sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "0.37.97"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/formatters/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/rest": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz",
"integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "0.37.97",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz",
"integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.3.0",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "0.37.83",
"tslib": "^2.6.2",
"ws": "^8.16.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/discord-api-types": {
"version": "0.37.83",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz",
"integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==",
"license": "MIT"
},
"node_modules/@jsdevtools/ono": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
"integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
"dev": true,
"license": "MIT"
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
"integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
"integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v16"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz",
"integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.8"
}
},
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
"license": "MIT"
},
"node_modules/@types/ws": {
"version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
"integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0"
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"license": "MIT"
},
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
"license": "MIT",
"dependencies": {
"color-convert": "^1.9.3",
"color-string": "^1.6.0"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"license": "MIT",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"license": "MIT"
},
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"license": "MIT",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
"license": "MIT",
"dependencies": {
"color": "^3.1.3",
"text-hex": "1.0.x"
}
},
"node_modules/discord-api-types": {
"version": "0.37.100",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.100.tgz",
"integrity": "sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==",
"license": "MIT"
},
"node_modules/discord.js": {
"version": "14.16.3",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.16.3.tgz",
"integrity": "sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/builders": "^1.9.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.5.0",
"@discordjs/rest": "^2.4.0",
"@discordjs/util": "^1.1.1",
"@discordjs/ws": "1.1.1",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "0.37.100",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-xml-parser": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz",
"integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
},
{
"type": "paypal",
"url": "https://paypal.me/naturalintelligence"
}
],
"license": "MIT",
"dependencies": {
"strnum": "^1.0.5"
},
"bin": {
"fxparser": "src/cli/cli.js"
}
},
"node_modules/fdir": {
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz",
"integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
"license": "MIT"
},
"node_modules/fn.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
"license": "MIT"
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT"
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-schema-to-typescript": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-15.0.3.tgz",
"integrity": "sha512-iOKdzTUWEVM4nlxpFudFsWyUiu/Jakkga4OZPEt7CGoSEsAsUgdOZqR6pcgx2STBek9Gm4hcarJpXSzIvZ/hKA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^11.5.5",
"@types/json-schema": "^7.0.15",
"@types/lodash": "^4.17.7",
"is-glob": "^4.0.3",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"minimist": "^1.2.8",
"prettier": "^3.2.5",
"tinyglobby": "^0.2.9"
},
"bin": {
"json2ts": "dist/src/cli.js"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"license": "MIT"
},
"node_modules/logform": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
"integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
"license": "MIT",
"dependencies": {
"@colors/colors": "1.6.0",
"@types/triple-beam": "^1.3.2",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/magic-bytes.js": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==",
"license": "MIT"
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/one-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
"license": "MIT",
"dependencies": {
"fn.name": "1.x.x"
}
},
"node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prettier": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safe-stable-stringify": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/strnum": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==",
"license": "MIT"
},
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz",
"integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.2",
"picomatch": "^4.0.2"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/triple-beam": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
"license": "MIT",
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/ts-mixer": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
"integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/winston": {
"version": "3.17.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz",
"integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
"license": "MIT",
"dependencies": {
"@colors/colors": "^1.6.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
"logform": "^2.7.0",
"one-time": "^1.0.0",
"readable-stream": "^3.4.0",
"safe-stable-stringify": "^2.3.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.9.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/winston-transport": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz",
"integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
"license": "MIT",
"dependencies": {
"logform": "^2.7.0",
"readable-stream": "^3.6.2",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

View file

@ -1,10 +1,11 @@
{
"name": "ls25discord",
"version": "0.1.1",
"name": "ls25-discord-bot",
"version": "0.1.8",
"description": "A simple discord bot for farming simulator 25",
"main": "source/Main.ts",
"scripts": {
"start": "npx tsc && node build/Main.js",
"start-only": "node build/Main.js",
"build": "npx tsc",
"schema": "npx json2ts -i source/Schema/ServerStats.json -o ./source/Schema/ServerStats.d.ts --unreachableDefinitions",
"test": "echo \"Error: no test specified\" && exit 1"

580
pnpm-lock.yaml Normal file
View file

@ -0,0 +1,580 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
discord.js:
specifier: ^14.16.3
version: 14.16.3
fast-xml-parser:
specifier: ^4.5.0
version: 4.5.0
winston:
specifier: ^3.17.0
version: 3.17.0
devDependencies:
'@types/node':
specifier: ^22.9.0
version: 22.10.1
json-schema-to-typescript:
specifier: ^15.0.3
version: 15.0.3
typescript:
specifier: ^5.6.3
version: 5.7.2
packages:
'@apidevtools/json-schema-ref-parser@11.7.2':
resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==}
engines: {node: '>= 16'}
'@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
'@dabh/diagnostics@2.0.3':
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
'@discordjs/builders@1.9.0':
resolution: {integrity: sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==}
engines: {node: '>=18'}
'@discordjs/collection@1.5.3':
resolution: {integrity: sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==}
engines: {node: '>=16.11.0'}
'@discordjs/collection@2.1.1':
resolution: {integrity: sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==}
engines: {node: '>=18'}
'@discordjs/formatters@0.5.0':
resolution: {integrity: sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==}
engines: {node: '>=18'}
'@discordjs/rest@2.4.0':
resolution: {integrity: sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==}
engines: {node: '>=18'}
'@discordjs/util@1.1.1':
resolution: {integrity: sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==}
engines: {node: '>=18'}
'@discordjs/ws@1.1.1':
resolution: {integrity: sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==}
engines: {node: '>=16.11.0'}
'@jsdevtools/ono@7.1.3':
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
'@sapphire/async-queue@1.5.5':
resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
'@sapphire/shapeshift@4.0.0':
resolution: {integrity: sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==}
engines: {node: '>=v16'}
'@sapphire/snowflake@3.5.3':
resolution: {integrity: sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/lodash@4.17.13':
resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==}
'@types/node@22.10.1':
resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==}
'@types/triple-beam@1.3.5':
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
'@types/ws@8.5.13':
resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==}
'@vladfrangu/async_event_emitter@2.4.6':
resolution: {integrity: sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
color-string@1.9.1:
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
color@3.2.1:
resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
colorspace@1.1.4:
resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
discord-api-types@0.37.100:
resolution: {integrity: sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==}
discord-api-types@0.37.83:
resolution: {integrity: sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==}
discord-api-types@0.37.97:
resolution: {integrity: sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==}
discord.js@14.16.3:
resolution: {integrity: sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==}
engines: {node: '>=18'}
enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-xml-parser@4.5.0:
resolution: {integrity: sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==}
hasBin: true
fdir@6.4.2:
resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==}
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
fn.name@1.1.0:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-arrayish@0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
json-schema-to-typescript@15.0.3:
resolution: {integrity: sha512-iOKdzTUWEVM4nlxpFudFsWyUiu/Jakkga4OZPEt7CGoSEsAsUgdOZqR6pcgx2STBek9Gm4hcarJpXSzIvZ/hKA==}
engines: {node: '>=16.0.0'}
hasBin: true
kuler@2.0.0:
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
lodash.snakecase@4.1.1:
resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
logform@2.7.0:
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
engines: {node: '>= 12.0.0'}
magic-bytes.js@1.10.0:
resolution: {integrity: sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==}
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
one-time@1.0.0:
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
picomatch@4.0.2:
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
engines: {node: '>=12'}
prettier@3.4.1:
resolution: {integrity: sha512-G+YdqtITVZmOJje6QkXQWzl3fSfMxFwm1tjTyo9exhkmWSqC4Yhd1+lug++IlR2mvRVAxEDDWYkQdeSztajqgg==}
engines: {node: '>=14'}
hasBin: true
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
strnum@1.0.5:
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
tinyglobby@0.2.10:
resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==}
engines: {node: '>=12.0.0'}
triple-beam@1.4.1:
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
engines: {node: '>= 14.0.0'}
ts-mixer@6.0.4:
resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
typescript@5.7.2:
resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@6.20.0:
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
undici@6.19.8:
resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==}
engines: {node: '>=18.17'}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
winston-transport@4.9.0:
resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
engines: {node: '>= 12.0.0'}
winston@3.17.0:
resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==}
engines: {node: '>= 12.0.0'}
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
snapshots:
'@apidevtools/json-schema-ref-parser@11.7.2':
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.1.0
'@colors/colors@1.6.0': {}
'@dabh/diagnostics@2.0.3':
dependencies:
colorspace: 1.1.4
enabled: 2.0.0
kuler: 2.0.0
'@discordjs/builders@1.9.0':
dependencies:
'@discordjs/formatters': 0.5.0
'@discordjs/util': 1.1.1
'@sapphire/shapeshift': 4.0.0
discord-api-types: 0.37.97
fast-deep-equal: 3.1.3
ts-mixer: 6.0.4
tslib: 2.8.1
'@discordjs/collection@1.5.3': {}
'@discordjs/collection@2.1.1': {}
'@discordjs/formatters@0.5.0':
dependencies:
discord-api-types: 0.37.97
'@discordjs/rest@2.4.0':
dependencies:
'@discordjs/collection': 2.1.1
'@discordjs/util': 1.1.1
'@sapphire/async-queue': 1.5.5
'@sapphire/snowflake': 3.5.3
'@vladfrangu/async_event_emitter': 2.4.6
discord-api-types: 0.37.97
magic-bytes.js: 1.10.0
tslib: 2.8.1
undici: 6.19.8
'@discordjs/util@1.1.1': {}
'@discordjs/ws@1.1.1':
dependencies:
'@discordjs/collection': 2.1.1
'@discordjs/rest': 2.4.0
'@discordjs/util': 1.1.1
'@sapphire/async-queue': 1.5.5
'@types/ws': 8.5.13
'@vladfrangu/async_event_emitter': 2.4.6
discord-api-types: 0.37.83
tslib: 2.8.1
ws: 8.18.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@jsdevtools/ono@7.1.3': {}
'@sapphire/async-queue@1.5.5': {}
'@sapphire/shapeshift@4.0.0':
dependencies:
fast-deep-equal: 3.1.3
lodash: 4.17.21
'@sapphire/snowflake@3.5.3': {}
'@types/json-schema@7.0.15': {}
'@types/lodash@4.17.13': {}
'@types/node@22.10.1':
dependencies:
undici-types: 6.20.0
'@types/triple-beam@1.3.5': {}
'@types/ws@8.5.13':
dependencies:
'@types/node': 22.10.1
'@vladfrangu/async_event_emitter@2.4.6': {}
argparse@2.0.1: {}
async@3.2.6: {}
color-convert@1.9.3:
dependencies:
color-name: 1.1.3
color-name@1.1.3: {}
color-name@1.1.4: {}
color-string@1.9.1:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
color@3.2.1:
dependencies:
color-convert: 1.9.3
color-string: 1.9.1
colorspace@1.1.4:
dependencies:
color: 3.2.1
text-hex: 1.0.0
discord-api-types@0.37.100: {}
discord-api-types@0.37.83: {}
discord-api-types@0.37.97: {}
discord.js@14.16.3:
dependencies:
'@discordjs/builders': 1.9.0
'@discordjs/collection': 1.5.3
'@discordjs/formatters': 0.5.0
'@discordjs/rest': 2.4.0
'@discordjs/util': 1.1.1
'@discordjs/ws': 1.1.1
'@sapphire/snowflake': 3.5.3
discord-api-types: 0.37.100
fast-deep-equal: 3.1.3
lodash.snakecase: 4.1.1
tslib: 2.8.1
undici: 6.19.8
transitivePeerDependencies:
- bufferutil
- utf-8-validate
enabled@2.0.0: {}
fast-deep-equal@3.1.3: {}
fast-xml-parser@4.5.0:
dependencies:
strnum: 1.0.5
fdir@6.4.2(picomatch@4.0.2):
optionalDependencies:
picomatch: 4.0.2
fecha@4.2.3: {}
fn.name@1.1.0: {}
inherits@2.0.4: {}
is-arrayish@0.3.2: {}
is-extglob@2.1.1: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-stream@2.0.1: {}
js-yaml@4.1.0:
dependencies:
argparse: 2.0.1
json-schema-to-typescript@15.0.3:
dependencies:
'@apidevtools/json-schema-ref-parser': 11.7.2
'@types/json-schema': 7.0.15
'@types/lodash': 4.17.13
is-glob: 4.0.3
js-yaml: 4.1.0
lodash: 4.17.21
minimist: 1.2.8
prettier: 3.4.1
tinyglobby: 0.2.10
kuler@2.0.0: {}
lodash.snakecase@4.1.1: {}
lodash@4.17.21: {}
logform@2.7.0:
dependencies:
'@colors/colors': 1.6.0
'@types/triple-beam': 1.3.5
fecha: 4.2.3
ms: 2.1.3
safe-stable-stringify: 2.5.0
triple-beam: 1.4.1
magic-bytes.js@1.10.0: {}
minimist@1.2.8: {}
ms@2.1.3: {}
one-time@1.0.0:
dependencies:
fn.name: 1.1.0
picomatch@4.0.2: {}
prettier@3.4.1: {}
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
safe-buffer@5.2.1: {}
safe-stable-stringify@2.5.0: {}
simple-swizzle@0.2.2:
dependencies:
is-arrayish: 0.3.2
stack-trace@0.0.10: {}
string_decoder@1.3.0:
dependencies:
safe-buffer: 5.2.1
strnum@1.0.5: {}
text-hex@1.0.0: {}
tinyglobby@0.2.10:
dependencies:
fdir: 6.4.2(picomatch@4.0.2)
picomatch: 4.0.2
triple-beam@1.4.1: {}
ts-mixer@6.0.4: {}
tslib@2.8.1: {}
typescript@5.7.2: {}
undici-types@6.20.0: {}
undici@6.19.8: {}
util-deprecate@1.0.2: {}
winston-transport@4.9.0:
dependencies:
logform: 2.7.0
readable-stream: 3.6.2
triple-beam: 1.4.1
winston@3.17.0:
dependencies:
'@colors/colors': 1.6.0
'@dabh/diagnostics': 2.0.3
async: 3.2.6
is-stream: 2.0.1
logform: 2.7.0
one-time: 1.0.0
readable-stream: 3.6.2
safe-stable-stringify: 2.5.0
stack-trace: 0.0.10
triple-beam: 1.4.1
winston-transport: 4.9.0
ws@8.18.0: {}

View file

@ -1,5 +1,4 @@
export default interface IDiscordConfiguration {
guildId: string;
channelId: string;
botToken: string;
}

View file

@ -1,5 +1,7 @@
import ITranslationDiscordEmbed from "./ITranslationDiscordEmbed";
import ITranslationCommon from "./ITranslationCommon";
export default interface ITranslation {
discordEmbed: ITranslationDiscordEmbed;
common: ITranslationCommon;
}

View file

@ -0,0 +1,14 @@
export default interface ITranslationCommon {
monthJanuary: string;
monthFebruary: string;
monthMarch: string;
monthApril: string;
monthMay: string;
monthJune: string;
monthJuly: string;
monthAugust: string;
monthSeptember: string;
monthOctober: string;
monthNovember: string;
monthDecember: string;
}

View file

@ -4,6 +4,8 @@ export default interface ITranslationDiscordEmbed {
descriptionOffline: string;
descriptionUnknown: string;
titleServerName: string;
titleServerMap: string;
titleServerMods: string;
titleServerPassword: string;
titleServerTime: string;
titlePlayerCount: string;

View file

@ -0,0 +1,5 @@
export default interface IMod {
name: string;
author: string;
version: string;
}

View file

@ -2,9 +2,46 @@ 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();
const appConfig: Configuration = new Configuration();
// Log the application start and version
const packageJson = require('../package.json');
appLogger.info(`Starting | App: ${packageJson.name} | Version: ${packageJson.version}`);
appLogger.info(`----------------------------------------------------`);
/**
* Check if the configuration is valid and exit the application if it is not
*/
if(!appConfig.isConfigurationValid()) {
appLogger.error("Configuration is not valid. Exiting application.");
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
*/
const discordClient = new Client({
intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages]
});
@ -20,7 +57,7 @@ discordClient.login(appConfig.discord.botToken).then(() => {
/**
* Start the DiscordService and restart it if an error occurred
*/
function startDiscordService(): void {
async function startDiscordService(): Promise<void> {
try {
new DiscordService(discordClient);
} catch (exception) {

View file

@ -2,19 +2,130 @@ import IDiscordConfiguration from "../Interfaces/Configuration/IDiscordConfigura
import IApplicationConfiguration from "../Interfaces/Configuration/IApplicationConfiguration";
import IConfiguration from "../Interfaces/Configuration/IConfiguration";
import ITranslation from "../Interfaces/Configuration/ITranslation";
import Logging from "./Logging";
import {Logger} from "winston";
export default class Configuration implements IConfiguration{
private readonly logger: Logger;
public readonly discord: IDiscordConfiguration;
public readonly application: IApplicationConfiguration;
public readonly translation: ITranslation;
constructor() {
this.logger = Logging.getLogger();
try {
let config = require('../../config.json');
this.discord = config.discord;
this.application = config.application;
this.translation = config.translation;
} catch (exception) {
this.logger.error("Error while loading configuration file, please check if the configuration file exists and is valid.");
process.exit(1);
}
}
/**
* Returns true if the value is empty or undefined
* @param value
* @private
*/
private isValueEmptyOrUndefined(value: any): boolean {
return value == null || value == "" || value == undefined;
}
/**
* Returns true if the value is undefined
* @param value
* @private
*/
private isValueUndefined(value: any): boolean {
return value == undefined;
}
/**
* Validates the discord configuration and returns true if the configuration is valid
* @private
*/
private validateDiscordConfiguration(): boolean {
return !(this.isValueEmptyOrUndefined(this.discord?.botToken) || this.isValueEmptyOrUndefined(this.discord?.channelId));
}
/**
* Validates the application configuration and returns true if the configuration is valid
* @private
*/
private validateApplicationConfiguration(): boolean {
return !(
this.isValueUndefined(this.application?.serverPassword)
|| this.isValueEmptyOrUndefined(this.application?.serverStatsUrl)
|| this.isValueEmptyOrUndefined(this.application?.serverMapUrl)
|| this.isValueEmptyOrUndefined(this.application?.updateIntervalSeconds)
);
}
/**
* Validates the translation configuration and returns true if the configuration is valid
* @private
*/
private validateTranslationConfiguration(): boolean {
return !(
this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.title)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.descriptionOnline)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.descriptionOffline)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.descriptionUnknown)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titleServerName)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titleServerPassword)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titleServerTime)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titleServerMap)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titleServerMods)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.titlePlayerCount)
|| this.isValueEmptyOrUndefined(this?.translation?.discordEmbed?.noPlayersOnline)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthJanuary)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthFebruary)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthMarch)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthApril)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthMay)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthJune)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthJuly)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthAugust)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthSeptember)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthOctober)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthNovember)
|| this.isValueEmptyOrUndefined(this?.translation?.common?.monthDecember)
);
}
/**
* Validates the configuration file and returns true if the configuration is valid
* @returns boolean True if the configuration is valid
*/
public isConfigurationValid(): boolean {
if(!this.validateDiscordConfiguration()) {
this.logger.error("Discord configuration is not valid. Please check your configuration file.");
return false;
} else {
this.logger.info("Discord configuration is valid.");
}
if(!this.validateApplicationConfiguration()) {
this.logger.error("Application configuration is not valid. Please check your configuration file.");
return false;
} else {
this.logger.info("Application configuration is valid.");
}
if(!this.validateTranslationConfiguration()) {
this.logger.error("Translation configuration is not valid. Please check your configuration file.");
return false;
} else {
this.logger.info("Translation configuration is valid.");
}
return true;
}
/**
* Returns the configuration object
*/
public static getConfiguration(): IConfiguration {
return new Configuration();
}

View file

@ -56,12 +56,14 @@ export default class DiscordEmbed {
this.generateEmbedFromStatusFeed(this.serverStatsFeed).then(embedMessage => {
if (this.firstMessageId !== null) {
(channel as TextChannel).messages.fetch(this.firstMessageId).then(message => {
this.appLogger.info(`Message found, editing message with new embed`);
message.edit({embeds: [embedMessage]});
}).catch(() => {
this.appLogger.warn('Message not found, sending new message');
sendInitialMessage(embedMessage);
});
} else {
this.appLogger.info(`No message found, sending new message`);
sendInitialMessage(embedMessage);
}
});
@ -90,6 +92,16 @@ 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
@ -111,24 +123,38 @@ 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 {
playerListString = serverStats.getPlayerList().map(p => p.username).join(', ');
}
let serverPassword = config.application.serverPassword;
if(config.application.serverPassword == "") {
serverPassword = "-/-";
}
let serverMods = serverStats.getServerMods();
let serverModsText = "-/-";
if(serverMods.length > 0) {
serverModsText = await this.truncateText(serverMods.map(mod => `${mod.name}`).join(', '));
}
// @ts-ignore
embed.addFields(
{name: config.translation.discordEmbed.titleServerName, value: serverStats.getServerName()},
{name: config.translation.discordEmbed.titleServerPassword, value: config.application.serverPassword},
{name: config.translation.discordEmbed.titleServerPassword, value: serverPassword},
{name: config.translation.discordEmbed.titleServerTime, value: serverStats.getServerTime()},
{name: config.translation.discordEmbed.titleServerMap, value: serverStats.getServerMap()},
{name: config.translation.discordEmbed.titleServerMods, value: serverModsText},
{
name: `${config.translation.discordEmbed.titlePlayerCount} (${serverStats.getPlayerCount()}/${serverStats.getMaxPlayerCount()}):`,
name: playerListTitleString,
value: playerListString
},
);
}
this.appLogger.debug(embed);
return embed;
}
}

View file

@ -5,8 +5,12 @@ export default class Logging {
return winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
winston.format.colorize(),
winston.format.simple(),
winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
),
transports: [
new winston.transports.Console(),

View file

@ -3,6 +3,8 @@ import Configuration from "./Configuration";
import {XMLParser} from "fast-xml-parser";
import Logging from "./Logging";
import IPlayer from "../Interfaces/Feed/IPlayer";
import IConfiguration from "../Interfaces/Configuration/IConfiguration";
import IMod from "../Interfaces/Feed/IMod";
export const CONNECTION_REFUSED = 'ECONNREFUSED';
export const NOT_FOUND = 'ENOTFOUND';
@ -102,7 +104,7 @@ export default class ServerStatusFeed {
* @returns {string} The server map name
*/
public getServerMap(): string {
return <string>this.getServerStats()?.Server.map;
return <string>this.getServerStats()?.Server.mapName;
}
/**
@ -117,6 +119,24 @@ export default class ServerStatusFeed {
return dayTime / (60 * 60 * 1000) + 0.0001;
}
/**
* Get the server mods from the server stats feed
* @returns {IMod[]} The server mods as an array of IMod objects
*/
public getServerMods(): IMod[] {
let modList = this.getServerStats()?.Server?.Mods?.Mod;
if(modList === undefined || !Array.isArray(modList) || modList == null) {
return [];
}
return modList.map((mod: any) => {
return {
name: mod['#text'],
author: mod.author,
version: mod.version
} as IMod;
});
}
/**
* Returns the server time in the format HH:MM
* @returns {string} The server time in the format HH:MM
@ -141,18 +161,18 @@ export default class ServerStatusFeed {
/**
* Returns the server player count
* @returns {number} The server player count
* @returns {number | null | undefined} The server player count
*/
public getPlayerCount(): number {
return <number>this.getServerStats()?.Server.Slots.numUsed;
public getPlayerCount(): number | null | undefined {
return <number>this.getServerStats()?.Server?.Slots?.numUsed;
}
/**
* Returns the server player count
* @returns {number} The server player count
* @returns {number | null | undefined} The server player count
*/
public getMaxPlayerCount(): number {
return <number>this.getServerStats()?.Server.Slots.capacity;
public getMaxPlayerCount(): number | null | undefined {
return <number>this.getServerStats()?.Server?.Slots?.capacity;
}
/**

View file

@ -0,0 +1,41 @@
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;
}
}