From a96cd9e8544502f7e0bd8a3c2e0401815a228b6f Mon Sep 17 00:00:00 2001 From: AnxietyisReal <96593068+AnxietyisReal@users.noreply.github.com> Date: Tue, 15 Aug 2023 20:47:31 +1000 Subject: [PATCH] Painful progress.. --- src/MPLoop.ts | 155 ++++++++++------ src/client.ts | 5 +- src/commands/mp.ts | 361 ++++++++---------------------------- src/config.json | 10 +- src/index.ts | 7 +- src/models/MPServer.ts | 16 +- src/typings/interfaces.d.ts | 23 ++- tsconfig.json | 2 +- 8 files changed, 216 insertions(+), 363 deletions(-) diff --git a/src/MPLoop.ts b/src/MPLoop.ts index 83a59d5..1453859 100644 --- a/src/MPLoop.ts +++ b/src/MPLoop.ts @@ -5,55 +5,95 @@ import {FSPlayer, FSData, FSCareerSavegame} from './typings/interfaces'; export default async(client:TClient,Channel:string,Message:string,ServerName:string)=>{ if (!client.config.botSwitches.mpstats) return; + const noContentImage = 'https://cdn.discordapp.com/attachments/1118960531135541318/1140906691236479036/68efx1.png'; const msg = await (client.channels.resolve(Channel) as Discord.TextChannel).messages.fetch(Message); const embed = new client.embed(); let playerData:Array = []; let error:Boolean; let isServerOnline = false; - const Server = await client.MPServer._content.findById(client.config.mainServer.id); - - const DSS = { - data: {} as FSData, fetchResult: '' as string - }; - const CSG = { - data: {} as FSCareerSavegame, fetchResult: '' as string - }; - - if (!Server?.ip?.match(/http|https/)) return msg.edit({content: '*Detected an invalid IP\nContact MP Manager or Bot Tech*', embeds: null}); - async function serverData(client:TClient, URL:string){ - return await client.axios.get(URL, {timeout: 5000, maxContentLength: Infinity, headers:{'User-Agent':`Daggerbot/axios ${client.axios.VERSION}`}}).catch((error:Error)=>error.message) + 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' + } } - await Promise.all([serverData(client, Server.ip+'/feed/dedicated-server-stats.json?code='+Server.code), serverData(client, Server.ip+'/feed/dedicated-server-savegame.html?code='+Server.code+'&file=careerSavegame')]).then(function(results){ - if (typeof results[0] === 'string'){ - DSS.fetchResult = `DagMP DSS failed, ${results[0]}`; + + // 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) + } + 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]}`; embed.addFields({name:'DSS Status',value:results[0]}) - } else if (results[0].status != 200){ - DSS.fetchResult = `DagMP DSS failed with ${results[0].status + ' ' + results[0].statusText}`; - embed.addFields({name:'DSS Status',value:results[0].status + ' ' + results[0].statusText}) - } else DSS.data = results[0].data as FSData + } 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 - if (typeof results[1] === 'string'){ - CSG.fetchResult = `DagMP CSG failed, ${results[1]}`; + // Main server's CSG + if (typeof results[1] === 'string') { + API.CSG.res = `DagMP:Main CSG failed, ${results[1]}`; embed.addFields({name:'CSG Status',value:results[1]}) - } else if (results[1].status != 200){ - if (results[1].status === 204) embed.setImage('https://httpcats.com/204.jpg'); - CSG.fetchResult = `DagMP CSG failed with ${results[1].status + ' ' + results[1].statusText}`; - embed.addFields({name:'CSG Status',value:results[1].status + ' ' + results[1].statusText}) - } else CSG.data = (client.xjs.xml2js(results[1].data,{compact:true}) as any).careerSavegame as FSCareerSavegame - }).catch(error=>console.error(error)); + } 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 - if (DSS.fetchResult.length != 0){ + // 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)) + + if (API.DSS.res.length != 0) { error = true; - if (DSS.data.slots === undefined) return; - console.log(client.logTime(), DSS.fetchResult); - } else if (CSG.fetchResult.length != 0){ + if (API.DSS.data.slots === undefined) return; + console.log(client.logTime(), API.DSS.res); + } else if (API.CSG.res.length != 0) { error = true; - console.log(client.logTime(), CSG.fetchResult); + console.log(client.logTime(), API.CSG.res); } - if (error){ // Blame Nawdic - embed.setTitle('Host is not responding').setColor(client.config.embedColorRed); - msg.edit({content:null, embeds: [embed]}) - return; + 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]}) } //Timescale formatting @@ -81,42 +121,43 @@ export default async(client:TClient,Channel:string,Message:string,ServerName:str 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; - const playersOnServer = DSS.data.slots?.players.filter(x=>x.isUsed); - const playersInCache = client.MPServerCache.players; - if (!playersOnServer) return console.error('[MPLoop] Empty filter, ignoring...'); // For the love of god, stop throwing errors everytime. + 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. playersOnServer.forEach(player=>playerData.push(`**${player.name} ${player.isAdmin ? '| admin' : ''}**\nFarming for ${client.formatPlayerUptime(player.uptime)}`)); - ServerName = client.MPServerCache.name; - if (DSS.data.server.name === 'Official Daggerwin Game Server') client.MPServerCache.name = 'Daggerwin'; + 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. - if (DSS.data.server.name.length === 0){ + if (API.DSS.data.server.name.length === 0){ embed.setTitle('The server seems to be offline.').setColor(client.config.embedColorRed); msg.edit({content: 'This embed will resume when the server is back online.', embeds: [embed]}); - if (client.MPServerCache.status === 'online') serverLog.send({embeds:[serverIndicatorEmbed('offline')]}); - client.MPServerCache.status = 'offline'; + if (client.MPServerCache[ServerName].status === 'online') serverLog.send({embeds:[serverIndicatorEmbed('offline')]}); + client.MPServerCache[ServerName].status = 'offline'; } else { - if (client.MPServerCache.status === 'offline'){ + if (client.MPServerCache[ServerName].status === 'offline'){ serverLog.send({embeds:[serverIndicatorEmbed('online')]}); isServerOnline = true }; - client.MPServerCache.status = 'online'; + client.MPServerCache[ServerName].status = 'online'; const statusEmbed = new client.embed().setColor(client.config.embedColor).setTitle('Server details').setFields( - {name: 'Current Map', value: DSS.data.server.mapName.length === 0 ? '\u200b' : DSS.data.server.mapName, inline: true}, - {name: 'Version', value: DSS.data.server.version.length === 0 ? '\u200b' : DSS.data.server.version, inline: true}, - {name: 'In-game Time', value: `${('0'+Math.floor((DSS.data.server.dayTime/3600/1000))).slice(-2)}:${('0'+Math.floor((DSS.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true}, - {name: 'Slot Usage', value: isNaN(Number(CSG.data.slotSystem?._attributes.slotUsage)) === true ? 'Unavailable' : Number(CSG.data.slotSystem?._attributes.slotUsage).toLocaleString('en-us'), inline: true}, - {name: 'Autosave Interval', value: isNaN(Number(CSG.data.settings?.autoSaveInterval._text)) === true ? 'Unavailable' : Number(CSG.data.settings?.autoSaveInterval._text).toFixed(0)+' mins', inline:true}, - {name: 'Timescale', value: isNaN(Number(CSG.data.settings?.timeScale._text)) === true ? 'Unavailable' : formatTimescale(Number(CSG.data.settings?.timeScale._text), 0, 'x'), inline: true} + {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} ); - embed.setColor(client.config.embedColor).setTitle(DSS.data.server.name).setDescription(DSS.data.slots.used === 0 ? '*No players online*' : playerData.join('\n\n')).setAuthor({name:`${DSS.data.slots.used}/${DSS.data.slots.capacity}`}); + 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}`}); msg.edit({content:'This embed updates every minute.',embeds:[statusEmbed,embed]}); } if (!isServerOnline){ playerLog(); - const Database:Array = JSON.parse(readFileSync('src/database/MPPlayerData.json',{encoding:'utf8',flag:'r+'})); - Database.push(DSS.data.slots?.used); - writeFileSync('src/database/MPPlayerData.json', JSON.stringify(Database)); - client.MPServerCache.players = playersOnServer + const Database:Array = 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 } } diff --git a/src/client.ts b/src/client.ts index 4288af6..bb1d0e8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -85,7 +85,10 @@ export default class TClient extends Client { this.punishments = new punishments(this); this.bannedWords = new bannedWords(this); this.MPServer = new MPServer(this); - this.MPServerCache = {players: [], status: null, name: null} as MPServerCache; + this.MPServerCache = { + main: { players: [], status: null, name: null }, + second: { players: [], status: null, name: null } + } as MPServerCache; this.suggestion = new suggestion(this); this.tags = new tags(this); this.repeatedMessages = {}; diff --git a/src/commands/mp.ts b/src/commands/mp.ts index 094bb60..411222a 100644 --- a/src/commands/mp.ts +++ b/src/commands/mp.ts @@ -4,303 +4,90 @@ import path from 'node:path'; import canvas from 'canvas'; import {readFileSync} from 'node:fs'; -async function MPdata(client:TClient, interaction:Discord.ChatInputCommandInteraction, embed: Discord.EmbedBuilder) { - let FSserver; - if (!await client.MPServer._content.findOne({_id:interaction.guildId})) return interaction.reply('This server isn\'t linked to the bot.'); - const ServerURL = await client.MPServer._content.findById(interaction.guildId); - if (!ServerURL) return interaction.reply(`No FS server found, please notify <@&${client.config.mainServer.roles.mpmanager}> to add it.`); - if (!ServerURL.ip.match(/http|https/)) return interaction.reply(`The server IP for this server is currently invalid, please notify <@&${client.config.mainServer.roles.mpmanager}>`); - - // Fetch dss - try {// I am aware timeout has decreased from 2800 to 2588 to fit within Discord's interaction timeouts (3s) -Toast - FSserver = await client.axios.get(ServerURL.ip+'/feed/dedicated-server-stats.json?code='+ServerURL.code, {timeout: 2588, headers: {'User-Agent': `Daggerbot - mp cmd/axios ${client.axios.VERSION}`}}) - } catch(err) { - // Blame Nawdic - embed.setTitle('Host is not responding.'); - embed.setColor(client.config.embedColorRed); - console.log(client.logTime(), 'DagMP failed to fetch, host didn\'t respond in time.'); - return interaction.reply('Server didn\'t respond in time, please try again later.'); - } return FSserver -} - export default { run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + async function hitEndpoint(){ + const array = (await client.MPServer?._content.find())?.map(x=>x._id).filter(c=>['mainServer','secondServer'].includes(c)); + console.log(array?.map(c=>c)); + + /* const database = { + mainServer: (await client.MPServer._content.findById(interaction.guildId)).mainServer, + secondServer: (await client.MPServer._content.findById(interaction.guildId)).secondServer + } + const endpoint = '/feed/dedicated-server-stats.json?code='; + if (serverSelector === 'mainServer') return database.mainServer.ip+endpoint+database.mainServer.code; + else if (serverSelector === 'secondServer') return database.secondServer.ip+endpoint+database.secondServer.code; + const Server = await client.axios.get(serverSelector, { + timeout: 7500, + headers: {'User-Agent':`Daggerbot - mp cmd/axios ${client.axios.VERSION}`} + }) */ + } + if (interaction.channelId === '468835769092669461' && !client.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) { interaction.reply(`Please use <#739084625862852715> for \`/mp status/players\` commands to prevent clutter in this channel.`).then(()=>setTimeout(()=>interaction.deleteReply(), 6000)); return; } ({ status: async()=>{ - const embed0 = new client.embed(); - const FSserver0 = await MPdata(client, interaction, embed0); - if (!FSserver0?.data) return console.log('FSserver0 failed - status'); - try { - if (FSserver0.data.server.name.length > 1){ - interaction.reply({embeds: [embed0.setTitle('Status/Details').setColor(client.config.embedColor).addFields( - {name: 'Server name', value: `${FSserver0?.data.server.name.length == 0 ? '\u200b' : `\`${FSserver0?.data.server.name}\``}`, inline: true}, - {name: 'Players', value: `${FSserver0.data.slots.used} out of ${FSserver0.data.slots.capacity}`, inline: true}, - {name: 'Current map', value: `${FSserver0?.data.server.mapName.length == 0 ? '\u200b' : FSserver0.data.server.mapName}`, inline: true}, - {name: 'Version', value: `${FSserver0?.data.server.version.length == 0 ? '\u200b' : FSserver0.data.server.version}`, inline: true}, - {name: 'In-game Time', value: `${('0' + Math.floor((FSserver0.data.server.dayTime/3600/1000))).slice(-2)}:${('0' + Math.floor((FSserver0.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true} - )]}) - } else if (FSserver0.data.server.name.length === 0) interaction.reply('Server is currently offline.') - } catch (err){ - console.log(err) - interaction.reply('FSserver0 Error placeholder') - }; + hitEndpoint() + interaction.reply('x') }, - info: async()=>{ - const embed2 = new client.embed().setColor(client.config.embedColor) - const FSserver2 = await MPdata(client, interaction, embed2) - if (!FSserver2?.data) return console.log('FSserver2 failed - info') - const MPURL = await client.MPServer._content.findById(interaction.guildId); - if (FSserver2.data.server.name.length == 0) embed2.setFooter({text: 'Server is currently offline.'}) - interaction.reply({embeds: [embed2.setDescription([ - `**Server name**: \`${FSserver2?.data.server.name.length === 0 ? '\u200b' : FSserver2?.data.server.name}\``, - '**Password:** `mf4700`', - '**Crossplay server**', - `**Map:** ${FSserver2.data.server.mapName.length == 0 ? 'Null Island' : FSserver2.data.server.mapName}`, - `**Mods:** [Click here](${MPURL.ip}/mods.html) **|** [Direct Download](${MPURL.ip}/all_mods_download?onlyActive=true)`, - '**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)', - 'Please see <#543494084363288637> for additional information.' - ].join('\n'))]}); - }, - url: async()=>{ - if (client.config.mainServer.id == interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager'); - } - const address = interaction.options.getString('address'); - if (!address){ - try { - const Url = await client.MPServer._content.findById(interaction.guildId); - if (Url.ip && Url.code) return interaction.reply(`${Url.get('ip')}`+'/feed/dedicated-server-stats.json?code='+`${Url.get('code')}`) - } catch(err){ - console.log(`MPDB :: ${err}`); - interaction.reply('**Database error:**\nThis server does not have the URL saved, try adding one.') - } - }else{ - if (!address.match(/dedicated-server-stats/)) return interaction.reply('The URL does not match `dedicated-server-stats.xml`'); - const newURL = address.replace('xml','json').split('/feed/dedicated-server-stats.json?code='); - try{ - console.log(`MPDB :: URL for ${interaction.guild.name} has been updated by ${interaction.member.displayName} (${interaction.member.id})`); - await client.MPServer._content.create({_id: interaction.guildId, ip: newURL[0], code: newURL[1], timesUpdated: 0}); - return interaction.reply('This server is now linked and URL has been added.'); - } catch(err){ - const affectedValues = await client.MPServer._content.findByIdAndUpdate({_id: interaction.guildId}, {ip: newURL[0], code: newURL[1]}); - await client.MPServer._increment(interaction.guildId); - if (affectedValues) return interaction.reply('URL successfully updated.') - } - } - }, - players: async()=>{ - const data = JSON.parse(readFileSync(path.join('src/database/MPPlayerData.json'), {encoding: 'utf8'})).slice(client.statsGraph) - // handle negative days - data.forEach((change: number, i: number) => { - if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0; - }); - - const first_graph_top = 16; - const second_graph_top = 16; - const textSize = 40; - - const img = canvas.createCanvas(1500, 750); - const ctx = img.getContext('2d'); - - const graphOrigin = [15, 65]; - const graphSize = [1300, 630]; - const nodeWidth = graphSize[0] / (data.length - 1); - ctx.fillStyle = '#36393f'; //'#111111'; - ctx.fillRect(0, 0, img.width, img.height); - - // grey horizontal lines - ctx.lineWidth = 5; - - let interval_candidates = []; - for (let i = 4; i < 10; i++) { - const interval = first_graph_top / i; - if (Number.isInteger(interval)) { - let intervalString = interval.toString(); - const reference_number = i * Math.max(intervalString.split('').filter(x => x === '0').length / intervalString.length, 0.3) * (['1', '2', '4', '5', '6', '8'].includes(intervalString[0]) ? 1.5 : 0.67) - interval_candidates.push([interval, i, reference_number]); - } - } - const chosen_interval = interval_candidates.sort((a, b) => b[2] - a[2])[0]; - - const previousY: Array = []; - - ctx.strokeStyle = '#202225'; //'#555B63'; - for (let i = 0; i <= chosen_interval[1]; i++) { - const y = graphOrigin[1] + graphSize[1] - (i * (chosen_interval[0] / second_graph_top) * graphSize[1]); - if (y < graphOrigin[1]) continue; - const even = ((i + 1) % 2) === 0; - if (even) ctx.strokeStyle = '#2c2f33'; //'#3E4245'; - ctx.beginPath(); - ctx.lineTo(graphOrigin[0], y); - ctx.lineTo(graphOrigin[0] + graphSize[0], y); - ctx.stroke(); - ctx.closePath(); - if (even) ctx.strokeStyle = '#202225'; //'#555B63'; - previousY.push(y, i * chosen_interval[0]); - } - - // 30m mark - ctx.setLineDash([8, 16]); - ctx.beginPath(); - const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30)); - ctx.lineTo(lastMonthStart, graphOrigin[1]); - ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]); - ctx.stroke(); - ctx.closePath(); - ctx.setLineDash([]); - - // draw points - ctx.lineWidth = 5; - - function getYCoordinate(value: number) { - return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1]; - } - - function colorAtPlayercount(playercount: number) { - if (playercount === first_graph_top) return client.config.embedColorRed as string; - else if (playercount > 9) return client.config.embedColorYellow as string; - else return client.config.embedColorGreen as string - } - let lastCoords: Array = []; - data.forEach((curPC: number /* current player count */, i: number) => { - if (curPC < 0) curPC = 0; - const x = i * nodeWidth + graphOrigin[0]; - const y = getYCoordinate(curPC); - const nexPC /* next player count */ = data[i + 1]; - const prvPC /* previous player count */ = data[i - 1]; - const curColor = colorAtPlayercount(curPC); // color now - const prvColor = colorAtPlayercount(prvPC); // color at last point - if (curColor !== prvColor && !isNaN(prvPC) && lastCoords.length > 0) { // gradient should be used when the color between now and last point is not the same - // gradient from now to last point - const grd = ctx.createLinearGradient(lastCoords[0], lastCoords[1], x, y); - grd.addColorStop(0, colorAtPlayercount(prvPC)); // prev color at the beginning - grd.addColorStop(1, colorAtPlayercount(curPC)); // cur color at the end - // special case: playercount rises or falls rapidly accross all colors (eg. straight from red to green) - if (curColor !== client.config.embedColorYellow && prvColor !== client.config.embedColorYellow) { - const yellowY = getYCoordinate(10); // y coordinate at which line should be yellow - const stop = (yellowY - lastCoords[1]) / (y - lastCoords[1]); // between 0 and 1, where is yellowY between y and nextPointCoords[1] ? - grd.addColorStop(stop, client.config.embedColorYellow as string); // add a yellow stop to the gradient - } - ctx.strokeStyle = grd; - } else ctx.strokeStyle = colorAtPlayercount(curPC); - ctx.beginPath(); - if (lastCoords.length > 0) ctx.moveTo(lastCoords[0], lastCoords[1]); - // if the line being drawn is horizontal, make it go until it has to go down - if (y === lastCoords[1]) { - let newX = x; - for (let j = i + 1; j <= data.length; j++) { - if (data[j] === curPC) newX += nodeWidth; else break; - } - ctx.lineTo(newX, y); - } else ctx.lineTo(x, y); - lastCoords = [x, y]; - ctx.stroke(); - ctx.closePath(); - - if (curPC === prvPC && curPC === nexPC) return; // no ball because no vertical difference to next or prev point - else { - // ball - ctx.fillStyle = colorAtPlayercount(curPC); - ctx.beginPath(); - ctx.arc(x, y, ctx.lineWidth * 1.3, 0, 2 * Math.PI) - ctx.closePath(); - ctx.fill(); - } - }); - - // draw text - ctx.font = '400 ' + textSize + 'px sans-serif'; - ctx.fillStyle = 'white'; - - // highest value - const maxx = graphOrigin[0] + graphSize[0] + textSize / 2; - const maxy = previousY[0] + (textSize / 3); - ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy); - - // lowest value - const lowx = graphOrigin[0] + graphSize[0] + textSize / 2; - const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3); - ctx.fillText('0 players', lowx, lowy); - - // 30m - ctx.fillText('30 mins ago', lastMonthStart, graphOrigin[1] - (textSize / 2)); - - // time -> - const tx = graphOrigin[0] + (textSize / 2); - const ty = graphOrigin[1] + graphSize[1] + (textSize); - ctx.fillText('time ->', tx, ty); - - const embed1 = new client.embed(); - const FSserver1 = await MPdata(client, interaction, embed1) - if (!FSserver1?.data) return console.log('FSserver1 failed - players') - embed1.setTitle(FSserver1?.data.server.name.length === 0 ? 'Offline' : FSserver1?.data.server.name) - .setDescription(`${FSserver1?.data.slots.used}/${FSserver1?.data.slots.capacity}`) - .setColor(FSserver1?.data.server.name.length === 0 ? client.config.embedColorRed : client.config.embedColor) - .setImage('attachment://FSStats.png'); - FSserver1?.data.slots.players.filter(x=>x.isUsed).forEach(player=>embed1.addFields({name: `${player.name} ${player.isAdmin ? '| admin' : ''}`, value: `Farming for ${client.formatPlayerUptime(player.uptime)}`})) - interaction.reply({embeds: [embed1], files: [new client.attachmentBuilder(img.toBuffer(),{name:'FSStats.png'})]}) - }, - maintenance: ()=>{ - if (client.config.mainServer.id == interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager'); - } - const maintenanceMessage = interaction.options.getString('message'); - const activePlayersChannel = '739084625862852715'; - const channel = (client.channels.cache.get(activePlayersChannel) as Discord.TextChannel); - if (channel.permissionOverwrites.cache.get(interaction.guildId).deny.has('SendMessages')) { - channel.permissionOverwrites.edit(interaction.guildId, {SendMessages: true}, {type: 0, reason: `Unlocked by ${interaction.member.displayName}`}); - channel.send({embeds: [new client.embed().setColor(client.config.embedColor).setAuthor({name: interaction.member.displayName, iconURL: interaction.member.displayAvatarURL({size:1024})}).setTitle('🔓 Channel unlocked').setDescription(`**Reason:**\n${maintenanceMessage}`).setTimestamp()]}); - interaction.reply({content: `<#${activePlayersChannel}> has been unlocked!`, ephemeral: true}); - } else if (channel.permissionOverwrites.cache.get(interaction.guildId).allow.has('SendMessages')) { - channel.permissionOverwrites.edit(interaction.guildId, {SendMessages: false}, {type: 0, reason: `Locked by ${interaction.member.displayName}`}); - channel.send({embeds: [new client.embed().setColor(client.config.embedColor).setAuthor({name: interaction.member.displayName, iconURL: interaction.member.displayAvatarURL({size:1024})}).setTitle('🔒 Channel locked').setDescription(`**Reason:**\n${maintenanceMessage}`).setTimestamp()]}); - interaction.reply({content: `<#${activePlayersChannel}> has been locked!`, ephemeral: true}); - } - }/*, - series: ()=>{ - const embed3 = new client.embed().setColor(client.config.embedColor).setTitle('How to join the Daggerwin MP series') - .setDescription([ - 'To join the Daggerwin MP series, you first need to:', - '**1:** Note that only PC players can join the MP series due to the mods that are used.', - '**2:** Become a YouTube Member by pressing the `Join` button on [Daggerwin\'s YouTube page](https://www.youtube.com/c/Daggerwin) next to the `Subscribe` button.', - '**3:** Link your YouTube account to your Discord account via Settings>Connections>Add connection. Be sure that you link the same YouTube account you used to become a channel member.', - '**4:** If you don\'t receive the role within a day or so, please message an Admin and they will sort it out.', - '**5:** Take a look in <#511657659364147200> to get information on how to join the server.' - ].join('\n')); - interaction.reply({embeds: [embed3]}) - }*/ + info: async()=>{}, + url: async()=>{}, + players: async()=>{} } as any)[interaction.options.getSubcommand()](); }, data: new Discord.SlashCommandBuilder() - .setName('mp') - .setDescription('Display MP status and other things') - .addSubcommand(x=>x - .setName('status') - .setDescription('Check server status and details')) - .addSubcommand(x=>x - .setName('players') - .setDescription('Check who\'s playing on the server')) - .addSubcommand(x=>x - .setName('info') - .setDescription('Provides you with server information such as filters and so on')) - .addSubcommand(x=>x - .setName('url') - .setDescription('View the URL for this server\'s FSMP server or update the URL') - .addStringOption(x=>x - .setName('address') - .setDescription('Insert a \'dedicated-server-stats\' URL'))) - .addSubcommand(x=>x - .setName('maintenance') - .setDescription('Lock/unlock "#mp-active-players" channel when server is unavailable to the public') - .addStringOption(x=>x - .setName('message') - .setDescription('The reason why is the server unavailable for?') - .setRequired(true)))/* - .addSubcommand(x=>x - .setName('series') - .setDescription('Step-by-step on joining Daggerwin\'s MP series'))*/ + .setName('mp') + .setDescription('Display MP status and other things') + .addSubcommand(x=>x + .setName('status') + .setDescription('Check server status and details') + .addStringOption(x=>x + .setName('server') + .setDescription('Which server to pull info from') + .setChoices( + {name: 'Main Server', value: 'mainServer'}, + {name: 'Second Server', value: 'secondServer'}) + .setRequired(true))) + .addSubcommand(x=>x + .setName('players') + .setDescription('Check who\'s playing on the server') + .addStringOption(x=>x + .setName('server') + .setDescription('Which server to pull the info from') + .setChoices( + {name: 'Main Server', value: 'mainServer'}, + {name: 'Second Server', value: 'secondServer'}) + .setRequired(true))) + .addSubcommand(x=>x + .setName('info') + .setDescription('Provides you with server information such as filters and so on') + .addStringOption(x=>x + .setName('server') + .setDescription('Which server to pull the info from') + .setChoices( + {name: 'Main Server', value: 'mainServer'}, + {name: 'Second Server', value: 'secondServer'}) + .setRequired(true))) + .addSubcommand(x=>x + .setName('url') + .setDescription('View the URL for this server\'s FSMP server or update the URL') + .addStringOption(x=>x + .setName('server') + .setDescription('Which server to view/update the URL') + .setChoices( + {name: 'Main Server', value: 'mainServer'}, + {name: 'Second Server', value: 'secondServer'}) + .setRequired(true)) + .addStringOption(x=>x + .setName('address') + .setDescription('Insert a \'dedicated-server-stats\' URL'))) + .addSubcommand(x=>x + .setName('maintenance') + .setDescription('Lock/unlock "#mp-active-players" channel when server is unavailable to the public') + .addStringOption(x=>x + .setName('message') + .setDescription('The reason why is the server unavailable for?') + .setRequired(true))) } diff --git a/src/config.json b/src/config.json index f5e970c..943c242 100644 --- a/src/config.json +++ b/src/config.json @@ -12,8 +12,14 @@ "929807948748832798", "468835415093411861", "1058183358267543552", "549114074273677314" ], "MPStatsLocation": { - "channel": "543494084363288637", - "message": "1023699243183112192" + "main": { + "channel": "543494084363288637", + "message": "1023699243183112192" + }, + "second": { + "channel": "", + "message": "" + } }, "botSwitches": { "dailyMsgsBackup": true, diff --git a/src/index.ts b/src/index.ts index db3eb15..54eb02b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -66,11 +66,14 @@ if (client.config.botSwitches.music){ } // YouTube Upload notification and Daggerwin MP loop -setInterval(()=>MPLoop(client, client.config.MPStatsLocation.channel, client.config.MPStatsLocation.message, 'Daggerwin'), 60000); +setInterval(()=>{ + MPLoop(client, client.config.MPStatsLocation.main.channel, client.config.MPStatsLocation.main.message, 'Daggerwin') + MPLoop(client, client.config.MPStatsLocation.second.channel, client.config.MPStatsLocation.second.message, 'SecondServer') +}, 60000); setInterval(async()=>{ client.YTLoop('UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '528967918772551702'); // 528967918772551702 = #videos-and-streams client.YTLoop('UCguI73--UraJpso4NizXNzA', 'Machinery Restorer', '767444045520961567') // 767444045520961567 = #machinery-restorer -}, 600000) +}, 300000) // Event loop for punishments and daily msgs setInterval(async()=>{ diff --git a/src/models/MPServer.ts b/src/models/MPServer.ts index 8d7e8b8..fc83a4d 100644 --- a/src/models/MPServer.ts +++ b/src/models/MPServer.ts @@ -3,9 +3,14 @@ import mongoose from 'mongoose'; const Schema = mongoose.model('mpserver', new mongoose.Schema({ _id: {type: String, required:true}, - ip: {type: String}, - code: {type: String}, - timesUpdated: {type: Number, required: true} + mainServer: {required:true, type: new mongoose.Schema({ + ip: {type: String, required:true}, + code: {type: String, required:true} + }, {versionKey: false})}, + secondServer: {required:true, type: new mongoose.Schema({ + ip: {type: String, required:true}, + code: {type: String, required:true} + }, {versionKey: false})}, }, {versionKey: false})); export default class MPServer extends Schema { @@ -16,9 +21,4 @@ export default class MPServer extends Schema { this.client = client; this._content = Schema; } - async _increment(serverId: string){ - const server = await this._content.findById(serverId) - if (server) await this._content.findByIdAndUpdate(server, {timesUpdated: server.timesUpdated + 1}) - else await this._content.create({_id:serverId, timesUpdated: 1}) - } } diff --git a/src/typings/interfaces.d.ts b/src/typings/interfaces.d.ts index c59d698..4e05c10 100644 --- a/src/typings/interfaces.d.ts +++ b/src/typings/interfaces.d.ts @@ -137,8 +137,14 @@ export interface Config { LRSstart: number, whitelistedServers: Array, MPStatsLocation: { - channel: string, - message: string + main: { + channel: string + message: string + }, + second: { + channel: string + message: string + } }, botSwitches: { dailyMsgsBackup: boolean, @@ -187,7 +193,14 @@ export interface Config { } } export interface MPServerCache { - players: FSPlayer[], - status: 'online' | 'offline' | null, - name: string | null + main: { + players: FSPlayer[], + status: 'online' | 'offline' | null, + name: string | null + }, + second: { + players: FSPlayer[], + status: 'online' | 'offline' | null, + name: string | null + } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 4d5971f..82249c8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,5 @@ "typeRoots": [ "./src/typings" ], }, "include": [ "src/" ], - "exclude": [ ".yarn/cache", ".yarn/unplugged",".git", "src/config.json", "src/DB-Beta.config.json", "src/Toast-Testbot.config.json" ] + "exclude": [ ".yarn/cache", ".yarn/unplugged",".git", "src/config.json", "src/DB-Beta.config.json", "src/Toast-Testbot.config.json", "src/disabled" ] }