1
0
mirror of https://github.com/toast-ts/Daggerbot-TS.git synced 2024-09-29 04:11:00 -04:00

Stupid GitHub formatting...

This commit is contained in:
toast-ts 2023-03-05 21:04:10 +11:00
parent ee5378e374
commit 499cb07b8c
37 changed files with 1659 additions and 1709 deletions

View File

@ -1,2 +1,3 @@
# Daggerbot-TS ![https://discord.gg/4SnUAFu](https://cdn.discordapp.com/attachments/1015195575693627442/1081877631068295178/DaggerwinServerBanner2023.gif)
TypeScript-based Daggerbot converted from JavaScript at [SpaceManBuzz/DaggerBot-](https://github.com/SpaceManBuzz/DaggerBot-) # Daggerbot-TS Description
This is 1st generation bot that is a TypeScript-based Daggerbot converted from JavaScript at (now archived and privated) ~~[SpaceManBuzz/DaggerBot-](https://github.com/SpaceManBuzz/DaggerBot-)~~

View File

@ -16,185 +16,184 @@ import tokens from './tokens.json';
let importconfig:Config let importconfig:Config
try{ try{
importconfig = require('./DB-Beta.config.json') importconfig = require('./DB-Beta.config.json')
console.log('Using development config :: Daggerbot Beta') console.log('Using development config :: Daggerbot Beta')
//importconfig = require('./Toast-Testbot.config.json') //importconfig = require('./Toast-Testbot.config.json')
//console.log('Using development config :: Toast-Testbot') //console.log('Using development config :: Toast-Testbot')
} catch(e){ } catch(e){
importconfig = require('./config.json') importconfig = require('./config.json')
console.log('Using production config') console.log('Using production config')
} }
export default class TClient extends Client { export default class TClient extends Client {
invites: Map<any, any>; invites: Map<any, any>;
commands: Discord.Collection<string, any>; commands: Discord.Collection<string, any>;
registry: Array<Discord.ApplicationCommandDataResolvable>; registry: Array<Discord.ApplicationCommandDataResolvable>;
config: Config; config: Config;
tokens: Tokens; tokens: Tokens;
YTCache: any; YTCache: any;
embed: typeof Discord.EmbedBuilder; embed: typeof Discord.EmbedBuilder;
collection: any; collection: any;
messageCollector: any; messageCollector: any;
attachmentBuilder: any; attachmentBuilder: any;
moment: typeof moment; moment: typeof moment;
xjs: any; xjs: any;
axios: typeof axios; axios: typeof axios;
ms: any; ms: any;
userLevels: userLevels; userLevels: userLevels;
punishments: punishments; punishments: punishments;
bonkCount: bonkCount; bonkCount: bonkCount;
bannedWords: bannedWords; bannedWords: bannedWords;
MPServer: MPServer; MPServer: MPServer;
suggestion: suggestion; suggestion: suggestion;
repeatedMessages: repeatedMessages; repeatedMessages: repeatedMessages;
statsGraph: number; statsGraph: number;
constructor(){ constructor(){
super({ super({
intents: [ intents: [
GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildBans, GatewayIntentBits.GuildInvites, GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildInvites,
GatewayIntentBits.GuildPresences, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages GatewayIntentBits.GuildPresences, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages
], ],
partials: [ partials: [
Partials.Channel, Partials.Channel,
Partials.Reaction, Partials.Reaction,
Partials.Message Partials.Message
], ],
allowedMentions: { users: [], roles: [] } allowedMentions: {users:[],roles:[]}
}) })
this.invites = new Map(); this.invites = new Map();
this.commands = new Discord.Collection(); this.commands = new Discord.Collection();
this.registry = []; this.registry = [];
this.config = importconfig as Config; this.config = importconfig as Config;
this.tokens = tokens as Tokens; this.tokens = tokens as Tokens;
this.YTCache = { this.YTCache = {
'UCQ8k8yTDLITldfWYKDs3xFg': undefined, // Daggerwin 'UCQ8k8yTDLITldfWYKDs3xFg': undefined, // Daggerwin
'UCguI73--UraJpso4NizXNzA': undefined // Machinery Restorer 'UCguI73--UraJpso4NizXNzA': undefined // Machinery Restorer
}
this.embed = Discord.EmbedBuilder;
this.collection = Discord.Collection;
this.messageCollector = Discord.MessageCollector;
this.attachmentBuilder = Discord.AttachmentBuilder;
this.moment = moment;
this.xjs = require('xml-js');
this.axios = axios;
this.ms = require('ms');
this.userLevels = new userLevels(this);
this.bonkCount = new bonkCount(this);
this.punishments = new punishments(this);
this.bannedWords = new bannedWords(this);
this.MPServer = new MPServer(this);
this.suggestion = new suggestion(this);
this.repeatedMessages = {};
this.setMaxListeners(80);
this.statsGraph = -60;
} }
async init(){ this.embed = Discord.EmbedBuilder;
mongoose.set('strictQuery', true); this.collection = Discord.Collection;
await mongoose.connect(this.tokens.mongodb_uri, { this.messageCollector = Discord.MessageCollector;
replicaSet: 'toastyy', this.attachmentBuilder = Discord.AttachmentBuilder;
autoIndex: true, this.moment = moment;
keepAlive: true, this.xjs = require('xml-js');
serverSelectionTimeoutMS: 15000, this.axios = axios;
waitQueueTimeoutMS: 50000, this.ms = require('ms');
socketTimeoutMS: 30000, this.userLevels = new userLevels(this);
family: 4 this.bonkCount = new bonkCount(this);
}).then(()=>console.log(this.logTime(), 'Successfully connected to MongoDB')).catch((err)=>{console.error(this.logTime(), `Failed to connect to MongoDB\n${err.reason}`); exec('pm2 stop Daggerbot')}) this.punishments = new punishments(this);
await this.login(this.tokens.main); this.bannedWords = new bannedWords(this);
const commandFiles = fs.readdirSync('src/commands').filter(file=>file.endsWith('.ts')); this.MPServer = new MPServer(this);
for (const file of commandFiles){ this.suggestion = new suggestion(this);
const command = require(`./commands/${file}`); this.repeatedMessages = {};
this.commands.set(command.default.data.name, command) this.setMaxListeners(80);
this.registry.push(command.default.data.toJSON()) this.statsGraph = -60;
} }
fs.readdirSync('src/events').forEach((file)=>{ async init(){
const eventFile = require(`./events/${file}`); console.time('Startup');
this.on(file.replace('.ts', ''), async(...args)=>eventFile.default.run(this,...args)); mongoose.set('strictQuery', true);
}); await mongoose.connect(this.tokens.mongodb_uri, {
replicaSet: 'toastyy',
autoIndex: true,
keepAlive: true,
serverSelectionTimeoutMS: 15000,
waitQueueTimeoutMS: 50000,
socketTimeoutMS: 30000,
family: 4
}).then(()=>console.log(this.logTime(), 'Successfully connected to MongoDB')).catch((err)=>{console.error(this.logTime(), `Failed to connect to MongoDB\n${err.reason}`); exec('pm2 stop Daggerbot')})
await this.login(this.tokens.main);
const commandFiles = fs.readdirSync('src/commands').filter(file=>file.endsWith('.ts'));
for (const file of commandFiles){
const command = require(`./commands/${file}`);
this.commands.set(command.default.data.name, command)
this.registry.push(command.default.data.toJSON())
} }
formatTime(integer: number, accuracy = 1, options?: formatTimeOpt){ fs.readdirSync('src/events').forEach((file)=>{
let achievedAccuracy = 0; const eventFile = require(`./events/${file}`);
let text:any = ''; this.on(file.replace('.ts', ''), async(...args)=>eventFile.default.run(this,...args));
for (const timeName of timeNames){ });
if (achievedAccuracy < accuracy){ }
const fullTimelengths = Math.floor(integer/timeName.length); formatTime(integer: number, accuracy = 1, options?: formatTimeOpt){
if (fullTimelengths == 0) continue; let achievedAccuracy = 0;
achievedAccuracy++; let text:any = '';
text += fullTimelengths + (options?.longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (options?.commas ? ', ' : ' '); for (const timeName of timeNames){
integer -= fullTimelengths*timeName.length; if (achievedAccuracy < accuracy){
} else { const fullTimelengths = Math.floor(integer/timeName.length);
break; if (fullTimelengths == 0) continue;
} achievedAccuracy++;
} text += fullTimelengths + (options?.longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (options?.commas ? ', ' : ' ');
if (text.length == 0) text = integer + (options?.longNames ? ' milliseconds' : 'ms') + (options?.commas ? ', ' : ''); integer -= fullTimelengths*timeName.length;
if (options?.commas){ } else break;
text = text.slice(0, -2);
if (options?.longNames){
text = text.split('');
text[text.lastIndexOf(',')] = ' and';
text = text.join('');
}
} return text.trim();
} }
isStaff(guildMember: Discord.GuildMember){ if (text.length == 0) text = integer + (options?.longNames ? ' milliseconds' : 'ms') + (options?.commas ? ', ' : '');
return this.config.mainServer.staffRoles.map((x: string)=>this.config.mainServer.roles[x]).some((x: string)=>guildMember.roles.cache.has(x)) if (options?.commas){
} text = text.slice(0, -2);
youNeedRole(interaction: Discord.CommandInteraction, role:string){ if (options?.longNames){
return interaction.reply(`This command is restricted to <@&${this.config.mainServer.roles[role]}>`) text = text.split('');
} text[text.lastIndexOf(',')] = ' and';
logTime(){ text = text.join('');
return `[${this.moment().format('DD/MM/YY HH:mm:ss')}]` }
} } return text.trim();
alignText(text: string, length: number, alignment: string, emptyChar = ' '){ }
if (alignment == 'right'){ isStaff = (guildMember:Discord.GuildMember)=>this.config.mainServer.staffRoles.map((x: string)=>this.config.mainServer.roles[x]).some((x: string)=>guildMember.roles.cache.has(x));
text = emptyChar.repeat(length - text.length)+text;
} else if (alignment == 'middle'){
const emptyCharsPerSide = (length - text.length)/2;
text = emptyChar.repeat(Math.floor(emptyCharsPerSide))+text+emptyChar.repeat(Math.floor(emptyCharsPerSide));
} else {
text = text + emptyChar.repeat(length - text.length);
} return text;
}
async punish(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string){
if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, "dcmod");
const time = interaction.options.getString('time') ?? undefined; youNeedRole = (interaction:Discord.CommandInteraction, role:string)=>interaction.reply(`This command is restricted to <@&${this.config.mainServer.roles[role]}>`);
const reason = interaction.options.getString('reason') ?? 'Reason unspecified';
const GuildMember = interaction.options.getMember('member') ?? undefined;
const User = interaction.options.getUser('member', true);
if (interaction.user.id == User.id) return interaction.reply(`You cannot ${type} yourself.`); logTime = ()=>`[${this.moment().format('DD/MM/YY HH:mm:ss')}]`;
if (!GuildMember && type != 'ban') return interaction.reply(`You cannot ${type} someone who is not in the server.`);
if (User.bot) return interaction.reply(`You cannot ${type} a bot!`);
await interaction.deferReply(); alignText(text: string, length: number, alignment: string, emptyChar = ' '){
await client.punishments.addPunishment(type, { time, interaction }, interaction.user.id, reason, User, GuildMember); if (alignment == 'right') text = emptyChar.repeat(length - text.length)+text;
else if (alignment == 'middle'){
const emptyCharsPerSide = (length - text.length)/2;
text = emptyChar.repeat(Math.floor(emptyCharsPerSide))+text+emptyChar.repeat(Math.floor(emptyCharsPerSide));
} else text = text + emptyChar.repeat(length - text.length);
return text;
}
async punish(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string){
if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, "dcmod");
const time = interaction.options.getString('time') ?? undefined;
const reason = interaction.options.getString('reason') ?? 'Reason unspecified';
const GuildMember = interaction.options.getMember('member') ?? undefined;
const User = interaction.options.getUser('member', true);
if (interaction.user.id == User.id) return interaction.reply(`You cannot ${type} yourself.`);
if (!GuildMember && type != 'ban') return interaction.reply(`You cannot ${type} someone who is not in the server.`);
if (User.bot) return interaction.reply(`You cannot ${type} a bot!`);
await interaction.deferReply();
await client.punishments.addPunishment(type, { time, interaction }, interaction.user.id, reason, User, GuildMember);
}
async YTLoop(YTChannelID: string, YTChannelName: string, DCChannelID: string){
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(this.logTime(), `${YTChannelName} YT fail`)
} }
async YTLoop(YTChannelID: string, YTChannelName: string, DCChannelID: string){
let Data:any;
let error;
try { if (!Data) return;
await this.axios.get(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {timeout: 5000}).then((xml:any)=>{ if (this.YTCache[YTChannelID] == undefined){
Data = this.xjs.xml2js(xml.data, {compact: true, spaces: 2}); this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
}) return;
} catch(err){
error = true;
console.log(this.logTime(), `${YTChannelName} YT fail`)
}
if (!Data) return;
if (this.YTCache[YTChannelID] == undefined){
this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
return;
}
if (Data.feed.entry[1]['yt:videoId']._text == this.YTCache[YTChannelID]){
this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
(this.channels.resolve(DCChannelID) as Discord.TextChannel).send(`**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`)
}
} }
if (Data.feed.entry[1]['yt:videoId']._text == this.YTCache[YTChannelID]){
this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
(this.channels.resolve(DCChannelID) as Discord.TextChannel).send(`**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`)
}
}
} }
export class WClient extends WebhookClient {tokens: Tokens; constructor(){super({url: tokens.webhook_url})}} export class WClient extends WebhookClient {
tokens: Tokens;
constructor(){
super({
url: tokens.webhook_url
})
}
}
// hi tae, ik you went to look for secret hello msgs in here too. // hi tae, ik you went to look for secret hello msgs in here too.

View File

@ -1,20 +1,20 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
client.punish(client, interaction, 'ban'); client.punish(client, interaction, 'ban');
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('ban') .setName('ban')
.setDescription('Ban a member from the server') .setDescription('Ban a member from the server')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member to ban?') .setDescription('Which member to ban?')
.setRequired(true)) .setRequired(true))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('time') .setName('time')
.setDescription('How long the ban will be?')) .setDescription('How long the ban will be?'))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('reason') .setName('reason')
.setDescription('Reason for the ban')) .setDescription('Reason for the ban'))
} }

View File

@ -1,42 +1,42 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (!client.isStaff(interaction.member) && !client.config.eval.whitelist.includes(interaction.member.id)) return client.youNeedRole(interaction, 'admin') if (!client.isStaff(interaction.member) && !client.config.eval.whitelist.includes(interaction.member.id)) return client.youNeedRole(interaction, 'admin')
const word = interaction.options.getString('word', true); const word = interaction.options.getString('word', true);
const wordExists = await client.bannedWords._content.findById(word); const wordExists = await client.bannedWords._content.findById(word);
({ ({
add: async()=>{ add: async()=>{
if (wordExists) return interaction.reply({content: `\`${word}\` is already added.`, ephemeral: true}); if (wordExists) return interaction.reply({content: `\`${word}\` is already added.`, ephemeral: true});
await client.bannedWords._content.create({_id:word}).then(a=>a.save()); await client.bannedWords._content.create({_id:word}).then(a=>a.save());
interaction.reply(`Successfully added \`${word}\` to the database.`) interaction.reply(`Successfully added \`${word}\` to the database.`)
}, },
remove: async()=>{ remove: async()=>{
if (!wordExists) return interaction.reply({content: `\`${word}\` doesn't exist on the list.`, ephemeral: true}); if (!wordExists) return interaction.reply({content: `\`${word}\` doesn't exist on the list.`, ephemeral: true});
await client.bannedWords._content.findOneAndDelete({_id:word}); await client.bannedWords._content.findOneAndDelete({_id:word});
interaction.reply(`Successfully removed \`${word}\` from the database.`) interaction.reply(`Successfully removed \`${word}\` from the database.`)
}, },
//view: ()=>interaction.reply({content: 'Here is a complete list of banned words!\n*You can open it with a web browser, e.g Chrome/Firefox/Safari, or you can use Visual Studio Code/Notepad++*', files: ['src/database/bannedWords.json'], ephemeral: true}) //view: ()=>interaction.reply({content: 'Here is a complete list of banned words!\n*You can open it with a web browser, e.g Chrome/Firefox/Safari, or you can use Visual Studio Code/Notepad++*', files: ['src/database/bannedWords.json'], ephemeral: true})
} as any)[interaction.options.getSubcommand()](); } as any)[interaction.options.getSubcommand()]();
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('bannedwords') .setName('bannedwords')
.setDescription('description placeholder') .setDescription('description placeholder')/*
/*.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('view') .setName('view')
.setDescription('View the list of currently banned words.')) .setDescription('View the list of currently banned words.'))*/
*/.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('add') .setName('add')
.setDescription('What word do you want to add?') .setDescription('What word do you want to add?')
.addStringOption((optt)=>optt .addStringOption((optt)=>optt
.setName('word') .setName('word')
.setDescription('Add the specific word to automod\'s bannedWords database.') .setDescription('Add the specific word to automod\'s bannedWords database.')
.setRequired(true))) .setRequired(true)))
.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('remove') .setName('remove')
.setDescription('What word do you want to remove?') .setDescription('What word do you want to remove?')
.addStringOption((optt)=>optt .addStringOption((optt)=>optt
.setName('word') .setName('word')
.setDescription('Remove the specific word from automod\'s bannedWords list.') .setDescription('Remove the specific word from automod\'s bannedWords list.')
.setRequired(true))) .setRequired(true)))
} }

View File

