mirror of
https://github.com/cloudmaker97/FS25-Discord-Bot.git
synced 2025-12-06 08:28:33 +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 Configuration from "./Services/Configuration";
|
||||||
import Logging from "./Services/Logging";
|
import Logging from "./Services/Logging";
|
||||||
import ServerStatsFeed from "./Services/ServerStatsFeed";
|
import DiscordService from "./Services/DiscordEmbed";
|
||||||
|
|
||||||
const appLogger = Logging.getLogger();
|
const appLogger = Logging.getLogger();
|
||||||
const appConfig: Configuration = new Configuration();
|
const appConfig: Configuration = new Configuration();
|
||||||
const appClient = new Client({
|
const discordClient = new Client({
|
||||||
intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages]
|
intents: [IntentsBitField.Flags.Guilds, IntentsBitField.Flags.GuildMessages]
|
||||||
});
|
});
|
||||||
appClient.login(appConfig.discord.botToken);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all messages in a text channel
|
* Start the discord client and log in
|
||||||
* @param textChannel
|
* After that create a new DiscordService instance to start the server stats feed
|
||||||
*/
|
*/
|
||||||
function deleteAllMessages(textChannel: TextChannel) {
|
discordClient.login(appConfig.discord.botToken).then(() => {
|
||||||
appLogger.info(`Deleting all messages in channel ${textChannel.id}`);
|
appLogger.info(`Login successful to discord with token`);
|
||||||
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
|
discordClient.on('ready', () => {
|
||||||
(async () => {
|
appLogger.info(`Discord client ready. Logged in as ${discordClient.user?.username}!`);
|
||||||
await serverStats.updateServerFeed();
|
new DiscordService(discordClient);
|
||||||
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);
|
|
||||||
})();
|
|
||||||
});
|
});
|
||||||
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 CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||||
export const NOT_FOUND = 'ENOTFOUND';
|
export const NOT_FOUND = 'ENOTFOUND';
|
||||||
|
|
||||||
export default class ServerStatsFeed {
|
export default class ServerStatusFeed {
|
||||||
private _serverStats: ServerStats | null = null;
|
private _serverStats: ServerStats | null = null;
|
||||||
private _isOnline: boolean = false;
|
private _isOnline: boolean = false;
|
||||||
private _isFetching: boolean = false;
|
private _isFetching: boolean = false;
|
||||||
|
|
@ -15,10 +15,19 @@ export default class ServerStatsFeed {
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fetching status of the server stats feed
|
||||||
|
* @returns {boolean} The fetching status of the server stats feed
|
||||||
|
*/
|
||||||
public isFetching(): boolean {
|
public isFetching(): boolean {
|
||||||
return this._isFetching;
|
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 {
|
private getServerStats(): ServerStats | null {
|
||||||
if(this._isOnline && !this._isFetching && this._serverStats) {
|
if(this._isOnline && !this._isFetching && this._serverStats) {
|
||||||
return this._serverStats;
|
return this._serverStats;
|
||||||
|
|
@ -26,9 +35,13 @@ export default class ServerStatsFeed {
|
||||||
return null;
|
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> {
|
public async updateServerFeed(): Promise<ServerStats|null> {
|
||||||
this._isFetching = true;
|
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)
|
await fetch(Configuration.getConfiguration().application.serverStatsUrl)
|
||||||
.then(
|
.then(
|
||||||
r => r.text()
|
r => r.text()
|
||||||
|
|
@ -39,7 +52,7 @@ export default class ServerStatsFeed {
|
||||||
|
|
||||||
// Parse the XML response
|
// Parse the XML response
|
||||||
const parsedFeed = new XMLParser({ignoreAttributes: false, attributeNamePrefix: ''}).parse(response) as ServerStats;
|
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;
|
this._serverStats = parsedFeed;
|
||||||
}
|
}
|
||||||
).catch(
|
).catch(
|
||||||
|
|
@ -50,13 +63,13 @@ export default class ServerStatsFeed {
|
||||||
// Handle different error codes
|
// Handle different error codes
|
||||||
switch (reason.cause.code) {
|
switch (reason.cause.code) {
|
||||||
case CONNECTION_REFUSED:
|
case CONNECTION_REFUSED:
|
||||||
Logging.getLogger().error(`Connection refused to server stats feed`);
|
Logging.getLogger().error(`Connection refused to server status feed`);
|
||||||
break;
|
break;
|
||||||
case NOT_FOUND:
|
case NOT_FOUND:
|
||||||
Logging.getLogger().error(`Server stats feed not found`);
|
Logging.getLogger().error(`Server status feed not found`);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logging.getLogger().error(`Error fetching server stats`);
|
Logging.getLogger().error(`Error fetching server status feed`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -68,6 +81,10 @@ export default class ServerStatsFeed {
|
||||||
return this._serverStats;
|
return this._serverStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the online status of the server
|
||||||
|
* @returns {boolean} The online status of the server
|
||||||
|
*/
|
||||||
public isOnline(): boolean {
|
public isOnline(): boolean {
|
||||||
return this._isOnline;
|
return this._isOnline;
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue