From 252d386a8920b1adf2d61dddeb43a1aa435d6da1 Mon Sep 17 00:00:00 2001 From: AnxietyisReal <96593068+AnxietyisReal@users.noreply.github.com> Date: Fri, 10 Feb 2023 08:45:52 +1100 Subject: [PATCH] Use Object literal lookups and add User-Agent --- src/commands/bannedWords.ts | 16 +- src/commands/case.ts | 82 +++++----- src/commands/dev.ts | 39 ++--- src/commands/faq.ts | 36 ++--- src/commands/mp.ts | 134 ++++++++-------- src/commands/rank.ts | 312 ++++++++++++++++++------------------ src/commands/whois.ts | 38 +++-- 7 files changed, 315 insertions(+), 342 deletions(-) diff --git a/src/commands/bannedWords.ts b/src/commands/bannedWords.ts index ddcdf1f..1cb4d59 100644 --- a/src/commands/bannedWords.ts +++ b/src/commands/bannedWords.ts @@ -3,22 +3,20 @@ import TClient from 'src/client'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (!client.isStaff(interaction.member) && !client.config.eval.whitelist.includes(interaction.member.id)) return client.youNeedRole(interaction, 'admin') - const Sub = interaction.options.getSubcommand(); const word = interaction.options.getString('word'); - switch(Sub){ - case 'view': - interaction.reply({content: 'Here is a complete list of banned words!\n*You can open it with a web browser, e.g Chrome/Firefox/Safari, or you can use Visual Studio Code/Notepad++*', files: ['src/database/bannedWords.json'], ephemeral: true}) - break; - case 'add': + ({ + add: ()=>{ if (client.bannedWords._content.includes(word)) return interaction.reply({content: `\`${word}\` is already added.`, ephemeral: true}); client.bannedWords.addData(word).forceSave(); interaction.reply(`Successfully added \`${word}\` to the list.`) - break; - case 'remove': + }, + remove: ()=>{ if (client.bannedWords._content.includes(word) == false) return interaction.reply({content: `\`${word}\` doesn't exist on the list.`, ephemeral: true}); client.bannedWords.removeData(word, 0, 0).forceSave(); interaction.reply(`Successfully removed \`${word}\` from the list.`) - } + }, + view: ()=>interaction.reply({content: 'Here is a complete list of banned words!\n*You can open it with a web browser, e.g Chrome/Firefox/Safari, or you can use Visual Studio Code/Notepad++*', files: ['src/database/bannedWords.json'], ephemeral: true}) + } as any)[interaction.options.getSubcommand()](); }, data: new SlashCommandBuilder() .setName('bannedwords') diff --git a/src/commands/case.ts b/src/commands/case.ts index 8d49f33..9145897 100644 --- a/src/commands/case.ts +++ b/src/commands/case.ts @@ -4,50 +4,48 @@ import { Punishment } from "src/typings/interfaces"; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod'); - const Subb = interaction.options.getSubcommand(); const caseId = interaction.options.getInteger('id'); - if (Subb == 'update'){ - const reason = interaction.options.getString('reason') - client.punishments._content.find((x:Punishment)=>x.id==caseId).reason = reason; - client.punishments.forceSave(); - const embed = new client.embed().setColor(client.config.embedColorGreen).setTitle('Case updated').setDescription(`Case #${caseId} has been successfully updated with new reason:\n\`${reason}\``); - await interaction.reply({embeds: [embed]}) - } else if (Subb == 'view'){ - const punishment = client.punishments._content.find((x:Punishment)=>x.id==caseId); - if (!punishment) return interaction.reply('Invalid case #'); - const cancelledBy = punishment.expired ? client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id) : null; - const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null; - const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${client.formatPunishmentType(punishment, client, cancels)} | Case #${punishment.id}`).addFields( - {name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true}, - {name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true}, - {name: '\u200b', value: '\u200b', inline: true}, - {name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true}) - if (punishment.duration){ - embed.addFields({name: '🔹 Duration', value: client.formatTime(punishment.duration, 100)}) + ({ + update: async()=>{ + const reason = interaction.options.getString('reason'); + client.punishments._content.find((x:Punishment)=>x.id==caseId).reason = reason; + client.punishments.forceSave(); + await interaction.reply({embeds: [new client.embed().setColor(client.config.embedColorGreen).setTitle('Case updated').setDescription(`Case #${caseId} has been successfully updated with new reason:\n\`${reason}\``)]}) + }, + view: ()=>{ + const punishment = client.punishments._content.find((x:Punishment)=>x.id==caseId); + if (!punishment) return interaction.reply('Invalid case #'); + const cancelledBy = punishment.expired ? client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id) : null; + const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null; + const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${client.formatPunishmentType(punishment, client, cancels)} | Case #${punishment.id}`).addFields( + {name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true}, + {name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true}, + {name: '\u200b', value: '\u200b', inline: true}, + {name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true}) + if (punishment.duration) embed.addFields({name: '🔹 Duration', value: client.formatTime(punishment.duration, 100)}) + if (punishment.expired) embed.addFields({name: '🔹 Expired', value: `This case has been overwritten by case #${cancelledBy.id} for reason \`${cancelledBy.reason}\``}) + if (punishment.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites case #${cancels.id} with reason \`${cancels.reason}\``}) + interaction.reply({embeds: [embed]}); + }, + member: ()=>{ + // if caseid is user id, show their punishment history sorted by most recent. + const user = (interaction.options.getUser('user') as Discord.User); + if (user.bot) return interaction.reply(`<@${user.id}>'s punishment history cannot be viewed.`) + const punishment = client.punishments._content.find((x:Punishment)=>x.member===user.id); + if (!punishment) return interaction.reply(`<@${user.id}> has a clean record.`) + const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null; + const userPunishment = client.punishments._content.filter((x:Punishment)=>x.member==user.id).sort((a:Punishment,b:Punishment)=>a.time-b.time).map((punishment:Punishment)=>{ + return { + name: `${client.formatPunishmentType(punishment, client, cancels)} | Case #${punishment.id}`, + value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${client.formatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by case #${client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id).id}` : ''}${punishment.cancels ? `\nOverwrites case #${punishment.cancels}` : ''}` + } + }); + // if caseid is not a punishment nor a user, failed + if (!userPunishment || userPunishment.length == 0) return interaction.reply('No punishments found for that case # or User ID'); + const pageNum = interaction.options.getInteger('page') ?? 1; + return interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle(`${user.username}'s punishment history`).setDescription(`**ID:** \`${user.id}\``).setFooter({text: `${userPunishment.length} total punishments. Viewing page ${pageNum} out of ${Math.ceil(userPunishment.length/6)}.`}).addFields(userPunishment.slice((pageNum - 1) * 6, pageNum * 6))]}); } - if (punishment.expired) embed.addFields({name: '🔹 Expired', value: `This case has been overwritten by case #${cancelledBy.id} for reason \`${cancelledBy.reason}\``}) - if (punishment.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites case #${cancels.id} with reason \`${cancels.reason}\``}) - interaction.reply({embeds: [embed]}); - } else { - // if caseid is user id, show their punishment history sorted by most recent. - const user = (interaction.options.getUser('user') as Discord.User); - if (user.bot) return interaction.reply(`<@${user.id}>'s punishment history cannot be viewed.`) - const punishment = client.punishments._content.find((x:Punishment)=>x.member===user.id); - if (!punishment) return interaction.reply(`<@${user.id}> has a clean record.`) - const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null; - const userPunishment = client.punishments._content.filter((x:Punishment)=>x.member==user.id).sort((a:Punishment,b:Punishment)=>a.time-b.time).map((punishment:Punishment)=>{ - return { - name: `${client.formatPunishmentType(punishment, client, cancels)} | Case #${punishment.id}`, - value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${client.formatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by case #${client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id).id}` : ''}${punishment.cancels ? `\nOverwrites case #${punishment.cancels}` : ''}` - } - }); - // if caseid is not a punishment nor a user, failed - if (!userPunishment || userPunishment.length == 0) return interaction.reply('No punishments found for that case # or User ID'); - - const pageNum = interaction.options.getInteger('page') ?? 1; - const embed = new client.embed().setColor(client.config.embedColor).setTitle(`${user.username}'s punishment history`).setDescription(`**ID:** \`${user.id}\``).setFooter({text: `${userPunishment.length} total punishments. Viewing page ${pageNum} out of ${Math.ceil(userPunishment.length/6)}.`}).addFields(userPunishment.slice((pageNum - 1) * 6, pageNum * 6)); - return interaction.reply({embeds: [embed]}); - } + } as any)[interaction.options.getSubcommand()](); }, data: new SlashCommandBuilder() .setName('case') diff --git a/src/commands/dev.ts b/src/commands/dev.ts index dbe12e3..839bd7a 100644 --- a/src/commands/dev.ts +++ b/src/commands/dev.ts @@ -22,8 +22,8 @@ const removeUsername = (text: string)=>{ export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>) { if (!client.config.eval.whitelist.includes(interaction.user.id)) return client.youNeedRole(interaction, 'bottech'); - switch (interaction.options.getSubcommand()){ - case 'eval': + ({ + eval: async()=>{ if (!client.config.eval.allowed) return interaction.reply({content: 'Eval is disabled.', ephemeral: true}); const code = interaction.options.getString('code') as string; let output = 'error'; @@ -59,16 +59,8 @@ export default { {name: 'Output', value: `\`\`\`${removeUsername(output).slice(0,1016)}\n\`\`\``} ); interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})); - break - case 'logs': - interaction.deferReply(); - (client.channels.resolve(client.config.mainServer.channels.console) as Discord.TextChannel).send({content: `Uploaded the current console dump as of `, files: [`${process.env.pm2_home}/logs/Daggerbot-out-0.log`, `${process.env.pm2_home}/logs/Daggerbot-error-0.log`]}).then(()=>interaction.editReply('It has been uploaded to dev server.')).catch((e:Error)=>interaction.editReply(`\`${e.message}\``)) - break - case 'restart': - client.userLevels.forceSave(); - interaction.reply(`Uptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot')) - break - case 'update': + }, + update: async()=>{ var githubRepo = {owner: 'AnxietyisReal', repo: 'Daggerbot-TS', ref: 'HEAD'} const octokit = new Octokit({timeZone: 'Australia/NSW', userAgent: 'Daggerbot'}) const fetchCommitMsg = await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.message); @@ -83,12 +75,8 @@ export default { setTimeout(()=>{clarkson.edit(`Commit: **${fetchCommitMsg}**\nCommit author: **${fetchCommitAuthor}**\n\nUptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot'))},650) } }); - break - case 'statsgraph': - client.statsGraph = -(interaction.options.getInteger('number', true)) - interaction.reply(`Successfully set to \`${client.statsGraph}\`\n*Total data points: **${JSON.parse(readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).length.toLocaleString()}***`); - break - case 'presence': + }, + presence: ()=>{ function convertType(Type?: number){ switch (Type) { case 0: return 'Playing'; @@ -117,7 +105,20 @@ export default { `Name: **${currentActivities[0].name}**`, `URL: \`${currentActivities[0].url}\`` ].join('\n')) - } + }, + statsgraph: ()=>{ + client.statsGraph = -(interaction.options.getInteger('number', true)); + interaction.reply(`Successfully set to \`${client.statsGraph}\`\n*Total data points: **${JSON.parse(readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).length.toLocaleString()}***`) + }, + logs: ()=>{ + interaction.deferReply(); + (client.channels.resolve(client.config.mainServer.channels.console) as Discord.TextChannel).send({content: `Uploaded the current console dump as of `, files: [`${process.env.pm2_home}/logs/Daggerbot-out-0.log`, `${process.env.pm2_home}/logs/Daggerbot-error-0.log`]}).then(()=>interaction.editReply('It has been uploaded to dev server.')).catch((e:Error)=>interaction.editReply(`\`${e.message}\``)) + }, + restart: ()=>{ + client.userLevels.forceSave(); + interaction.reply(`Uptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot')) + } + } as any)[interaction.options.getSubcommand()](); }, data: new SlashCommandBuilder() .setName('dev') diff --git a/src/commands/faq.ts b/src/commands/faq.ts index 7ce1f35..b3f4e12 100644 --- a/src/commands/faq.ts +++ b/src/commands/faq.ts @@ -2,33 +2,15 @@ import Discord,{SlashCommandBuilder} from 'discord.js'; import TClient from 'src/client'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - switch(interaction.options.getString('question')){ - case 'srp': - const embed = new client.embed().setColor(client.config.embedColor).setTitle('When will SRP (Survival Roleplay) return?').setImage('https://cdn.discordapp.com/attachments/1023338018720989305/1059259250242752562/IMG_8964.png'); - interaction.reply({embeds: [embed]}) - break; - case 'dlskin': - const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Daggerwin Logistics hex code').setDescription('The main color will be Onyx (`#353839`) with red bumpers.').setImage('https://cdn.discordapp.com/attachments/801965516947324969/806871878736019456/image0.png'); - interaction.reply({embeds: [embed1]}) - break; - case 'vtcR': - interaction.reply(`You can get the <@&${client.config.mainServer.roles.vtcmember}> role from <#802283932430106624> by reacting the ProBot\'s message with :truck:\n*VTC skin can also be found in <#801975222609641472> as well.*`) - break; - case 'mpR': - interaction.reply(`You can get the <@&${client.config.mainServer.roles.mpplayer}> role from <#802283932430106624> by reacting the ProBot\'s message with :tractor:`) - break; - case 'fsShader': - const embed2 = new client.embed().setColor(client.config.embedColor).setTitle('Clearing your shader cache folder').setDescription('If your game kees crashing shortly after opening your game, then the shaders might be an issue.\nTo resolve this, you can go to `Documents/My Games/FarmingSimulator2022` and delete the folder called `shader_cache`').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195687970943016/unknown.png'); - interaction.reply({embeds: [embed2]}) - break; - case 'fsLogfile': - const embed3 = new client.embed().setColor(client.config.embedColor).setTitle('Uploading your log file').setDescription('You can find `log.txt` in `Documents/My Games/FarmingSimulator2022` and upload it into <#596989522395398144> along with your issue, so people can assist you further and help you resolve.').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195643528101958/unknown.png'); - interaction.reply({embeds: [embed3]}) - break; - case 'ytscam': - const embed4 = new client.embed().setColor(client.config.embedColor).setTitle('Scammers in YouTube comments section').setDescription('If you ever see a comment mentioning a giveaway or anything else, **it\'s a scam!**\nYou should report it to YouTube and move on or ignore it.\nP.S: They\'re on every channel and not just Daggerwin.').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1068078284996345916/image.png'); - interaction.reply({embeds: [embed4]}) - } + ({ + srp: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('When will SRP (Survival Roleplay) return?').setImage('https://cdn.discordapp.com/attachments/1023338018720989305/1059259250242752562/IMG_8964.png')]}), + dlskin: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Daggerwin Logistics hex code').setDescription('The main color will be Onyx (`#353839`) with red bumpers.').setImage('https://cdn.discordapp.com/attachments/801965516947324969/806871878736019456/image0.png')]}), + vtcR: ()=>interaction.reply(`You can get the <@&${client.config.mainServer.roles.vtcmember}> role from <#802283932430106624> by reacting <@282859044593598464>'s message with :truck:\n*VTC skin can also be found in <#801975222609641472> as well.*`), + mpR: ()=>interaction.reply(`You can get the <@&${client.config.mainServer.roles.mpplayer}> role from <#802283932430106624> by reacting <@282859044593598464>'s message with :tractor:`), + ytscam: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Scammers in YouTube comments section').setDescription('If you ever see a comment mentioning a giveaway or anything else, **it\'s a scam!**\nYou should report it to YouTube and move on or ignore it.\nP.S: They\'re on every channel and not just Daggerwin.').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1068078284996345916/image.png')]}), + fsShader: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Clearing your shader cache folder').setDescription('If your game kees crashing shortly after opening your game, then the shaders might be an issue.\nTo resolve this, you can go to `Documents/My Games/FarmingSimulator2022` and delete the folder called `shader_cache`').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195687970943016/unknown.png')]}), + fsLogfile: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Uploading your log file').setDescription('You can find `log.txt` in `Documents/My Games/FarmingSimulator2022` and upload it into <#596989522395398144> along with your issue, so people can assist you further and help you resolve.').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195643528101958/unknown.png')]}) + } as any)[interaction.options.getString('question', true)](); }, data: new SlashCommandBuilder() .setName('faq') diff --git a/src/commands/mp.ts b/src/commands/mp.ts index cb8485f..0d37d08 100644 --- a/src/commands/mp.ts +++ b/src/commands/mp.ts @@ -18,7 +18,7 @@ async function MPdata(client:TClient, interaction:Discord.ChatInputCommandIntera // Fetch dss try { // v I am aware timeout has decreased from 2800 to 2588 to fit within Discord's interaction timeouts (3s) -Toast - FSserver = await client.axios.get(completedURL, {timeout: 2588}) // Finally got around to fixing the command when it cannot ping the host. + FSserver = await client.axios.get(completedURL, {timeout: 2588, headers: {'User-Agent': `Daggerbot - mp cmd/axios ${client.axios.VERSION}`}}) // Finally got around to fixing the command when it cannot ping the host. } catch(err) { // Blame Nawdic & RedRover92 embed.setTitle('Host is not responding.'); @@ -37,9 +37,8 @@ export default { }); return; } - const Subb = interaction.options.getSubcommand(); - switch (Subb){ - case 'status': + ({ + status: async()=>{ const embed0 = new client.embed(); const FSserver0 = await MPdata(client, interaction, embed0); if (!FSserver0?.data) return console.log('FSserver0 failed - status'); @@ -60,11 +59,66 @@ export default { console.log(err) interaction.reply('FSserver0 Placeholder') }; - break; - case 'players': + }, + 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 DBURL = MPDB.findOne({where: {serverId: interaction.guildId}}) + 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](${(await DBURL).ip}/mods.html) **|** [Direct Download](${(await DBURL).ip}/all_mods_download?onlyActive=true)`, + '**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)', + 'Please see <#543494084363288637> for additional information.' + ].join('\n')); + if (FSserver2?.data.server.name.length == 0){ + embed2.setFooter({text: 'Server is currently offline.'}) + } + interaction.reply({embeds: [embed2]}) + }, + url: async()=>{ + 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'); + MPDB.sync(); + const address = interaction.options.getString('address'); + if (!address){ + try { + const Url = await MPDB.findOne({where:{serverId: 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:**\nTry inserting an URL first.') + } + }else{ + const verifyURL = address.match(/dedicated-server-stats/) + if (!verifyURL) 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})`); + const Url = await MPDB.create({ + serverId: interaction.guildId, + ip: newURL[0], + code: newURL[1], + timesUpdated: 0 + }); + return interaction.reply(`Successfully set the URL to ${Url.ip}`) + } catch(err){ + if (err.name == 'SequelizeUniqueConstraintError'){ + const AffectedRows = await MPDB.update({ip: newURL[0], code: newURL[1]},{where:{serverId: interaction.guildId}}); + await MPDB.increment('timesUpdated',{where:{serverId: interaction.guildId}}); + if (AffectedRows) return interaction.reply(`Successfully updated the URL to ${newURL[0]}`) + }else{ + console.log(err) + interaction.reply(`\`MPDB\` has caught an error, notify <@&${client.config.mainServer.roles.bottech}>`) + } + } + } + }, + players: async()=>{ const embed1 = new client.embed(); const data = JSON.parse(fs.readFileSync(path.join(__dirname, '../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; @@ -224,65 +278,8 @@ export default { embed1.addFields({name: `${player.name} ${player.isAdmin ? '| admin' : ''}`, value: `Farming for ${(Math.floor(player.uptime/60))} hr, ${('0' + (player.uptime % 60)).slice(-2)} min`}) }) interaction.reply({embeds: [embed1], files: [Image]}) - break; - case 'info': - 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 DBURL = MPDB.findOne({where: {serverId: interaction.guildId}}) - 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](${(await DBURL).ip}/mods.html) **|** [Direct Download](${(await DBURL).ip}/all_mods_download?onlyActive=true)`, - '**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)', - 'Please see <#543494084363288637> for additional information.' - ].join('\n')); - if (FSserver2?.data.server.name.length == 0){ - embed2.setFooter({text: 'Server is currently offline.'}) - } - interaction.reply({embeds: [embed2]}) - break; - case 'url': - 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'); - MPDB.sync(); - const address = interaction.options.getString('address'); - - if (!address){ - try { - const Url = await MPDB.findOne({where:{serverId: 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:**\nTry inserting an URL first.') - } - }else{ - const verifyURL = address.match(/dedicated-server-stats/) - if (!verifyURL) 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})`); - const Url = await MPDB.create({ - serverId: interaction.guildId, - ip: newURL[0], - code: newURL[1], - timesUpdated: 0 - }); - return interaction.reply(`Successfully set the URL to ${Url.ip}`) - } catch(err){ - if (err.name == 'SequelizeUniqueConstraintError'){ - const AffectedRows = await MPDB.update({ip: newURL[0], code: newURL[1]},{where:{serverId: interaction.guildId}}); - await MPDB.increment('timesUpdated',{where:{serverId: interaction.guildId}}); - if (AffectedRows) return interaction.reply(`Successfully updated the URL to ${newURL[0]}`) - }else{ - console.log(err) - interaction.reply(`\`MPDB\` has caught an error, notify <@&${client.config.mainServer.roles.bottech}>`) - } - } - } - break; - /* case 'series': + }/*, + 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:', @@ -292,8 +289,9 @@ export default { '**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]}) */ - } + interaction.reply({embeds: [embed3]}) + }*/ + } as any)[interaction.options.getSubcommand()](); }, data: new SlashCommandBuilder() .setName('mp') diff --git a/src/commands/rank.ts b/src/commands/rank.ts index 4d583a1..77a08c7 100644 --- a/src/commands/rank.ts +++ b/src/commands/rank.ts @@ -6,172 +6,164 @@ import fs from 'node:fs'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (interaction.guildId !== client.config.mainServer.id) return interaction.reply({content: 'This command doesn\'t work in this server.', ephemeral: true}); - const subCmd = interaction.options.getSubcommand(); + ({ + view: ()=>{ + // fetch user or user interaction sender + const member = interaction.options.getMember("member") ?? interaction.member as Discord.GuildMember; + if (member.user.bot) return interaction.reply('Bots don\'t level up, try viewing non-bots instead.') + const embed = new client.embed().setColor(member.displayColor) + // information about users progress on level roles + const information = client.userLevels._content[member.user.id]; - if (subCmd === "leaderboard") { - const messageCountsTotal = Object.values(client.userLevels._content).reduce((a, b) => a + b.messages, 0); - const timeActive = Math.floor((Date.now() - client.config.LRSstart)/1000/60/60/24); - - const dailyMsgsPath = path.join(__dirname, '../database/dailyMsgs.json'); - const data = JSON.parse(fs.readFileSync(dailyMsgsPath, {encoding: 'utf8'})).map((x: Array, i: number, a: any) => { - const yesterday = a[i - 1] || []; - return x[1] - (yesterday[1] || x[1]); - }).slice(1).slice(-60); - - // handle negative days - data.forEach((change: number, i: number) => { - if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0; - }); - - const maxValue = Math.max(...data); - const maxValueArr = maxValue.toString().split(''); - - const first_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 1)) * 10 ** (maxValueArr.length - 1); - // console.log({ first_graph_top }); - - const second_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 2)) * 10 ** (maxValueArr.length - 2); - // console.log({ second_graph_top }); - - const textSize = 32; - - const canvas = require('canvas'); - const img = canvas.createCanvas(950, 450); - const ctx = img.getContext('2d'); - - const graphOrigin = [10, 50]; - const graphSize = [700, 360]; - const nodeWidth = graphSize[0] / (data.length - 1); - ctx.fillStyle = '#36393f'; - ctx.fillRect(0, 0, img.width, img.height); - - // grey horizontal lines - ctx.lineWidth = 3; - - 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 pronounBool = (you: string, they: string) => { // takes 2 words and chooses which to use based on if user did this command on themself + if (interaction.user.id === member.user.id) return you || true; + else return they || false; + }; + if (!information) { + return interaction.reply(`${pronounBool('You', 'They')} currently don't have a level, send some messages to level up.`) } - } - // console.log({ interval_candidates }); - const chosen_interval = interval_candidates.sort((a, b) => b[2] - a[2])[0]; - // console.log({ chosen_interval }); - - let previousY: Array = []; - - ctx.strokeStyle = '#202225'; - 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'; + + const index = Object.entries(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).map(x => x[0]).indexOf(member.id) + 1; + const memberDifference = information.messages - client.userLevels.algorithm(information.level); + const levelDifference = client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level); + + embed.setThumbnail(member.user.avatarURL({ extension: 'png', size: 256}) || member.user.defaultAvatarURL) + embed.setAuthor({name: `Ranking for ${member.user.tag}`}) + embed.setTitle(`Level: **${information.level}**\nRank: **${index ? '#' + index : 'last'}**\nProgress: **${information.messages - client.userLevels.algorithm(information.level)}/${client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level)} (${(memberDifference/levelDifference*100).toFixed(2)}%)**\nTotal: **${information.messages}**`); + interaction.reply({embeds: [embed]}) + }, + leaderboard: ()=>{ + const messageCountsTotal = Object.values(client.userLevels._content).reduce((a, b) => a + b.messages, 0); + const timeActive = Math.floor((Date.now() - client.config.LRSstart)/1000/60/60/24); + + const dailyMsgsPath = path.join(__dirname, '../database/dailyMsgs.json'); + const data = JSON.parse(fs.readFileSync(dailyMsgsPath, {encoding: 'utf8'})).map((x: Array, i: number, a: any) => { + const yesterday = a[i - 1] || []; + return x[1] - (yesterday[1] || x[1]); + }).slice(1).slice(-60); + + // handle negative days + data.forEach((change: number, i: number) => { + if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0; + }); + + const maxValue = Math.max(...data); + const maxValueArr = maxValue.toString().split(''); + + const first_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 1)) * 10 ** (maxValueArr.length - 1); + const second_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 2)) * 10 ** (maxValueArr.length - 2); + + const textSize = 32; + + const canvas = require('canvas'); + const img = canvas.createCanvas(950, 450); + const ctx = img.getContext('2d'); + + const graphOrigin = [10, 50]; + const graphSize = [700, 360]; + const nodeWidth = graphSize[0] / (data.length - 1); + ctx.fillStyle = '#36393f'; + ctx.fillRect(0, 0, img.width, img.height); + + // grey horizontal lines + ctx.lineWidth = 3; + + 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]; + + let previousY: Array = []; + + ctx.strokeStyle = '#202225'; + 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'; + ctx.beginPath(); + ctx.lineTo(graphOrigin[0], y); + ctx.lineTo(graphOrigin[0] + graphSize[0], y); + ctx.stroke(); + ctx.closePath(); + if (even) ctx.strokeStyle = '#202225'; + previousY = [y, i * chosen_interval[0]]; + } + + // 30d mark + ctx.setLineDash([8, 16]); ctx.beginPath(); - ctx.lineTo(graphOrigin[0], y); - ctx.lineTo(graphOrigin[0] + graphSize[0], y); + const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30)); + ctx.lineTo(lastMonthStart, graphOrigin[1]); + ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]); ctx.stroke(); ctx.closePath(); - if (even) ctx.strokeStyle = '#202225'; - previousY = [y, i * chosen_interval[0]]; + ctx.setLineDash([]); + + // draw points + ctx.strokeStyle = client.config.embedColor; + ctx.fillStyle = client.config.embedColor; + ctx.lineWidth = 3; + + function getYCoordinate(value: number) { + return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1]; + } + + let lastCoords: Array = []; + data.forEach((val: number, i: number) => { + ctx.beginPath(); + if (lastCoords) ctx.moveTo(...lastCoords); + if (val < 0) val = 0; + const x = i * nodeWidth + graphOrigin[0]; + const y = getYCoordinate(val); + ctx.lineTo(x, y); + lastCoords = [x, y]; + ctx.stroke(); + ctx.closePath(); + + // ball + ctx.beginPath(); + ctx.arc(x, y, ctx.lineWidth * 1.2, 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; + const maxy = previousY[0] + (textSize / 3); + ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy); + + // lowest value + const lowx = graphOrigin[0] + graphSize[0] + textSize; + const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3); + ctx.fillText('0 msgs/day', lowx, lowy); + + // 30d + ctx.fillText('30d ago', lastMonthStart, graphOrigin[1] - (textSize / 3)); + + // time -> + const tx = graphOrigin[0] + (textSize / 2); + const ty = graphOrigin[1] + graphSize[1] + (textSize); + ctx.fillText('time ->', tx, ty); + + const yeahok = new client.attachmentBuilder(img.toBuffer(), {name: 'dailymsgs.png'}) + const embed = new client.embed().setTitle('Ranking leaderboard') + .setDescription(`Level System was created **${timeActive}** days ago. Since then, a total of **${messageCountsTotal.toLocaleString('en-US')}** messages have been sent in this server.\nGraph updates daily @ `) + .addFields({name: 'Top users by messages sent:', value: Object.entries(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).slice(0, 10).map((x, i) => `\`${i + 1}.\` <@${x[0]}>: ${x[1].messages.toLocaleString('en-US')}`).join('\n')}) + .setImage('attachment://dailymsgs.png').setColor(client.config.embedColor) + interaction.reply({embeds: [embed], files: [yeahok]}) } - - // 30d 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.strokeStyle = client.config.embedColor; - ctx.fillStyle = client.config.embedColor; - ctx.lineWidth = 3; - - function getYCoordinate(value: number) { - return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1]; - } - - let lastCoords: Array = []; - data.forEach((val: number, i: number) => { - ctx.beginPath(); - if (lastCoords) ctx.moveTo(...lastCoords); - if (val < 0) val = 0; - const x = i * nodeWidth + graphOrigin[0]; - const y = getYCoordinate(val); - ctx.lineTo(x, y); - lastCoords = [x, y]; - ctx.stroke(); - ctx.closePath(); - - // ball - ctx.beginPath(); - ctx.arc(x, y, ctx.lineWidth * 1.2, 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; - const maxy = previousY[0] + (textSize / 3); - ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy); - - // lowest value - const lowx = graphOrigin[0] + graphSize[0] + textSize; - const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3); - ctx.fillText('0 msgs/day', lowx, lowy); - - // 30d - ctx.fillText('30d ago', lastMonthStart, graphOrigin[1] - (textSize / 3)); - - // time -> - const tx = graphOrigin[0] + (textSize / 2); - const ty = graphOrigin[1] + graphSize[1] + (textSize); - ctx.fillText('time ->', tx, ty); - - const yeahok = new client.attachmentBuilder(img.toBuffer(), {name: 'dailymsgs.png'}) - const embed = new client.embed().setTitle('Ranking leaderboard') - .setDescription(`Level System was created **${timeActive}** days ago. Since then, a total of **${messageCountsTotal.toLocaleString('en-US')}** messages have been sent in this server.\nGraph updates daily @ `) - .addFields({name: 'Top users by messages sent:', value: Object.entries(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).slice(0, 10).map((x, i) => `\`${i + 1}.\` <@${x[0]}>: ${x[1].messages.toLocaleString('en-US')}`).join('\n')}) - .setImage('attachment://dailymsgs.png').setColor(client.config.embedColor) - interaction.reply({embeds: [embed], files: [yeahok]}); - return; - } else if (subCmd === "view") { - - // fetch user or user interaction sender - const member = interaction.options.getMember("member") ?? interaction.member as Discord.GuildMember; - if (member.user.bot) return interaction.reply('Bots don\'t level up, try viewing non-bots instead.') - const embed = new client.embed().setColor(member.displayColor) - - // information about users progress on level roles - const information = client.userLevels._content[member.user.id]; - - const pronounBool = (you: string, they: string) => { // takes 2 words and chooses which to use based on if user did this command on themself - if (interaction.user.id === member.user.id) return you || true; - else return they || false; - }; - - if (!information) { - return interaction.reply(`${pronounBool('You', 'They')} currently don't have a level, send some messages to level up.`) - } - - const index = Object.entries(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).map(x => x[0]).indexOf(member.id) + 1; - const memberDifference = information.messages - client.userLevels.algorithm(information.level); - const levelDifference = client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level); - - embed.setThumbnail(member.user.avatarURL({ extension: 'png', size: 256}) || member.user.defaultAvatarURL) - embed.setAuthor({name: `Ranking for ${member.user.tag}`}) - embed.setTitle(`Level: **${information.level}**\nRank: **${index ? '#' + index : 'last'}**\nProgress: **${information.messages - client.userLevels.algorithm(information.level)}/${client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level)} (${(memberDifference/levelDifference*100).toFixed(2)}%)**\nTotal: **${information.messages}**`); - interaction.reply({embeds: [embed]}); - } + } as any)[interaction.options.getSubcommand()](); }, data: new SlashCommandBuilder() .setName('rank') diff --git a/src/commands/whois.ts b/src/commands/whois.ts index 5b6f67d..daf9065 100644 --- a/src/commands/whois.ts +++ b/src/commands/whois.ts @@ -1,22 +1,21 @@ import Discord,{SlashCommandBuilder} from 'discord.js'; import TClient from 'src/client'; -function convert(status:string){ - switch (status){ - case 'offline': - return '⚫' - case 'idle': - return '🟡' - case 'dnd': - return '🔴' - case 'online': - return '🟢' +function convert(status?:Discord.ClientPresenceStatus){ + if (status){ + return { + idle: '🟡', + dnd: '🔴', + online: '🟢' + }[status]; + } else { + return '⚫' } } export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - const member = interaction.options.getMember('member') as Discord.GuildMember; + const member = interaction.options.getMember('member'); if (member == null){ const user = interaction.options.getUser('member') as Discord.User; const embed = new client.embed() @@ -30,22 +29,27 @@ export default { } else { await member.user.fetch(); const embedArray = []; + const presence = member.presence.clientStatus as Discord.ClientPresenceStatusData; + let title = 'Member'; + if (member.user.bot) { + title = 'Bot' + } else if (member.user.id == interaction.guild.ownerId) { + title = ':crown: Server Owner' + }; const embed0 = new client.embed() .setColor(member.displayColor || client.config.embedColor) .setURL(`https://discord.com/users/${member.user.id}`) .setThumbnail(member.user.avatarURL({size:2048}) || member.user.defaultAvatarURL) .setImage(member.user.bannerURL({size:1024}) as string) - .setTitle(`${member.user.bot ? 'Bot' : 'Member'} Info: ${member.user.tag}`) - .setDescription(`<@${member.user.id}>\n\`${member.user.id}\`${member.user.id === interaction.guild.ownerId ? '\n__**Server Owner**__ 👑' : ''}`) + .setTitle(`${title} Info: ${member.user.tag}`) + .setDescription(`<@${member.user.id}>\n\`${member.user.id}\``) .addFields( {name: '🔹 Account Creation Date', value: `\n`}, {name: '🔹 Server Join Date', value: `\n`}, {name: `🔹 Roles: ${member.roles.cache.size - 1}`, value: member.roles.cache.size > 1 ? member.roles.cache.filter(x=>x.id !== interaction.guild.roles.everyone.id).sort((a,b)=>b.position - a.position).map(x=>x).join(member.roles.cache.size > 4 ? ' ' : '\n').slice(0,1024) : 'No roles'} ) - if (member.premiumSinceTimestamp !== null){ - embed0.addFields({name: '🔹 Server Boosting since', value: `\n`, inline: true}) - } - if (member.presence){embed0.addFields({name: `🔹 Status: ${member.presence.status}`, value: `${member.presence.status === 'offline' ? '⚫' : `Desktop: ${(member.presence.clientStatus as Discord.ClientPresenceStatusData).desktop ? convert((member.presence.clientStatus as Discord.ClientPresenceStatusData).desktop as string) : convert('offline')}\nWeb: ${(member.presence.clientStatus as Discord.ClientPresenceStatusData).web ? convert((member.presence.clientStatus as Discord.ClientPresenceStatusData).web as string) : convert('offline')}\nMobile: ${(member.presence.clientStatus as Discord.ClientPresenceStatusData).mobile ? convert((member.presence.clientStatus as Discord.ClientPresenceStatusData).mobile as string) : convert('offline')}`}`, inline: true})} + if (member.premiumSinceTimestamp !== null) embed0.addFields({name: '🔹 Server Boosting since', value: `\n`, inline: true}) + if (member.presence) embed0.addFields({name: `🔹 Status: ${member.presence.status}`, value: `${member.presence.status === 'offline' ? '⚫' : `Desktop: ${convert(presence.desktop)}\nWeb: ${convert(presence.web)}\nMobile: ${convert(presence.mobile)}`}`, inline: true}) embedArray.push(embed0) interaction.reply({embeds: embedArray}) }