@ -1,79 +1,79 @@
import Discord,{SlashCommandBuilder} from "discord.js"; import Discord,{SlashCommandBuilder} from "discord.js";
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod'); if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod');
const caseId = interaction.options.getInteger('id'); const caseId = interaction.options.getInteger('id');
({ ({
update: async()=>{ update: async()=>{
const reason = interaction.options.getString('reason'); const reason = interaction.options.getString('reason');
await client.punishments._content.findByIdAndUpdate(caseId, {reason}); await client.punishments._content.findByIdAndUpdate(caseId, {reason});
await interaction.reply({embeds: [new client.embed().setColor(client.config.embedColorGreen).setTitle('Case updated').setDescription(`Case #${caseId} has been successfully updated with new reason:\n\`${reason}\``)]}) await interaction.reply({embeds: [new client.embed().setColor(client.config.embedColorGreen).setTitle('Case updated').setDescription(`Case #${caseId} has been successfully updated with new reason:\n\`${reason}\``)]})
}, },
view: async()=>{ view: async()=>{
const punishment = await client.punishments._content.findById(caseId); const punishment = await client.punishments._content.findById(caseId);
if (!punishment) return interaction.reply('Invalid Case #'); if (!punishment) return interaction.reply('Invalid Case #');
const cancelledBy = punishment.expired ? await client.punishments._content.findOne({cancels:punishment.id}) : null; const cancelledBy = punishment.expired ? await client.punishments._content.findOne({cancels:punishment.id}) : null;
const cancels = punishment.cancels ? await client.punishments._content.findOne({_id:punishment.cancels}) : null; const cancels = punishment.cancels ? await client.punishments._content.findOne({_id:punishment.cancels}) : null;
const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`).addFields( const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`).addFields(
{name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true}, {name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true},
{name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true}, {name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true},
{name: '\u200b', value: '\u200b', inline: true}, {name: '\u200b', value: '\u200b', inline: true},
{name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true}) {name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true})
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: client.formatTime(punishment.duration, 100)}) if (punishment.duration) embed.addFields({name: '🔹 Duration', value: client.formatTime(punishment.duration, 100)})
if (punishment.expired) embed.addFields({name: '🔹 Expired', value: `This case has been overwritten by case #${cancelledBy.id} for reason \`${cancelledBy.reason}\``}) if (punishment.expired) embed.addFields({name: '🔹 Expired', value: `This case has been overwritten by case #${cancelledBy.id} for reason \`${cancelledBy.reason}\``})
if (punishment.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites case #${cancels.id} with reason \`${cancels.reason}\``}) if (punishment.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites case #${cancels.id} with reason \`${cancels.reason}\``})
interaction.reply({embeds: [embed]}); interaction.reply({embeds: [embed]});
}, },
member: async()=>{ member: async()=>{
// if caseid is user id, show their punishment history sorted by most recent. // if caseid is user id, show their punishment history sorted by most recent.
const user = (interaction.options.getUser('user') as Discord.User); const user = (interaction.options.getUser('user') as Discord.User);
if (user.bot) return interaction.reply(`<@${user.id}>'s punishment history cannot be viewed.`) if (user.bot) return interaction.reply(`<@${user.id}>'s punishment history cannot be viewed.`)
const punishments = await client.punishments._content.find({}); const punishments = await client.punishments._content.find({});
if (!punishments) return interaction.reply(`<@${user.id}> has a clean record.`) if (!punishments) return interaction.reply(`<@${user.id}> has a clean record.`)
const userPunishmentData = await client.punishments._content.find({'member':user.id}); const userPunishmentData = await client.punishments._content.find({'member':user.id});
const userPunishment = userPunishmentData.sort((a,b)=>a.time-b.time).map((punishment)=>{ const userPunishment = userPunishmentData.sort((a,b)=>a.time-b.time).map((punishment)=>{
return { return {
name: `${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`, name: `${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`,
value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${client.formatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites case #${punishment.cancels}` : ''}` value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${client.formatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites case #${punishment.cancels}` : ''}`
} }
}); });
// if caseid is not a punishment nor a user, failed // if caseid is not a punishment nor a user, failed
if (!userPunishment || userPunishment.length == 0) return interaction.reply('No punishments found for that case # or User ID'); if (!userPunishment || userPunishment.length == 0) return interaction.reply('No punishments found for that case # or User ID');
const pageNum = interaction.options.getInteger('page') ?? 1; const pageNum = interaction.options.getInteger('page') ?? 1;
return interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle(`${user.username}'s punishment history`).setDescription(`**ID:** \`${user.id}\``).setFooter({text: `${userPunishment.length} total punishments. Viewing page ${pageNum} out of ${Math.ceil(userPunishment.length/6)}.`}).addFields(userPunishment.slice((pageNum - 1) * 6, pageNum * 6))]}); return interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle(`${user.username}'s punishment history`).setDescription(`**ID:** \`${user.id}\``).setFooter({text: `${userPunishment.length} total punishments. Viewing page ${pageNum} out of ${Math.ceil(userPunishment.length/6)}.`}).addFields(userPunishment.slice((pageNum - 1) * 6, pageNum * 6))]});
} }
} as any)[interaction.options.getSubcommand()](); } as any)[interaction.options.getSubcommand()]();
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('case') .setName('case')
.setDescription('Retrieve case information or user\'s punishment history') .setDescription('Retrieve case information or user\'s punishment history')
.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('view') .setName('view')
.setDescription('View a single case.') .setDescription('View a single case.')
.addIntegerOption((optt)=>optt .addIntegerOption((optt)=>optt
.setName('id') .setName('id')
.setDescription('Case #') .setDescription('Case #')
.setRequired(true))) .setRequired(true)))
.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('member') .setName('member')
.setDescription('View member\'s punishment history') .setDescription('View member\'s punishment history')
.addUserOption((optt)=>optt .addUserOption((optt)=>optt
.setName('user') .setName('user')
.setDescription('Which user do you want to view their punishment history?') .setDescription('Which user do you want to view their punishment history?')
.setRequired(true)) .setRequired(true))
.addIntegerOption((optt)=>optt .addIntegerOption((optt)=>optt
.setName('page') .setName('page')
.setDescription('Select the page number'))) .setDescription('Select the page number')))
.addSubcommand((opt)=>opt .addSubcommand((opt)=>opt
.setName('update') .setName('update')
.setDescription('Update the case with new reason') .setDescription('Update the case with new reason')
.addIntegerOption((optt)=>optt .addIntegerOption((optt)=>optt
.setName('id') .setName('id')
.setDescription('Case # to be updated') .setDescription('Case # to be updated')
.setRequired(true)) .setRequired(true))
.addStringOption((optt)=>optt .addStringOption((optt)=>optt
.setName('reason') .setName('reason')
.setDescription('New reason for the case') .setDescription('New reason for the case')
.setRequired(true))) .setRequired(true)))
}; };

View File

@ -1,20 +1,20 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Daggerbot contributors').setDescription([ interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Daggerbot contributors').setDescription([
'**Thanks to those below that contributed to the bot!**', '**Thanks to those below that contributed to the bot!**',
'Toast <@190407856527376384>', 'Toast <@190407856527376384>',
'TÆMBØ <@615761944154210305>', 'TÆMBØ <@615761944154210305>',
'Buzz <@593696856165449749>', 'Buzz <@593696856165449749>',
'Monster <@215497515934416896>', 'Monster <@215497515934416896>',
'RainbowDave <@141304507249197057>', 'RainbowDave <@141304507249197057>',
'Hitchhiker <@506022868157595648>', 'Hitchhiker <@506022868157595648>',
'RedRover92 <@633345781780185099>', 'RedRover92 <@633345781780185099>',
'Nawdic <@178941218510602240>' 'Nawdic <@178941218510602240>'
].join('\n'))]}) ].join('\n'))]})
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('contributors') .setName('contributors')
.setDescription('List of people who contributed to the bot.') .setDescription('List of people who contributed to the bot.')
} }

View File

@ -20,155 +20,147 @@ const removeUsername = (text: string)=>{
} return array.join('\\'); } return array.join('\\');
}; };
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>) { run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>) {
if (!client.config.eval.whitelist.includes(interaction.user.id)) return client.youNeedRole(interaction, 'bottech'); if (!client.config.eval.whitelist.includes(interaction.user.id)) return client.youNeedRole(interaction, 'bottech');
({ ({
eval: async()=>{ eval: async()=>{
if (!client.config.eval.allowed) return interaction.reply({content: 'Eval is disabled.', ephemeral: true}); if (!client.config.eval.allowed) return interaction.reply({content: 'Eval is disabled.', ephemeral: true});
const code = interaction.options.getString('code') as string; const code = interaction.options.getString('code') as string;
let output = 'error'; let output = 'error';
let error = false; let error = false;
try { try {
output = await eval(code); output = await eval(code);
} catch (err: any) { } catch (err: any) {
error = true error = true
const embed = new client.embed().setColor('#ff0000').setTitle('__Eval__').addFields( const embed = new client.embed().setColor('#ff0000').setTitle('__Eval__').addFields(
{name: 'Input', value: `\`\`\`js\n${code.slice(0, 1010)}\n\`\`\``}, {name: 'Input', value: `\`\`\`js\n${code.slice(0, 1010)}\n\`\`\``},
{name: 'Output', value: `\`\`\`\n${err}\`\`\``} {name: 'Output', value: `\`\`\`\n${err}\`\`\``}
) )
interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})).then(errorEmbedMessage=>{ interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})).then(errorEmbedMessage=>{
const filter = (x:any)=>x.content === 'stack' && x.author.id === interaction.user.id const filter = (x:any)=>x.content === 'stack' && x.author.id === interaction.user.id
const messagecollector = (interaction.channel as Discord.TextChannel).createMessageCollector({filter, max: 1, time: 60000}); const messagecollector = (interaction.channel as Discord.TextChannel).createMessageCollector({filter, max: 1, time: 60000});
messagecollector.on('collect', collected=>{ messagecollector.on('collect', collected=>{
collected.reply({content: `\`\`\`\n${removeUsername(err.stack)}\n\`\`\``, allowedMentions: {repliedUser: false}}); collected.reply({content: `\`\`\`\n${removeUsername(err.stack)}\n\`\`\``, allowedMentions: {repliedUser: false}});
}); });
}); });
} }
if (error) return; if (error) return;
if (typeof output == 'object') { if (typeof output == 'object') output = 'js\n'+util.formatWithOptions({depth: 1}, '%O', output)
output = 'js\n'+util.formatWithOptions({depth: 1}, '%O', output) else output = '\n' + String(output);
} else {
output = '\n' + String(output); [client.tokens.main,client.tokens.beta,client.tokens.toast,client.tokens.tae,client.tokens.webhook_url,client.tokens.webhook_url_test,client.tokens.mongodb_uri,client.tokens.mongodb_uri_dev].forEach((x)=>{
} const regexp = new RegExp(x as string,'g');
[client.tokens.main,client.tokens.beta,client.tokens.toast,client.tokens.tae,client.tokens.webhook_url,client.tokens.webhook_url_test,client.tokens.mongodb_uri,client.tokens.mongodb_uri_dev].forEach((x)=>{ output = output.replace(regexp, ':noblank: No token?');
const regexp = new RegExp(x as string,'g'); })
output = output.replace(regexp, ':noblank: No token?'); const embed = new client.embed().setColor(client.config.embedColor).setTitle('__Eval__').addFields(
}) {name: 'Input', value: `\`\`\`js\n${code.slice(0,1010)}\n\`\`\``},
const embed = new client.embed().setColor(client.config.embedColor).setTitle('__Eval__').addFields( {name: 'Output', value: `\`\`\`${removeUsername(output).slice(0,1016)}\n\`\`\``}
{name: 'Input', value: `\`\`\`js\n${code.slice(0,1010)}\n\`\`\``}, );
{name: 'Output', value: `\`\`\`${removeUsername(output).slice(0,1016)}\n\`\`\``} interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]}));
); },
interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})); update: async()=>{
}, var githubRepo = {owner: 'AnxietyisReal', repo: 'Daggerbot-TS', ref: 'HEAD'}
update: async()=>{ const octokit = new Octokit({timeZone: 'Australia/NSW', userAgent: 'Daggerbot'})
var githubRepo = {owner: 'AnxietyisReal', repo: 'Daggerbot-TS', ref: 'HEAD'} const fetchCommitMsg = await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.message).catch(err=>{console.log(err); interaction.reply({content: 'Placeholder error for `fetchCommitMsg`', ephemeral: true})});
const octokit = new Octokit({timeZone: 'Australia/NSW', userAgent: 'Daggerbot'}) const fetchCommitAuthor = await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.author.name).catch(err=>{console.log(err); interaction.reply({content: 'Placeholder error for `fetchCommitAuthor`', ephemeral: true})});
const fetchCommitMsg = await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.message).catch(err=>{console.log(err); interaction.reply({content: 'Placeholder error for `fetchCommitMsg`', ephemeral: true})}); const clarkson = await interaction.reply({content: 'Pulling from repository...', fetchReply: true});
const fetchCommitAuthor = await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.author.name).catch(err=>{console.log(err); interaction.reply({content: 'Placeholder error for `fetchCommitAuthor`', ephemeral: true})}); exec('git pull',(err:Error,stdout)=>{
const clarkson = await interaction.reply({content: 'Pulling from repository...', fetchReply: true}); if (err) clarkson.edit(`\`\`\`${removeUsername(err.message)}\`\`\``)
exec('git pull',(err:Error,stdout)=>{ else if (stdout.includes('Already up to date')) clarkson.edit('Bot is already up to date with the repository, did you forgor to push the changes? :skull:')
if (err){ else setTimeout(()=>clarkson.edit(`Commit: **${fetchCommitMsg}**\nCommit author: **${fetchCommitAuthor}**\n\nUptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot')),650)
clarkson.edit(`\`\`\`${removeUsername(err.message)}\`\`\``) });
} else if (stdout.includes('Already up to date')){ },
clarkson.edit('Bot is already up to date with the repository, did you forgor to push the changes? :skull:') presence: ()=>{
} else { function convertType(Type?: number){
setTimeout(()=>{clarkson.edit(`Commit: **${fetchCommitMsg}**\nCommit author: **${fetchCommitAuthor}**\n\nUptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot'))},650) switch (Type) {
} case 0: return 'Playing';
}); case 1: return 'Streaming';
}, case 2: return 'Listening to';
presence: ()=>{ case 3: return 'Watching';
function convertType(Type?: number){ case 5: return 'Competing in';
switch (Type) {
case 0: return 'Playing';
case 1: return 'Streaming';
case 2: return 'Listening to';
case 3: return 'Watching';
case 5: return 'Competing in';
}
};
const status = interaction.options.getString('status') as Discord.PresenceStatusData | null;
const type = interaction.options.getInteger('type');
const name = interaction.options.getString('name');
const url = interaction.options.getString('url');
const currentActivities = client.config.botPresence.activities as Discord.ActivitiesOptions[];
if (status) client.config.botPresence.status = status;
if (type) currentActivities[0].type = type;
if (name) currentActivities[0].name = name;
if (url) currentActivities[0].url = url;
client.user.setPresence(client.config.botPresence);
interaction.reply([
'Presence updated:',
`Status: **${client.config.botPresence.status}**`,
`Type: **${convertType(currentActivities[0].type)}**`,
`Name: **${currentActivities[0].name}**`,
`URL: \`${currentActivities[0].url}\``
].join('\n'))
},
statsgraph: ()=>{
client.statsGraph = -(interaction.options.getInteger('number', true));
interaction.reply(`Successfully set to \`${client.statsGraph}\`\n*Total data points: **${JSON.parse(readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).length.toLocaleString()}***`)
},
logs: ()=>{
interaction.deferReply();
(client.channels.resolve(client.config.mainServer.channels.console) as Discord.TextChannel).send({content: `Uploaded the current console dump as of <t:${Math.round(Date.now()/1000)}:R>`, files: [`${process.env.pm2_home}/logs/Daggerbot-out-0.log`, `${process.env.pm2_home}/logs/Daggerbot-error-0.log`]}).then(()=>interaction.editReply('It has been uploaded to dev server.')).catch((e:Error)=>interaction.editReply(`\`${e.message}\``))
},
restart: ()=>{
interaction.reply(`Uptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot'))
} }
} as any)[interaction.options.getSubcommand()](); };
const status = interaction.options.getString('status') as Discord.PresenceStatusData | null;
const type = interaction.options.getInteger('type');
const name = interaction.options.getString('name');
const url = interaction.options.getString('url');
const currentActivities = client.config.botPresence.activities as Discord.ActivitiesOptions[];
if (status) client.config.botPresence.status = status;
if (type) currentActivities[0].type = type;
if (name) currentActivities[0].name = name;
if (url) currentActivities[0].url = url;
client.user.setPresence(client.config.botPresence);
interaction.reply([
'Presence updated:',
`Status: **${client.config.botPresence.status}**`,
`Type: **${convertType(currentActivities[0].type)}**`,
`Name: **${currentActivities[0].name}**`,
`URL: \`${currentActivities[0].url}\``
].join('\n'))
},
statsgraph: ()=>{
client.statsGraph = -(interaction.options.getInteger('number', true));
interaction.reply(`Successfully set to \`${client.statsGraph}\`\n*Total data points: **${JSON.parse(readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).length.toLocaleString()}***`)
},
logs: ()=>{
interaction.deferReply();
(client.channels.resolve(client.config.mainServer.channels.console) as Discord.TextChannel).send({content: `Uploaded the current console dump as of <t:${Math.round(Date.now()/1000)}:R>`, files: [`${process.env.pm2_home}/logs/Daggerbot-out-0.log`, `${process.env.pm2_home}/logs/Daggerbot-error-0.log`]}).then(()=>interaction.editReply('It has been uploaded to dev server.')).catch((e:Error)=>interaction.editReply(`\`${e.message}\``))
},
restart: ()=>interaction.reply(`Uptime before restarting: **${client.formatTime(client.uptime as number, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot'))
} as any)[interaction.options.getSubcommand()]();
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('dev') .setName('dev')
.setDescription('Developer commands') .setDescription('Developer commands')
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('eval') .setName('eval')
.setDescription('Execute the code to the bot') .setDescription('Execute the code to the bot')
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('code') .setName('code')
.setDescription('Execute your code') .setDescription('Execute your code')
.setRequired(true))) .setRequired(true)))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('logs') .setName('logs')
.setDescription('Retrieve the logs from host and sends it to dev server')) .setDescription('Retrieve the logs from host and sends it to dev server'))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('restart') .setName('restart')
.setDescription('Restart the bot for technical reasons')) .setDescription('Restart the bot for technical reasons'))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('update') .setName('update')
.setDescription('Pull from repository and restart')) .setDescription('Pull from repository and restart'))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('statsgraph') .setName('statsgraph')
.setDescription('Edit the number of data points to pull') .setDescription('Edit the number of data points to pull')
.addIntegerOption((hiTae)=>hiTae .addIntegerOption((hiTae)=>hiTae
.setName('number') .setName('number')
.setDescription('Number of data points to pull') .setDescription('Number of data points to pull')
.setRequired(true))) .setRequired(true)))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('presence') .setName('presence')
.setDescription('Update the bot\'s presence') .setDescription('Update the bot\'s presence')
.addIntegerOption((hiTae)=>hiTae .addIntegerOption((hiTae)=>hiTae
.setName('type') .setName('type')
.setDescription('Set an activity type') .setDescription('Set an activity type')
.addChoices( .addChoices(
{name: 'Playing', value: Discord.ActivityType.Playing}, {name: 'Playing', value: Discord.ActivityType.Playing},
{name: 'Streaming', value: Discord.ActivityType.Streaming}, {name: 'Streaming', value: Discord.ActivityType.Streaming},
{name: 'Listening to', value: Discord.ActivityType.Listening}, {name: 'Listening to', value: Discord.ActivityType.Listening},
{name: 'Watching', value: Discord.ActivityType.Watching}, {name: 'Watching', value: Discord.ActivityType.Watching},
{name: 'Competing in', value: Discord.ActivityType.Competing} {name: 'Competing in', value: Discord.ActivityType.Competing}
)) ))
.addStringOption((hiAgain)=>hiAgain .addStringOption((hiAgain)=>hiAgain
.setName('name') .setName('name')
.setDescription('Set a message for the activity status')) .setDescription('Set a message for the activity status'))
.addStringOption((hiAgainx2)=>hiAgainx2 .addStringOption((hiAgainx2)=>hiAgainx2
.setName('url') .setName('url')
.setDescription('Set an url for streaming status')) .setDescription('Set an url for streaming status'))
.addStringOption((hiAgainx3)=>hiAgainx3 .addStringOption((hiAgainx3)=>hiAgainx3
.setName('status') .setName('status')
.setDescription('Set a status indicator for the bot') .setDescription('Set a status indicator for the bot')
.setChoices( .setChoices(
{name: 'Online', value: Discord.PresenceUpdateStatus.Online}, {name: 'Online', value: Discord.PresenceUpdateStatus.Online},
{name: 'Idle', value: Discord.PresenceUpdateStatus.Idle}, {name: 'Idle', value: Discord.PresenceUpdateStatus.Idle},
{name: 'Do Not Distrub', value: Discord.PresenceUpdateStatus.DoNotDisturb}, {name: 'Do Not Distrub', value: Discord.PresenceUpdateStatus.DoNotDisturb},
{name: 'Invisible', value: Discord.PresenceUpdateStatus.Offline} {name: 'Invisible', value: Discord.PresenceUpdateStatus.Offline}
))) )))
} }

View File

@ -1,7 +1,7 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
({ ({
srp: ()=>interaction.reply('Ballyspring is the map that is used in Survival Roleplay S4.'), srp: ()=>interaction.reply('Ballyspring is the map that is used in Survival Roleplay S4.'),
dlskin: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Daggerwin Logistics hex code').setDescription('The main color will be Onyx (`#353839`) with red bumpers.').setImage('https://cdn.discordapp.com/attachments/801965516947324969/806871878736019456/image0.png')]}), dlskin: ()=>interaction.reply({embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Daggerwin Logistics hex code').setDescription('The main color will be Onyx (`#353839`) with red bumpers.').setImage('https://cdn.discordapp.com/attachments/801965516947324969/806871878736019456/image0.png')]}),

View File

@ -1,17 +1,17 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
client.punish(client, interaction, 'kick'); client.punish(client, interaction, 'kick');
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('kick') .setName('kick')
.setDescription('Boot a member from the server') .setDescription('Boot a member from the server')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member to kick?') .setDescription('Which member to kick?')
.setRequired(true)) .setRequired(true))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('reason') .setName('reason')
.setDescription('Reason for the kick')) .setDescription('Reason for the kick'))
} }

View File

