diff --git a/.gitignore b/.gitignore index eb295f8..e0183c9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ package-lock.json # TypeScript stuff dist/ # Bot stuff -src/database/MPDB.dat \ No newline at end of file +src/database/MPDB.dat +src/commands/disabled +src/commands/roleinfo.ts +src/commands/rank.ts \ No newline at end of file diff --git a/botStartup.sh b/botStartup.sh new file mode 100644 index 0000000..3566602 --- /dev/null +++ b/botStartup.sh @@ -0,0 +1 @@ +npx ts-node src/index.ts \ No newline at end of file diff --git a/package.json b/package.json index 70d91bc..63fcecd 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,9 @@ "discord.js": "14.6.0", "moment": "2.29.4", "ms": "2.1.3", - "sequelize": "7.0.0-alpha.9", + "sequelize": "6.25.6", "sqlite3": "5.1.2", + "systeminformation": "5.12.15", "xml-js": "1.6.11" }, "devDependencies": { diff --git a/src/client.ts b/src/client.ts index 1a33d55..97e28e5 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2,6 +2,7 @@ import Discord, { Client, GatewayIntentBits, Partials } from 'discord.js'; import fs from 'node:fs'; import { Database } from './database'; import timeNames from './timeNames'; +import { Punishment, formatTimeOpt, createTableOpt, punOpt } from './typings/interfaces'; export class TClient extends Client { invites: Map; commands: Discord.Collection; @@ -30,14 +31,14 @@ export class TClient extends Client { GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildBans, GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildPresences, GatewayIntentBits.GuildMessageReactions, - GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent + GatewayIntentBits.DirectMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages ], partials: [ Partials.Channel, Partials.Reaction, Partials.Message ], - allowedMentions: { users: [], roles: [] } // idk if it would work but requires testing... + allowedMentions: { users: [], roles: [] } }) this.invites = new Map(); this.commands = new Discord.Collection(); @@ -77,7 +78,7 @@ export class TClient extends Client { this.registry.push(command.default.data.toJSON()) } } - formatPunishmentType(punishment: Punishment, client: TClient, cancels: Punishment){ + formatPunishmentType(punishment: Punishment, client: TClient, cancels?: Punishment){ if (punishment.type == 'removeOtherPunishment'){ cancels ||= this.punishments._content.find((x: Punishment)=>x.id === punishment.cancels) return cancels.type[0].toUpperCase()+cancels.type.slice(1)+' Removed'; @@ -86,13 +87,12 @@ export class TClient extends Client { formatTime(integer: number, accuracy = 1, options?: formatTimeOpt){ let achievedAccuracy = 0; let text:any = ''; - const { longNames, commas } = options for (const timeName of timeNames){ if (achievedAccuracy < accuracy){ const fullTimelengths = Math.floor(integer/timeName.length); if (fullTimelengths == 0) continue; achievedAccuracy++; - text += fullTimelengths + (longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (commas ? ', ' : ' '); + text += fullTimelengths + (options?.longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (options?.commas ? ', ' : ' '); integer -= fullTimelengths*timeName.length; } else { break; @@ -182,8 +182,8 @@ export class TClient extends Client { async punish(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string){ let result: any; if (!client.isStaff(interaction.member as Discord.GuildMember)) return this.youNeedRole(interaction, 'dcmod') - if (type !== ('warn' || 'mute') && (interaction.member as Discord.GuildMember).roles.cache.has(client.config.mainServer.roles.idk)) return this.youNeedRole(interaction, 'dcmod'); - const time = this.ms(interaction.options.getString('time')); + //if (type !== ('warn' || 'mute') && (interaction.member as Discord.GuildMember).roles.cache.has(client.config.mainServer.roles.idk)) return this.youNeedRole(interaction, 'dcmod'); + const time = interaction.options.getString('time') as string; const reason = interaction.options.getString('reason') ?? 'Reason unspecified'; if (type == 'ban'){ const user = interaction.options.getUser('member') as Discord.User; @@ -208,7 +208,18 @@ export class TClient extends Client { } async YTLoop(YTChannelID: string, YTChannelName: string, DCChannelID: string){ - const Data = this.xjs.xml2js((await this.axios.get(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {timeout: 5000})), {compact: true, spaces: 2}).catch(()=>{return null}); + let Data:any; + let error; + + try { + await this.axios.get(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {timeout: 5000}).then((xml:any)=>{ + Data = this.xjs.xml2js(xml.data, {compact: true, spaces: 2}); + }) + } catch(err){ + error = true; + console.log(`\x1b[36m[${this.moment().format('DD/MM/YY HH:mm:ss')}]`, `\x1b[31m${YTChannelName} YT fail`) + } + if (!Data) return; if (this.YTCache[YTChannelID] == undefined){ this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text; @@ -251,7 +262,7 @@ class punishments extends Database { } switch (type) { case 'ban': - const banData: Punishment={type, id: this.createId(), member: member.id, moderator, time: now}; + const banData:Punishment={type, id: this.createId(), member: member.id, moderator, time: now}; const dm1: Discord.Message = await member.send(`You've been banned from ${interaction.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${banData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')}) const banResult = await interaction.guild.bans.create(member.id, {reason: `${reason || 'Reason unspecified'} | Case #${banData.id}`}).catch((err:Error)=>err.message); if (typeof banResult === 'string'){ @@ -272,16 +283,84 @@ class punishments extends Database { } case 'softban': const guild = member.guild; - const softbanData: Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now}; + const softbanData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now}; const dm2 = Discord.Message = await member.send(`You've been softbanned from ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${softbanData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')}) const softbanResult = await member.ban({deleteMessageDays: 3, reason: `${reason || 'Reason unspecified'} | Case #${softbanData.id}`}).catch((err:Error)=>err.message); if (typeof softbanResult === 'string') { dm2.delete(); return `Softban was unsuccessful: ${softbanResult}`; + } else { + const unbanResult = guild.members.unban(softbanData.member, `${reason || 'Reason unspecified'} | Case #${softbanData.id}`).catch((err:Error)=>err.message); + if (typeof unbanResult === 'string'){ + return `Softban (unban) was unsuccessful: ${softbanResult}` + } else { + if (reason) softbanData.reason = reason; + this.client.makeModlogEntry(softbanData, this.client); + this.addData(softbanData).forceSave(); + return new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${softbanData.id}: Softban`).setDescription(`${member.user.tag}\n<@${member.user.tag}>\n(\`${member.user.id}\`)`).addFields( + {name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``} + ) + } } case 'kick': + const kickData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now}; + const dm3: Discord.Message = await member.send(`You've been kicked from ${member.guild.name} for reason \`${reason || 'Reason unspecified'}\` (Case #${kickData.id})`).catch(()=>{interaction.channel.send(`Failed to DM <@${member.user.id}>.`); return null}); + const kickResult = await member.kick(`${reason || 'Reason unspecified'} | Case #${kickData.id}`).catch((err:Error)=>err.message) + if (typeof kickResult === 'string'){ + if (dm3) dm3.delete(); + return `Kick was unsuccessful: ${kickResult}` + } else { + if (reason) kickData.reason = reason; + this.client.makeModlogEntry(kickData, this.client); + this.addData(kickData).forceSave(); + return new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${kickData.id}: Kick`).setDescription(`${member.user.tag}\n<@${member.user.id}>\n(\`${member.user.id}\`)`).addFields( + {name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``} + ) + } case 'warn': + const warnData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now}; + const warnResult: Discord.Message = await member.send(`You've been warned in ${member.guild.name} for reason \`${reason || 'Reason unspecified'}\` (Case #${warnData.id})`).catch(()=>{interaction.channel.send(`Failed to DM <@${member.user.id}>`); return null;}) + if (typeof warnResult === 'string'){ + return `Warn was unsuccessful: ${warnResult}` + } else { + if (reason) warnData.reason = reason; + this.client.makeModlogEntry(warnData, this.client); + this.addData(warnData).forceSave(); + const embedw = new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${warnData.id}: Warn`).setDescription(`${member.user.tag}\n<@${member.user.id}>\n(\`${member.user.id}\`)`).addFields( + {name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``} + ) + if (moderator !== '795443537356521502') {return embedw} + } case 'mute': + const muteData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now}; + let muteResult; + if (this.client.isStaff(member)) return 'Staff cannot be muted.' + const dm4: Discord.Message = await member.send(`You've been muted in ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${muteData.id})`).catch(()=>{interaction.channel.send('Failed to DM user.'); return null}); + if (timeInMillis) { + muteResult = await member.timeout(timeInMillis, `${reason || 'Reason unspecified'} | Case #${muteData.id}`).catch((err:Error)=>err.message); + } else { + muteResult = await member.timeout(2419200000, `${reason || 'Reason unspecified'} | Case #${muteData.id}`).catch((err:Error)=>err.message); + } + if (typeof muteResult === 'string'){ + if (dm4) dm4.delete(); + return `Mute was unsuccessful: ${muteResult}`; + } else { + if (timeInMillis) { + muteData.endTime = now + timeInMillis; + muteData.duration = timeInMillis; + } + if (reason) muteData.reason = reason; + this.client.makeModlogEntry(muteData, this.client); + this.addData(muteData).forceSave(); + const embedm = new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${muteData.id}: Mute`).setDescription(`${member.user.tag}\n<@${member.user.id}>\n(\`${member.user.id}\`)`).addFields( + {name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``}, + {name: 'Duration', + value: `${this.client.formatTime(timeInMillis, 4, { + longNames: true, + commas: true + })} (${timeInMillis}ms)`}) + if (moderator !== '795443537356521502') {return embedm}; + } } } async removePunishment(caseId:number, moderator:any, reason:string):Promise{} diff --git a/src/commands/bonk.ts b/src/commands/bonk.ts new file mode 100644 index 0000000..e9af244 --- /dev/null +++ b/src/commands/bonk.ts @@ -0,0 +1,32 @@ +import Discord,{SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + if (!client.isStaff(interaction.member) && interaction.channelId == '468835415093411863') return interaction.reply('This command is restricted to staff only in this channel due to high usage.') + const member = interaction.options.getUser('member'); + const reason = interaction.options.getString('reason'); + const adminPerm = interaction.member.permissions.has('Administrator') + if (!member) { + return interaction.reply('You can\'t bonk the ghost.') + } else { + //if (member && adminPerm) return interaction.reply('You cannot bonk an admin!') + if (member) { + const embed = new client.embed().setColor(client.config.embedColor) + .setDescription(`> <@${member.id}> has been bonked!\n${reason?.length == null ? '' : `> Reason: ${reason}`}`) + .setImage('https://media.tenor.com/7tRddlNUNNcAAAAd/hammer-on-head-minions.gif') + .setFooter({text: `Bonk count for ${member.tag}: ${await client.bonkCount.getUser(member.id).toLocaleString('en-US')}`}) + interaction.reply({embeds: [embed]}) + client.bonkCount._incrementUser(member.id).forceSave(); + } + } + }, + data: new SlashCommandBuilder() + .setName('bonk') + .setDescription('Bonk a member') + .addUserOption((opt)=>opt + .setName('member') + .setDescription('Which member to bonk?')) + .addStringOption((opt)=>opt + .setName('reason') + .setDescription('Reason for the bonk')) +} \ No newline at end of file diff --git a/src/commands/contributors.ts b/src/commands/contributors.ts new file mode 100644 index 0000000..aec7072 --- /dev/null +++ b/src/commands/contributors.ts @@ -0,0 +1,20 @@ +import Discord,{SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + const embed = new client.embed().setColor(client.config.embedColor).setTitle('Daggerbot contributors').setDescription([ + 'Toast <@190407856527376384>', + 'TÆMBØ <@615761944154210305>', + 'Buzz <@593696856165449749>', + 'Monster <@215497515934416896>', + 'RainbowDave <@141304507249197057>', + 'Hitchhiker <@506022868157595648>', + 'RedRover92 <@633345781780185099>', + 'Nawdic <@178941218510602240>' + ].join('\n')) + interaction.reply({embeds: [embed]}) + }, + data: new SlashCommandBuilder() + .setName('contributors') + .setDescription('List of people who helped and worked on the bot.') +} \ No newline at end of file diff --git a/src/commands/fsurl.ts b/src/commands/fsurl.ts new file mode 100644 index 0000000..2724b97 --- /dev/null +++ b/src/commands/fsurl.ts @@ -0,0 +1,58 @@ +import Discord,{SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +import MPDB from '../models/MPServer'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + if (interaction.guildId !== client.config.mainServer.id) return interaction.reply('This command doesn\'t work in this server.'); + 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 newServerId = interaction.guildId + const address = interaction.options.getString('address') + + if (!address) { + try { + const Url = await MPDB.findOne({where: {serverId: newServerId}}) + if (Url.ip && Url.code){return interaction.reply(`${Url.get('ip')}` + '/feed/dedicated-server-stats.json?code=' + `${Url.get('code')}`)} + } catch(err) { + if (err.name == 'SequelizeDatabaseError'){ + console.log('MPDB | File doesn\'t exist in database folder, generating one now.') + interaction.reply('`MPDB.dat` is being createdm run the command again.') + } else { + console.log(`MPDB | Error: ${err}`) + interaction.reply('Database error:\nTry inserting an URL first.') + } + } + } else { + const verifyURL = address.match(/feed/) + if (!verifyURL) return interaction.reply('Invalid URL, try again.') + const convertURL = address + const newURL = convertURL.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: newServerId, + ip: newURL[0], + code: newURL[1] + }); + 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: newServerId}}); + 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}>`) + } + + } + } + }, + data: new SlashCommandBuilder() + .setName('url') + .setDescription('Update the URL for FSMP functions') + .addStringOption((opt)=>opt + .setName('address') + .setDescription('Insert a \'dedicated-server-stats\' url') + .setRequired(false)) +} \ No newline at end of file diff --git a/src/commands/mp.ts b/src/commands/mp.ts new file mode 100644 index 0000000..6e54b58 --- /dev/null +++ b/src/commands/mp.ts @@ -0,0 +1,262 @@ +import Discord,{EmbedBuilder, SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +import MPDB from '../models/MPServer'; + +async function MPdata(client:TClient, interaction:Discord.ChatInputCommandInteraction, embed: EmbedBuilder) { + let FSserver; + MPDB.sync(); + const newServerId = interaction.guildId; + const ServerURL = await MPDB.findOne({where: { serverId: newServerId }}); + const DBURL = ServerURL.ip + const DBCode = ServerURL.code + const verifyURL = DBURL.match(/http/) + const completedURL = DBURL + '/feed/dedicated-server-stats.json?code=' + DBCode + if (!verifyURL) return interaction.reply(`No gameserver found, please contact <@&${client.config.mainServer.roles.mpmanager}> to update it.`) + + // Fetch dss + try { + FSserver = await client.axios.get(completedURL, {timeout: 2800}) // 2800 seems doable with database latency added to it. + } catch(err) { + // Blame Nawdic & RedRover92 + embed.setTitle('Host is not responding.'); + embed.setColor(client.config.embedColorRed); + interaction.reply({embeds: [embed]}); + console.log('dag mp fail to fetch, host is not responding.'); + return; + } + return FSserver +} + +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + 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((msg)=>{ + setTimeout(()=>{interaction.deleteReply()}, 500) + }); + return; + } + const Subb = interaction.options.getSubcommand(); + switch (Subb){ + case 'status': + const embed0 = new client.embed(); + const FSserver0 = await MPdata(client, interaction, embed0); + if (FSserver0.data.server.name.length > 1) { + 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} + ) + } + interaction.reply({embeds: [embed0]}) + break; + case 'players': + const embed1 = new client.embed(); + const data = require('../database/MPPlayerData.json').slice(-60) + + // 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 canvas = require('canvas'); + 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'; + 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'; + 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.push(y, i * chosen_interval[0]); + } + + // 30m mark + ctx.setLineDash([8, 16]); + ctx.beginPath(); + const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 60)); + 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; + } else if (playercount > 9) { + return client.config.embedColorYellow; + } else {return client.config.embedColorGreen;} + } + 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, 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); // add a yellow stop to the gradient + } + ctx.strokeStyle = grd; + } else { + ctx.strokeStyle = colorAtPlayercount(curPC); + } + ctx.beginPath(); + if (lastCoords.length > 0) ctx.moveTo(...lastCoords); + // 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 Image = new client.attachmentBuilder(img.toBuffer(),{name: 'FSStats.png'}) + embed1.setImage('attachment://FSStats.png') + const FSserver1 = await MPdata(client, interaction, embed1) + 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); + FSserver1?.data.slots.players.filter(x=>x.isUsed).forEach(player=>{ + 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) + const DBURL = MPDB.findOne({where: {serverId: interaction.guildId}}) + embed2.setDescription([ + `**Server name**: ${FSserver2?.data.server.name.length == 0 ? 'Unknown Server' : `\`${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)' + ].join('\n')); + interaction.reply({embeds: [embed2]}) + break; + case '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]}) + } + }, + data: new SlashCommandBuilder() + .setName('mp') + .setDescription('Display MP status and other things') + .addSubcommand((opt)=>opt + .setName('status') + .setDescription('Check server status and details')) + .addSubcommand((opt)=>opt + .setName('players') + .setDescription('Check who\'s playing on the server')) + .addSubcommand((opt)=>opt + .setName('info') + .setDescription('Provides you with server information such as filters and so on')) + .addSubcommand((opt)=>opt + .setName('series') + .setDescription('Step-by-step on joining Daggerwin\'s MP series')) +} \ No newline at end of file diff --git a/src/commands/mute.ts b/src/commands/mute.ts new file mode 100644 index 0000000..693ef1b --- /dev/null +++ b/src/commands/mute.ts @@ -0,0 +1,20 @@ +import Discord,{SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + client.punish(client, interaction, 'mute'); + }, + data: new SlashCommandBuilder() + .setName('mute') + .setDescription('Mute a member') + .addUserOption((opt)=>opt + .setName('member') + .setDescription('Which member to mute?') + .setRequired(true)) + .addStringOption((opt)=>opt + .setName('time') + .setDescription('Mute duration')) + .addStringOption((opt)=>opt + .setName('reason') + .setDescription('Reason for the mute')) +} \ No newline at end of file diff --git a/src/commands/ping.ts b/src/commands/ping.ts index ae8f8b9..506517b 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -4,7 +4,7 @@ export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ const msg = await interaction.reply({content: 'Pinging...', fetchReply: true}) const time = msg.createdTimestamp - interaction.createdTimestamp; - msg.edit(`Websocket: \`${client.ws.ping}\`ms\nBot: \`${time}\``) + msg.edit(`Websocket: \`${client.ws.ping}\`ms\nBot: \`${time}\`ms`) }, data: new SlashCommandBuilder() .setName('ping') diff --git a/src/commands/restart.ts b/src/commands/restart.ts new file mode 100644 index 0000000..fd97131 --- /dev/null +++ b/src/commands/restart.ts @@ -0,0 +1,11 @@ +import Discord,{SlashCommandBuilder} from 'discord.js'; +import { TClient } from 'src/client'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + if (!client.config.eval.whitelist.includes(interaction.user.id)) return client.youNeedRole(interaction, 'bottech'); + interaction.reply('Restarting...').then(()=>require('node:child_process').exec('pm2 restart Daggerbot')) + }, + data: new SlashCommandBuilder() + .setName('restart') + .setDescription('Restart the bot for technical reasons') +} \ No newline at end of file diff --git a/src/commands/statistics.ts b/src/commands/statistics.ts new file mode 100644 index 0000000..a818cd7 --- /dev/null +++ b/src/commands/statistics.ts @@ -0,0 +1,85 @@ +import Discord,{SlashCommandBuilder, version} from 'discord.js'; +import { TClient } from 'src/client'; +import si from 'systeminformation'; +import os from 'node:os'; +import { versionMajorMinor } from 'typescript'; +import { VERSION } from 'ts-node'; +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + switch (interaction.options.getSubcommand()) { + case 'commands': + const columns = ['Command name', 'Count']; + const includedCommands = client.commands.filter(x=>x.uses).sort((a,b)=>b.uses - a.uses); + if (includedCommands.size == 0) return interaction.reply(`No commands have been used yet.\nUptime: ${client.formatTime(client.uptime as number, 2, {longNames: true, commas: true})}`); + const nameLength = Math.max(...includedCommands.map(x=>x.default.data.name.length), columns[0].length) + 2; + const amountLength = Math.max(...includedCommands.map(x=>x.uses.toLocaleString().length), columns[1].length) + 1; + const rows = [`${columns[0] + ' '.repeat(nameLength - columns[0].length)}|${' '.repeat(amountLength - columns[1].length) + columns[1]}\n`, '-'.repeat(nameLength) + '-'.repeat(amountLength) + '\n']; + includedCommands.forEach(command=>{ + const name = command.default.data.name; + const count = command.uses.toString(); + rows.push(`${name + ' '.repeat(nameLength - name.length)}${' '.repeat(amountLength - count.length) + count}\n`); + }); + const embed = new client.embed().setColor(client.config.embedColor).setTitle('Statistics: Command Usage') + .setDescription([ + 'List of commands that have been used in this session, ordered by amount of use. Table contains command name and amount of uses.', + `Total amount of commands used in this session: ${client.commands.filter(x=>x.uses).map(x=>x.uses).reduce((a,b)=>a+b, 0)}` + ].join('\n')) + if (rows.join('').length > 1024){ + let fieldValue = ''; + rows.forEach(row=>{ + if (fieldValue.length + row.length > 1024){ + embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``}); + fieldValue = row; + } else { + fieldValue += row; + } + }); + embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``}); + } else { + embed.addFields({name: '\u200b', value: `\`\`\`\n${rows.join('')}\`\`\``}) + }; + interaction.reply({embeds: [embed]}) + break; + case 'host': + // Bytes conversion + function formatBytes(bytes, decimals = 2) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; + } + await interaction.deferReply(); + const cpu = await si.cpu(); + const ram = await si.mem(); + const osInfo = await si.osInfo(); + const currentLoad = await si.currentLoad(); + const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Statistics: Host Info').setDescription([ + `> __Dependencies__`, + `**TypeScript:** ${versionMajorMinor}`, + `**TS-Node:** ${VERSION}`, + `**NodeJS:** ${process.version}`, + `**DiscordJS:** ${version}`, + `> __Host__`, + `**Operating System:** ${osInfo.distro + ' ' + osInfo.release}`, + `**CPU:** ${cpu.manufacturer, cpu.brand}`, + `**Memory:** ${formatBytes(ram.used)}/${formatBytes(ram.total)}`, + `**NodeJS:** ${formatBytes(process.memoryUsage().heapUsed)}/${formatBytes(process.memoryUsage().heapTotal)}`, + `**Load Usage:**\nUser: ${currentLoad.currentLoadUser.toFixed(1)}%\nSystem: ${currentLoad.currentLoadSystem.toFixed(1)}%`, + `**Uptime:**\nHost: ${client.formatTime((os.uptime()*1000), 2, {longNames: true, commas: true})}\nBot: ${client.formatTime(client.uptime as number, 2, {commas: true, longNames: true})}` + ].join('\n')) + .setFooter({text: `Load time: ${Date.now() - interaction.createdTimestamp}ms`}); + interaction.editReply({embeds: [embed1]}); + } + }, + data: new SlashCommandBuilder() + .setName('statistics') + .setDescription('See a list of commands ordered by their usage or bot stats') + .addSubcommand((opt)=>opt + .setName('commands') + .setDescription('View command usage stats')) + .addSubcommand((opt)=>opt + .setName('host') + .setDescription('View host stats')) +} \ No newline at end of file diff --git a/src/commands/update.ts b/src/commands/update.ts index 748663e..97840f1 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -12,7 +12,7 @@ export default { } else if (stdout.includes('Already up to date')){ msg.edit(`Pull aborted:\nUp to date with the repository`) } else { - setTimeout(()=>{msg.edit('Restarting...').then(()=>eval(process.exit(-1)))},1000) + setTimeout(()=>{msg.edit('Restarting...').then(()=>require('node:child_process').exec('pm2 restart Daggerbot'))},1000) } } ) diff --git a/src/commands/whois.ts b/src/commands/whois.ts new file mode 100644 index 0000000..78b7a9b --- /dev/null +++ b/src/commands/whois.ts @@ -0,0 +1,61 @@ +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 '🟢' + } +} + +export default { + async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ + const member = interaction.options.getMember('member') as Discord.GuildMember; + if (member == null){ + const user = interaction.options.getUser('member') as Discord.User; + const embed = new client.embed() + .setColor(client.config.embedColor) + .setURL(`https://discord.com/users/${user.id}`) + .setThumbnail(user.avatarURL({size:2048}) || user.defaultAvatarURL) + .setTitle(`${user.bot ? 'Bot': 'User'} Info: ${user.tag}`) + .setDescription(`<@${user.id}>\n\`${user.id}\``) + .addFields({name: '🔹 Account Creation Date', value: `\n`}) + interaction.reply({embeds: [embed]}) + } else { + await member.user.fetch(); + const embedArray = []; + 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**__ 👑' : ''}`) + .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'} + ) + console.log(member.guild.banner) + 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})} + embedArray.push(embed0) + interaction.reply({embeds: embedArray}) + } + }, + data: new SlashCommandBuilder() + .setName('whois') + .setDescription('View your own or someone else\'s information') + .addUserOption((opt)=>opt + .setName('member') + .setDescription('Member or user to view their information') + .setRequired(true)) +} \ No newline at end of file diff --git a/src/database/MPPlayerData.json b/src/database/MPPlayerData.json index c3881fa..79019e6 100644 --- a/src/database/MPPlayerData.json +++ b/src/database/MPPlayerData.json @@ -1 +1 @@ -[1,2,3,4,5,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,4,4,3,3,4,5,4,4,4,4,4,4,4,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,5,5,5,5,5,5,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,0,0,0,2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,5,5,5,5,6,6,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,6,6,5,5,5,5,5,5,7,6,6,7,7,7,7,7,8,7,8,8,8,8,8,8,8,9,9,9,9,9,10,9,9,9,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,6,6,6,6,6,6,6,6,5,5,4,3,3,3,3,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,1,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,4,4,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,3,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,4,5,5,5,5,5,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,5,5,5,5,5,4,4,4,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,2,2,2,2,2,2,2,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,2,2,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,5,5,5,5,4,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,4,4,4,4,5,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,7,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,9,9,9,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,9,10,11,11,11,11,11,11,11,11,12,12,12,12,13,13,13,13,13,11,11,11,11,11,11,12,12,12,12,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,12,12,12,12,12,12,12,12,11,11,11,10,10,9,9,9,8,8,8,8,8,8,9,9,9,9,9,9,8,8,8,8,8,8,8,7,7,7,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,5,5,4,4,5,5,3,4,4,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,4,4,4,4,3,3,4,4,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,3,2,3,3,4,4,3,3,3,3,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,3,3,4,4,4,4,3,3,3,3,4,4,3,4,4,4,3,3,3,4,3,3,3,3,3,3,3,3,4,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0] \ No newline at end of file +[1,2,3,4,5,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,4,4,3,3,4,5,4,4,4,4,4,4,4,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,3,3,3,3,4,4,5,5,5,5,5,5,5,5,5,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,0,0,0,2,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,5,5,5,5,6,6,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,6,6,5,5,5,5,5,5,7,6,6,7,7,7,7,7,8,7,8,8,8,8,8,8,8,9,9,9,9,9,10,9,9,9,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,6,6,6,6,6,6,6,6,5,5,4,3,3,3,3,0,0,0,0,0,0,0,0,0,0,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,1,1,1,2,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,4,4,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,3,4,4,4,4,4,4,4,5,5,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,4,4,4,4,4,4,4,4,4,4,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,4,5,5,5,5,5,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,5,5,5,5,5,4,4,4,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,2,2,2,2,2,2,2,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,2,2,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,5,5,5,5,4,5,5,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,4,4,4,4,5,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,6,7,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,9,9,9,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,9,10,11,11,11,11,11,11,11,11,12,12,12,12,13,13,13,13,13,11,11,11,11,11,11,12,12,12,12,11,11,11,11,11,10,11,11,11,10,11,11,11,11,11,12,12,12,12,12,12,12,12,11,11,11,10,10,9,9,9,8,8,8,8,8,8,9,9,9,9,9,9,8,8,8,8,8,8,8,7,7,7,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,5,5,4,4,5,5,3,4,4,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,4,4,4,4,3,3,4,4,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,3,3,2,3,3,4,4,3,3,3,3,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,3,3,4,4,4,4,3,3,3,3,4,4,3,4,4,4,3,3,3,4,3,3,3,3,3,3,3,3,4,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,2,1,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,3,2,2,3,3,3,3,2,2,2,3,3,2,2,2,2,3,3,3,4,4,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,3,3,3,3,3,3,3,3,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4] \ No newline at end of file diff --git a/src/database/bannedWords.json b/src/database/bannedWords.json index cbcb40e..a6a676a 100644 --- a/src/database/bannedWords.json +++ b/src/database/bannedWords.json @@ -1 +1 @@ -["fuck","fck","fů","fuk","fúck","fùck","fûck","fück","fūck","fůck","fűck","fùçk","fûćk","fucking","fųck","shit","shite","sht","shìt","shít","shît","shït","shīt","shįt","shıt","shlt","whore","pelotudo","boludo","nigger","nigga","nigs","niggas@","cunt","cnut","bitch","dick","pussy","asshole","b1tch","b!tch","blowjob","cock","c0ck","retard","fag","faggot","tits","boobs","dick","sex","porn","bjob","stfu","‎","­","͏","͏","؜","ᅟ","ᅠ","឴","឵","​","‏","‌","stfu","ass","kkk","<@1011334031478100099>","<@468835415093411861>","asf","af","wtf","piss","pissed","pissing"] \ No newline at end of file +["fuck","fck","fů","fuk","fúck","fùck","fûck","fück","fūck","fůck","fűck","fùçk","fûćk","fucking","fųck","shit","shite","sht","shìt","shít","shît","shït","shīt","shįt","shıt","shlt","whore","pelotudo","boludo","nigger","nigga","nigs","niggas@","cunt","cnut","bitch","dick","pussy","asshole","b1tch","b!tch","blowjob","cock","c0ck","retard","fag","faggot","tits","boobs","dick","sex","porn","bjob","stfu","‎","­","͏","͏","؜","ᅟ","ᅠ","឴","឵","​","‏","‌","stfu","ass","kkk","<@1011334031478100099>","<@468835415093411861>","asf","af","wtf","piss","pissed"] \ No newline at end of file diff --git a/src/database/dailyMsgs.json b/src/database/dailyMsgs.json index 9c8b38c..9b40716 100644 --- a/src/database/dailyMsgs.json +++ b/src/database/dailyMsgs.json @@ -1 +1 @@ -[[0,674965],[1,675885],[2,676639],[3,677245],[4,677609],[5,678188],[6,678916],[7,679465],[8,679938],[9,680540],[10,681614],[11,682079],[12,682780],[13,683187],[14,683547],[15,683900],[16,684592],[17,685466],[18,686078],[19,686481],[20,676748],[21,676968],[22,677427],[23,677592],[24,677894],[25,678116],[26,676785],[27,677570],[28,678491],[29,679137],[30,679818],[31,680094],[32,680417],[33,680783],[34,681563],[35,682070],[36,682670],[37,683504],[38,684078],[39,684383],[40,684692],[41,685448],[42,685664],[43,685994],[44,686366],[45,687118],[46,687626],[47,688008],[48,688754],[49,688942],[50,689107],[51,689472],[52,690143],[53,690644],[54,691124],[55,692196],[56,692624],[57,692906],[58,693456],[59,693952],[60,694586],[61,695070],[62,696163],[63,696564],[64,697315],[65,698548],[66,699138],[67,699558],[68,700307],[69,701063],[70,701394],[71,701868],[72,702453],[73,702917],[76,705187],[77,705243],[78,705488],[79,705961],[80,706585],[81,707467],[82,708212],[83,709024],[84,709773],[85,710256]] \ No newline at end of file +[[0,674965],[1,675885],[2,676639],[3,677245],[4,677609],[5,678188],[6,678916],[7,679465],[8,679938],[9,680540],[10,681614],[11,682079],[12,682780],[13,683187],[14,683547],[15,683900],[16,684592],[17,685466],[18,686078],[19,686481],[20,676748],[21,676968],[22,677427],[23,677592],[24,677894],[25,678116],[26,676785],[27,677570],[28,678491],[29,679137],[30,679818],[31,680094],[32,680417],[33,680783],[34,681563],[35,682070],[36,682670],[37,683504],[38,684078],[39,684383],[40,684692],[41,685448],[42,685664],[43,685994],[44,686366],[45,687118],[46,687626],[47,688008],[48,688754],[49,688942],[50,689107],[51,689472],[52,690143],[53,690644],[54,691124],[55,692196],[56,692624],[57,692906],[58,693456],[59,693952],[60,694586],[61,695070],[62,696163],[63,696564],[64,697315],[65,698548],[66,699138],[67,699558],[68,700307],[69,701063],[70,701394],[71,701868],[72,702453],[73,702917],[76,705187],[77,705243],[78,705488],[79,705961],[80,706585],[81,707467],[82,708212],[83,709024],[84,709773],[85,710256],[86,710980]] \ No newline at end of file diff --git a/src/database/punishments.json b/src/database/punishments.json index 5f73038..f988ccc 100644 --- a/src/database/punishments.json +++ b/src/database/punishments.json @@ -94,5 +94,13 @@ "moderator": "532662366354276352", "time": 1668331244780, "reason": "innapropiate profile picture" + }, + { + "type": "ban", + "id": 13, + "member": "985447227755495425", + "moderator": "141304507249197057", + "time": 1668526989338, + "reason": "steam scammer" } ] \ No newline at end of file diff --git a/src/database/userLevels.json b/src/database/userLevels.json index 7c1cede..1a06870 100644 --- a/src/database/userLevels.json +++ b/src/database/userLevels.json @@ -1,54 +1,54 @@ { "190407856527376384": { - "messages": 53012, + "messages": 53029, "level": 59 }, "593696856165449749": { - "messages": 51421, + "messages": 51456, "level": 58 }, "141304507249197057": { - "messages": 67626, + "messages": 67631, "level": 67 }, "533707949831487488": { - "messages": 56992, + "messages": 57038, "level": 61 }, "532662366354276352": { - "messages": 33701, + "messages": 33775, "level": 47 }, "824043915539513406": { - "messages": 18442, + "messages": 18452, "level": 35 }, "178941218510602240": { - "messages": 6375, + "messages": 6394, "level": 20 }, "215497515934416896": { - "messages": 33124, + "messages": 33129, "level": 46 }, "301350210926280704": { - "messages": 14317, + "messages": 14334, "level": 30 }, "695323013813633076": { - "messages": 14076, + "messages": 14090, "level": 30 }, "389237487094071337": { - "messages": 12240, + "messages": 12254, "level": 28 }, "716355511552966737": { - "messages": 9138, + "messages": 9146, "level": 24 }, "633345781780185099": { - "messages": 29365, + "messages": 29379, "level": 44 }, "711527768185372742": { @@ -64,31 +64,31 @@ "level": 18 }, "458688902102908928": { - "messages": 6209, + "messages": 6237, "level": 20 }, "475037861725339649": { - "messages": 10194, + "messages": 10201, "level": 26 }, "392699530912727041": { - "messages": 4937, + "messages": 4944, "level": 18 }, "468837263577579524": { - "messages": 7677, + "messages": 7695, "level": 22 }, "734703851558535188": { - "messages": 20203, + "messages": 20211, "level": 36 }, "488683638310043677": { - "messages": 15457, + "messages": 15477, "level": 32 }, "606595407769894995": { - "messages": 23240, + "messages": 23251, "level": 39 }, "322835877027905547": { @@ -96,7 +96,7 @@ "level": 25 }, "485793265568841728": { - "messages": 37883, + "messages": 37885, "level": 50 }, "837979120142778388": { @@ -112,7 +112,7 @@ "level": 18 }, "690090143008555064": { - "messages": 9974, + "messages": 9978, "level": 25 }, "849633082440941628": { @@ -120,15 +120,15 @@ "level": 0 }, "687720203773149238": { - "messages": 572, - "level": 0 + "messages": 573, + "level": 6 }, "452576735494406175": { - "messages": 1679, + "messages": 1684, "level": 10 }, "763055599654666291": { - "messages": 971, + "messages": 972, "level": 8 }, "623176215800446976": { @@ -148,11 +148,11 @@ "level": 0 }, "169891949464125441": { - "messages": 521, + "messages": 538, "level": 5 }, "718453763932946432": { - "messages": 13548, + "messages": 13549, "level": 30 }, "472809522226790420": { @@ -160,7 +160,7 @@ "level": 13 }, "763803832542035978": { - "messages": 683, + "messages": 685, "level": 6 }, "931816463113814066": { @@ -172,7 +172,7 @@ "level": 0 }, "771892617599516674": { - "messages": 626, + "messages": 630, "level": 6 }, "468437000232501269": { @@ -188,7 +188,7 @@ "level": 0 }, "309373272594579456": { - "messages": 3585, + "messages": 3588, "level": 15 }, "399625244588900362": { @@ -200,7 +200,7 @@ "level": 0 }, "615761944154210305": { - "messages": 3063, + "messages": 3070, "level": 14 }, "397101726047666197": { @@ -228,7 +228,7 @@ "level": 2 }, "645342896312156181": { - "messages": 684, + "messages": 693, "level": 6 }, "488505753133645826": { @@ -240,7 +240,7 @@ "level": 2 }, "848489550065827850": { - "messages": 42, + "messages": 44, "level": 1 }, "862138850423472128": { @@ -248,7 +248,7 @@ "level": 1 }, "700053257996992573": { - "messages": 75, + "messages": 76, "level": 2 }, "949404588673482872": { @@ -280,7 +280,7 @@ "level": 0 }, "509374532109336576": { - "messages": 29, + "messages": 31, "level": 1 }, "464887328138199042": { @@ -344,7 +344,7 @@ "level": 1 }, "132183685658050561": { - "messages": 8, + "messages": 9, "level": 0 }, "549295707304099846": { @@ -400,8 +400,8 @@ "level": 0 }, "903147621961588757": { - "messages": 14, - "level": 0 + "messages": 15, + "level": 1 }, "386627658773168137": { "messages": 2, @@ -416,15 +416,15 @@ "level": 0 }, "774337023804833825": { - "messages": 81, - "level": 0 + "messages": 83, + "level": 2 }, "700434438890323990": { "messages": 9, "level": 0 }, "281518331427553280": { - "messages": 28, + "messages": 29, "level": 1 }, "708758234407895050": { @@ -528,7 +528,7 @@ "level": 0 }, "818595993121062974": { - "messages": 46, + "messages": 48, "level": 1 }, "273755695680192513": { @@ -540,7 +540,7 @@ "level": 0 }, "869718328313278555": { - "messages": 97, + "messages": 98, "level": 2 }, "772197160372404234": { @@ -632,7 +632,7 @@ "level": 0 }, "280115935057281024": { - "messages": 1, + "messages": 2, "level": 0 }, "595727217271635969": { @@ -652,7 +652,7 @@ "level": 0 }, "188304766923833344": { - "messages": 21, + "messages": 22, "level": 1 }, "749311773819142326": { @@ -668,8 +668,8 @@ "level": 1 }, "673950857238413362": { - "messages": 12, - "level": 0 + "messages": 16, + "level": 1 }, "434344693824749568": { "messages": 3, @@ -764,7 +764,7 @@ "level": 0 }, "552473047144071168": { - "messages": 20, + "messages": 21, "level": 1 }, "587377817893994538": { @@ -820,7 +820,7 @@ "level": 0 }, "513009978315898891": { - "messages": 20, + "messages": 23, "level": 1 }, "667815332047486978": { @@ -1052,7 +1052,7 @@ "level": 0 }, "781289786143932499": { - "messages": 107, + "messages": 111, "level": 2 }, "348617165135544321": { @@ -1232,8 +1232,8 @@ "level": 0 }, "444415772001959936": { - "messages": 14, - "level": 0 + "messages": 16, + "level": 1 }, "629585796290314250": { "messages": 103, @@ -1312,7 +1312,7 @@ "level": 0 }, "486352184707907585": { - "messages": 4, + "messages": 5, "level": 0 }, "309170189164085249": { @@ -1340,8 +1340,8 @@ "level": 0 }, "98464148379148288": { - "messages": 1436, - "level": 9 + "messages": 1504, + "level": 10 }, "1025723411680460840": { "messages": 1, @@ -1372,7 +1372,7 @@ "level": 0 }, "786733440003211285": { - "messages": 15, + "messages": 17, "level": 1 }, "853043920505012255": { @@ -1472,7 +1472,7 @@ "level": 0 }, "889624632724963329": { - "messages": 38, + "messages": 51, "level": 1 }, "687692546314600530": { @@ -1656,7 +1656,7 @@ "level": 0 }, "937750032344571934": { - "messages": 102, + "messages": 103, "level": 2 }, "873149038709571594": { @@ -1684,7 +1684,7 @@ "level": 0 }, "633339908827643974": { - "messages": 12, + "messages": 13, "level": 0 }, "174267277187874816": { @@ -1724,8 +1724,8 @@ "level": 0 }, "796040852400635914": { - "messages": 367, - "level": 4 + "messages": 396, + "level": 5 }, "1011307308325818438": { "messages": 23, @@ -1740,7 +1740,7 @@ "level": 0 }, "673289306424475659": { - "messages": 122, + "messages": 129, "level": 2 }, "907163452043305000": { @@ -1824,7 +1824,7 @@ "level": 0 }, "335637071878422529": { - "messages": 125, + "messages": 127, "level": 2 }, "646684489573203968": { @@ -1856,7 +1856,7 @@ "level": 0 }, "1031950921233600662": { - "messages": 15, + "messages": 17, "level": 1 }, "907951346769092618": { @@ -1924,7 +1924,7 @@ "level": 0 }, "813866707365265419": { - "messages": 109, + "messages": 114, "level": 2 }, "726188513518813275": { @@ -1944,7 +1944,7 @@ "level": 0 }, "715615331938467962": { - "messages": 8, + "messages": 9, "level": 0 }, "1033047374597214298": { @@ -1968,7 +1968,7 @@ "level": 0 }, "1030526730462572558": { - "messages": 191, + "messages": 192, "level": 3 }, "530533266206359552": { @@ -2092,7 +2092,7 @@ "level": 0 }, "123080080946757632": { - "messages": 37, + "messages": 39, "level": 1 }, "1001530115148226711": { @@ -2120,7 +2120,7 @@ "level": 0 }, "969867332304400404": { - "messages": 6, + "messages": 7, "level": 0 }, "881867367418826812": { @@ -2228,7 +2228,7 @@ "level": 0 }, "591972887812898816": { - "messages": 1, + "messages": 2, "level": 0 }, "718100655851438090": { @@ -2320,7 +2320,7 @@ "level": 0 }, "1029465066174677132": { - "messages": 10, + "messages": 11, "level": 0 }, "387345064927428611": { @@ -2340,7 +2340,7 @@ "level": 0 }, "938332923042598933": { - "messages": 1, + "messages": 2, "level": 0 }, "719181129390751755": { @@ -2352,7 +2352,7 @@ "level": 0 }, "972513020972519484": { - "messages": 87, + "messages": 94, "level": 2 }, "953310806538330163": { @@ -2376,7 +2376,7 @@ "level": 0 }, "703628676310368257": { - "messages": 4, + "messages": 6, "level": 0 }, "444715401159639042": { @@ -2488,7 +2488,7 @@ "level": 0 }, "284482432684785664": { - "messages": 2, + "messages": 3, "level": 0 }, "727948378067173407": { @@ -2500,7 +2500,7 @@ "level": 0 }, "490183428990304286": { - "messages": 75, + "messages": 77, "level": 2 }, "787352638811013151": { @@ -2520,7 +2520,7 @@ "level": 0 }, "534500956688351242": { - "messages": 4, + "messages": 8, "level": 0 }, "989466115803590686": { @@ -2544,7 +2544,7 @@ "level": 0 }, "697953686487564348": { - "messages": 110, + "messages": 123, "level": 2 }, "664233182941413427": { @@ -2592,7 +2592,7 @@ "level": 0 }, "340587045250531358": { - "messages": 84, + "messages": 86, "level": 2 }, "911709434554777660": { @@ -2680,7 +2680,7 @@ "level": 0 }, "999736341220839464": { - "messages": 7, + "messages": 9, "level": 0 }, "901565970974392331": { @@ -2688,7 +2688,7 @@ "level": 0 }, "388211913621897216": { - "messages": 21, + "messages": 22, "level": 1 }, "1039241529912475729": { @@ -2700,12 +2700,12 @@ "level": 0 }, "995622955897864272": { - "messages": 70, + "messages": 92, "level": 2 }, "153323923633733632": { - "messages": 8, - "level": 0 + "messages": 17, + "level": 1 }, "190028480786726913": { "messages": 2, @@ -2716,7 +2716,7 @@ "level": 0 }, "590595536537518091": { - "messages": 2, + "messages": 4, "level": 0 }, "576383133763764225": { @@ -2728,7 +2728,7 @@ "level": 0 }, "328602696040841216": { - "messages": 7, + "messages": 10, "level": 0 }, "222861120329744387": { @@ -2736,11 +2736,11 @@ "level": 0 }, "927957676120473671": { - "messages": 1, + "messages": 2, "level": 0 }, "846431793715871749": { - "messages": 1, + "messages": 3, "level": 0 }, "838471706871988234": { @@ -2748,11 +2748,31 @@ "level": 0 }, "259798326705127435": { - "messages": 1, + "messages": 2, "level": 0 }, "910647475570901054": { "messages": 2, "level": 0 + }, + "1037133851190890589": { + "messages": 2, + "level": 0 + }, + "945263081095852042": { + "messages": 5, + "level": 0 + }, + "521087871776718869": { + "messages": 4, + "level": 0 + }, + "299940980680032279": { + "messages": 3, + "level": 0 + }, + "248226483967885312": { + "messages": 2, + "level": 0 } } \ No newline at end of file diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts index 39d1088..67b644b 100644 --- a/src/events/guildMemberAdd.ts +++ b/src/events/guildMemberAdd.ts @@ -26,7 +26,7 @@ export default { const usedInvite = newInvites.find((inv:any)=>oldInvites.get(inv.code)?.uses < inv.uses); newInvites.forEach((inv:any)=>client.invites.set(inv.code,{uses: inv.uses, creator: inv.inviter.id})); - const embed1 = new client.embed().setColor(client.config.embedColorGreen).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Joined: ${member.user.tag}`).setDescription(`<@${member.user.id}>\n\`${member.user.id}\``).addFields( + const embed1 = new client.embed().setColor(client.config.embedColorGreen).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Joined: ${member.user.tag}`).setDescription(`<@${member.user.id}>\n\`${member.user.id}\``).setFooter({text: `Total members: ${index}${suffix}`}).addFields( {name: '🔹 Account Creation Date', value: `\n`}, {name: '🔹 Invite Data:', value: usedInvite ? `Invite: \`${usedInvite.code}\`\nCreated by: **${usedInvite.inviter?.tag}**` : 'I couldn\'t find out how they joined!'} ); diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 34545d0..be26ccc 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -3,7 +3,7 @@ import { TClient } from '../client'; export default { name: 'messageCreate', execute: async(client:TClient, message:Discord.Message)=>{ - if (!client.config.botSwitches.commands && !client.config.eval.whitelist.includes(message.author.id)) return + if (!client.config.botSwitches.commands && !client.config.eval.whitelist.includes(message.author.id)) return if (message.author.bot) return; if (message.channel.type === ChannelType.DM) return; const msgarr = message.content.toLowerCase().split(' '); @@ -73,15 +73,15 @@ export default { } // Autoresponse:tm: - if (!client.config.botSwitches.autores && !automodded) { + if (client.config.botSwitches.autores && !automodded) { if (message.mentions.members.has('309373272594579456') && !client.isStaff(message.member) && message.type != 19){ message.reply('Please don\'t tag Daggerwin, read rule 14 in <#468846117405196289>') } if (message.mentions.members.has('215497515934416896') && !client.isStaff(message.member) && message.type != 19){ message.reply('Please don\'t tag Monster unless it\'s important!') } - if (message.content.toLowerCase().startsWith(`${client.config.prefix}players`) || message.content.toLowerCase().startsWith(`${client.config.prefix}status`)){ - message.reply('Commands for the MP server have been moved to `*mp players` and `*mp status`.') + if (message.content.toLowerCase().startsWith(`*mp players`) || message.content.toLowerCase().startsWith(`*mp status`)){ + message.reply('Prefix-based MP commands has been moved to `/mp players` and `/mp status`') } if (message.content.toLowerCase().includes('whats the password') || message.content.toLowerCase().includes('what\'s the password') || message.content.toLowerCase().includes('password pls')){ message.reply('Password and other details can be found in <#543494084363288637>') diff --git a/src/events/messageUpdate.ts b/src/events/messageUpdate.ts index 31d3d5c..3e5464d 100644 --- a/src/events/messageUpdate.ts +++ b/src/events/messageUpdate.ts @@ -12,7 +12,7 @@ export default { const msgarr = newMsg.content.toLowerCase().split(' '); if (client.bannedWords._content.some((word:string)=>msgarr.includes(word)) && (!client.isStaff(newMsg.member))) newMsg.delete(); if (newMsg.content === oldMsg.content) return; - const embed = new client.embed().setColor(client.config.embedColor).setTimestamp().setAuthor({name: `Author: ${oldMsg.author.tag} (${oldMsg.author.id})`, iconURL: `${oldMsg.author.displayAvatarURL()}`}).setTitle('Message edited').setDescription(`<@${oldMsg.author.id}>\nOld content:\n\`\`\`\n${oldMsg.content}\n\`\`\`\nNew content:\n\`\`\`\nChannel: <#${oldMsg.channelId}>`); + const embed = new client.embed().setColor(client.config.embedColor).setTimestamp().setAuthor({name: `Author: ${oldMsg.author.tag} (${oldMsg.author.id})`, iconURL: `${oldMsg.author.displayAvatarURL()}`}).setTitle('Message edited').setDescription(`<@${oldMsg.author.id}>\nOld content:\n\`\`\`\n${oldMsg.content}\n\`\`\`\nNew content:\n\`\`\`\n${newMsg.content}\`\`\`\nChannel: <#${oldMsg.channelId}>`); (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [embed], components: [new ActionRowBuilder().addComponents(new ButtonBuilder().setStyle(5).setURL(`${oldMsg.url}`).setLabel('Jump to message'))]}); } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 6d589ac..1b13846 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,20 @@ +interface punOpt { + time?: string, + reason?: string, + interaction?: any +} import Discord from 'discord.js'; import { TClient } from './client'; const client = new TClient; client.init(); import fs from 'node:fs'; import MPDB from './models/MPServer'; +import { Punishment } from './typings/interfaces'; client.on('ready', async()=>{ client.guilds.cache.forEach(async(e: { members: { fetch: () => any; }; })=>{await e.members.fetch()}); setInterval(async()=>{ - client.user.setPresence({activities: [{ name: 'TypeScript is pog', type: 0 }], status: 'online'}); + client.user.setPresence({activities: [{ name: 'Slash commands!', type: 0 }], status: 'online'}); // Playing: 0, Streaming (Requires YT/Twitch URL to work): 1, Listening to: 2, Watching: 3, Competing in: 5 }, 60000); if (client.config.botSwitches.registerCommands) (client.guilds.cache.get(client.config.mainServer.id) as Discord.Guild).commands.set(client.registry).catch((e)=>{console.log(`Couldn't register slash commands: ${e}`)}) @@ -22,7 +28,7 @@ client.on('ready', async()=>{ }, 500000); console.log(`${client.user.tag} has logged into Discord API and now ready for operation`); console.log(client.config.botSwitches); - (client.channels.resolve(client.config.mainServer.channels.bot_status) as Discord.TextChannel).send(`${client.user.username} is active\n\`\`\`json\n${Object.entries(client.config.botSwitches).map((hi)=>`${hi[0]}: ${hi[1]}`).join('\n')}\`\`\``); + //(client.channels.resolve(client.config.mainServer.channels.bot_status) as Discord.TextChannel).send(`${client.user.username} is active\n\`\`\`json\n${Object.entries(client.config.botSwitches).map((hi)=>`${hi[0]}: ${hi[1]}`).join('\n')}\`\`\``); // Event handler const eventFiles = fs.readdirSync('src/events').filter(file=>file.endsWith('.ts')); @@ -35,21 +41,21 @@ client.on('ready', async()=>{ // Handle errors process.on('unhandledRejection', async(error: Error)=>{ console.log(error); - (client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) + //(client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) }); process.on('uncaughtException', async(error: Error)=>{ console.log(error); - (client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) + //(client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) }); process.on('error', async(error: Error)=>{ console.log(error); - (client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) + //(client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send({embeds: [new client.embed().setColor('#420420').setTitle('Error caught!').setDescription(`**Error:** \`${error.message}\`\n\n**Stack:** \`${`${error.stack}`.slice(0, 2500)}\``)]}) }); // Daggerwin MP loop setInterval(async()=>{ if (!client.config.botSwitches.mpstats) return; - const msg = await (client.channels.resolve('904192878140608563') as Discord.TextChannel).messages.fetch('1042464209709051974') + const msg = await (client.channels.resolve('904192878140608563') as Discord.TextChannel).messages.fetch('1042835699906400346') const embed = new client.embed(); let Players = []; let Server: any; @@ -115,7 +121,7 @@ setInterval(async()=>{ } else { const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Server details').addFields( {name: 'Current Map', value: `${Server.data.server.mapName.length == 0 ? '\u200b' : Server.data.server.mapName}`, inline: true}, - {name: 'Game Version', value: `${Server.data.server.version.length == 0 ? '\u200b' : Server.data.server.version}`, inline: true}, + {name: 'Version', value: `${Server.data.server.version.length == 0 ? '\u200b' : Server.data.server.version}`, inline: true}, {name: 'In-game Time', value: `${('0' + Math.floor((Server.data.server.dayTime/3600/1000))).slice(-2)}:${('0' + Math.floor((Server.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true}, {name: 'Slot Usage', value: `${Number(xmlData?.slotSystem?._attributes?.slotUsage).toLocaleString('en-US')}`, inline: true}, {name: 'Timescale', value: `${formatNumber(timeScale, 0, 'x')}`, inline: true} @@ -131,7 +137,7 @@ setInterval(async()=>{ // YouTube Upload notification setInterval(async()=>{ - client.YTLoop('UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '528967918772551702'); // 528967918772551702 = #videos-and-streams + client.YTLoop('UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '904192878140608563'); // 528967918772551702 = #videos-and-streams client.YTLoop('UCguI73--UraJpso4NizXNzA', 'Machinery Restorer', '767444045520961567') // 767444045520961567 = #machinery-restorer }, 300000) @@ -159,7 +165,7 @@ setInterval(async()=>{ total = yesterday } dailyMsgs.push([formattedDate, total]); - fs.writeFileSync(__dirname + 'dailyMsgs.json', JSON.stringify(dailyMsgs)) + fs.writeFileSync(__dirname + '/database/dailyMsgs.json', JSON.stringify(dailyMsgs)) console.log(`\x1b[36m[${client.moment().format('DD/MM/YY HH:mm:ss')}] \x1b[33m`, `Pushed [${formattedDate}, ${total}] to dailyMsgs`) } }, 5000) diff --git a/src/typings/interfaces.d.ts b/src/typings/interfaces.d.ts index aa65234..da24350 100644 --- a/src/typings/interfaces.d.ts +++ b/src/typings/interfaces.d.ts @@ -1,18 +1,18 @@ -interface formatTimeOpt { +export interface formatTimeOpt { longNames: boolean, commas: boolean } -interface createTableOpt { +export interface createTableOpt { columnAlign: any, columnSeparator: any, columnEmptyChar: any } -interface punOpt { +export interface punOpt { time?: string, reason?: string, interaction?: any } -interface Punishment { +export interface Punishment { id: number; type: string; member: string; diff --git a/tsconfig.json b/tsconfig.json index 143ded2..e1bdec5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,4 @@ { - "ts-node": { "transpileOnly": true, "compilerOptions": { "esModuleInterop": true, @@ -12,11 +11,8 @@ "rootDir": "src/", "outDir": "dist/", "moduleResolution": "Node", - "declarationDir": "src/typings", - "typeRoots": [ "node_modules/@types", "src/typings" ], + "typeRoots": [ "node_modules/@types", "./src/typings" ], }, - - }, "include": [ "node_modules/@types", "src/**/*" ], "exclude": [ "dist", "node_modules" ], } \ No newline at end of file