2023-08-18 20:54:17 -04:00
interface TServer {
ip : string
code : string
}
2023-05-05 18:41:35 -04:00
import Discord from 'discord.js' ;
import TClient from './client' ;
2023-05-23 01:14:17 -04:00
import { writeFileSync , readFileSync } from 'node:fs' ;
2023-05-07 04:41:20 -04:00
import { FSPlayer , FSData , FSCareerSavegame } from './typings/interfaces' ;
2023-05-05 18:41:35 -04:00
2023-08-18 20:54:17 -04:00
export default async ( client :TClient , Channel :string , Message :string , Server :TServer ) = > {
2023-08-18 07:18:33 -04:00
if ( ! client . config . botSwitches . mpstats ) return ;
2023-08-18 20:54:17 -04:00
let MPLoopPrefix = '[MPLoop] ' ;
let noContentImage = 'https://cdn.discordapp.com/attachments/1118960531135541318/1140906691236479036/68efx1.png' ;
2023-08-18 07:18:33 -04:00
const msg = await ( client . channels . resolve ( Channel ) as Discord . TextChannel ) . messages . fetch ( Message ) ;
2023-08-18 20:54:17 -04:00
2023-08-18 07:18:33 -04:00
// Log bot uptime for the sake of debugging.
( client . channels . resolve ( '1091300529696673792' ) as Discord . TextChannel ) . send ( client . formatTime ( client . uptime , 2 , { longNames : true , commas : true } ) ) ;
const HITALL = async ( ) = > {
2023-08-18 20:54:17 -04:00
let sessionInit = { signal : AbortSignal.timeout ( 7500 ) , headers : { 'User-Agent' : ` Daggerbot - HITALL/fetch ` } } ;
2023-08-18 07:18:33 -04:00
try {
2023-08-18 20:54:17 -04:00
const hitDSS = await fetch ( Server . ip + '/feed/dedicated-server-stats.json?code=' + Server . code , sessionInit ) . then ( r = > r . json ( ) as Promise < FSData > ) ;
const hitCSG = await fetch ( Server . ip + '/feed/dedicated-server-savegame.html?code=' + Server . code + '&file=careerSavegame' , sessionInit ) . then ( async r = > {
if ( r . status === 204 ) return msg . edit ( { embeds : [ new client . embed ( ) . setImage ( noContentImage ) ] } ) ;
else return ( client . xjs . xml2js ( await r . text ( ) , { compact : true } ) as any ) . careerSavegame as FSCareerSavegame
} ) . catch ( ( ) = > console . log ( client . logTime ( ) , ` ${ MPLoopPrefix } CSG failed for ${ Server . ip . replace ( /^(https?)(\:\/\/)/ , '' ) } ` ) ) ;
if ( ! hitDSS ? ? ! hitCSG ) {
if ( hitDSS && ! hitDSS . slots ) return new Error ( ` ${ MPLoopPrefix } DSS failed for unknown slots table ` ) ;
return msg . edit ( { embeds : [ new client . embed ( ) . setColor ( client . config . embedColorRed ) . setTitle ( 'Host did not respond back in time' ) ] } ) ;
}
2023-08-18 07:18:33 -04:00
msg . edit ( { content : [
2023-08-18 20:54:17 -04:00
Server . ip . replace ( /^(https?)(\:\/\/)/ , '' ) ,
hitDSS . server . name . length > 0 ? hitDSS . server . name : 'Offline'
] . join ( '\n' ) , embeds : [ ] } )
2023-08-18 07:18:33 -04:00
} catch ( err ) {
2023-08-18 20:54:17 -04:00
throw new Error ( ` ${ MPLoopPrefix } Failed to make a promise request. ` , { cause : err.cause } )
2023-08-18 07:18:33 -04:00
}
}
HITALL ( ) ;
}
/ * e x p o r t d e f a u l t a s y n c ( c l i e n t : T C l i e n t , C h a n n e l : s t r i n g , M e s s a g e : s t r i n g , S e r v e r N a m e : s t r i n g ) = > {
2023-05-05 18:41:35 -04:00
if ( ! client . config . botSwitches . mpstats ) return ;
2023-08-15 06:47:31 -04:00
const noContentImage = 'https://cdn.discordapp.com/attachments/1118960531135541318/1140906691236479036/68efx1.png' ;
2023-05-05 18:41:35 -04:00
const msg = await ( client . channels . resolve ( Channel ) as Discord . TextChannel ) . messages . fetch ( Message ) ;
const embed = new client . embed ( ) ;
2023-05-07 04:41:20 -04:00
let playerData :Array < string > = [ ] ;
2023-05-05 18:41:35 -04:00
let error :Boolean ;
2023-05-07 04:41:20 -04:00
let isServerOnline = false ;
2023-08-15 06:47:31 -04:00
const fetchServer = await client . MPServer . _content . findById ( client . config . mainServer . id ) ;
const API = { // Fetch needed data from Farming Simulator server's API endpoints.
DSS : {
data : { } as FSData ,
res : '' as string ,
endpoint : '/feed/dedicated-server-stats.json?code='
} ,
CSG : {
data : { } as FSCareerSavegame ,
res : '' as string ,
endpoint : '/feed/dedicated-server-savegame.html?code=' ,
endpoint_file : '&file=careerSavegame'
}
}
2023-05-05 18:41:35 -04:00
2023-08-15 06:47:31 -04:00
// Fetch needed data from database server and hit them with a GET request.
if ( ! fetchServer . mainServer . ip ( /http|https/ ) ? ? ! fetchServer . secondServer . ip ( /http|https/ ) ) return msg . edit ( { content : '*This server doesn\'t seem to be setup yet!*' , embeds :null } ) ;
async function hitServer ( client :TClient , URL :string ) {
return await client . axios . get ( URL , {
timeout : 7500 , // Increased the timeout a bit just in case.
maxContentLength : Infinity ,
headers : {
'User-Agent' : ` Daggerbot/axios ${ client . axios . VERSION } `
}
} ) . catch ( ( err :Error ) = > err . message )
2023-05-05 18:41:35 -04:00
}
2023-08-15 06:47:31 -04:00
await Promise . all ( [
hitServer ( client , fetchServer . mainServer . ip + API . DSS . endpoint + fetchServer . mainServer . code ) ,
hitServer ( client , fetchServer . mainServer . ip + API . CSG . endpoint + fetchServer . mainServer . code + API . CSG . endpoint_file ) ,
hitServer ( client , fetchServer . secondServer . ip + API . DSS . endpoint + fetchServer . secondServer . code ) ,
hitServer ( client , fetchServer . secondServer . ip + API . CSG . endpoint + fetchServer . secondServer . code + API . CSG . endpoint_file )
] ) . then ( function ( results ) {
// Main server's DSS
if ( typeof results [ 0 ] === 'string' ) {
API . DSS . res = ` DagMP:Main DSS failed, ${ results [ 0 ] } ` ;
2023-05-05 18:41:35 -04:00
embed . addFields ( { name : 'DSS Status' , value :results [ 0 ] } )
2023-08-15 06:47:31 -04:00
} else if ( results [ 0 ] . status != 200 ) {
API . DSS . res = ` DagMP:Main DSS failed with ${ results [ 0 ] . status + ' ' + results [ 0 ] . statusText } ` ;
embed . addFields ( { name : 'DSS Status' , value :results [ 0 ] . status + ' ' + results [ 0 ] . statusText } )
} else API . DSS . data = results [ 0 ] . data as FSData
2023-05-05 18:41:35 -04:00
2023-08-15 06:47:31 -04:00
// Main server's CSG
if ( typeof results [ 1 ] === 'string' ) {
API . CSG . res = ` DagMP:Main CSG failed, ${ results [ 1 ] } ` ;
2023-05-05 18:41:35 -04:00
embed . addFields ( { name : 'CSG Status' , value :results [ 1 ] } )
2023-08-15 06:47:31 -04:00
} else if ( results [ 1 ] . status != 200 ) {
if ( results [ 1 ] . status === 204 ) embed . setImage ( noContentImage ) ;
API . CSG . res = ` DagMP:Main CSG failed with ${ results [ 1 ] . status + ' ' + results [ 1 ] . statusText } ` ;
embed . addFields ( { name : 'CSG Status' , value :results [ 1 ] . status + ' ' + results [ 1 ] . statusText } )
} else API . CSG . data = ( client . xjs . xml2js ( results [ 1 ] . data , { compact :true } ) as any ) . careerSavegame as FSCareerSavegame
// Second server's DSS
if ( typeof results [ 2 ] === 'string' ) {
API . DSS . res = ` DagMP:Second DSS failed, ${ results [ 2 ] } ` ;
embed . addFields ( { name : 'DSS Status' , value :results [ 2 ] } )
} else if ( results [ 2 ] . status != 200 ) {
API . DSS . res = ` DagMP:Second DSS failed with ${ results [ 2 ] . status + ' ' + results [ 2 ] . statusText } ` ;
embed . addFields ( { name : 'DSS Status' , value :results [ 2 ] . status + ' ' + results [ 2 ] . statusText } )
} else API . DSS . data = results [ 2 ] . data as FSData
// Second server's CSG
if ( typeof results [ 3 ] === 'string' ) {
API . CSG . res = ` DagMP:Second CSG failed, ${ results [ 3 ] } ` ;
embed . addFields ( { name : 'CSG Status' , value :results [ 3 ] } )
} else if ( results [ 3 ] . status != 200 ) {
if ( results [ 3 ] . status === 204 ) embed . setImage ( noContentImage ) ;
API . CSG . res = ` DagMP:Second CSG failed with ${ results [ 3 ] . status + ' ' + results [ 3 ] . statusText } ` ;
embed . addFields ( { name : 'CSG Status' , value :results [ 3 ] . status + ' ' + results [ 3 ] . statusText } )
} else API . CSG . data = ( client . xjs . xml2js ( results [ 3 ] . data , { compact :true } ) as any ) . careerSavegame as FSCareerSavegame
} ) . catch ( ( err :Error ) = > console . error ( err . message ) )
2023-05-05 18:41:35 -04:00
2023-08-15 06:47:31 -04:00
if ( API . DSS . res . length != 0 ) {
2023-05-05 18:41:35 -04:00
error = true ;
2023-08-15 06:47:31 -04:00
if ( API . DSS . data . slots === undefined ) return ;
console . log ( client . logTime ( ) , API . DSS . res ) ;
} else if ( API . CSG . res . length != 0 ) {
2023-05-05 18:41:35 -04:00
error = true ;
2023-08-15 06:47:31 -04:00
console . log ( client . logTime ( ) , API . CSG . res ) ;
2023-05-05 18:41:35 -04:00
}
2023-08-15 06:47:31 -04:00
if ( error ) { // Nawdic broke it in his dream
embed . setTitle ( 'Host did not respond back in time' ) . setColor ( client . config . embedColorRed ) ;
return msg . edit ( { content :null , embeds : [ embed ] } )
2023-05-05 18:41:35 -04:00
}
//Timescale formatting
function formatTimescale ( number : number , digits :number , icon :string ) {
var n = Number ( number ) ;
return n . toLocaleString ( undefined , { minimumFractionDigits : digits } ) + icon
}
2023-06-11 09:37:22 -04:00
// Join/Leave log
2023-05-07 04:41:20 -04:00
function playerLogEmbed ( player :FSPlayer , joinLog :boolean ) {
const logEmbed = new client . embed ( ) . setDescription ( ` ** ${ player . name } ${ player . isAdmin ? '| admin' : '' } ** ${ joinLog ? 'joined' : 'left' } ** ${ ServerName } ** at <t: ${ Math . round ( Date . now ( ) / 1000 ) } :t> ` ) ;
if ( joinLog ) return logEmbed . setColor ( client . config . embedColorGreen ) ;
2023-06-14 08:08:54 -04:00
else if ( player . uptime > 0 ) return logEmbed . setColor ( client . config . embedColorRed ) . setFooter ( { text : ` Farmed for ${ client . formatPlayerUptime ( player . uptime ) } ` } ) ;
else return logEmbed . setColor ( client . config . embedColorRed ) ;
2023-05-07 04:41:20 -04:00
}
function playerLog ( ) {
// Player leaving
playersInCache . filter ( x = > ! playersOnServer . some ( y = > y . name === x . name ) ) . forEach ( player = > serverLog . send ( { embeds : [ playerLogEmbed ( player , false ) ] } ) ) ;
// Player joining
let playerObject ;
if ( playersInCache . length === 0 && ( client . uptime as number ) > 60010 ) playerObject = playersOnServer ;
else if ( playersInCache . length !== 0 ) playerObject = playersOnServer . filter ( x = > ! playersInCache . some ( y = > y . name === x . name ) ) ;
if ( playerObject ) playerObject . forEach ( x = > serverLog . send ( { embeds : [ playerLogEmbed ( x , true ) ] } ) ) ;
}
const serverIndicatorEmbed = ( indicator :string ) = > new client . embed ( ) . setTitle ( ` ** ${ ServerName } ** is now ${ indicator } ` ) . setColor ( client . config . embedColorOrange ) . setTimestamp ( ) ;
const serverLog = client . channels . resolve ( client . config . mainServer . channels . fs_server_log ) as Discord . TextChannel ;
2023-08-15 06:47:31 -04:00
const playersOnServer = API . DSS . data . slots ? . players . filter ( x = > x . isUsed ) ;
const playersInCache = client . MPServerCache [ ServerName ] . players ;
if ( ! playersOnServer ) return console . error ( client . logTime ( ) , '[MPLoop] Empty filter, ignoring...' ) ; // For the love of god, stop throwing errors everytime.
2023-05-07 04:41:20 -04:00
playersOnServer . forEach ( player = > playerData . push ( ` ** ${ player . name } ${ player . isAdmin ? '| admin' : '' } ** \ nFarming for ${ client . formatPlayerUptime ( player . uptime ) } ` ) ) ;
2023-08-15 06:47:31 -04:00
ServerName = client . MPServerCache [ ServerName ] . name ; // Truncate unnecessary name for the embed
if ( API . DSS . data . server . name === 'Official Daggerwin Game Server' ) client . MPServerCache [ 'main' ] . name = 'Daggerwin' ;
//Second server name is unknown, will come back and update this later.
2023-05-07 04:41:20 -04:00
2023-08-15 06:47:31 -04:00
if ( API . DSS . data . server . name . length === 0 ) {
2023-05-05 18:41:35 -04:00
embed . setTitle ( 'The server seems to be offline.' ) . setColor ( client . config . embedColorRed ) ;
2023-05-07 04:41:20 -04:00
msg . edit ( { content : 'This embed will resume when the server is back online.' , embeds : [ embed ] } ) ;
2023-08-15 06:47:31 -04:00
if ( client . MPServerCache [ ServerName ] . status === 'online' ) serverLog . send ( { embeds : [ serverIndicatorEmbed ( 'offline' ) ] } ) ;
client . MPServerCache [ ServerName ] . status = 'offline' ;
2023-05-05 18:41:35 -04:00
} else {
2023-08-15 06:47:31 -04:00
if ( client . MPServerCache [ ServerName ] . status === 'offline' ) {
2023-05-07 04:41:20 -04:00
serverLog . send ( { embeds : [ serverIndicatorEmbed ( 'online' ) ] } ) ;
isServerOnline = true
} ;
2023-08-15 06:47:31 -04:00
client . MPServerCache [ ServerName ] . status = 'online' ;
2023-05-05 18:41:35 -04:00
const statusEmbed = new client . embed ( ) . setColor ( client . config . embedColor ) . setTitle ( 'Server details' ) . setFields (
2023-08-15 06:47:31 -04:00
{ name : 'Current Map' , value : API.DSS.data.server.mapName.length === 0 ? '\u200b' : API . DSS . data . server . mapName , inline : true } ,
{ name : 'Version' , value : API.DSS.data.server.version.length === 0 ? '\u200b' : API . DSS . data . server . version , inline : true } ,
{ name : 'In-game Time' , value : ` ${ ( '0' + Math . floor ( ( API . DSS . data . server . dayTime / 3600 / 1000 ) ) ) . slice ( - 2 ) } : ${ ( '0' + Math . floor ( ( API . DSS . data . server . dayTime / 60 / 1000 ) % 60 ) ) . slice ( - 2 ) } ` , inline : true } ,
{ name : 'Slot Usage' , value : isNaN ( Number ( API . CSG . data . slotSystem ? . _attributes . slotUsage ) ) === true ? 'Unavailable' : Number ( API . CSG . data . slotSystem ? . _attributes . slotUsage ) . toLocaleString ( 'en-us' ) , inline : true } ,
{ name : 'Autosave Interval' , value : isNaN ( Number ( API . CSG . data . settings ? . autoSaveInterval . _text ) ) === true ? 'Unavailable' : Number ( API . CSG . data . settings ? . autoSaveInterval . _text ) . toFixed ( 0 ) + ' mins' , inline :true } ,
{ name : 'Timescale' , value : isNaN ( Number ( API . CSG . data . settings ? . timeScale . _text ) ) === true ? 'Unavailable' : formatTimescale ( Number ( API . CSG . data . settings ? . timeScale . _text ) , 0 , 'x' ) , inline : true }
2023-05-05 18:41:35 -04:00
) ;
2023-08-15 06:47:31 -04:00
embed . setColor ( client . config . embedColor ) . setTitle ( API . DSS . data . server . name ) . setDescription ( API . DSS . data . slots . used === 0 ? '*No players online*' : playerData . join ( '\n\n' ) ) . setAuthor ( { name : ` ${ API . DSS . data . slots . used } / ${ API . DSS . data . slots . capacity } ` } ) ;
2023-05-07 04:41:20 -04:00
msg . edit ( { content : 'This embed updates every minute.' , embeds : [ statusEmbed , embed ] } ) ;
}
if ( ! isServerOnline ) {
playerLog ( ) ;
2023-08-15 06:47:31 -04:00
const Database :Array < number > = JSON . parse ( readFileSync ( ` src/database/ ${ ServerName } PlayerData.json ` , { encoding : 'utf8' , flag : 'r+' } ) ) ;
Database . push ( API . DSS . data . slots ? . used ) ;
writeFileSync ( ` src/database/ ${ ServerName } PlayerData.json ` , JSON . stringify ( Database ) ) ;
client . MPServerCache [ ServerName ] . players = playersOnServer
2023-05-05 18:41:35 -04:00
}
}
2023-08-18 07:18:33 -04:00
* /