@ -5,296 +5,284 @@ import canvas from 'canvas';
import fs from 'node:fs'; import fs from 'node:fs';
async function MPdata(client:TClient, interaction:Discord.ChatInputCommandInteraction, embed: Discord.EmbedBuilder) { async function MPdata(client:TClient, interaction:Discord.ChatInputCommandInteraction, embed: Discord.EmbedBuilder) {
let FSserver; let FSserver;
if (!await client.MPServer._content.findOne({_id:interaction.guildId})) return interaction.reply('This server isn\'t linked to the bot.'); if (!await client.MPServer._content.findOne({_id:interaction.guildId})) return interaction.reply('This server isn\'t linked to the bot.');
const ServerURL = await client.MPServer._content.findById(interaction.guildId); const ServerURL = await client.MPServer._content.findById(interaction.guildId);
if (!ServerURL) return interaction.reply(`No FS server found, please notify <@&${client.config.mainServer.roles.mpmanager}> to add it.`) if (!ServerURL) return interaction.reply(`No FS server found, please notify <@&${client.config.mainServer.roles.mpmanager}> to add it.`)
const MPURL = ServerURL.ip const MPURL = ServerURL.ip
const MPCode = ServerURL.code const MPCode = ServerURL.code
const verifyURL = MPURL.match(/http|https/) const verifyURL = MPURL.match(/http|https/)
const completedURL = MPURL+'/feed/dedicated-server-stats.json?code='+MPCode const completedURL = MPURL+'/feed/dedicated-server-stats.json?code='+MPCode
if (!verifyURL) return interaction.reply(`The server IP for this server is currently invalid, please notify <@&${client.config.mainServer.roles.mpmanager}>`) if (!verifyURL) return interaction.reply(`The server IP for this server is currently invalid, please notify <@&${client.config.mainServer.roles.mpmanager}>`)
// Fetch dss // Fetch dss
try { // v I am aware timeout has decreased from 2800 to 2588 to fit within Discord's interaction timeouts (3s) -Toast try { // v I am aware timeout has decreased from 2800 to 2588 to fit within Discord's interaction timeouts (3s) -Toast
FSserver = await client.axios.get(completedURL, {timeout: 2588, headers: {'User-Agent': `Daggerbot - mp cmd/axios ${client.axios.VERSION}`}}) FSserver = await client.axios.get(completedURL, {timeout: 2588, headers: {'User-Agent': `Daggerbot - mp cmd/axios ${client.axios.VERSION}`}})
} catch(err) { } catch(err) {
// Blame Nawdic & RedRover92 // Blame Nawdic & RedRover92
embed.setTitle('Host is not responding.'); embed.setTitle('Host is not responding.');
embed.setColor(client.config.embedColorRed); embed.setColor(client.config.embedColorRed);
console.log(client.logTime(), 'DagMP failed to fetch, host didn\'t respond in time.'); console.log(client.logTime(), 'DagMP failed to fetch, host didn\'t respond in time.');
return interaction.reply('Server didn\'t respond in time.'); return interaction.reply('Server didn\'t respond in time.');
} } return FSserver
return FSserver
} }
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (interaction.channelId == '468835769092669461' && !client.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) { 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()}, 6000)}); interaction.reply(`Please use <#739084625862852715> for \`/mp status/players\` commands to prevent clutter in this channel.`).then((msg)=>{setTimeout(()=>{interaction.deleteReply()}, 6000)});
return; return;
}
({
status: async()=>{
const embed0 = new client.embed();
const FSserver0 = await MPdata(client, interaction, embed0);
if (!FSserver0?.data) return console.log('FSserver0 failed - status');
try {
if (FSserver0.data.server.name.length > 1){
interaction.reply({embeds: [embed0.setTitle('Status/Details').setColor(client.config.embedColor).addFields(
{name: 'Server name', value: `${FSserver0?.data.server.name.length == 0 ? '\u200b' : `\`${FSserver0?.data.server.name}\``}`, inline: true},
{name: 'Players', value: `${FSserver0.data.slots.used} out of ${FSserver0.data.slots.capacity}`, inline: true},
{name: 'Current map', value: `${FSserver0?.data.server.mapName.length == 0 ? '\u200b' : FSserver0.data.server.mapName}`, inline: true},
{name: 'Version', value: `${FSserver0?.data.server.version.length == 0 ? '\u200b' : FSserver0.data.server.version}`, inline: true},
{name: 'In-game Time', value: `${('0' + Math.floor((FSserver0.data.server.dayTime/3600/1000))).slice(-2)}:${('0' + Math.floor((FSserver0.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true}
)]})
} else if (FSserver0.data.server.name.length == 0) interaction.reply('Server is currently offline.')
} catch (err){
console.log(err)
interaction.reply('FSserver0 Error placeholder')
};
},
info: async()=>{
const embed2 = new client.embed().setColor(client.config.embedColor)
const FSserver2 = await MPdata(client, interaction, embed2)
if (!FSserver2?.data) return console.log('FSserver2 failed - info')
const MPURL = await client.MPServer._content.findById(interaction.guildId);
interaction.reply({embeds: [embed2.setDescription([
`**Server name**: \`${FSserver2?.data.server.name.length == 0 ? '\u200b' : FSserver2?.data.server.name}\``,
'**Password:** `mf4700`',
'**Crossplay server**',
`**Map:** ${FSserver2.data.server.mapName.length == 0 ? 'Null Island' : FSserver2.data.server.mapName}`,
`**Mods:** [Click here](${MPURL.ip}/mods.html) **|** [Direct Download](${MPURL.ip}/all_mods_download?onlyActive=true)`,
'**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)',
'Please see <#543494084363288637> for additional information.'
].join('\n'))]});
if (FSserver2?.data.server.name.length == 0) embed2.setFooter({text: 'Server is currently offline.'})
},
url: async()=>{
if (client.config.mainServer.id == interaction.guildId) {
if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager');
} }
({ const address = interaction.options.getString('address');
status: async()=>{ if (!address){
const embed0 = new client.embed(); try {
const FSserver0 = await MPdata(client, interaction, embed0); const Url = await client.MPServer._content.findById(interaction.guildId);
if (!FSserver0?.data) return console.log('FSserver0 failed - status'); if (Url.ip && Url.code) return interaction.reply(`${Url.get('ip')}`+'/feed/dedicated-server-stats.json?code='+`${Url.get('code')}`)
try { } catch(err){
if (FSserver0.data.server.name.length > 1){ console.log(`MPDB :: ${err}`)
embed0.setTitle('Status/Details').setColor(client.config.embedColor).addFields( interaction.reply('**Database error:**\nTry inserting an URL first.')
{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}, }else{
{name: 'Current map', value: `${FSserver0?.data.server.mapName.length == 0 ? '\u200b' : FSserver0.data.server.mapName}`, inline: true}, const verifyURL = address.match(/dedicated-server-stats/)
{name: 'Version', value: `${FSserver0?.data.server.version.length == 0 ? '\u200b' : FSserver0.data.server.version}`, inline: true}, if (!verifyURL) return interaction.reply('The URL does not match `dedicated-server-stats.xml`')
{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} const newURL = address.replace('xml','json').split('/feed/dedicated-server-stats.json?code=')
) try{
interaction.reply({embeds: [embed0]}) console.log(`MPDB :: URL for ${interaction.guild.name} has been updated by ${interaction.member.displayName} (${interaction.member.id})`);
} else if (FSserver0.data.server.name.length == 0) interaction.reply('Server is currently offline.') await client.MPServer._content.create({_id: interaction.guildId, ip: newURL[0], code: newURL[1], timesUpdated: 0})
} catch (err){ return interaction.reply('This server is now linked and URL has been added.');
console.log(err) } catch(err){
interaction.reply('FSserver0 Error placeholder') const affectedValues = await client.MPServer._content.findByIdAndUpdate({_id: interaction.guildId}, {ip: newURL[0], code: newURL[1]});
}; await client.MPServer._increment(interaction.guildId);
}, if (affectedValues) return interaction.reply('URL successfully updated.')
info: async()=>{ }
const embed2 = new client.embed().setColor(client.config.embedColor) }
const FSserver2 = await MPdata(client, interaction, embed2) },
if (!FSserver2?.data) return console.log('FSserver2 failed - info') players: async()=>{
const MPURL = await client.MPServer._content.findById(interaction.guildId); const embed1 = new client.embed();
embed2.setDescription([ const data = JSON.parse(fs.readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).slice(client.statsGraph)
`**Server name**: \`${FSserver2?.data.server.name.length == 0 ? '\u200b' : FSserver2?.data.server.name}\``, // handle negative days
'**Password:** `mf4700`', data.forEach((change: number, i: number) => {
'**Crossplay server**', if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0;
`**Map:** ${FSserver2.data.server.mapName.length == 0 ? 'Null Island' : FSserver2.data.server.mapName}`, });
`**Mods:** [Click here](${MPURL.ip}/mods.html) **|** [Direct Download](${MPURL.ip}/all_mods_download?onlyActive=true)`,
'**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)',
'Please see <#543494084363288637> for additional information.'
].join('\n'));
if (FSserver2?.data.server.name.length == 0) embed2.setFooter({text: 'Server is currently offline.'})
interaction.reply({embeds: [embed2]})
},
url: async()=>{
if (client.config.mainServer.id == interaction.guildId) {
if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager');
}
const address = interaction.options.getString('address');
if (!address){
try {
const Url = await client.MPServer._content.findById(interaction.guildId);
if (Url.ip && Url.code) return interaction.reply(`${Url.get('ip')}`+'/feed/dedicated-server-stats.json?code='+`${Url.get('code')}`)
} catch(err){
console.log(`MPDB :: ${err}`)
interaction.reply('**Database error:**\nTry inserting an URL first.')
}
}else{
const verifyURL = address.match(/dedicated-server-stats/)
if (!verifyURL) return interaction.reply('The URL does not match `dedicated-server-stats.xml`')
const newURL = address.replace('xml','json').split('/feed/dedicated-server-stats.json?code=')
try{
console.log(`MPDB :: URL for ${interaction.guild.name} has been updated by ${interaction.member.displayName} (${interaction.member.id})`);
await client.MPServer._content.create({_id: interaction.guildId, ip: newURL[0], code: newURL[1], timesUpdated: 0})
return interaction.reply('This server is now linked and URL has been added.');
} catch(err){
const affectedValues = await client.MPServer._content.findByIdAndUpdate({_id: interaction.guildId}, {ip: newURL[0], code: newURL[1]});
await client.MPServer._increment(interaction.guildId);
if (affectedValues) return interaction.reply('URL successfully updated.')
}
}
},
players: async()=>{
const embed1 = new client.embed();
const data = JSON.parse(fs.readFileSync(path.join(__dirname, '../database/MPPlayerData.json'), {encoding: 'utf8'})).slice(client.statsGraph)
// handle negative days
data.forEach((change: number, i: number) => {
if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0;
});
const first_graph_top = 16; const first_graph_top = 16;
const second_graph_top = 16; const second_graph_top = 16;
const textSize = 40; const textSize = 40;
const img = canvas.createCanvas(1500, 750); const img = canvas.createCanvas(1500, 750);
const ctx = img.getContext('2d'); 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<number> = [];
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 - 30));
ctx.lineTo(lastMonthStart, graphOrigin[1]);
ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]);
ctx.stroke();
ctx.closePath();
ctx.setLineDash([]);
// draw points
ctx.lineWidth = 5;
function getYCoordinate(value: number) {
return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1];
}
function colorAtPlayercount(playercount: number) { const graphOrigin = [15, 65];
if (playercount === first_graph_top) { const graphSize = [1300, 630];
return client.config.embedColorRed as string; const nodeWidth = graphSize[0] / (data.length - 1);
} else if (playercount > 9) { ctx.fillStyle = '#36393f';
return client.config.embedColorYellow as string; ctx.fillRect(0, 0, img.width, img.height);
} else {return client.config.embedColorGreen as string}
}
let lastCoords: Array<number> = [];
data.forEach((curPC: number /* current player count */, i: number) => {
if (curPC < 0) curPC = 0;
const x = i * nodeWidth + graphOrigin[0];
const y = getYCoordinate(curPC);
const nexPC /* next player count */ = data[i + 1];
const prvPC /* previous player count */ = data[i - 1];
const curColor = colorAtPlayercount(curPC); // color now
const prvColor = colorAtPlayercount(prvPC); // color at last point
if (curColor !== prvColor && !isNaN(prvPC) && lastCoords.length > 0) { // gradient should be used when the color between now and last point is not the same
// gradient from now to last point
const grd = ctx.createLinearGradient(lastCoords[0], lastCoords[1], x, y);
grd.addColorStop(0, colorAtPlayercount(prvPC)); // prev color at the beginning
grd.addColorStop(1, colorAtPlayercount(curPC)); // cur color at the end
// special case: playercount rises or falls rapidly accross all colors (eg. straight from red to green)
if (curColor !== client.config.embedColorYellow && prvColor !== client.config.embedColorYellow) {
const yellowY = getYCoordinate(10); // y coordinate at which line should be yellow
const stop = (yellowY - lastCoords[1]) / (y - lastCoords[1]); // between 0 and 1, where is yellowY between y and nextPointCoords[1] ?
grd.addColorStop(stop, client.config.embedColorYellow as string); // add a yellow stop to the gradient
}
ctx.strokeStyle = grd;
} else {
ctx.strokeStyle = colorAtPlayercount(curPC);
}
ctx.beginPath();
if (lastCoords.length > 0) ctx.moveTo(lastCoords[0], lastCoords[1]);
// if the line being drawn is horizontal, make it go until it has to go down
if (y === lastCoords[1]) {
let newX = x;
for (let j = i + 1; j <= data.length; j++) {
if (data[j] === curPC) newX += nodeWidth; else break;
}
ctx.lineTo(newX, y);
} else {
ctx.lineTo(x, y);
}
lastCoords = [x, y];
ctx.stroke();
ctx.closePath();
if (curPC === prvPC && curPC === nexPC) {
return; // no ball because no vertical difference to next or prev point
} else {
// ball
ctx.fillStyle = colorAtPlayercount(curPC);
ctx.beginPath();
ctx.arc(x, y, ctx.lineWidth * 1.3, 0, 2 * Math.PI)
ctx.closePath();
ctx.fill();
}
});
// draw text
ctx.font = '400 ' + textSize + 'px sans-serif';
ctx.fillStyle = 'white';
// highest value
const maxx = graphOrigin[0] + graphSize[0] + textSize / 2;
const maxy = previousY[0] + (textSize / 3);
ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy);
// lowest value // grey horizontal lines
const lowx = graphOrigin[0] + graphSize[0] + textSize / 2; ctx.lineWidth = 5;
const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3);
ctx.fillText('0 players', lowx, lowy);
// 30m let interval_candidates = [];
ctx.fillText('30 mins ago', lastMonthStart, graphOrigin[1] - (textSize / 2)); 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];
// time -> const previousY: Array<number> = [];
const tx = graphOrigin[0] + (textSize / 2);
const ty = graphOrigin[1] + graphSize[1] + (textSize); ctx.strokeStyle = '#202225';
ctx.fillText('time ->', tx, ty); for (let i = 0; i <= chosen_interval[1]; i++) {
const y = graphOrigin[1] + graphSize[1] - (i * (chosen_interval[0] / second_graph_top) * graphSize[1]);
const Image = new client.attachmentBuilder(img.toBuffer(),{name: 'FSStats.png'}) if (y < graphOrigin[1]) continue;
embed1.setImage('attachment://FSStats.png') const even = ((i + 1) % 2) === 0;
const FSserver1 = await MPdata(client, interaction, embed1) if (even) ctx.strokeStyle = '#2c2f33';
if (!FSserver1?.data) return console.log('FSserver1 failed - players') ctx.beginPath();
embed1.setTitle(FSserver1?.data.server.name.length == 0 ? 'Offline' : FSserver1?.data.server.name) ctx.lineTo(graphOrigin[0], y);
.setDescription(`${FSserver1?.data.slots.used}/${FSserver1?.data.slots.capacity}`) ctx.lineTo(graphOrigin[0] + graphSize[0], y);
.setColor(FSserver1?.data.server.name.length == 0 ? client.config.embedColorRed : client.config.embedColor); ctx.stroke();
FSserver1?.data.slots.players.filter(x=>x.isUsed).forEach(player=>{ ctx.closePath();
embed1.addFields({name: `${player.name} ${player.isAdmin ? '| admin' : ''}`, value: `Farming for ${(Math.floor(player.uptime/60))} hr, ${('0' + (player.uptime % 60)).slice(-2)} min`}) if (even) ctx.strokeStyle = '#202225';
}) previousY.push(y, i * chosen_interval[0]);
interaction.reply({embeds: [embed1], files: [Image]}) }
}/*,
series: ()=>{ // 30m mark
const embed3 = new client.embed().setColor(client.config.embedColor).setTitle('How to join the Daggerwin MP series') ctx.setLineDash([8, 16]);
.setDescription([ ctx.beginPath();
'To join the Daggerwin MP series, you first need to:', const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30));
'**1:** Note that only PC players can join the MP series due to the mods that are used.', ctx.lineTo(lastMonthStart, graphOrigin[1]);
'**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.', ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]);
'**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.', ctx.stroke();
'**4:** If you don\'t receive the role within a day or so, please message an Admin and they will sort it out.', ctx.closePath();
'**5:** Take a look in <#511657659364147200> to get information on how to join the server.' ctx.setLineDash([]);
].join('\n'));
interaction.reply({embeds: [embed3]}) // draw points
}*/ ctx.lineWidth = 5;
} as any)[interaction.options.getSubcommand()]();
}, function getYCoordinate(value: number) {
data: new SlashCommandBuilder() return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1];
.setName('mp') }
.setDescription('Display MP status and other things')
.addSubcommand((opt)=>opt function colorAtPlayercount(playercount: number) {
.setName('status') if (playercount === first_graph_top) return client.config.embedColorRed as string;
.setDescription('Check server status and details')) else if (playercount > 9) return client.config.embedColorYellow as string;
.addSubcommand((opt)=>opt else return client.config.embedColorGreen as string
.setName('players') }
.setDescription('Check who\'s playing on the server')) let lastCoords: Array<number> = [];
.addSubcommand((opt)=>opt data.forEach((curPC: number /* current player count */, i: number) => {
.setName('info') if (curPC < 0) curPC = 0;
.setDescription('Provides you with server information such as filters and so on')) const x = i * nodeWidth + graphOrigin[0];
.addSubcommand((opt)=>opt const y = getYCoordinate(curPC);
.setName('url') const nexPC /* next player count */ = data[i + 1];
.setDescription('View the URL for this server\'s FSMP server or update the URL') const prvPC /* previous player count */ = data[i - 1];
.addStringOption((opt)=>opt const curColor = colorAtPlayercount(curPC); // color now
.setName('address') const prvColor = colorAtPlayercount(prvPC); // color at last point
.setDescription('Insert a \'dedicated-server-stats\' URL'))) if (curColor !== prvColor && !isNaN(prvPC) && lastCoords.length > 0) { // gradient should be used when the color between now and last point is not the same
/* .addSubcommand((opt)=>opt // gradient from now to last point
.setName('series') const grd = ctx.createLinearGradient(lastCoords[0], lastCoords[1], x, y);
.setDescription('Step-by-step on joining Daggerwin\'s MP series')) */ grd.addColorStop(0, colorAtPlayercount(prvPC)); // prev color at the beginning
grd.addColorStop(1, colorAtPlayercount(curPC)); // cur color at the end
// special case: playercount rises or falls rapidly accross all colors (eg. straight from red to green)
if (curColor !== client.config.embedColorYellow && prvColor !== client.config.embedColorYellow) {
const yellowY = getYCoordinate(10); // y coordinate at which line should be yellow
const stop = (yellowY - lastCoords[1]) / (y - lastCoords[1]); // between 0 and 1, where is yellowY between y and nextPointCoords[1] ?
grd.addColorStop(stop, client.config.embedColorYellow as string); // add a yellow stop to the gradient
}
ctx.strokeStyle = grd;
} else ctx.strokeStyle = colorAtPlayercount(curPC);
ctx.beginPath();
if (lastCoords.length > 0) ctx.moveTo(lastCoords[0], lastCoords[1]);
// if the line being drawn is horizontal, make it go until it has to go down
if (y === lastCoords[1]) {
let newX = x;
for (let j = i + 1; j <= data.length; j++) {
if (data[j] === curPC) newX += nodeWidth; else break;
}
ctx.lineTo(newX, y);
} else ctx.lineTo(x, y);
lastCoords = [x, y];
ctx.stroke();
ctx.closePath();
if (curPC === prvPC && curPC === nexPC) return; // no ball because no vertical difference to next or prev point
else {
// ball
ctx.fillStyle = colorAtPlayercount(curPC);
ctx.beginPath();
ctx.arc(x, y, ctx.lineWidth * 1.3, 0, 2 * Math.PI)
ctx.closePath();
ctx.fill();
}
});
// draw text
ctx.font = '400 ' + textSize + 'px sans-serif';
ctx.fillStyle = 'white';
// highest value
const maxx = graphOrigin[0] + graphSize[0] + textSize / 2;
const maxy = previousY[0] + (textSize / 3);
ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy);
// lowest value
const lowx = graphOrigin[0] + graphSize[0] + textSize / 2;
const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3);
ctx.fillText('0 players', lowx, lowy);
// 30m
ctx.fillText('30 mins ago', lastMonthStart, graphOrigin[1] - (textSize / 2));
// time ->
const tx = graphOrigin[0] + (textSize / 2);
const ty = graphOrigin[1] + graphSize[1] + (textSize);
ctx.fillText('time ->', tx, ty);
const Image = new client.attachmentBuilder(img.toBuffer(),{name: 'FSStats.png'})
embed1.setImage('attachment://FSStats.png')
const FSserver1 = await MPdata(client, interaction, embed1)
if (!FSserver1?.data) return console.log('FSserver1 failed - players')
embed1.setTitle(FSserver1?.data.server.name.length == 0 ? 'Offline' : FSserver1?.data.server.name)
.setDescription(`${FSserver1?.data.slots.used}/${FSserver1?.data.slots.capacity}`)
.setColor(FSserver1?.data.server.name.length == 0 ? client.config.embedColorRed : client.config.embedColor);
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]})
}/*,
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]})
}*/
} as any)[interaction.options.getSubcommand()]();
},
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('url')
.setDescription('View the URL for this server\'s FSMP server or update the URL')
.addStringOption((opt)=>opt
.setName('address')
.setDescription('Insert a \'dedicated-server-stats\' URL')))/*
.addSubcommand((opt)=>opt
.setName('series')
.setDescription('Step-by-step on joining Daggerwin\'s MP series'))*/
} }

