mirror of
https://github.com/cloudmaker97/FS25-Discord-Bot.git
synced 2025-12-06 00:18:34 +00:00
Extracted the logic from the main file into a separate service
This commit is contained in:
parent
5d2f9cfb88
commit
35c887383a
3 changed files with 152 additions and 90 deletions
|
|
@ -1,95 +1,23 @@
|
|||
import {Client, EmbedBuilder, IntentsBitField, Snowflake, TextChannel} from 'discord.js';
|
||||
import {Client, IntentsBitField} from 'discord.js';
|
||||
import Configuration from "./Services/Configuration";
|
||||
import Logging from "./Services/Logging";
|
||||
import ServerStatsFeed from "./Services/ServerStatsFeed";
|
||||
import DiscordService from "./Services/DiscordEmbed";
|
||||
|
||||
const appLogger = Logging.getLogger();
|
||||
const appConfig: Configuration = new Configuration();
|
||||
const appClient = new Client({
|
||||
const discordClient = new Client({
|
||||
intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages]
|
||||
});
|
||||
appClient.login(appConfig.discord.botToken);
|
||||
|
||||
/**
|
||||
* Delete all messages in a text channel
|
||||
* @param textChannel
|
||||
* Start the discord client and log in
|
||||
* After that create a new DiscordService instance to start the server stats feed
|
||||
*/
|
||||
function deleteAllMessages(textChannel: TextChannel) {
|
||||
appLogger.info(`Deleting all messages in channel ${textChannel.id}`);
|
||||
textChannel.messages.fetch().then(messages => {
|
||||
messages.forEach(message => {
|
||||
message.delete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send server stats embed in a channel
|
||||
* @param serverStats
|
||||
*/
|
||||
async function generateEmbedFromStatusFeed(serverStats: ServerStatsFeed): Promise<EmbedBuilder> {
|
||||
let embed = new EmbedBuilder();
|
||||
embed.setTitle('Server Status');
|
||||
if (!serverStats.isOnline()) {
|
||||
embed.setDescription('Der Server ist aktuell offline.');
|
||||
} else if (serverStats.isFetching()) {
|
||||
embed.setDescription('Der Serverstatus wird aktuell abgefragt...');
|
||||
} else {
|
||||
embed.setDescription(`Der Server ist aktuell ${serverStats.isOnline() ? 'online' : 'offline'}`);
|
||||
embed.setTimestamp();
|
||||
embed.setThumbnail(Configuration.getConfiguration().application.serverMapUrl);
|
||||
|
||||
let playerListString: string = '';
|
||||
if(serverStats.getPlayerList().length === 0) {
|
||||
playerListString = 'Keine Spieler online';
|
||||
} else {
|
||||
playerListString = serverStats.getPlayerList().map(p => p.username).join(', ');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
embed.addFields(
|
||||
{name: 'Name:', value: serverStats.getServerName()},
|
||||
{name: 'Passwort:', value: appConfig.application.serverPassword},
|
||||
{name: 'Uhrzeit im Spiel:', value: serverStats.getServerTime()},
|
||||
{
|
||||
name: `Spieler online (${serverStats.getPlayerCount()}/${serverStats.getMaxPlayerCount()}):`,
|
||||
value: playerListString
|
||||
},
|
||||
);
|
||||
}
|
||||
return embed;
|
||||
}
|
||||
|
||||
appClient.on('ready', () => {
|
||||
appLogger.info(`Discord client ready. Logged in as ${appClient.user?.username}!`);
|
||||
const serverStats = new ServerStatsFeed();
|
||||
|
||||
// Fetch channel and delete all messages
|
||||
appClient.channels.fetch(appConfig.discord.channelId as Snowflake).then(channel => {
|
||||
let textChannel = channel as TextChannel;
|
||||
deleteAllMessages(textChannel);
|
||||
});
|
||||
|
||||
// Start fetching server stats
|
||||
(async () => {
|
||||
await serverStats.updateServerFeed();
|
||||
let firstMessageId: any = null;
|
||||
setInterval(async () => {
|
||||
await serverStats.updateServerFeed();
|
||||
appClient.channels.fetch(appConfig.discord.channelId as Snowflake).then(async channel => {
|
||||
generateEmbedFromStatusFeed(serverStats).then(embedMessage => {
|
||||
console.log(embedMessage);
|
||||
if (firstMessageId !== null) {
|
||||
(channel as TextChannel).messages.fetch(firstMessageId).then(message => {
|
||||
message.edit({embeds: [embedMessage]});
|
||||
});
|
||||
} else {
|
||||
(channel as TextChannel).send({embeds: [embedMessage]}).then(message => {
|
||||
firstMessageId = message.id;
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
}, appConfig.application.updateIntervalSeconds * 1000);
|
||||
})();
|
||||
discordClient.login(appConfig.discord.botToken).then(() => {
|
||||
appLogger.info(`Login successful to discord with token`);
|
||||
});
|
||||
|
||||
discordClient.on('ready', () => {
|
||||
appLogger.info(`Discord client ready. Logged in as ${discordClient.user?.username}!`);
|
||||
new DiscordService(discordClient);
|
||||
});
|
||||
117
source/Services/DiscordEmbed.ts
Normal file
117
source/Services/DiscordEmbed.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import {Client, EmbedBuilder, Snowflake, TextChannel} from "discord.js";
|
||||
import Configuration from "./Configuration";
|
||||
import ServerStatusFeed from "./ServerStatusFeed";
|
||||
import {Logger} from "winston";
|
||||
import Logging from "./Logging";
|
||||
|
||||
export default class DiscordEmbed {
|
||||
private appLogger: Logger;
|
||||
private discordAppClient: Client;
|
||||
private appConfiguration: Configuration;
|
||||
private serverStatsFeed: ServerStatusFeed;
|
||||
private firstMessageId: Snowflake | null = null;
|
||||
|
||||
public constructor(discordAppClient: Client) {
|
||||
this.appLogger = Logging.getLogger();
|
||||
this.discordAppClient = discordAppClient;
|
||||
this.appConfiguration = new Configuration();
|
||||
this.serverStatsFeed = new ServerStatusFeed();
|
||||
|
||||
(async () => {
|
||||
// Delete all messages in the channel
|
||||
await this.deleteAllMessages();
|
||||
// Start the update loop, which updates the discord embed every x seconds itself
|
||||
await this.updateDiscordEmbed();
|
||||
})();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the discord embed with the server status, player list and server time
|
||||
* This method is called every x seconds to update the discord embed.
|
||||
* @private
|
||||
*/
|
||||
private async updateDiscordEmbed(): Promise<void> {
|
||||
try {
|
||||
await this.serverStatsFeed.updateServerFeed();
|
||||
if(this.serverStatsFeed.isFetching()) {
|
||||
this.appLogger.info('Server status feed is still fetching, try again...');
|
||||
setTimeout(() => {
|
||||
this.updateDiscordEmbed();
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
this.discordAppClient.channels.fetch(this.appConfiguration.discord.channelId as Snowflake).then(async channel => {
|
||||
this.generateEmbedFromStatusFeed(this.serverStatsFeed).then(embedMessage => {
|
||||
if (this.firstMessageId !== null) {
|
||||
(channel as TextChannel).messages.fetch(this.firstMessageId).then(message => {
|
||||
message.edit({embeds: [embedMessage]});
|
||||
});
|
||||
} else {
|
||||
(channel as TextChannel).send({embeds: [embedMessage]}).then(message => {
|
||||
this.firstMessageId = message.id;
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (exception) {
|
||||
this.appLogger.error(exception);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.updateDiscordEmbed();
|
||||
}, this.appConfiguration.application.updateIntervalSeconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all messages in a text channel to clear the channel
|
||||
* @private
|
||||
*/
|
||||
private async deleteAllMessages(): Promise<boolean> {
|
||||
let textChannel = this.discordAppClient.channels.cache.get(this.appConfiguration.discord.channelId as Snowflake) as TextChannel;
|
||||
this.appLogger.info(`Deleting all messages in discord text channel ${textChannel.id}`);
|
||||
textChannel.messages.fetch().then(messages => {
|
||||
messages.forEach(message => {
|
||||
message.delete();
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send server stats embed in a channel
|
||||
* @param serverStats
|
||||
*/
|
||||
private async generateEmbedFromStatusFeed(serverStats: ServerStatusFeed): Promise<EmbedBuilder> {
|
||||
let embed = new EmbedBuilder();
|
||||
embed.setTitle('Server Status');
|
||||
if (!serverStats.isOnline()) {
|
||||
embed.setDescription('Der Server ist aktuell offline.');
|
||||
} else if (serverStats.isFetching()) {
|
||||
embed.setDescription('Der Serverstatus wird aktuell abgefragt...');
|
||||
} else {
|
||||
embed.setDescription(`Der Server ist aktuell ${serverStats.isOnline() ? 'online' : 'offline'}`);
|
||||
embed.setTimestamp();
|
||||
embed.setThumbnail(this.appConfiguration.application.serverMapUrl);
|
||||
|
||||
let playerListString: string = '';
|
||||
if(serverStats.getPlayerList().length === 0) {
|
||||
playerListString = 'Keine Spieler online';
|
||||
} else {
|
||||
playerListString = serverStats.getPlayerList().map(p => p.username).join(', ');
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
embed.addFields(
|
||||
{name: 'Name:', value: serverStats.getServerName()},
|
||||
{name: 'Passwort:', value: this.appConfiguration.application.serverPassword},
|
||||
{name: 'Uhrzeit im Spiel:', value: serverStats.getServerTime()},
|
||||
{
|
||||
name: `Spieler online (${serverStats.getPlayerCount()}/${serverStats.getMaxPlayerCount()}):`,
|
||||
value: playerListString
|
||||
},
|
||||
);
|
||||
}
|
||||
this.appLogger.debug(embed);
|
||||
return embed;
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import IPlayer from "../Interfaces/Feed/IPlayer";
|
|||
export const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||
export const NOT_FOUND = 'ENOTFOUND';
|
||||
|
||||
export default class ServerStatsFeed {
|
||||
export default class ServerStatusFeed {
|
||||
private _serverStats: ServerStats | null = null;
|
||||
private _isOnline: boolean = false;
|
||||
private _isFetching: boolean = false;
|
||||
|
|
@ -15,10 +15,19 @@ export default class ServerStatsFeed {
|
|||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fetching status of the server stats feed
|
||||
* @returns {boolean} The fetching status of the server stats feed
|
||||
*/
|
||||
public isFetching(): boolean {
|
||||
return this._isFetching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server stats object
|
||||
* @returns {ServerStats | null} The server stats object or null if the server is offline or fetching
|
||||
* @private
|
||||
*/
|
||||
private getServerStats(): ServerStats | null {
|
||||
if(this._isOnline && !this._isFetching && this._serverStats) {
|
||||
return this._serverStats;
|
||||
|
|
@ -26,9 +35,13 @@ export default class ServerStatsFeed {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the server feed from the server status feed url
|
||||
* @returns {Promise<ServerStats | null>} The server stats object or null if the fetch failed
|
||||
*/
|
||||
public async updateServerFeed(): Promise<ServerStats|null> {
|
||||
this._isFetching = true;
|
||||
Logging.getLogger().info(`Fetching server stats from feed url`);
|
||||
Logging.getLogger().info(`Fetching server status from feed url`);
|
||||
await fetch(Configuration.getConfiguration().application.serverStatsUrl)
|
||||
.then(
|
||||
r => r.text()
|
||||
|
|
@ -39,7 +52,7 @@ export default class ServerStatsFeed {
|
|||
|
||||
// Parse the XML response
|
||||
const parsedFeed = new XMLParser({ignoreAttributes: false, attributeNamePrefix: ''}).parse(response) as ServerStats;
|
||||
Logging.getLogger().info(`Server stats received`);
|
||||
Logging.getLogger().info(`Server status feed successful received`);
|
||||
this._serverStats = parsedFeed;
|
||||
}
|
||||
).catch(
|
||||
|
|
@ -50,13 +63,13 @@ export default class ServerStatsFeed {
|
|||
// Handle different error codes
|
||||
switch (reason.cause.code) {
|
||||
case CONNECTION_REFUSED:
|
||||
Logging.getLogger().error(`Connection refused to server stats feed`);
|
||||
Logging.getLogger().error(`Connection refused to server status feed`);
|
||||
break;
|
||||
case NOT_FOUND:
|
||||
Logging.getLogger().error(`Server stats feed not found`);
|
||||
Logging.getLogger().error(`Server status feed not found`);
|
||||
break;
|
||||
default:
|
||||
Logging.getLogger().error(`Error fetching server stats`);
|
||||
Logging.getLogger().error(`Error fetching server status feed`);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -68,6 +81,10 @@ export default class ServerStatsFeed {
|
|||
return this._serverStats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the online status of the server
|
||||
* @returns {boolean} The online status of the server
|
||||
*/
|
||||
public isOnline(): boolean {
|
||||
return this._isOnline;
|
||||
}
|
||||
Loading…
Reference in a new issue