View File

@ -1,20 +1,20 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
client.punish(client, interaction, 'mute'); client.punish(client, interaction, 'mute');
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('mute') .setName('mute')
.setDescription('Mute a member') .setDescription('Mute a member')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member to mute?') .setDescription('Which member to mute?')
.setRequired(true)) .setRequired(true))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('time') .setName('time')
.setDescription('Mute duration')) .setDescription('Mute duration'))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('reason') .setName('reason')
.setDescription('Reason for the mute')) .setDescription('Reason for the mute'))
} }

View File

@ -1,12 +1,12 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const msg = await interaction.reply({content: 'Pinging...', fetchReply: true}) const msg = await interaction.reply({content: 'Pinging...', fetchReply: true})
const time = msg.createdTimestamp - interaction.createdTimestamp; const time = msg.createdTimestamp - interaction.createdTimestamp;
msg.edit(`Websocket: \`${client.formatTime(client.ws.ping, 3, {longNames: false, commas: true})}\`\nBot: \`${client.formatTime(time, 3, {longNames: false, commas: true})}\``) msg.edit(`Websocket: \`${client.formatTime(client.ws.ping, 3, {longNames: false, commas: true})}\`\nBot: \`${client.formatTime(time, 3, {longNames: false, commas: true})}\``)
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('ping') .setName('ping')
.setDescription('Check bot\'s latency') .setDescription('Check bot\'s latency')
} }

View File

@ -1,37 +1,35 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod'); if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod');
const amount = interaction.options.getInteger('amount') as number; const amount = interaction.options.getInteger('amount') as number;
if (amount > 100) return interaction.reply({content: 'Discord API limits purging up to 100 messages.', ephemeral: true}) if (amount > 100) return interaction.reply({content: 'Discord API limits purging up to 100 messages.', ephemeral: true})
const user = interaction.options.getUser('user'); const user = interaction.options.getUser('user');
let messagesArray: Array<string> = []; let messagesArray: Array<string> = [];
if (user){ if (user){
(interaction.channel as Discord.TextChannel).messages.fetch({limit: amount}).then((msgs)=>{ (interaction.channel as Discord.TextChannel).messages.fetch({limit: amount}).then(msgs=>{
const msgList = msgs.filter(x=>x.author.id == user.id); const msgList = msgs.filter(x=>x.author.id == user.id);
(interaction.channel as Discord.TextChannel).bulkDelete(msgList); (interaction.channel as Discord.TextChannel).bulkDelete(msgList);
}) })
} else { } else {
(interaction.channel as Discord.TextChannel).messages.fetch({limit: amount}).then(async messages=>{ (interaction.channel as Discord.TextChannel).messages.fetch({limit: amount}).then(async messages=>{
messages.forEach(message=>{ messages.forEach(message=>messagesArray.push(message.id));
messagesArray.push(message.id); await (interaction.channel as Discord.TextChannel).bulkDelete(messagesArray);
}); })
await (interaction.channel as Discord.TextChannel).bulkDelete(messagesArray); }
}) await interaction.reply({content: `Successfully purged ${amount} messages.`, ephemeral: true})
} },
await interaction.reply({content: `Successfully purged ${amount} messages.`, ephemeral: true}) data: new SlashCommandBuilder()
}, .setName('purge')
data: new SlashCommandBuilder() .setDescription('Purge the amount of messages in this channel')
.setName('purge') .addIntegerOption((opt)=>opt
.setDescription('Purge the amount of messages in this channel') .setName('amount')
.addIntegerOption((opt)=>opt .setDescription('Amount of messages to be obliterated')
.setName('amount') .setRequired(true))
.setDescription('Amount of messages to be obliterated') .addUserOption((opt)=>opt
.setRequired(true)) .setName('user')
.addUserOption((opt)=>opt .setDescription('Which user to have their messages obliterated?'))
.setName('user')
.setDescription('Which user to have their messages obliterated?'))
} }

View File

@ -1,12 +1,12 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const embed = new client.embed().setColor(Math.floor(Math.random()*16777215)); const embed = new client.embed().setColor(Math.floor(Math.random()*16777215));
embed.addFields({name: 'Hex code', value: `#${embed.data.color.toString(16).toUpperCase()}`}); embed.addFields({name: 'Hex code', value: `#${embed.data.color.toString(16).toUpperCase()}`});
interaction.reply({embeds: [embed]}); interaction.reply({embeds: [embed]});
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('randomcolor') .setName('randomcolor')
.setDescription('Generate a random hex code') .setDescription('Generate a random hex code')
} }

View File

@ -4,171 +4,171 @@ import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import canvas from 'canvas'; import canvas from 'canvas';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (interaction.guildId !== client.config.mainServer.id) return interaction.reply({content: 'This command doesn\'t work in this server.', ephemeral: true}); if (interaction.guildId !== client.config.mainServer.id) return interaction.reply({content: 'This command doesn\'t work in this server.', ephemeral: true});
const allData = await client.userLevels._content.find({}); const allData = await client.userLevels._content.find({});
({ ({
view: async()=>{ view: async()=>{
// fetch user or user interaction sender // fetch user or user interaction sender
const member = interaction.options.getMember("member") ?? interaction.member as Discord.GuildMember; const member = interaction.options.getMember("member") ?? interaction.member as Discord.GuildMember;
if (member.user.bot) return interaction.reply('Bots don\'t level up, try viewing non-bots instead.') if (member.user.bot) return interaction.reply('Bots don\'t level up, try viewing non-bots instead.')
// information about users progress on level roles // information about users progress on level roles
const userData = await client.userLevels._content.findById(member.user.id); const userData = await client.userLevels._content.findById(member.user.id);
const pronounBool = (you: string, they: string) => { // takes 2 words and chooses which to use based on if user did this command on themself const pronounBool = (you: string, they: string) => { // takes 2 words and chooses which to use based on if user did this command on themself
if (interaction.user.id === member.user.id) return you || true; if (interaction.user.id === member.user.id) return you || true;
else return they || false; else return they || false;
}; };
if (!userData) return interaction.reply(`${pronounBool('You', 'They')} currently don't have a level, send some messages to level up.`) if (!userData) return interaction.reply(`${pronounBool('You', 'They')} currently don't have a level, send some messages to level up.`)
const index = allData.sort((a, b) => b.messages - a.messages).map(x => x._id).indexOf(member.id) + 1; const index = allData.sort((a, b) => b.messages - a.messages).map(x => x._id).indexOf(member.id) + 1;
const memberDifference = userData.messages - client.userLevels.algorithm(userData.level); const memberDifference = userData.messages - client.userLevels.algorithm(userData.level);
const levelDifference = client.userLevels.algorithm(userData.level+1) - client.userLevels.algorithm(userData.level); const levelDifference = client.userLevels.algorithm(userData.level+1) - client.userLevels.algorithm(userData.level);
interaction.reply({embeds: [new client.embed().setColor(member.displayColor).setTitle(`Level: **${userData.level}**\nRank: **${index ? '#' + index : 'last'}**\nProgress: **${memberDifference}/${levelDifference} (${(memberDifference/levelDifference*100).toFixed(2)}%)**\nTotal: **${userData.messages}**`).setThumbnail(member.user.avatarURL({ extension: 'png', size: 256}) || member.user.defaultAvatarURL)]}) interaction.reply({embeds: [new client.embed().setColor(member.displayColor).setTitle(`Level: **${userData.level}**\nRank: **${index ? '#' + index : 'last'}**\nProgress: **${memberDifference}/${levelDifference} (${(memberDifference/levelDifference*100).toFixed(2)}%)**\nTotal: **${userData.messages}**`).setThumbnail(member.user.avatarURL({ extension: 'png', size: 256}) || member.user.defaultAvatarURL)]})
}, },
leaderboard: ()=>{ leaderboard: ()=>{
const messageCountsTotal = allData.reduce((a, b) => a + b.messages, 0); const messageCountsTotal = allData.reduce((a, b) => a + b.messages, 0);
const timeActive = Math.floor((Date.now() - client.config.LRSstart)/1000/60/60/24); const timeActive = Math.floor((Date.now() - client.config.LRSstart)/1000/60/60/24);
const dailyMsgsPath = path.join(__dirname, '../database/dailyMsgs.json'); const dailyMsgsPath = path.join(__dirname, '../database/dailyMsgs.json');
const data = JSON.parse(fs.readFileSync(dailyMsgsPath, 'utf8')).map((x: Array<number>, i: number, a: any) => { const data = JSON.parse(fs.readFileSync(dailyMsgsPath, 'utf8')).map((x: Array<number>, i: number, a: any) => {
const yesterday = a[i - 1] || []; const yesterday = a[i - 1] || [];
return x[1] - (yesterday[1] || x[1]); return x[1] - (yesterday[1] || x[1]);
}).slice(1).slice(-60); }).slice(1).slice(-60);
// handle negative days // handle negative days
data.forEach((change: number, i: number) => { data.forEach((change: number, i: number) => {
if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0; if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0;
}); });
const maxValue = Math.max(...data); const maxValue = Math.max(...data);
const maxValueArr = maxValue.toString().split(''); const maxValueArr = maxValue.toString().split('');
const first_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 1)) * 10 ** (maxValueArr.length - 1); const first_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 1)) * 10 ** (maxValueArr.length - 1);
const second_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 2)) * 10 ** (maxValueArr.length - 2); const second_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 2)) * 10 ** (maxValueArr.length - 2);
const textSize = 32; const textSize = 32;
const img = canvas.createCanvas(950, 450); const img = canvas.createCanvas(950, 450);
const ctx = img.getContext('2d'); const ctx = img.getContext('2d');
const graphOrigin = [10, 50]; const graphOrigin = [10, 50];
const graphSize = [700, 360]; const graphSize = [700, 360];
const nodeWidth = graphSize[0] / (data.length - 1); const nodeWidth = graphSize[0] / (data.length - 1);
ctx.fillStyle = '#36393f'; ctx.fillStyle = '#36393f';
ctx.fillRect(0, 0, img.width, img.height); ctx.fillRect(0, 0, img.width, img.height);
// grey horizontal lines // grey horizontal lines
ctx.lineWidth = 3; ctx.lineWidth = 3;
let interval_candidates = []; let interval_candidates = [];
for (let i = 4; i < 10; i++) { for (let i = 4; i < 10; i++) {
const interval = first_graph_top / i; const interval = first_graph_top / i;
if (Number.isInteger(interval)) { if (Number.isInteger(interval)) {
let intervalString = interval.toString(); 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) 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]); interval_candidates.push([interval, i, reference_number]);
} }
} }
const chosen_interval = interval_candidates.sort((a, b) => b[2] - a[2])[0]; const chosen_interval = interval_candidates.sort((a, b) => b[2] - a[2])[0];
let previousY: Array<number> = []; let previousY: Array<number> = [];
ctx.strokeStyle = '#202225'; ctx.strokeStyle = '#202225';
for (let i = 0; i <= chosen_interval[1]; i++) { for (let i = 0; i <= chosen_interval[1]; i++) {
const y = graphOrigin[1] + graphSize[1] - (i * (chosen_interval[0] / second_graph_top) * graphSize[1]); const y = graphOrigin[1] + graphSize[1] - (i * (chosen_interval[0] / second_graph_top) * graphSize[1]);
if (y < graphOrigin[1]) continue; if (y < graphOrigin[1]) continue;
const even = ((i + 1) % 2) === 0; const even = ((i + 1) % 2) === 0;
if (even) ctx.strokeStyle = '#2c2f33'; if (even) ctx.strokeStyle = '#2c2f33';
ctx.beginPath(); ctx.beginPath();
ctx.lineTo(graphOrigin[0], y); ctx.lineTo(graphOrigin[0], y);
ctx.lineTo(graphOrigin[0] + graphSize[0], y); ctx.lineTo(graphOrigin[0] + graphSize[0], y);
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
if (even) ctx.strokeStyle = '#202225'; if (even) ctx.strokeStyle = '#202225';
previousY = [y, i * chosen_interval[0]]; previousY = [y, i * chosen_interval[0]];
} }
// 30d mark // 30d mark
ctx.setLineDash([8, 16]); ctx.setLineDash([8, 16]);
ctx.beginPath(); ctx.beginPath();
const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30)); const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30));
ctx.lineTo(lastMonthStart, graphOrigin[1]); ctx.lineTo(lastMonthStart, graphOrigin[1]);
ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]); ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]);
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
ctx.setLineDash([]); ctx.setLineDash([]);
// draw points // draw points
ctx.strokeStyle = client.config.embedColor as string; ctx.strokeStyle = client.config.embedColor as string;
ctx.fillStyle = client.config.embedColor as string; ctx.fillStyle = client.config.embedColor as string;
ctx.lineWidth = 3; ctx.lineWidth = 3;
function getYCoordinate(value: number) { function getYCoordinate(value: number) {
return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1]; return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1];
} }
let lastCoords: Array<number> = []; let lastCoords: Array<number> = [];
data.forEach((val: number, i: number) => { data.forEach((val: number, i: number) => {
ctx.beginPath(); ctx.beginPath();
if (lastCoords) ctx.moveTo(lastCoords[0], lastCoords[1]); if (lastCoords) ctx.moveTo(lastCoords[0], lastCoords[1]);
if (val < 0) val = 0; if (val < 0) val = 0;
const x = i * nodeWidth + graphOrigin[0]; const x = i * nodeWidth + graphOrigin[0];
const y = getYCoordinate(val); const y = getYCoordinate(val);
ctx.lineTo(x, y); ctx.lineTo(x, y);
lastCoords = [x, y]; lastCoords = [x, y];
ctx.stroke(); ctx.stroke();
ctx.closePath(); ctx.closePath();
// ball // ball
ctx.beginPath(); ctx.beginPath();
ctx.arc(x, y, ctx.lineWidth * 1.2, 0, 2 * Math.PI) ctx.arc(x, y, ctx.lineWidth * 1.2, 0, 2 * Math.PI)
ctx.closePath(); ctx.closePath();
ctx.fill(); ctx.fill();
}); });
// draw text // draw text
ctx.font = '400 ' + textSize + 'px sans-serif'; ctx.font = '400 ' + textSize + 'px sans-serif';
ctx.fillStyle = 'white'; ctx.fillStyle = 'white';
// highest value // highest value
const maxx = graphOrigin[0] + graphSize[0] + textSize; const maxx = graphOrigin[0] + graphSize[0] + textSize;
const maxy = previousY[0] + (textSize / 3); const maxy = previousY[0] + (textSize / 3);
ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy); ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy);
// lowest value // lowest value
const lowx = graphOrigin[0] + graphSize[0] + textSize; const lowx = graphOrigin[0] + graphSize[0] + textSize;
const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3); const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3);
ctx.fillText('0 msgs/day', lowx, lowy); ctx.fillText('0 msgs/day', lowx, lowy);
// 30d // 30d
ctx.fillText('30d ago', lastMonthStart, graphOrigin[1] - (textSize / 3)); ctx.fillText('30d ago', lastMonthStart, graphOrigin[1] - (textSize / 3));
// time -> // time ->
const tx = graphOrigin[0] + (textSize / 2); const tx = graphOrigin[0] + (textSize / 2);
const ty = graphOrigin[1] + graphSize[1] + (textSize); const ty = graphOrigin[1] + graphSize[1] + (textSize);
ctx.fillText('time ->', tx, ty); ctx.fillText('time ->', tx, ty);
const topUsers = allData.sort((a,b)=>b.messages - a.messages).slice(0,10).map((x,i)=>`\`${i+1}.\` <@${x._id}>: ${x.messages.toLocaleString('en-US')}`).join('\n'); const topUsers = allData.sort((a,b)=>b.messages - a.messages).slice(0,10).map((x,i)=>`\`${i+1}.\` <@${x._id}>: ${x.messages.toLocaleString('en-US')}`).join('\n');
const graphImage = new client.attachmentBuilder(img.toBuffer(), {name: 'dailymsgs.png'}) const graphImage = new client.attachmentBuilder(img.toBuffer(), {name: 'dailymsgs.png'})
const embed = new client.embed().setTitle('Ranking leaderboard') const embed = new client.embed().setTitle('Ranking leaderboard')
.setDescription(`Level System was created **${timeActive}** days ago. Since then, a total of **${messageCountsTotal.toLocaleString('en-US')}** messages have been sent in this server.`) .setDescription(`Level System was created **${timeActive}** days ago. Since then, a total of **${messageCountsTotal.toLocaleString('en-US')}** messages have been sent in this server.`)
.addFields({name: 'Top users by messages sent:', value: topUsers}) .addFields({name: 'Top users by messages sent:', value: topUsers})
.setImage('attachment://dailymsgs.png').setColor(client.config.embedColor) .setImage('attachment://dailymsgs.png').setColor(client.config.embedColor)
.setFooter({text: 'Graph updates daily.'}) .setFooter({text: 'Graph updates daily.'})
interaction.reply({embeds: [embed], files: [graphImage]}) interaction.reply({embeds: [embed], files: [graphImage]})
} }
} as any)[interaction.options.getSubcommand()](); } as any)[interaction.options.getSubcommand()]();
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('rank') .setName('rank')
.setDescription('Level system') .setDescription('Level system')
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('view') .setName('view')
.setDescription('View your rank or someone else\'s rank') .setDescription('View your rank or someone else\'s rank')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member do you want to view?'))) .setDescription('Which member do you want to view?')))
.addSubcommand((optt)=>optt .addSubcommand((optt)=>optt
.setName('leaderboard') .setName('leaderboard')
.setDescription('View top 10 users on leaderboard')) .setDescription('View top 10 users on leaderboard'))
} }

View File

@ -1,23 +1,23 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const role = interaction.options.getRole('role') as Discord.Role; const role = interaction.options.getRole('role') as Discord.Role;
const permissions = role.permissions.toArray(); const permissions = role.permissions.toArray();
const Role = role.members.map((e:Discord.GuildMember)=>`**${e.user.tag}**`).join('\n') || ''; const Role = role.members.map((e:Discord.GuildMember)=>`**${e.user.tag}**`).join('\n') || '';
interaction.reply({embeds: [new client.embed().setColor(role.color || '#fefefe').setThumbnail(role?.iconURL()).setTitle(`Role Info: ${role.name}`).addFields( interaction.reply({embeds: [new client.embed().setColor(role.color || '#fefefe').setThumbnail(role?.iconURL()).setTitle(`Role Info: ${role.name}`).addFields(
{name: '🔹 ID', value: `\`${role.id}\``, inline: true}, {name: '🔹 ID', value: `\`${role.id}\``, inline: true},
{name: '🔹 Color', value: `\`${role.hexColor}\``, inline: true}, {name: '🔹 Color', value: `\`${role.hexColor}\``, inline: true},
{name: '🔹 Creation Date', value: `<t:${Math.round(role.createdTimestamp/1000)}>\n<t:${Math.round(role.createdTimestamp/1000)}:R>`, inline: true}, {name: '🔹 Creation Date', value: `<t:${Math.round(role.createdTimestamp/1000)}>\n<t:${Math.round(role.createdTimestamp/1000)}:R>`, inline: true},
{name: '🔹 Misc', value: `Hoist: \`${role.hoist}\`\nMentionable: \`${role.mentionable}\`\nPosition: \`${role.position}\` from bottom\nMembers: \`${role.members.size}\`\n${role.members.size < 21 ? Role : ''}`, inline: true}, {name: '🔹 Misc', value: `Hoist: \`${role.hoist}\`\nMentionable: \`${role.mentionable}\`\nPosition: \`${role.position}\` from bottom\nMembers: \`${role.members.size}\`\n${role.members.size < 21 ? Role : ''}`, inline: true},
{name: '🔹 Permissions', value: `${permissions.includes('Administrator') ? ['Administrator'] : permissions.join(', ') || 'None'}`, inline: true} {name: '🔹 Permissions', value: `${permissions.includes('Administrator') ? ['Administrator'] : permissions.join(', ') || 'None'}`, inline: true}
)]}) )]})
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('roleinfo') .setName('roleinfo')
.setDescription('View information about the selected role') .setDescription('View information about the selected role')
.addRoleOption((opt)=>opt .addRoleOption((opt)=>opt
.setName('role') .setName('role')
.setDescription('Role name to view information') .setDescription('Role name to view information')
.setRequired(true)) .setRequired(true))
} }

View File

@ -1,17 +1,17 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
client.punish(client, interaction, 'softban'); client.punish(client, interaction, 'softban');
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('softban') .setName('softban')
.setDescription('Softban a member from the server') .setDescription('Softban a member from the server')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member to softban?') .setDescription('Which member to softban?')
.setRequired(true)) .setRequired(true))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('reason') .setName('reason')
.setDescription('Reason for the softban')) .setDescription('Reason for the softban'))
} }

View File

@ -4,73 +4,69 @@ import si from 'systeminformation';
import TClient from 'src/client'; import TClient from 'src/client';
import os from 'node:os'; import os from 'node:os';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
// Host specification (L9-L22, L55-L70) // Host specification (L9-L22, L51-L66)
// Bytes conversion // Bytes conversion
function formatBytes(bytes:number, decimals:number = 2) { function formatBytes(bytes:number, decimals:number = 2) {
if (bytes === 0) return '0 Bytes'; if (bytes === 0) return '0 Bytes';
const k = 1024; const k = 1024;
const dm = decimals < 0 ? 0 : decimals; const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB']; const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k)); const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}; };
var DJSver = require('discord.js').version; var DJSver = require('discord.js').version;
const cpu = await si.cpu(); const cpu = await si.cpu();
const ram = await si.mem(); const ram = await si.mem();
const osInfo = await si.osInfo(); const osInfo = await si.osInfo();
const currentLoad = await si.currentLoad(); const currentLoad = await si.currentLoad();
// Command usage (L25-L54) // Command usage (L25-L50)
const columns = ['Command name', 'Count']; const columns = ['Command name', 'Count'];
const includedCommands = client.commands.filter(x=>x.uses).sort((a,b)=>b.uses - a.uses); 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, 3, {longNames: true, commas: true})}**`); if (includedCommands.size == 0) return interaction.reply(`No commands have been used yet.\nUptime: **${client.formatTime(client.uptime as number, 3, {longNames: true, commas: true})}**`);
const nameLength = Math.max(...includedCommands.map(x=>x.default.data.name.length), columns[0].length) + 2; 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.toString().length), columns[1].length) + 1; const amountLength = Math.max(...includedCommands.map(x=>x.uses.toString().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']; 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=>{ includedCommands.forEach(command=>{
const name = command.default.data.name; const name = command.default.data.name;
const count = command.uses.toString(); const count = command.uses.toString();
rows.push(`${name + ' '.repeat(nameLength - name.length)}${' '.repeat(amountLength - count.length) + count}\n`); 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') const embed = new client.embed().setColor(client.config.embedColor).setTitle('Statistics: Command Usage')
.setDescription([ .setDescription([
'List of commands that have been used in this session, ordered by amount of use. Table contains command name and amount of uses.', '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)}` `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')) ].join('\n'))
if (rows.join('').length > 1024){ if (rows.join('').length > 1024){
let fieldValue = ''; let fieldValue = '';
rows.forEach(row=>{ rows.forEach(row=>{
if (fieldValue.length + row.length > 1024){ if (fieldValue.length + row.length > 1024){
embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``}); embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``});
fieldValue = row; fieldValue = row;
} else { } else fieldValue += row
fieldValue += row; });
} embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``});
}); } else embed.addFields({name: '\u200b', value: `\`\`\`\n${rows.join('')}\`\`\``});
embed.addFields({name: '\u200b', value: `\`\`\`\n${fieldValue}\`\`\``}); embed.addFields(
} else { {name: '> __Dependencies__', value: [
embed.addFields({name: '\u200b', value: `\`\`\`\n${rows.join('')}\`\`\``}) `**TypeScript:** ${version}`,
}; `**NodeJS:** ${process.version}`,
embed.addFields( `**DiscordJS:** ${DJSver}`,
{name: '> __Dependencies__', value: [ `**Axios:** ${client.axios.VERSION}`
`**TypeScript:** ${version}`, ].join('\n')},
`**NodeJS:** ${process.version}`, {name: '> __Host__', value: [
`**DiscordJS:** ${DJSver}`, `**Operating System:** ${osInfo.distro + ' ' + osInfo.release}`,
`**Axios:** ${client.axios.VERSION}` `**CPU:** ${cpu.manufacturer} ${cpu.brand}`,
].join('\n')}, `**Memory:** ${formatBytes(ram.used)}/${formatBytes(ram.total)}`,
{name: '> __Host__', value: [ `**NodeJS:** ${formatBytes(process.memoryUsage().heapUsed)}/${formatBytes(process.memoryUsage().heapTotal)}`,
`**Operating System:** ${osInfo.distro + ' ' + osInfo.release}`, `**Load Usage:**\nUser: ${currentLoad.currentLoadUser.toFixed(1)}%\nSystem: ${currentLoad.currentLoadSystem.toFixed(1)}%`,
`**CPU:** ${cpu.manufacturer} ${cpu.brand}`, `**Uptime:**\nHost: ${client.formatTime((os.uptime()*1000), 2, {longNames: true, commas: true})}\nBot: ${client.formatTime(client.uptime as number, 2, {commas: true, longNames: true})}`
`**Memory:** ${formatBytes(ram.used)}/${formatBytes(ram.total)}`, ].join('\n')}
`**NodeJS:** ${formatBytes(process.memoryUsage().heapUsed)}/${formatBytes(process.memoryUsage().heapTotal)}`, );
`**Load Usage:**\nUser: ${currentLoad.currentLoadUser.toFixed(1)}%\nSystem: ${currentLoad.currentLoadSystem.toFixed(1)}%`, interaction.reply({embeds: [embed], fetchReply: true}).then((x)=>x.edit({embeds: [new client.embed(x.embeds[0].data).setFooter({text: `Load time: ${client.formatTime(x.createdTimestamp - interaction.createdTimestamp, 2, {longNames: true, commas: true})}`})]}))
`**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')} // Nice data: new SlashCommandBuilder()// Nice
); .setName('statistics')
interaction.reply({embeds: [embed], fetchReply: true}).then((x)=>x.edit({embeds: [new client.embed(x.embeds[0].data).setFooter({text: `Load time: ${client.formatTime(x.createdTimestamp - interaction.createdTimestamp, 2, {longNames: true, commas: true})}`})]})) .setDescription('See a list of commands ordered by their usage or host stats')
},
data: new SlashCommandBuilder()
.setName('statistics')
.setDescription('See a list of commands ordered by their usage or host stats')
} }

View File

@ -56,7 +56,7 @@ export default {
]}); ]});
await client.suggestion._content.findByIdAndUpdate(suggestionIDReply, {state: 'Rejected'}); await client.suggestion._content.findByIdAndUpdate(suggestionIDReply, {state: 'Rejected'});
return interaction.reply({embeds:[new client.embed().setColor(client.config.embedColorRed).setTitle(`Suggestion rejected | ${suggestionIDReply}`).setDescription(stateChanged)]}); return interaction.reply({embeds:[new client.embed().setColor(client.config.embedColorRed).setTitle(`Suggestion rejected | ${suggestionIDReply}`).setDescription(stateChanged)]});
}, }
} as any)[interaction.options.getSubcommand()](); } as any)[interaction.options.getSubcommand()]();
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()

View File

@ -1,18 +1,18 @@
import Discord,{SlashCommandBuilder} from 'discord.js'; import Discord,{SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
client.punish(client, interaction, 'warn'); client.punish(client, interaction, 'warn');
}, },
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('warn') .setName('warn')
.setDescription('Warn a member') .setDescription('Warn a member')
.addUserOption((opt)=>opt .addUserOption((opt)=>opt
.setName('member') .setName('member')
.setDescription('Which member to warn?') .setDescription('Which member to warn?')
.setRequired(true)) .setRequired(true))
.addStringOption((opt)=>opt .addStringOption((opt)=>opt
.setName('reason') .setName('reason')
.setDescription('Reason for the warning') .setDescription('Reason for the warning')
.setRequired(false)) .setRequired(false))
} }

View File

@ -2,64 +2,59 @@ import Discord,{GuildMember, SlashCommandBuilder} from 'discord.js';
import TClient from 'src/client'; import TClient from 'src/client';
function convert(status?:Discord.ClientPresenceStatus){ function convert(status?:Discord.ClientPresenceStatus){
if (status){ if (status) return {
return { idle: '🟡',
idle: '🟡', dnd: '🔴',
dnd: '🔴', online: '🟢'
online: '🟢' }[status];
}[status]; else return '⚫'
} else {
return '⚫'
}
} }
export default { export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const member = interaction.options.getMember('member') as GuildMember; const member = interaction.options.getMember('member') as GuildMember;
if (member == null){ if (member == null){
const user = interaction.options.getUser('member') as Discord.User; const user = interaction.options.getUser('member') as Discord.User;
const embed = new client.embed() const embed = new client.embed()
.setColor(client.config.embedColor) .setColor(client.config.embedColor)
.setURL(`https://discord.com/users/${user.id}`) .setURL(`https://discord.com/users/${user.id}`)
.setThumbnail(user.avatarURL({size:2048}) || user.defaultAvatarURL) .setThumbnail(user.avatarURL({size:2048}) || user.defaultAvatarURL)
.setTitle(`${user.bot ? 'Bot': 'User'} Info: ${user.tag}`) .setTitle(`${user.bot ? 'Bot': 'User'} Info: ${user.tag}`)
.setDescription(`<@${user.id}>\n\`${user.id}\``) .setDescription(`<@${user.id}>\n\`${user.id}\``)
.addFields({name: '🔹 Account Creation Date', value: `<t:${Math.round(user.createdTimestamp/1000)}>\n<t:${Math.round(user.createdTimestamp/1000)}:R>`}) .addFields({name: '🔹 Account Creation Date', value: `<t:${Math.round(user.createdTimestamp/1000)}>\n<t:${Math.round(user.createdTimestamp/1000)}:R>`})
interaction.reply({embeds: [embed]}) interaction.reply({embeds: [embed]})
} else { } else {
await member.user.fetch(); await member.user.fetch();
const presence = member.presence?.clientStatus as Discord.ClientPresenceStatusData; const presence = member.presence?.clientStatus as Discord.ClientPresenceStatusData;
const embedArray = []; const embedArray = [];
let title = 'Member'; let title = 'Member';
if (member.user.bot) { if (member.user.bot) title = 'Bot';
title = 'Bot' else if (member.user.id == interaction.guild.ownerId) title = ':crown: Server Owner';
} else if (member.user.id == interaction.guild.ownerId) {
title = ':crown: Server Owner' const embed = new client.embed()
}; .setColor(member.displayColor || client.config.embedColor)
const embed = new client.embed() .setURL(`https://discord.com/users/${member.user.id}`)
.setColor(member.displayColor || client.config.embedColor) .setThumbnail(member.user.avatarURL({size:2048}) || member.user.defaultAvatarURL)
.setURL(`https://discord.com/users/${member.user.id}`) .setImage(member.user.bannerURL({size:1024}) as string)
.setThumbnail(member.user.avatarURL({size:2048}) || member.user.defaultAvatarURL) .setTitle(`${title} Info: ${member.user.tag}`)
.setImage(member.user.bannerURL({size:1024}) as string) .setDescription(`<@${member.user.id}>\n\`${member.user.id}\``)
.setTitle(`${title} Info: ${member.user.tag}`) .addFields(
.setDescription(`<@${member.user.id}>\n\`${member.user.id}\``) {name: '🔹 Account Creation Date', value: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`},
.addFields( {name: '🔹 Server Join Date', value: `<t:${Math.round((member.joinedTimestamp as number)/1000)}>\n<t:${Math.round((member.joinedTimestamp as number)/1000)}:R>`},
{name: '🔹 Account Creation Date', value: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`}, {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'}
{name: '🔹 Server Join Date', value: `<t:${Math.round((member.joinedTimestamp as number)/1000)}>\n<t:${Math.round((member.joinedTimestamp as number)/1000)}:R>`}, )
{name: `🔹 Roles: ${member.roles.cache.size - 1}`, value: member.roles.cache.size > 1 ? member.roles.cache.filter(x=>x.id !== interaction.guild.roles.everyone.id).sort((a,b)=>b.position - a.position).map(x=>x).join(member.roles.cache.size > 4 ? ' ' : '\n').slice(0,1024) : 'No roles'} if (member.premiumSinceTimestamp !== null) embed.addFields({name: '🔹 Server Boosting since', value: `<t:${Math.round(member.premiumSinceTimestamp/1000)}>\n<t:${Math.round(member.premiumSinceTimestamp/1000)}:R>`, inline: true})
) if (!presence) embed.addFields({name: `🔹 Status: Unavailable to retrieve`, value: '\u200b'})
if (member.premiumSinceTimestamp !== null) embed.addFields({name: '🔹 Server Boosting since', value: `<t:${Math.round(member.premiumSinceTimestamp/1000)}>\n<t:${Math.round(member.premiumSinceTimestamp/1000)}:R>`, inline: true}) if (member.presence) embed.addFields({name: `🔹 Status: ${member.presence.status}`, value: `${member.presence.status === 'offline' ? '⚫' : `Desktop: ${convert(presence.desktop)}\nWeb: ${convert(presence.web)}\nMobile: ${convert(presence.mobile)}`}`, inline: true})
if (!presence) embed.addFields({name: `🔹 Status: Unavailable to retrieve`, value: '\u200b'}) embedArray.push(embed)
if (member.presence) embed.addFields({name: `🔹 Status: ${member.presence.status}`, value: `${member.presence.status === 'offline' ? '⚫' : `Desktop: ${convert(presence.desktop)}\nWeb: ${convert(presence.web)}\nMobile: ${convert(presence.mobile)}`}`, inline: true}) interaction.reply({embeds: embedArray})
embedArray.push(embed) }
interaction.reply({embeds: embedArray}) },
} data: new SlashCommandBuilder()
}, .setName('whois')
data: new SlashCommandBuilder() .setDescription('View your own or someone else\'s information')
.setName('whois') .addUserOption((opt)=>opt
.setDescription('View your own or someone else\'s information') .setName('member')
.addUserOption((opt)=>opt .setDescription('Member or user to view their information')
.setName('member') .setRequired(true))
.setDescription('Member or user to view their information')
.setRequired(true))
} }

View File

@ -1,73 +1,73 @@
{ {
"embedColor": "#0052cf", "embedColor": "#0052cf",
"embedColorBackup": "#0052cf", "embedColorBackup": "#0052cf",
"embedColorGreen": "#57f287", "embedColorGreen": "#57f287",
"embedColorYellow": "#ffea00", "embedColorYellow": "#ffea00",
"embedColorRed": "#e62c3b", "embedColorRed": "#e62c3b",
"embedColorBCA": "#ff69b4", "embedColorBCA": "#ff69b4",
"embedColorXmas": "#FFFFFF", "embedColorXmas": "#FFFFFF",
"LRSstart": 1661236321433, "LRSstart": 1661236321433,
"whitelistedServers": [ "whitelistedServers": [
"929807948748832798", "468835415093411861", "1058183358267543552" "929807948748832798", "468835415093411861", "1058183358267543552"
], ],
"botSwitches": { "botSwitches": {
"registerCommands": true, "registerCommands": true,
"commands": true, "commands": true,
"logs": true, "logs": true,
"automod": true, "automod": true,
"mpstats": true, "mpstats": true,
"autores": true "autores": true
}, },
"botPresence": { "botPresence": {
"activities": [ "activities": [
{"name": "the new SRP", "url": "https://www.youtube.com/watch?v=5BDEzgbJ7c0", "type": 1} {"name": "the new SRP", "url": "https://www.youtube.com/watch?v=5BDEzgbJ7c0", "type": 1}
], ],
"status": "idle" "status": "idle"
}, },
"eval": { "eval": {
"allowed": true, "allowed": true,
"whitelist": [ "whitelist": [
"190407856527376384", "190407856527376384",
"615761944154210305", "615761944154210305",
"593696856165449749", "593696856165449749",
"633345781780185099", "633345781780185099",
"506022868157595648", "506022868157595648",
"215497515934416896", "215497515934416896",
"309373272594579456" "309373272594579456"
] ]
},
"mainServer": {
"id": "468835415093411861",
"staffRoles": [
"admin",
"dcmod",
"mpmanager",
"mpmod",
"vtcmanager",
"vtcstaff",
"ytmod"
],
"roles": {
"admin": "468842789053136897",
"bottech": "1011341005389307925",
"dcmod": "468841295150972929",
"mpmanager": "1028735939813585029",
"mpmod": "572151330710487041",
"vtcmanager": "1028735871370940490",
"vtcstaff": "801945627268481046",
"ytmod": "734888335851388958",
"mphelper": "1034453973412884500",
"mpplayer": "798285830669598762",
"vtcmember": "802282391652663338"
}, },
"mainServer": { "channels": {
"id": "468835415093411861", "console": "1011318687065710663",
"staffRoles": [ "errors": "1009754872188506192",
"admin", "thismeanswar": "930588618085502987",
"dcmod", "bot_status": "1009753780188884992",
"mpmanager", "logs": "548032776830582794",
"mpmod", "welcome": "621134751897616406",
"vtcmanager", "botcommands": "468888722210029588"
"vtcstaff",
"ytmod"
],
"roles": {
"admin": "468842789053136897",
"bottech": "1011341005389307925",
"dcmod": "468841295150972929",
"mpmanager": "1028735939813585029",
"mpmod": "572151330710487041",
"vtcmanager": "1028735871370940490",
"vtcstaff": "801945627268481046",
"ytmod": "734888335851388958",
"mphelper": "1034453973412884500",
"mpplayer": "798285830669598762",
"vtcmember": "802282391652663338"
},
"channels": {
"console": "1011318687065710663",
"errors": "1009754872188506192",
"thismeanswar": "930588618085502987",
"bot_status": "1009753780188884992",
"logs": "548032776830582794",
"welcome": "621134751897616406",
"botcommands": "468888722210029588"
}
} }
}
} }

View File

@ -15,26 +15,17 @@ export class Database {
this._content = dataType === 'array' ? [] : {}; this._content = dataType === 'array' ? [] : {};
} }
addData(data: any, data1?: any){ addData(data: any, data1?: any){
if (Array.isArray(this._content)){ if (Array.isArray(this._content)) this._content.push(data);
this._content.push(data); else if (typeof this._content === 'object') this._content[data] = data1;
} else if (typeof this._content === 'object'){
this._content[data] = data1;
}
return this; return this;
} }
removeData(key: any, type: number, element: any){ removeData(key: any, type: number, element: any){
if (this._dataType === 'array'){ if (this._dataType === 'array'){
switch (type){ ({
case 0: 0: ()=>this._content = this._content.filter((x:any)=>x!=key),
this._content = this._content.filter((x:any)=>x != key); 1: ()=>this._content = this._content.filter((x:any)=>x[element]!=key)
break; })[type]()
case 1: } else if (this._dataType === 'object') delete this._content[key];
this._content = this._content.filter((x:any)=>x[element] != key);
break;
}
} else if (this._dataType === 'object'){
delete this._content[key];
}
return this; return this;
} }
initLoad(){ initLoad(){
@ -64,6 +55,4 @@ export class Database {
console.log(this._path + ' "DB saved" Notifications disabled'); console.log(this._path + ' "DB saved" Notifications disabled');
return this; return this;
} }
}
} // Nice.

View File

@ -1,20 +1,17 @@
import Discord, { AuditLogEvent } from 'discord.js'; import Discord, { AuditLogEvent } from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, member:Discord.GuildMember){ async run(client:TClient, member:Discord.GuildMember){
if (member.guild?.id != client.config.mainServer.id) return; if (member.guild?.id != client.config.mainServer.id) return;
const fetchBanlog = await member.guild.fetchAuditLogs({limit: 1, type: AuditLogEvent.MemberBanAdd}) const fetchBanlog = await member.guild.fetchAuditLogs({limit: 1, type: AuditLogEvent.MemberBanAdd})
const banLog = fetchBanlog.entries.first(); const banLog = fetchBanlog.entries.first();
if (!banLog) return console.log(`${member.user.tag} was banned from ${member.guild.name} but no audit log for this user.`) if (!banLog) return console.log(`${member.user.tag} was banned from ${member.guild.name} but no audit log for this user.`)
const {executor, target, reason } = banLog; const {executor, target, reason } = banLog;
if (target.id == member.user.id) { if (target.id == member.user.id) (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [ new client.embed().setColor(client.config.embedColorRed).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Banned: ${target.tag}`).setDescription(`🔹 **User**\n<@${target.id}>\n\`${target.id}\``).addFields(
new client.embed().setColor(client.config.embedColorRed).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Banned: ${target.tag}`).setDescription(`🔹 **User**\n<@${target.id}>\n\`${target.id}\``).addFields( {name: '🔹 Moderator', value: `<@${executor.id}>\n\`${executor.id}\``},
{name: '🔹 Moderator', value: `<@${executor.id}>\n\`${executor.id}\``}, {name: '🔹 Reason', value: `${reason == null ? 'Reason unspecified': reason}`}
{name: '🔹 Reason', value: `${reason == null ? 'Reason unspecified': reason}`} )]});
)]}) else console.log(`${target.tag} was banned from ${member.guild.name} but no audit log could be fetched.`)
} else { }
console.log(`${target.tag} was banned from ${member.guild.name} but no audit log could be fetched.`)
}
}
} }

View File

@ -1,20 +1,17 @@
import Discord, { AuditLogEvent } from 'discord.js'; import Discord, { AuditLogEvent } from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, member:Discord.GuildMember){ async run(client:TClient, member:Discord.GuildMember){
if (member.guild?.id != client.config.mainServer.id) return; if (member.guild?.id != client.config.mainServer.id) return;
const fetchUnbanlog = await member.guild.fetchAuditLogs({limit: 1, type: AuditLogEvent.MemberBanRemove}) const fetchUnbanlog = await member.guild.fetchAuditLogs({limit: 1, type: AuditLogEvent.MemberBanRemove})
const unbanLog = fetchUnbanlog.entries.first(); const unbanLog = fetchUnbanlog.entries.first();
if (!unbanLog) return console.log(`${member.user.tag} was unbanned from ${member.guild.name} but no audit log for this user.`) if (!unbanLog) return console.log(`${member.user.tag} was unbanned from ${member.guild.name} but no audit log for this user.`)
const { executor, target, reason } = unbanLog; const { executor, target, reason } = unbanLog;
if (target.id == member.user.id) { if (target.id == member.user.id) (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [ new client.embed().setColor(client.config.embedColorGreen).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Unbanned: ${target.tag}`).setDescription(`🔹 **User**\n<@${target.id}>\n\`${target.id}\``).addFields(
new client.embed().setColor(client.config.embedColorGreen).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048})).setTitle(`Member Unbanned: ${target.tag}`).setDescription(`🔹 **User**\n<@${target.id}>\n\`${target.id}\``).addFields( {name: '🔹 Moderator', value: `<@${executor.id}>\n\`${executor.id}\``},
{name: '🔹 Moderator', value: `<@${executor.id}>\n\`${executor.id}\``}, {name: '🔹 Reason', value: `${reason == null ? 'Reason unspecified.': reason}`}
{name: '🔹 Reason', value: `${reason == null ? 'Reason unspecified.': reason}`} )]});
)]}) else console.log(`${target.tag} was unbanned from ${member.guild.name} but no audit log could be fetched.`)
} else { }
console.log(`${target.tag} was unbanned from ${member.guild.name} but no audit log could be fetched.`)
}
}
} }

View File

@ -1,31 +1,29 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, member:Discord.GuildMember){ async run(client:TClient, member:Discord.GuildMember){
if (member.partial || member.guild?.id != client.config.mainServer.id) return; if (member.partial || member.guild?.id != client.config.mainServer.id) return;
const index = member.guild.memberCount; const index = member.guild.memberCount;
const suffix = ((index)=>{ const suffix = ((index)=>{
const numbers = index.toString().split('').reverse(); // eg 1850 --> [0,5,8,1] const numbers = index.toString().split('').reverse(); // eg 1850 --> [0,5,8,1]
if (numbers[1] === '1'){// this is some -teen if (numbers[1] === '1') return 'th'; // this is some -teen
return 'th'; else {
} else { if (numbers[0] === '1') return 'st';
if (numbers[0] === '1') return 'st'; else if (numbers[0] === '2') return 'nd';
else if (numbers[0] === '2') return 'nd'; else if (numbers[0] === '3') return 'rd';
else if (numbers[0] === '3') return 'rd'; else return 'th';
else return 'th';
}
})(index);
(client.channels.resolve(client.config.mainServer.channels.welcome) as Discord.TextChannel).send({embeds: [new client.embed().setColor(client.config.embedColor).setThumbnail(member.user.displayAvatarURL({size: 2048}) || member.user.defaultAvatarURL).setTitle(`Welcome to Daggerwin, ${member.user.tag}!`).setFooter({text: `${index}${suffix} member`})]})
if (!client.config.botSwitches.logs) return;
const newInvites = await member.guild.invites.fetch();
const usedInvite = newInvites.find((inv:any)=>client.invites.get(inv.code)?.uses < inv.uses);
newInvites.forEach((inv:any)=>client.invites.set(inv.code,{uses: inv.uses, creator: inv.inviter.id}));
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [
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: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`},
{name: '🔹 Invite Data:', value: usedInvite ? `Invite: \`${usedInvite.code}\`\nCreated by: **${usedInvite.inviter?.tag}**` : 'No invite data could be found.'}
)]})
} }
})(index);
(client.channels.resolve(client.config.mainServer.channels.welcome) as Discord.TextChannel).send({embeds: [new client.embed().setColor(client.config.embedColor).setThumbnail(member.user.displayAvatarURL({size: 2048}) || member.user.defaultAvatarURL).setTitle(`Welcome to Daggerwin, ${member.user.tag}!`).setFooter({text: `${index}${suffix} member`})]})
if (!client.config.botSwitches.logs) return;
const newInvites = await member.guild.invites.fetch();
const usedInvite = newInvites.find((inv:any)=>client.invites.get(inv.code)?.uses < inv.uses);
newInvites.forEach((inv:any)=>client.invites.set(inv.code,{uses: inv.uses, creator: inv.inviter.id}));
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [
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: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`},
{name: '🔹 Invite Data:', value: usedInvite ? `Invite: \`${usedInvite.code}\`\nCreated by: **${usedInvite.inviter?.tag}**` : 'No invite data could be found.'}
)]})
}
} }

View File

@ -1,17 +1,17 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, member:Discord.GuildMember){ async run(client:TClient, member:Discord.GuildMember){
if (!client.config.botSwitches.logs) return; if (!client.config.botSwitches.logs) return;
if (!member.joinedTimestamp || member.guild?.id != client.config.mainServer.id) return; if (!member.joinedTimestamp || member.guild?.id != client.config.mainServer.id) return;
const levelData = await client.userLevels._content.findById(member.id); const levelData = await client.userLevels._content.findById(member.id);
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048}) as string).setTitle(`Member Left: ${member.user.tag}`).setDescription(`<@${member.user.id}>\n\`${member.user.id}\``).addFields( const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setThumbnail(member.user.displayAvatarURL({size: 2048}) as string).setTitle(`Member Left: ${member.user.tag}`).setDescription(`<@${member.user.id}>\n\`${member.user.id}\``).addFields(
{name: '🔹 Account Creation Date', value: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`}, {name: '🔹 Account Creation Date', value: `<t:${Math.round(member.user.createdTimestamp/1000)}>\n<t:${Math.round(member.user.createdTimestamp/1000)}:R>`},
{name: '🔹 Server Join Date', value: `<t:${Math.round(member.joinedTimestamp/1000)}>\n<t:${Math.round(member.joinedTimestamp/1000)}:R>`}, {name: '🔹 Server Join Date', value: `<t:${Math.round(member.joinedTimestamp/1000)}>\n<t:${Math.round(member.joinedTimestamp/1000)}:R>`},
{name: `🔹 Roles: ${member.roles.cache.size - 1}`, value: `${member.roles.cache.size > 1 ? member.roles.cache.filter((x)=>x.id !== member.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'}`, inline: true} {name: `🔹 Roles: ${member.roles.cache.size - 1}`, value: `${member.roles.cache.size > 1 ? member.roles.cache.filter((x)=>x.id !== member.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'}`, inline: true}
); );
if (levelData && levelData.messages > 1) embed.addFields({name: '🔹 Total messages', value: levelData.messages.toLocaleString('en-US'), inline: true}); if (levelData && levelData.messages > 1) embed.addFields({name: '🔹 Total messages', value: levelData.messages.toLocaleString('en-US'), inline: true});
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds:[embed]}); (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds:[embed]});
await client.userLevels._content.findByIdAndDelete(member.id) await client.userLevels._content.findByIdAndDelete(member.id)
} }
} }

View File

@ -1,23 +1,23 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, oldMember:Discord.GuildMember, newMember:Discord.GuildMember){ run(client:TClient, oldMember:Discord.GuildMember, newMember:Discord.GuildMember){
if (oldMember.guild.id != client.config.mainServer.id) return; if (oldMember.guild.id != client.config.mainServer.id) return;
if (!client.config.botSwitches.logs) return; if (!client.config.botSwitches.logs) return;
const channel = (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel) const channel = (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel)
if (oldMember.nickname != newMember.nickname){ if (oldMember.nickname != newMember.nickname){
const embed = new client.embed().setColor(client.config.embedColor).setTimestamp().setThumbnail(newMember.user.displayAvatarURL({size: 2048})).setTitle(`Nickname updated: ${newMember.user.tag}`).setDescription(`<@${newMember.user.id}>\n\`${newMember.user.id}\``) const embed = new client.embed().setColor(client.config.embedColor).setTimestamp().setThumbnail(newMember.user.displayAvatarURL({size: 2048})).setTitle(`Nickname updated: ${newMember.user.tag}`).setDescription(`<@${newMember.user.id}>\n\`${newMember.user.id}\``)
oldMember.nickname == null ? '' : embed.addFields({name: '🔹 Old nickname', value: `\`\`\`${oldMember.nickname}\`\`\``}) oldMember.nickname == null ? '' : embed.addFields({name: '🔹 Old nickname', value: `\`\`\`${oldMember.nickname}\`\`\``})
newMember.nickname == null ? '' : embed.addFields({name: '🔹 New nickname', value: `\`\`\`${newMember.nickname}\`\`\``}) newMember.nickname == null ? '' : embed.addFields({name: '🔹 New nickname', value: `\`\`\`${newMember.nickname}\`\`\``})
channel.send({embeds: [embed]}) channel.send({embeds: [embed]})
}
const newRoles = newMember.roles.cache.map((x,i)=>i).filter(x=>!oldMember.roles.cache.map((x,i)=>i).some(y=>y==x));
const oldRoles = oldMember.roles.cache.map((x,i)=>i).filter(x=>!newMember.roles.cache.map((x,i)=>i).some(y=>y==x));
if (newRoles.length == 0 && oldRoles.length == 0) return;
const embed = new client.embed().setColor(client.config.embedColor).setThumbnail(newMember.user.displayAvatarURL({size: 2048})).setTitle(`Role updated: ${newMember.user.tag}`).setDescription(`<@${newMember.user.id}>\n\`${newMember.user.id}\``)
if (newRoles.length != 0) embed.addFields({name: '🔹 Role added', value: newRoles.map((x)=>`<@&${x}>`).join(' ')});
if (oldRoles.length != 0) embed.addFields({name: '🔹 Role removed', value: oldRoles.map((x)=>`<@&${x}>`).join(' ')});
channel.send({embeds: [embed]})
} }
}
const newRoles = newMember.roles.cache.map((x,i)=>i).filter(x=>!oldMember.roles.cache.map((x,i)=>i).some(y=>y==x));
const oldRoles = oldMember.roles.cache.map((x,i)=>i).filter(x=>!newMember.roles.cache.map((x,i)=>i).some(y=>y==x));
if (newRoles.length == 0 && oldRoles.length == 0) return;
const embed = new client.embed().setColor(client.config.embedColor).setThumbnail(newMember.user.displayAvatarURL({size: 2048})).setTitle(`Role updated: ${newMember.user.tag}`).setDescription(`<@${newMember.user.id}>\n\`${newMember.user.id}\``)
if (newRoles.length != 0) embed.addFields({name: '🔹 Role added', value: newRoles.map((x)=>`<@&${x}>`).join(' ')});
if (oldRoles.length != 0) embed.addFields({name: '🔹 Role removed', value: oldRoles.map((x)=>`<@&${x}>`).join(' ')});
channel.send({embeds: [embed]})
}
}

View File

@ -1,7 +1,7 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, interaction:Discord.BaseInteraction){ run(client:TClient, interaction:Discord.BaseInteraction){
if (!interaction.inGuild() || !interaction.inCachedGuild()) return; if (!interaction.inGuild() || !interaction.inCachedGuild()) return;
if (interaction.isChatInputCommand()){ if (interaction.isChatInputCommand()){
const commandFile = client.commands.get(interaction.commandName); const commandFile = client.commands.get(interaction.commandName);
@ -9,13 +9,13 @@ export default {
if (!client.config.botSwitches.commands && !client.config.eval.whitelist.includes(interaction.user.id)) return interaction.reply({content: 'Bot is currently being run in development mode.', ephemeral: true}); if (!client.config.botSwitches.commands && !client.config.eval.whitelist.includes(interaction.user.id)) return interaction.reply({content: 'Bot is currently being run in development mode.', ephemeral: true});
if (commandFile){ if (commandFile){
try{ try{
commandFile.default.run(client, interaction); commandFile.default.run(client, interaction);
commandFile.uses ? commandFile.uses++ : commandFile.uses = 1; commandFile.uses ? commandFile.uses++ : commandFile.uses = 1;
} catch (error){ } catch (error){
console.log(`An error occured while running command "${commandFile.name}"`, error, error.stack); console.log(`An error occured while running command "${commandFile.name}"`, error, error.stack);
return interaction.reply('An error occured while executing that command.'); return interaction.reply('An error occured while executing that command.');
} }
} }
} }
} }
} }

View File

@ -1,9 +1,9 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, invite: Discord.Invite){ async run(client:TClient, invite: Discord.Invite){
if (!invite.guild) return; if (!invite.guild) return;
const newInvites = await (invite.guild as Discord.Guild).invites.fetch(); const newInvites = await (invite.guild as Discord.Guild).invites.fetch();
newInvites.forEach(inv=>client.invites.set(inv.code,{uses: inv.code, creator: inv.inviterId})) newInvites.forEach(inv=>client.invites.set(inv.code,{uses: inv.code, creator: inv.inviterId}))
} }
} }

View File

@ -1,7 +1,7 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, invite: Discord.Invite){ run(client:TClient, invite: Discord.Invite){
client.invites.delete(invite.code) client.invites.delete(invite.code)
} }
} }

View File

@ -1,131 +1,130 @@
import Discord, { ChannelType } from 'discord.js'; import Discord, { ChannelType } from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, message:Discord.Message){ async run(client:TClient, message:Discord.Message){
if (message.author.bot || message.channel.type === ChannelType.DM) return; if (message.author.bot || message.channel.type === ChannelType.DM) return;
const msgarr = message.content.toLowerCase().split(' '); const msgarr = message.content.toLowerCase().split(' ');
let automodded: boolean; let automodded: boolean;
function onTimeout(){ function onTimeout(){
delete client.repeatedMessages[message.author.id] delete client.repeatedMessages[message.author.id]
}
const Whitelist = [
// Arrary of channel ids for automod to be disabled in
]
if (await client.bannedWords._content.findOne({_id:msgarr}) && !message.member.roles.cache.has(client.config.mainServer.roles.dcmod) && message.guildId == client.config.mainServer.id && !Whitelist.includes(message.channelId) && client.config.botSwitches.automod){
automodded = true;
const threshold = 30000;
message.delete().catch(err=>console.log('bannedWords automod; msg got possibly deleted by another bot.'))
message.channel.send('That word is banned here.').then((x)=>setTimeout(()=>x.delete(), 10000));
if (client.repeatedMessages[message.author.id]){
// add this message to the list
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 0, ch: message.channelId});
// reset timeout
clearTimeout(client.repeatedMessages[message.author.id].timeout);
client.repeatedMessages[message.author.id].timeout = setTimeout(onTimeout, threshold);
// message mustve been sent after (now - threshold), so purge those that were sent earlier
client.repeatedMessages[message.author.id].data = client.repeatedMessages[message.author.id].data.filter((x, i)=>i >= Date.now() - threshold)
// a spammed message is one that has been sent atleast 4 times in the last threshold milliseconds
const spammedMessage = client.repeatedMessages[message.author.id]?.data.find((x)=>{
return client.repeatedMessages[message.author.id].data.size >= 4;
});
// if a spammed message exists;
if (spammedMessage){
delete client.repeatedMessages[message.author.id];
await client.punishments.addPunishment('mute', { time: '30m' }, (client.user as Discord.User).id, 'Automod; Banned words', message.author, message.member as Discord.GuildMember);
}
} else {
client.repeatedMessages[message.author.id] = { data: new client.collection(), timeout: setTimeout(onTimeout, threshold) };
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 0, ch: message.channelId});
}
}
if (message.content.toLowerCase().includes('discord.gg/') && !message.member.roles.cache.has(client.config.mainServer.roles.dcmod) && message.guildId == client.config.mainServer.id && !Whitelist.includes(message.channelId)) {
automodded = true;
const threshold = 60000;
message.delete().catch(err=>console.log('advertisement automod; msg got possibly deleted by another bot.'))
message.channel.send('Advertising other Discord servers is not allowed.').then(x=>setTimeout(()=>x.delete(), 15000))
if (client.repeatedMessages[message.author.id]){
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp,{cont:1,ch:message.channelId});
clearTimeout(client.repeatedMessages[message.author.id].timeout);
client.repeatedMessages[message.author.id].timeout = setTimeout(onTimeout, threshold);
client.repeatedMessages[message.author.id].data = client.repeatedMessages[message.author.id].data.filter((x, i)=> i >= Date.now() - threshold)
const spammedMessage = client.repeatedMessages[message.author.id].data.find((x)=>{
return client.repeatedMessages[message.author.id].data.filter((y)=>x.cont === y.cont).size >= 4;
});
if (spammedMessage){
delete client.repeatedMessages[message.author.id];
await client.punishments.addPunishment('mute', {time: '1h'}, (client.user as Discord.User).id, 'Automod; Discord advertisement', message.author, message.member as Discord.GuildMember);
}
}else{
client.repeatedMessages[message.author.id] = { data: new client.collection(), timeout: setTimeout(onTimeout, threshold) };
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 1, ch: message.channelId});
}
}
if (message.guildId == client.config.mainServer.id && !automodded) client.userLevels.incrementUser(message.author.id)
// Mop gifs from banned channels without Monster having to mop them.
const bannedChannels = [
'516344221452599306', // #mp-moderators
'742324777934520350', // #discord-moderators
]
const gifURL = ['tenor.com/view', 'giphy.com/gifs', 'giphy.com/media']
if (gifURL.some(e=>message.content.toLowerCase().includes(e)) && bannedChannels.includes(message.channelId)) message.reply('Gifs are not allowed in this channel.').then((msg)=>message.delete())
// Autoresponse:tm:
if (client.config.botSwitches.autores && !automodded) {
const MorningArray = ['good morning all', 'good morning everyone', 'morning all', 'morning everyone', 'morning lads', 'morning guys', 'good morning everybody']
const AfternoonArray = ['good afternoon', 'afternoon all', 'afternoon everyone']
const EveningArray = ['good evening', 'evening all', 'evening everyone']
const NightArray = ['night all', 'night everyone', 'night guys']
const PasswordArray = ['whats the password', 'what\'s the password', 'password pls']
const cantRead = ['i cant read', 'i can\'t read', 'cant read', 'can\'t read']
const NawdicBrokeIt = ['break', 'broke', 'broken']
const deadChat = ['dead chat', 'chat is dead', 'dead server']
const PersonnyMcPerson = `**${message.member.displayName}**`;
const GeneralChatID = '468835415093411863';
const MorningPhrases = [
`Morning ${PersonnyMcPerson}, did you sleep great?`, `Good morning ${PersonnyMcPerson}!`, `Hope you enjoyed your breakfast, ${PersonnyMcPerson}!`,
`Gm ${PersonnyMcPerson}`, `Uh.. What time is it? Oh yea, morning ${PersonnyMcPerson}`, `Morning and hope you had a good dream last night, ${PersonnyMcPerson}`,
'Time to get started with today\'s stuff!', `Don't forget to do your morning routine, ${PersonnyMcPerson}!`, 'Enjoy the breakfast and start your day.',
'Nuh! No morning message for you!\n*Just kidding, good morning!*'
]
const AfternoonPhrases = [
`Afternoon ${PersonnyMcPerson}!`, `What a nice day outside, ${PersonnyMcPerson}`, `Good afternoon ${PersonnyMcPerson}`,
'Hope you had a good day so far.', `Did you enjoy your day yet, ${PersonnyMcPerson}?`, 'Weather doesn\'t look too bad outside right?',
`How's the trip outside, ${PersonnyMcPerson}?`, `~~Morning~~ Afternoon ${PersonnyMcPerson}!`
]
const EveningPhrases = [
'I can\'t believe the time flies so quickly!', `Evening ${PersonnyMcPerson}!`, `Hope you enjoyed the dinner, ${PersonnyMcPerson}!`,
`Good evening ${PersonnyMcPerson}!`, 'You look tired, ready to go to sleep yet?', 'Being outside was an exhausting task isn\'t it?',
'Did you have a good day so far?', 'May I suggest sleep?', `You heard me! ${PersonnyMcPerson}, it's almost dinner time!`
]
const NightPhrases = [
`Good night ${PersonnyMcPerson}!`, `Night ${PersonnyMcPerson}!`, `Sweet dreams, ${PersonnyMcPerson}.`, `Don't fall out of sky in your dreamworld, ${PersonnyMcPerson}!`,
'Nighty night!', `I hope tomorrow is a good day for you, ${PersonnyMcPerson}!`, `Have a good sleep, ${PersonnyMcPerson}!`, `I :b:et you a cookie if you actually slept through the night! ${PersonnyMcPerson}`
]
if (message.mentions.members.has('309373272594579456') && !client.isStaff(message.member)) 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 (PasswordArray.some(e=>msgarr.includes(e))) message.reply('Password and other details can be found in <#543494084363288637>');
if (cantRead.some(e=>msgarr.includes(e))) message.reply('https://tenor.com/view/aristocats-george-pen-cap-meticulous-gif-5330931');
if (msgarr.includes('is daggerbot working')) message.reply('https://tenor.com/view/i-still-feel-alive-living-existing-active-singing-gif-14630579');
if (deadChat.some(e=>msgarr.includes(e))) message.reply('https://cdn.discordapp.com/attachments/925589318276382720/1011333656167579849/F57G5ZS.png');
if (msgarr.includes('nawdic') && NawdicBrokeIt.some(e=>msgarr.includes(e)) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Nawdic has done an oopsie*').setImage('https://c.tenor.com/JSj9ie_MD9kAAAAC/kopfsch%C3%BCtteln-an-kopf-fassen-oh-no.gif').setColor(client.config.embedColor)]});
if (MorningArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${MorningPhrases[Math.floor(Math.random()*MorningPhrases.length)]}`);
if (AfternoonArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${AfternoonPhrases[Math.floor(Math.random()*AfternoonPhrases.length)]}`);
if (EveningArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${EveningPhrases[Math.floor(Math.random()*EveningPhrases.length)]}`);
if (NightArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${NightPhrases[Math.floor(Math.random()*NightPhrases.length)]}`);
// Failsafe thingy (Toastproof maybe)
if (message.content.startsWith('!!_wepanikfrfr') && client.config.eval.whitelist.includes(message.author.id)) (client.guilds.cache.get(message.guildId) as Discord.Guild).commands.set(client.registry).then(()=>message.reply('How did you manage to lose the commands??? Anyways, it\'s re-registered now.')).catch((e:Error)=>message.reply(`Failed to deploy slash commands:\n\`\`\`${e.message}\`\`\``));
}
} }
const Whitelist = [] // Array of channel ids for automod to be disabled in (Disables bannedWords and advertisement, mind you.)
if (await client.bannedWords._content.findOne({_id:msgarr}) && !message.member.roles.cache.has(client.config.mainServer.roles.dcmod) && message.guildId == client.config.mainServer.id && !Whitelist.includes(message.channelId) && client.config.botSwitches.automod){
automodded = true;
const threshold = 30000;
message.delete().catch(err=>console.log('bannedWords automod; msg got possibly deleted by another bot.'))
message.channel.send('That word is banned here.').then((x)=>setTimeout(()=>x.delete(), 10000));
if (client.repeatedMessages[message.author.id]){
// add this message to the list
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 0, ch: message.channelId});
// reset timeout
clearTimeout(client.repeatedMessages[message.author.id].timeout);
client.repeatedMessages[message.author.id].timeout = setTimeout(onTimeout, threshold);
// message mustve been sent after (now - threshold), so purge those that were sent earlier
client.repeatedMessages[message.author.id].data = client.repeatedMessages[message.author.id].data.filter((x, i)=>i >= Date.now() - threshold)
// a spammed message is one that has been sent atleast 4 times in the last threshold milliseconds
const spammedMessage = client.repeatedMessages[message.author.id]?.data.find((x)=>{
return client.repeatedMessages[message.author.id].data.size >= 4;
});
// if a spammed message exists;
if (spammedMessage){
delete client.repeatedMessages[message.author.id];
await client.punishments.addPunishment('mute', { time: '30m' }, (client.user as Discord.User).id, 'Automod; Banned words', message.author, message.member as Discord.GuildMember);
}
} else {
client.repeatedMessages[message.author.id] = { data: new client.collection(), timeout: setTimeout(onTimeout, threshold) };
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 0, ch: message.channelId});
}
}
if (message.content.toLowerCase().includes('discord.gg/') && !message.member.roles.cache.has(client.config.mainServer.roles.dcmod) && message.guildId == client.config.mainServer.id && !Whitelist.includes(message.channelId)) {
automodded = true;
const threshold = 60000;
message.delete().catch(err=>console.log('advertisement automod; msg got possibly deleted by another bot.'))
message.channel.send('Advertising other Discord servers is not allowed.').then(x=>setTimeout(()=>x.delete(), 15000))
if (client.repeatedMessages[message.author.id]){
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp,{cont:1,ch:message.channelId});
clearTimeout(client.repeatedMessages[message.author.id].timeout);
client.repeatedMessages[message.author.id].timeout = setTimeout(onTimeout, threshold);
client.repeatedMessages[message.author.id].data = client.repeatedMessages[message.author.id].data.filter((x, i)=> i >= Date.now() - threshold)
const spammedMessage = client.repeatedMessages[message.author.id].data.find((x)=>{
return client.repeatedMessages[message.author.id].data.filter((y)=>x.cont === y.cont).size >= 4;
});
if (spammedMessage){
delete client.repeatedMessages[message.author.id];
await client.punishments.addPunishment('mute', {time: '1h'}, (client.user as Discord.User).id, 'Automod; Discord advertisement', message.author, message.member as Discord.GuildMember);
}
}else{
client.repeatedMessages[message.author.id] = { data: new client.collection(), timeout: setTimeout(onTimeout, threshold) };
client.repeatedMessages[message.author.id].data.set(message.createdTimestamp, {cont: 1, ch: message.channelId});
}
}
if (message.guildId == client.config.mainServer.id && !automodded) client.userLevels.incrementUser(message.author.id)
// Mop gifs from banned channels without Monster having to mop them.
const bannedChannels = [
'516344221452599306', // #mp-moderators
'742324777934520350', // #discord-moderators
]
const gifURL = ['tenor.com/view', 'giphy.com/gifs', 'giphy.com/media']
if (gifURL.some(e=>message.content.toLowerCase().includes(e)) && bannedChannels.includes(message.channelId)) message.reply('Gifs are not allowed in this channel.').then((msg)=>message.delete())
// Autoresponse:tm:
if (client.config.botSwitches.autores && !automodded) {
const MorningArray = ['good morning all', 'good morning everyone', 'morning all', 'morning everyone', 'morning lads', 'morning guys', 'good morning everybody']
const AfternoonArray = ['good afternoon', 'afternoon all', 'afternoon everyone']
const EveningArray = ['good evening', 'evening all', 'evening everyone']
const NightArray = ['night all', 'night everyone', 'night guys']
const PasswordArray = ['whats the password', 'what\'s the password', 'password pls']
const cantRead = ['i cant read', 'i can\'t read', 'cant read', 'can\'t read']
const NawdicBrokeIt = ['break', 'broke', 'broken']
const deadChat = ['dead chat', 'chat is dead', 'dead server']
const PersonnyMcPerson = `**${message.member.displayName}**`;
const GeneralChatID = '468835415093411863';
const MorningPhrases = [
`Morning ${PersonnyMcPerson}, did you sleep great?`, `Good morning ${PersonnyMcPerson}!`, `Hope you enjoyed your breakfast, ${PersonnyMcPerson}!`,
`Gm ${PersonnyMcPerson}`, `Uh.. What time is it? Oh yea, morning ${PersonnyMcPerson}`, `Morning and hope you had a good dream last night, ${PersonnyMcPerson}`,
'Time to get started with today\'s stuff!', `Don't forget to do your morning routine, ${PersonnyMcPerson}!`, 'Enjoy the breakfast and start your day.',
'Nuh! No morning message for you!\n*Just kidding, good morning!*'
]
const AfternoonPhrases = [
`Afternoon ${PersonnyMcPerson}!`, `What a nice day outside, ${PersonnyMcPerson}`, `Good afternoon ${PersonnyMcPerson}`,
'Hope you had a good day so far.', `Did you enjoy your day yet, ${PersonnyMcPerson}?`, 'Weather doesn\'t look too bad outside right?',
`How's the trip outside, ${PersonnyMcPerson}?`, `~~Morning~~ Afternoon ${PersonnyMcPerson}!`
]
const EveningPhrases = [
'I can\'t believe the time flies so quickly!', `Evening ${PersonnyMcPerson}!`, `Hope you enjoyed the dinner, ${PersonnyMcPerson}!`,
`Good evening ${PersonnyMcPerson}!`, 'You look tired, ready to go to sleep yet?', 'Being outside was an exhausting task isn\'t it?',
'Did you have a good day so far?', 'May I suggest sleep?', `You heard me! ${PersonnyMcPerson}, it's almost dinner time!`
]
const NightPhrases = [
`Good night ${PersonnyMcPerson}!`, `Night ${PersonnyMcPerson}!`, `Sweet dreams, ${PersonnyMcPerson}.`, `Don't fall out of sky in your dreamworld, ${PersonnyMcPerson}!`,
'Nighty night!', `I hope tomorrow is a good day for you, ${PersonnyMcPerson}!`, `Have a good sleep, ${PersonnyMcPerson}!`, `I :b:et you a cookie if you actually slept through the night! ${PersonnyMcPerson}`
]
if (message.mentions.members.has('309373272594579456') && !client.isStaff(message.member)) 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 (PasswordArray.some(e=>msgarr.includes(e))) message.reply('Password and other details can be found in <#543494084363288637>');
if (cantRead.some(e=>msgarr.includes(e))) message.reply('https://tenor.com/view/aristocats-george-pen-cap-meticulous-gif-5330931');
if (msgarr.includes('is daggerbot working')) message.reply('https://tenor.com/view/i-still-feel-alive-living-existing-active-singing-gif-14630579');
if (deadChat.some(e=>msgarr.includes(e))) message.reply('https://cdn.discordapp.com/attachments/925589318276382720/1011333656167579849/F57G5ZS.png');
if (msgarr.includes('nawdic') && NawdicBrokeIt.some(e=>msgarr.includes(e)) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Nawdic has done an oopsie*').setImage('https://c.tenor.com/JSj9ie_MD9kAAAAC/kopfsch%C3%BCtteln-an-kopf-fassen-oh-no.gif').setColor(client.config.embedColor)]});
if (MorningArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${MorningPhrases[Math.floor(Math.random()*MorningPhrases.length)]}`);
if (AfternoonArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${AfternoonPhrases[Math.floor(Math.random()*AfternoonPhrases.length)]}`);
if (EveningArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${EveningPhrases[Math.floor(Math.random()*EveningPhrases.length)]}`);
if (NightArray.some(e=>message.content.toLowerCase().startsWith(e)) && message.channelId == GeneralChatID && message.type == 0) message.reply(`${NightPhrases[Math.floor(Math.random()*NightPhrases.length)]}`);
// Failsafe thingy (Toastproof maybe)
if (message.content.startsWith('!!_wepanikfrfr') && client.config.eval.whitelist.includes(message.author.id)) (client.guilds.cache.get(message.guildId) as Discord.Guild).commands.set(client.registry).then(()=>message.reply('How did you manage to lose the commands??? Anyways, it\'s re-registered now.')).catch((e:Error)=>message.reply(`Failed to deploy slash commands:\n\`\`\`${e.message}\`\`\``));
}
}
} }

View File

@ -1,19 +1,19 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, msg:Discord.Message){ run(client:TClient, msg:Discord.Message){
if (!client.config.botSwitches.logs) return; if (!client.config.botSwitches.logs) return;
const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel; const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel;
const disabledChannels = ['548032776830582794', '541677709487505408'] const disabledChannels = [ '548032776830582794', '541677709487505408']
if (msg.guild?.id != client.config.mainServer.id || msg.partial || msg.author.bot || disabledChannels.includes(msg.channelId)) return; if (msg.guild?.id != client.config.mainServer.id || msg.partial || msg.author.bot || disabledChannels.includes(msg.channelId)) return;
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setAuthor({name: `Author: ${msg.author.tag} (${msg.author.id})`, iconURL: `${msg.author.displayAvatarURL()}`}).setTitle('Message deleted').setDescription(`<@${msg.author.id}>\n\`${msg.author.id}\``); const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setAuthor({name: `Author: ${msg.author.tag} (${msg.author.id})`, iconURL: `${msg.author.displayAvatarURL()}`}).setTitle('Message deleted').setDescription(`<@${msg.author.id}>\n\`${msg.author.id}\``);
if (msg.content.length != 0) embed.addFields({name: 'Content', value: `\`\`\`\n${msg.content.slice(0,1000)}\n\`\`\``}); if (msg.content.length != 0) embed.addFields({name: 'Content', value: `\`\`\`\n${msg.content.slice(0,1000)}\n\`\`\``});
embed.addFields( embed.addFields(
{ name: 'Channel', value: `<#${msg.channelId}>` }, { name: 'Channel', value: `<#${msg.channelId}>` },
{ name: 'Sent at', value: `<t:${Math.round(msg.createdTimestamp/1000)}>\n<t:${Math.round(msg.createdTimestamp/1000)}:R>` } { name: 'Sent at', value: `<t:${Math.round(msg.createdTimestamp/1000)}>\n<t:${Math.round(msg.createdTimestamp/1000)}:R>` }
) )
const attachments: Array<string> = []; const attachments: Array<string> = [];
msg.attachments.forEach((x) => attachments.push(x.url)); msg.attachments.forEach((x) => attachments.push(x.url));
channel.send({embeds: [embed], files: attachments}) channel.send({embeds: [embed], files: attachments})
} }
} }

View File

@ -1,10 +1,14 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, messages:Discord.Collection<string, Discord.Message<boolean>>){ run(client:TClient, messages:Discord.Collection<string, Discord.Message<boolean>>){
if (!client.config.botSwitches.logs) return; if (!client.config.botSwitches.logs) return;
if (client.config.mainServer.id != '468835415093411861') return; if (client.config.mainServer.id != '468835415093411861') return;
const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel; const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel;
channel.send({embeds: [new client.embed().setColor(client.config.embedColorRed).setTimestamp().setTitle(`${messages.size} messages were purged`).setDescription(`\`\`\`${messages.map((msgs)=>`${msgs.member?.displayName}: ${msgs.content}`).reverse().join('\n').slice(0,3900)}\`\`\``).addFields({name: 'Channel', value: `<#${messages.first().channel.id}>`})]}) channel.send({embeds: [new client.embed().setColor(client.config.embedColorRed).setTimestamp().setTitle(`${messages.size} messages were purged`)
} .setDescription(`\`\`\`${messages.map((msgs)=>`${msgs.member?.displayName}: ${msgs.content}`).reverse().join('\n').slice(0,3900)}\`\`\``).addFields(
} {name: 'Channel', value: `<#${messages.first().channel.id}>`}
)]
})
}
}

View File

@ -1,13 +1,13 @@
import Discord, { ActionRowBuilder, ButtonBuilder } from 'discord.js'; import Discord, { ActionRowBuilder, ButtonBuilder } from 'discord.js';
import TClient from '../client'; import TClient from '../client';
export default { export default {
async run(client:TClient, oldMsg:Discord.Message, newMsg:Discord.Message){ async run(client:TClient, oldMsg:Discord.Message, newMsg:Discord.Message){
if (!client.config.botSwitches.logs) return; if (!client.config.botSwitches.logs) return;
const disabledChannels = ['548032776830582794', '541677709487505408'] const disabledChannels = ['548032776830582794', '541677709487505408']
if (oldMsg.guild?.id != client.config.mainServer.id || oldMsg.author == null || oldMsg?.author.bot || oldMsg.partial || newMsg.partial || !newMsg.member || disabledChannels.includes(newMsg.channelId)) return; if (oldMsg.guild?.id != client.config.mainServer.id || oldMsg.author == null || oldMsg?.author.bot || oldMsg.partial || newMsg.partial || !newMsg.member || disabledChannels.includes(newMsg.channelId)) return;
const msgarr = newMsg.content.toLowerCase().split(' '); const msgarr = newMsg.content.toLowerCase().split(' ');
if (await client.bannedWords._content.findOne({_id:msgarr}) && (!client.isStaff(newMsg.member))) newMsg.delete(); if (await client.bannedWords._content.findOne({_id:msgarr}) && (!client.isStaff(newMsg.member))) newMsg.delete();
if (newMsg.content === oldMsg.content) return; if (newMsg.content === oldMsg.content) return;
(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [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}>`)], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setStyle(5).setURL(`${oldMsg.url}`).setLabel('Jump to message'))]}); (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [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.length < 1 ? '(Attachment)' : oldMsg.content}\n\`\`\`\nNew content:\n\`\`\`\n${newMsg.content}\`\`\`\nChannel: <#${oldMsg.channelId}>`)], components: [new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder().setStyle(5).setURL(`${oldMsg.url}`).setLabel('Jump to message'))]});
} }
} }

View File

@ -12,14 +12,15 @@ client.on('ready', async()=>{
setInterval(()=>guild.invites.fetch().then(invites=>invites.forEach(inv=>client.invites.set(inv.code, {uses: inv.uses, creator: inv.inviterId}))),300000) setInterval(()=>guild.invites.fetch().then(invites=>invites.forEach(inv=>client.invites.set(inv.code, {uses: inv.uses, creator: inv.inviterId}))),300000)
}); });
if (client.config.botSwitches.registerCommands){ if (client.config.botSwitches.registerCommands){
client.config.whitelistedServers.forEach((guildId)=>(client.guilds.cache.get(guildId) as Discord.Guild).commands.set(client.registry).catch((e:Error)=>{ client.config.whitelistedServers.forEach((guildId)=>(client.guilds.cache.get(guildId) as Discord.Guild).commands.set(client.registry).catch((e:Error)=>{
console.log(`Couldn't register slash commands for ${guildId} because`, e.stack); console.log(`Couldn't register slash commands for ${guildId} because`, e.stack);
(client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send(`Cannot register slash commands for **${client.guilds.cache.get(guildId).name}** (\`${guildId}\`):\n\`\`\`${e.message}\`\`\``) (client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel).send(`Cannot register slash commands for **${client.guilds.cache.get(guildId).name}** (\`${guildId}\`):\n\`\`\`${e.message}\`\`\``)
})) }))
}; };
console.log(`${client.user.tag} has logged into Discord API`); console.log(`${client.user.tag} has logged into Discord API`);
console.log(client.config.botSwitches, client.config.whitelistedServers); console.log(client.config.botSwitches, client.config.whitelistedServers);
(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')}\`\`\``);
console.timeEnd('Startup')
}) })
// Handle errors // Handle errors
@ -49,80 +50,78 @@ setInterval(async()=>{
const verifyURL = MPURL.match(/http|https/); const verifyURL = MPURL.match(/http|https/);
const completedURL_DSS = MPURL+'/feed/dedicated-server-stats.json?code='+MPCode; const completedURL_DSS = MPURL+'/feed/dedicated-server-stats.json?code='+MPCode;
const completedURL_CSG = MPURL+'/feed/dedicated-server-savegame.html?code='+MPCode+'&file=careerSavegame'; const completedURL_CSG = MPURL+'/feed/dedicated-server-savegame.html?code='+MPCode+'&file=careerSavegame';
const FSdss = { const FSdss = {
data: {} as FSData, data: {} as FSData,
fetchResult: '' as string fetchResult: '' as string
}; };
const FScsg = { const FScsg = {
data: {} as FSCareerSavegame, data: {} as FSCareerSavegame,
fetchResult: '' as string fetchResult: '' as string
}; };
if (!verifyURL) return msg.edit({content: '*Detected an invalid IP.*', embeds: null}) if (!verifyURL) return msg.edit({content: '*Detected an invalid IP.*', embeds: null})
async function serverData(client:TClient, URL: string){ async function serverData(client:TClient, URL: string){
return await client.axios.get(URL, {timeout: 4000, maxContentLength: Infinity, headers: {'User-Agent': `Daggerbot/axios ${client.axios.VERSION}`}}).catch((error:Error)=>error.message) return await client.axios.get(URL, {timeout: 4000, maxContentLength: Infinity, headers: {'User-Agent': `Daggerbot/axios ${client.axios.VERSION}`}}).catch((error:Error)=>error.message)
} }
await Promise.all([serverData(client, completedURL_DSS), serverData(client, completedURL_CSG)]).then(function(results){ await Promise.all([serverData(client, completedURL_DSS), serverData(client, completedURL_CSG)]).then(function(results){
if (typeof results[0] == 'string'){ if (typeof results[0] == 'string'){
FSdss.fetchResult = `DagMP DSS failed, ${results[0]}`; FSdss.fetchResult = `DagMP DSS failed, ${results[0]}`;
embed.addFields({name: 'DSS Status', value: results[0]}) embed.addFields({name: 'DSS Status', value: results[0]})
} else if (results[0].status != 200){ } else if (results[0].status != 200){
FSdss.fetchResult = `DagMP DSS failed with ${results[0].status + ' ' + results[0].statusText}`; FSdss.fetchResult = `DagMP DSS failed with ${results[0].status + ' ' + results[0].statusText}`;
embed.addFields({name: 'DSS Status', value: results[0].status + ' ' + results[0].statusText}) embed.addFields({name: 'DSS Status', value: results[0].status + ' ' + results[0].statusText})
} else FSdss.data = results[0].data as FSData } else FSdss.data = results[0].data as FSData
if (typeof results[1] == 'string'){ if (typeof results[1] == 'string'){
FScsg.fetchResult = `DagMP CSG failed, ${results[1]}`; FScsg.fetchResult = `DagMP CSG failed, ${results[1]}`;
embed.addFields({name: 'CSG Status', value: results[1]}) embed.addFields({name: 'CSG Status', value: results[1]})
} else if (results[1].status != 200){ } else if (results[1].status != 200){
if (results[1].status == 204) embed.setImage('https://http.cat/204'); if (results[1].status == 204) embed.setImage('https://http.cat/204');
FScsg.fetchResult = `DagMP CSG failed with ${results[1].status + ' ' + results[1].statusText}`; FScsg.fetchResult = `DagMP CSG failed with ${results[1].status + ' ' + results[1].statusText}`;
embed.addFields({name: 'CSG Status', value: results[1].status + ' ' + results[1].statusText}) embed.addFields({name: 'CSG Status', value: results[1].status + ' ' + results[1].statusText})
} else FScsg.data = client.xjs.xml2js(results[1].data,{compact:true,spaces:2}).careerSavegame as FSCareerSavegame; } else FScsg.data = client.xjs.xml2js(results[1].data,{compact:true,spaces:2}).careerSavegame as FSCareerSavegame;
}).catch((error)=>console.log(error)) }).catch((error)=>console.log(error))
if (FSdss.fetchResult.length != 0){ if (FSdss.fetchResult.length != 0){
error = true; error = true;
console.log(client.logTime(), FSdss.fetchResult); console.log(client.logTime(), FSdss.fetchResult);
} }
if (FScsg.fetchResult.length != 0){ if (FScsg.fetchResult.length != 0){
error = true; error = true;
console.log(client.logTime(), FScsg.fetchResult); console.log(client.logTime(), FScsg.fetchResult);
} }
if (error) { // Blame RedRover and Nawdic if (error) { // Blame RedRover and Nawdic
embed.setTitle('Host is not responding').setColor(client.config.embedColorRed); embed.setTitle('Host is not responding').setColor(client.config.embedColorRed);
msg.edit({content: null, embeds: [embed]}) msg.edit({content: null, embeds: [embed]})
return; return;
} }
const DB = JSON.parse(fs.readFileSync(__dirname + '/database/MPPlayerData.json', {encoding: 'utf8'})); const DB = JSON.parse(fs.readFileSync(__dirname + '/database/MPPlayerData.json', {encoding: 'utf8'}));
DB.push(FSdss.data.slots.used) DB.push(FSdss.data.slots.used)
fs.writeFileSync(__dirname + '/database/MPPlayerData.json', JSON.stringify(DB)) fs.writeFileSync(__dirname + '/database/MPPlayerData.json', JSON.stringify(DB))
// Number format function
function formatNumber(number: any, digits: any, icon: any){
var n = Number(number)
return n.toLocaleString(undefined, {minimumFractionDigits: digits})+icon
} // Temporary workaround for fresh save.
const slotSystem = isNaN(Number(FScsg.data.slotSystem?._attributes.slotUsage)) == true ? 'Unavailable' : Number(FScsg.data.slotSystem?._attributes.slotUsage).toLocaleString('en-US');
const timeScale = isNaN(Number(FScsg.data.settings?.timeScale._text)) == true ? 'Unavailable' : formatNumber(Number(FScsg.data.settings?.timeScale._text), 0, 'x');
if (FSdss.data.server.name.length == 0){ // Number format function
embed.setTitle('The server seems to be offline.').setColor(client.config.embedColorRed); function formatNumber(number: any, digits: any, icon: any){
msg.edit({content: 'This embed will resume when server is back online.', embeds: [embed]}) var n = Number(number)
} else { return n.toLocaleString(undefined, {minimumFractionDigits: digits})+icon
const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Server details').addFields( } // Temporary workaround for fresh save.
{name: 'Current Map', value: `${FSdss.data.server.mapName.length == 0 ? '\u200b' : FSdss.data.server.mapName}`, inline: true}, const slotSystem = isNaN(Number(FScsg.data.slotSystem?._attributes.slotUsage)) == true ? 'Unavailable' : Number(FScsg.data.slotSystem?._attributes.slotUsage).toLocaleString('en-US');
{name: 'Version', value: `${FSdss.data.server.version.length == 0 ? '\u200b' : FSdss.data.server.version}`, inline: true}, const timeScale = isNaN(Number(FScsg.data.settings?.timeScale._text)) == true ? 'Unavailable' : formatNumber(Number(FScsg.data.settings?.timeScale._text), 0, 'x');
{name: 'In-game Time', value: `${('0' + Math.floor((FSdss.data.server.dayTime/3600/1000))).slice(-2)}:${('0' + Math.floor((FSdss.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true},
{name: 'Slot Usage', value: `${slotSystem}`, inline: true}, if (FSdss.data.server.name.length == 0){
{name: 'Timescale', value: `${timeScale}`, inline: true} embed.setTitle('The server seems to be offline.').setColor(client.config.embedColorRed);
); msg.edit({content: 'This embed will resume when server is back online.', embeds: [embed]})
FSdss.data.slots.players.filter((x)=>x.isUsed !== false).forEach(player=>{ } else {
Players.push(`**${player.name} ${player.isAdmin ? '| admin' : ''}**\nFarming for ${(Math.floor(player.uptime/60))} hr & ${('' + (player.uptime % 60)).slice(-2)} min`) const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Server details').addFields(
}) {name: 'Current Map', value: `${FSdss.data.server.mapName.length == 0 ? '\u200b' : FSdss.data.server.mapName}`, inline: true},
embed.setDescription(`${FSdss.data.slots.used == 0 ? '*No players online*' : Players.join('\n\n')}`).setTitle(FSdss.data.server.name).setColor(client.config.embedColor) {name: 'Version', value: `${FSdss.data.server.version.length == 0 ? '\u200b' : FSdss.data.server.version}`, inline: true},
embed.setAuthor({name: `${FSdss.data.slots.used}/${FSdss.data.slots.capacity}`}); {name: 'In-game Time', value: `${('0' + Math.floor((FSdss.data.server.dayTime/3600/1000))).slice(-2)}:${('0' + Math.floor((FSdss.data.server.dayTime/60/1000)%60)).slice(-2)}`, inline: true},
msg.edit({content: 'This embed updates every minute.', embeds: [embed1, embed]}) {name: 'Slot Usage', value: `${slotSystem}`, inline: true},
} {name: 'Timescale', value: `${timeScale}`, inline: true}
);
FSdss.data.slots.players.filter((x)=>x.isUsed !== false).forEach(player=>Players.push(`**${player.name} ${player.isAdmin ? '| admin' : ''}**\nFarming for ${(Math.floor(player.uptime/60))} hr & ${('' + (player.uptime % 60)).slice(-2)} min`))
embed.setDescription(`${FSdss.data.slots.used == 0 ? '*No players online*' : Players.join('\n\n')}`).setTitle(FSdss.data.server.name).setColor(client.config.embedColor)
embed.setAuthor({name: `${FSdss.data.slots.used}/${FSdss.data.slots.capacity}`});
msg.edit({content: 'This embed updates every minute.', embeds: [embed1, embed]})
}
}, 60000) }, 60000)
// YouTube Upload notification // YouTube Upload notification
@ -133,27 +132,25 @@ setInterval(async()=>{
// Event loop for punishments and daily msgs // Event loop for punishments and daily msgs
setInterval(async()=>{ setInterval(async()=>{
const now = Date.now(); const now = Date.now();
const lrsStart = client.config.LRSstart; const lrsStart = client.config.LRSstart;
const punishments = await client.punishments._content.find({}); const punishments = await client.punishments._content.find({});
punishments.filter(x=>x.endTime && x.endTime<= now && !x.expired).forEach(async punishment=>{ punishments.filter(x=>x.endTime && x.endTime<= now && !x.expired).forEach(async punishment=>{
console.log(client.logTime(), `${punishment.member}\'s ${punishment.type} should expire now`); console.log(client.logTime(), `${punishment.member}\'s ${punishment.type} should expire now`);
const unpunishResult = await client.punishments.removePunishment(punishment._id, client.user.id, 'Time\'s up!'); const unpunishResult = await client.punishments.removePunishment(punishment._id, client.user.id, 'Time\'s up!');
console.log(client.logTime(), unpunishResult); console.log(client.logTime(), unpunishResult);
}); });
const formattedDate = Math.floor((now - lrsStart)/1000/60/60/24); const formattedDate = Math.floor((now - lrsStart)/1000/60/60/24);
const dailyMsgs = JSON.parse(fs.readFileSync(__dirname + '/database/dailyMsgs.json', {encoding: 'utf8'})) const dailyMsgs = JSON.parse(fs.readFileSync(__dirname + '/database/dailyMsgs.json', {encoding: 'utf8'}))
if (!dailyMsgs.some((x:Array<number>)=>x[0] === formattedDate)){ if (!dailyMsgs.some((x:Array<number>)=>x[0] === formattedDate)){
let total = (await client.userLevels._content.find({})).reduce((a,b)=>a + b.messages, 0); // sum of all users let total = (await client.userLevels._content.find({})).reduce((a,b)=>a + b.messages, 0); // sum of all users
const yesterday = dailyMsgs.find((x:Array<number>)=>x[0] === formattedDate - 1); const yesterday = dailyMsgs.find((x:Array<number>)=>x[0] === formattedDate - 1);
if (total < yesterday){ // messages went down. if (total < yesterday) total = yesterday // messages went down.
total = yesterday dailyMsgs.push([formattedDate, total]);
} fs.writeFileSync(__dirname + '/database/dailyMsgs.json', JSON.stringify(dailyMsgs))
dailyMsgs.push([formattedDate, total]); console.log(client.logTime(), `Pushed [${formattedDate}, ${total}] to dailyMsgs`);
fs.writeFileSync(__dirname + '/database/dailyMsgs.json', JSON.stringify(dailyMsgs)) client.guilds.cache.get(client.config.mainServer.id).commands.fetch().then((commands)=>(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send(`:pencil: Pushed \`[${formattedDate}, ${total}]\` to </rank leaderboard:${commands.find(x=>x.name == 'rank').id}>`))
console.log(client.logTime(), `Pushed [${formattedDate}, ${total}] to dailyMsgs`); }
client.guilds.cache.get(client.config.mainServer.id).commands.fetch().then((commands)=>(client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send(`:pencil: Pushed \`[${formattedDate}, ${total}]\` to </rank leaderboard:${commands.find(x=>x.name == 'rank').id}>`))
}
}, 5000) }, 5000)

View File

@ -1,187 +1,187 @@
import Discord from 'discord.js'; import Discord from 'discord.js';
export interface UserLevels { export interface UserLevels {
messages: number, messages: number,
level: number level: number
} }
export interface formatTimeOpt { export interface formatTimeOpt {
longNames: boolean, longNames: boolean,
commas: boolean commas: boolean
} }
export interface punOpt { export interface punOpt {
time?: string, time?: string,
reason?: string, reason?: string,
interaction?: Discord.ChatInputCommandInteraction<"cached"> interaction?: Discord.ChatInputCommandInteraction<"cached">
} }
export interface repeatedMessages { export interface repeatedMessages {
[key:string]: {data: Discord.Collection<number,{cont:number,ch:string}>, timeout: NodeJS.Timeout} [key:string]: {data: Discord.Collection<number,{cont:number,ch:string}>, timeout: NodeJS.Timeout}
} }
export interface Punishment { export interface Punishment {
_id: number; _id: number;
type: string; type: string;
member: string; member: string;
moderator: string; moderator: string;
expired?: boolean; expired?: boolean;
time: number; time: number;
reason: string; reason: string;
endTime?: number; endTime?: number;
cancels?: number; cancels?: number;
duration?: number; duration?: number;
} }
export interface DSS_serverName { export interface DSS_serverName {
data: FSData data: FSData
} }
export interface FSData { export interface FSData {
server: FSServer, server: FSServer,
slots: FSslots slots: FSslots
} }
export interface FSServer { export interface FSServer {
dayTime: number, dayTime: number,
game: string, game: string,
mapName: string, mapName: string,
mapSize: number, mapSize: number,
mapOverviewFilename: string, mapOverviewFilename: string,
money: number, money: number,
name: string, name: string,
server: string, server: string,
version: string version: string
} }
export interface FSslots { export interface FSslots {
capacity: number, capacity: number,
used: number, used: number,
players: Array<FSPlayers> players: Array<FSPlayers>
} }
export interface FSPlayers { export interface FSPlayers {
isUsed: boolean, isUsed: boolean,
isAdmin: boolean, isAdmin: boolean,
uptime: number, uptime: number,
name: string name: string
} }
export interface FSCareerSavegame { export interface FSCareerSavegame {
settings: FSCareerSavegameSettings, settings: FSCareerSavegameSettings,
statistics: FSCareerSavegameStatistics, statistics: FSCareerSavegameStatistics,
slotSystem: FSCareerSavegameSlotSystem slotSystem: FSCareerSavegameSlotSystem
} }
export interface FSCareerSavegameSettings { export interface FSCareerSavegameSettings {
savegameName: XMLText, savegameName: XMLText,
creationDate: XMLText, creationDate: XMLText,
mapId: XMLText, mapId: XMLText,
mapTitle: XMLText, mapTitle: XMLText,
saveDataFormatted: XMLText, saveDataFormatted: XMLText,
saveDate: XMLText, saveDate: XMLText,
resetVehicles: XMLText, resetVehicles: XMLText,
trafficeEnabled: XMLText, trafficeEnabled: XMLText,
stopAndGoBraking: XMLText, stopAndGoBraking: XMLText,
trailerFillLimit: XMLText, trailerFillLimit: XMLText,
automaticMotorStartEnabled: XMLText, automaticMotorStartEnabled: XMLText,
growthMode: XMLText, growthMode: XMLText,
fixedSeasonalVisuals: XMLText, fixedSeasonalVisuals: XMLText,
plannedDaysPerPeriod: XMLText, plannedDaysPerPeriod: XMLText,
fruitDestruction: XMLText, fruitDestruction: XMLText,
plowingRequiredEnabled: XMLText, plowingRequiredEnabled: XMLText,
stonesEnabled: XMLText, stonesEnabled: XMLText,
weedsEnabled: XMLText, weedsEnabled: XMLText,
limeRequired: XMLText, limeRequired: XMLText,
isSnowEnabled: XMLText, isSnowEnabled: XMLText,
fuelUsage: XMLText, fuelUsage: XMLText,
helperBuyFuel: XMLText, helperBuyFuel: XMLText,
helperBuySeeds: XMLText, helperBuySeeds: XMLText,
helperSlurrySource: XMLText, helperSlurrySource: XMLText,
helperManureSource: XMLText, helperManureSource: XMLText,
densityMapRevision: XMLText, densityMapRevision: XMLText,
terrainTextureRevision: XMLText, terrainTextureRevision: XMLText,
terrainLodTextureRevision: XMLText, terrainLodTextureRevision: XMLText,
splitShapesRevision: XMLText, splitShapesRevision: XMLText,
tipCollisionRevision: XMLText, tipCollisionRevision: XMLText,
placementCollisionRevision: XMLText, placementCollisionRevision: XMLText,
navigationCollisionRevision: XMLText, navigationCollisionRevision: XMLText,
mapDensityMapRevision: XMLText, mapDensityMapRevision: XMLText,
mapTerrainTextureRevision: XMLText, mapTerrainTextureRevision: XMLText,
mapTerrainLodTextureRevision: XMLText, mapTerrainLodTextureRevision: XMLText,
mapSplitShapesRevision: XMLText, mapSplitShapesRevision: XMLText,
mapTipCollisionRevision: XMLText, mapTipCollisionRevision: XMLText,
mapPlacementCollisionRevision: XMLText, mapPlacementCollisionRevision: XMLText,
mapNavigationCollisionRevision: XMLText, mapNavigationCollisionRevision: XMLText,
difficulty: XMLText, difficulty: XMLText,
economicDifficulty: XMLText, economicDifficulty: XMLText,
dirtInterval: XMLText, dirtInterval: XMLText,
timeScale: XMLText, timeScale: XMLText,
autoSaveInterval: XMLText autoSaveInterval: XMLText
} }
export interface FSCareerSavegameStatistics { export interface FSCareerSavegameStatistics {
money: XMLText, money: XMLText,
playTime: XMLText playTime: XMLText
} }
export interface FSCareerSavegameSlotSystem { export interface FSCareerSavegameSlotSystem {
_attributes: slotUsage _attributes: slotUsage
} }
interface slotUsage { interface slotUsage {
slotUsage: string slotUsage: string
} }
interface XMLText { interface XMLText {
_text: string _text: string
} }
export interface Tokens { export interface Tokens {
main: string main: string
beta: string beta: string
toast: string toast: string
tae: string tae: string
webhook_url: string webhook_url: string
webhook_url_test: string webhook_url_test: string
mongodb_uri: string mongodb_uri: string
mongodb_uri_dev: string mongodb_uri_dev: string
} }
export interface Config { export interface Config {
embedColor: Discord.ColorResolvable, embedColor: Discord.ColorResolvable,
embedColorGreen: Discord.ColorResolvable, embedColorGreen: Discord.ColorResolvable,
embedColorYellow: Discord.ColorResolvable, embedColorYellow: Discord.ColorResolvable,
embedColorRed: Discord.ColorResolvable, embedColorRed: Discord.ColorResolvable,
embedColorBCA: Discord.ColorResolvable, embedColorBCA: Discord.ColorResolvable,
embedColorXmas: Discord.ColorResolvable, embedColorXmas: Discord.ColorResolvable,
LRSstart: number, LRSstart: number,
whitelistedServers: Array<string>, whitelistedServers: Array<string>,
botSwitches: botSwitches, botSwitches: botSwitches,
botPresence: Discord.PresenceData, botPresence: Discord.PresenceData,
eval: Eval, eval: Eval,
mainServer: mainServer mainServer: mainServer
} }
interface botSwitches { interface botSwitches {
registerCommands: boolean, registerCommands: boolean,
commands: boolean, commands: boolean,
logs: boolean, logs: boolean,
automod: boolean, automod: boolean,
mpstats: boolean, mpstats: boolean,
autores: boolean autores: boolean
} }
interface Eval { interface Eval {
allowed: boolean, allowed: boolean,
whitelist: Array<string> whitelist: Array<string>
} }
interface mainServer { interface mainServer {
id: string, id: string,
staffRoles: Array<string>, staffRoles: Array<string>,
roles: mainServerRoles, roles: mainServerRoles,
channels: mainServerChannels channels: mainServerChannels
} }
interface mainServerRoles { interface mainServerRoles {
admin: string, admin: string,
bottech: string, bottech: string,
dcmod: string, dcmod: string,
mpmanager: string, mpmanager: string,
mpmod: string, mpmod: string,
vtcmanager: string, vtcmanager: string,
vtcstaff: string, vtcstaff: string,
ytmod: string, ytmod: string,
mphelper: string, mphelper: string,
mpplayer: string, mpplayer: string,
vtcmember: string vtcmember: string
} }
interface mainServerChannels { interface mainServerChannels {
console: string, console: string,
errors: string, errors: string,
thismeanswar: string, thismeanswar: string,
bot_status: string, bot_status: string,
logs: string, logs: string,
welcome: string, welcome: string,
botcommands: string botcommands: string
} }