1
0
mirror of https://github.com/toast-ts/Daggerbot-TS.git synced 2024-12-27 04:15:38 -05:00

Add reliability to DatabaseServer and logging

This commit is contained in:
toast-ts 2023-09-30 07:27:32 +10:00
parent e64faac2ea
commit 30078c34fa
15 changed files with 64 additions and 59 deletions

View File

@ -95,7 +95,7 @@ export default class TClient extends Discord.Client {
}
async init(){
console.time('Startup');
DatabaseServer.connect(this);
DatabaseServer.init();
this.login(this.tokens.main);
for (const file of readdirSync('dist/events')){
const eventFile = await import(`./events/${file}`);
@ -116,5 +116,4 @@ export default class TClient extends Discord.Client {
}
isStaff = (guildMember:Discord.GuildMember)=>this.config.mainServer.staffRoles.map((x: string)=>this.config.mainServer.roles[x]).some((x: string)=>guildMember.roles.cache.has(x));
youNeedRole = (interaction:Discord.CommandInteraction, role:string)=>interaction.reply(`This command is restricted to <@&${this.config.mainServer.roles[role]}>`);
logTime = ()=>`[${this.moment().format('DD/MM/YY HH:mm:ss')}]`;
}

View File

@ -35,16 +35,3 @@ export default {
.setDescription('The expression to be calculated')
.setRequired(true))
}
// Copilot conversation:
//Q: Why do we need to replace the string?
//A: Because eval() is dangerous and can run any code
// So we need to make sure that the string is a math expression
// and nothing else
//Q: If we receive a string like "1+1; console.log('Hello World')"
// will it run the console.log()?
//A: No, because we are replacing all characters that are not
// numbers, operators, parenthesis, etc. So it will only run
// the math expression

View File

@ -4,7 +4,7 @@ import path from 'node:path';
import canvas from 'canvas';
import FormatPlayer from '../helpers/FormatPlayer.js';
import MessageTool from '../helpers/MessageTool.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
import {readFileSync} from 'node:fs';
import {FSData} from '../typings/interfaces';
@ -197,18 +197,18 @@ export default {
const Url = await client.MPServer._content.findById(interaction.guildId);
if (Url[serverSelector].ip && Url[serverSelector].code) return interaction.reply(Url[serverSelector].ip+'/feed/dedicated-server-stats.json?code='+Url[serverSelector].code)
} catch(err){
console.log(client.logTime(), LogPrefix('MPDB'), err);
Logger.forwardToConsole('error', 'MPDB', err);
interaction.reply(`\`\`\`${err}\`\`\``)
}
} else {
if (!address.match(/dedicated-server-stats/)) return interaction.reply('The URL does not match `dedicated-server-stats.xml`');
const newURL = address.replace('xml','json').split('/feed/dedicated-server-stats.json?code=');
try {
console.log(client.logTime(), LogPrefix('MPDB'), `${serverSelector}\'s URL for ${interaction.guild.name} has been updated by ${interaction.member.displayName} (${interaction.member.id})`);
Logger.forwardToConsole('log', 'MPDB', `${serverSelector}\'s URL for ${interaction.guild.name} has been updated by ${interaction.member.displayName} (${interaction.member.id})`);
const affected = await client.MPServer._content.findByIdAndUpdate({_id: interaction.guildId}, {$set: {[serverSelector]: {ip: newURL[0], code: newURL[1]}}})
if (affected) return interaction.reply('URL successfully updated.')
} catch (err) {
console.log(client.logTime(), LogPrefix('MPDB'), `${serverSelector}\'s URL for ${interaction.guild.name} has been created by ${interaction.member.displayName} (${interaction.member.id})`);
Logger.forwardToConsole('log', 'MPDB', `${serverSelector}\'s URL for ${interaction.guild.name} has been created by ${interaction.member.displayName} (${interaction.member.id})`);
await client.MPServer._content.create({_id: interaction.guildId, [serverSelector]: { ip: newURL[0], code: newURL[1] }})
.then(()=>interaction.reply('This server doesn\'t have any data in the database, therefore I have created it for you.'))
.catch((err:Error)=>interaction.reply(`I got hit by a flying brick while trying to populate the server data:\n\`\`\`${err.message}\`\`\``))

View File

@ -1,6 +1,6 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
@ -10,7 +10,7 @@ export default {
if (punishment.expired) return interaction.reply('This case has been overwritten by another case.');
const reason = interaction.options.getString('reason') ?? 'Reason unspecified';
await client.punishments.removePunishment(punishment.id, interaction.user.id, reason, interaction);
console.log(client.logTime(), LogPrefix('UnpunishmentLog'), `Case #${interaction.options.getInteger('case_id')} was used in /${interaction.commandName} for ${reason}`);
Logger.forwardToConsole('log', 'UnpunishmentLog', `Case #${interaction.options.getInteger('case_id')} was used in /${interaction.commandName} for ${reason}`);
(client.channels.cache.get(client.config.mainServer.channels.punishment_log) as Discord.TextChannel).send({embeds:[new client.embed().setColor(client.config.embedColor).setTitle('Unpunishment Log').setDescription(`Case #${interaction.options.getInteger('case_id')} was used in \`/${interaction.commandName}\` for \`${reason}\``).setTimestamp()]});
},
data: new Discord.SlashCommandBuilder()

View File

@ -1,11 +1,13 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import Logger from '../helpers/Logger.js';
export default {
async run(client:TClient, interaction:Discord.BaseInteraction){
if (!interaction.inGuild() || !interaction.inCachedGuild()) return;
if (interaction.isChatInputCommand()){
const commandFile = client.commands.get(interaction.commandName);
console.log(client.logTime(), `${interaction.user.username} used /${interaction.commandName} ${interaction.options.getSubcommandGroup(false) ?? ''} ${interaction.options.getSubcommand(false) ?? ''} in #${interaction.channel.name}`);
Logger.forwardToConsole('log', 'InteractionLog', `${interaction.user.username} used /${interaction.commandName} ${interaction.options.getSubcommandGroup(false) ?? ''} ${interaction.options.getSubcommand(false) ?? ''} in #${interaction.channel.name}`);
if (!client.config.botSwitches.commands && !client.config.whitelist.includes(interaction.user.id)) return interaction.reply({content: `I am currently operating in development mode.\nPlease notify <@${client.config.whitelist[0]}> if this is a mistake.`, ephemeral: true});
if (commandFile){
try{
@ -43,7 +45,7 @@ export default {
interaction.member.roles.add(RoleID, 'Button Role');
interaction.reply({content: `You have been added to <@&${RoleID}>`, ephemeral: true})
}
} else console.log(client.logTime(), `Button pressed at ${interaction.message.url}`);
} else Logger.forwardToConsole('log', 'InteractionLog', `Button has been pressed at ${interaction.message.url}`);
}
}
}

View File

@ -2,7 +2,7 @@ import Discord from 'discord.js';
import TClient from '../client.js';
import Response from '../funcs/ResponseModule.js';
import CmdTrigger from '../funcs/CmdModule.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
import Automoderator from '../funcs/Automod.js';
export default {
@ -15,7 +15,7 @@ export default {
const automodFailReason = 'msg got possibly deleted by another bot.';
if (await client.bannedWords._content.findById(Automoderator.scanMsg(message))/* && !Whitelist.includes(message.channelId) */){
automodded = true;
message.delete().catch(()=>console.log(LogPrefix('AUTOMOD-BANNEDWORDS'), automodFailReason));
message.delete().catch(()=>Logger.forwardToConsole('log', 'AUTOMOD-BANNEDWORDS', automodFailReason));
message.channel.send('That word isn\'t allowed here.').then(x=>setTimeout(()=>x.delete(), 10000));
await Automoderator.repeatedMessages(client, message, 30000, 3, 'bw', '30m', 'Constant swearing');
} else if (message.content.toLowerCase().includes('discord.gg/') && !client.isStaff(message.member as Discord.GuildMember)){
@ -23,7 +23,7 @@ export default {
const validInvite = await client.fetchInvite(url).catch(()=>undefined);
if (validInvite && validInvite.guild?.id !== client.config.mainServer.id){
automodded = true;
message.delete().catch(()=>console.log(LogPrefix('AUTOMOD-ADVERT'), automodFailReason));
message.delete().catch(()=>Logger.forwardToConsole('log', 'AUTOMOD-ADVERT', automodFailReason));
message.channel.send('Please don\'t advertise other Discord servers.').then(x=>setTimeout(()=>x.delete(), 15000));
await Automoderator.repeatedMessages(client, message, 60000, 2, 'adv', '1h', 'Discord advertisement');
}

View File

@ -1,13 +1,13 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
export default {
run(client:TClient, msg:Discord.Message){
if (!client.config.botSwitches.logs) return;
const disabledChannels = ['548032776830582794', '541677709487505408', '949380187668242483']
if (msg.guild?.id != client.config.mainServer.id || msg.partial || msg.author.bot || disabledChannels.includes(msg.channelId)) return;
if (Discord.DiscordAPIError.name === '10008') return console.log(client.logTime(), LogPrefix('MsgDelete'), 'Caught an unexpected error returned by Discord API. (Unknown Message)');
if (Discord.DiscordAPIError.name === '10008') return Logger.forwardToConsole('log', 'MsgDelete', 'Caught an unexpected error returned by Discord API. (Unknown Message)');
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setAuthor({name: `Author: ${msg.author.username} (${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\`\`\``});
embed.addFields(

View File

@ -1,14 +1,24 @@
import TClient from '../client';
import mongoose from 'mongoose';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
import {readFileSync} from 'node:fs';
import {Tokens} from '../typings/interfaces';
const tokens:Tokens = JSON.parse(readFileSync('src/tokens.json', 'utf-8'));
const connection:mongoose.Connection = mongoose.connection;
export default class DatabaseServer {
static connect(client: TClient) {
const Logger = (logType:'log'|'error', msg:string)=>console[logType](client.logTime(), LogPrefix('DATABASE'), msg);
protected static eventManager() {
let dbPrefix = 'Database';
connection
.on('connected', ()=>Logger.forwardToConsole('log', dbPrefix, 'Connection to MongoDB has been established'))
.on('disconnected', ()=>Logger.forwardToConsole('log', dbPrefix, 'Connection to MongoDB has been lost'))
.on('close', ()=>Logger.forwardToConsole('log', dbPrefix, 'MongoDB has closed the connection'))
.on('all', ()=>Logger.forwardToConsole('log', dbPrefix, 'Successfully established a connection to all members'))
.on('fullsetup', ()=>Logger.forwardToConsole('log', dbPrefix, 'Successfully established a connection to Primary server & atleast one member'))
.on('error', (err:mongoose.Error)=>Logger.forwardToConsole('error', dbPrefix, `Encountered an error in MongoDB: ${err.message}`))
}
protected static connect() {
connection.set('strictQuery', true);
connection.openUri(client.tokens.mongodb_uri, {
connection.openUri(tokens.mongodb_uri, {
replicaSet: 'toastyy',
autoIndex: true,
authMechanism: 'SCRAM-SHA-256',
@ -18,16 +28,10 @@ export default class DatabaseServer {
socketTimeoutMS: 30000,
tls: false,
family: 4
});
connection
.on('connecting', ()=>Logger('log','Establishing connection to MongoDB'))
.on('connected', ()=>Logger('log','Connection to MongoDB has been established'))
.on('disconnecting', ()=>Logger('log','Disconnecting from MongoDB'))
.on('disconnected', ()=>Logger('log','Disconnected from MongoDB'))
.on('close', ()=>Logger('log','MongoDB has closed the connection'))
.on('all', ()=>Logger('log','Successfully established a connection to all members'))
.on('fullsetup', ()=>Logger('log','Successfully established a connection to Primary server & atleast one member'))
.on('error', (err:mongoose.Error)=>Logger('error',`Encountered an error in MongoDB: ${err.message}`));
})
}
static init() {
this.connect();
this.eventManager();
}
}

View File

@ -5,7 +5,7 @@ interface TServer {
import Discord from 'discord.js';
import TClient from '../client.js';
import FormatPlayer from '../helpers/FormatPlayer.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
import {writeFileSync, readFileSync} from 'node:fs';
import {FSPlayer, FSData, FSCareerSavegame} from '../typings/interfaces';
@ -23,7 +23,7 @@ export default async(client:TClient, Channel:string, Message:string, Server:TSer
const hitCSG = await fetch(Server.ip+'/feed/dedicated-server-savegame.html?code='+Server.code+'&file=careerSavegame', sessionInit).then(async r=>(client.xjs.xml2js(await r.text(), {compact: true}) as any).careerSavegame as FSCareerSavegame);
if (!hitDSS ?? !hitCSG){
if (hitDSS && !hitDSS.slots) return console.log(LogPrefix('MPModule'), `DSS failed with unknown slots table for ${client.MPServerCache[ServerName].name}`);
if (hitDSS && !hitDSS.slots) return Logger.forwardToConsole('log', 'MPModule', `DSS failed with unknown slots table for ${client.MPServerCache[ServerName].name}`);
else return msg.edit({embeds: [serverErrorEmbed]});
}
@ -51,7 +51,7 @@ export default async(client:TClient, Channel:string, Message:string, Server:TSer
const serverLog = client.channels.resolve(client.config.mainServer.channels.fs_server_log) as Discord.TextChannel;
const playersOnServer = hitDSS.slots?.players.filter(x=>x.isUsed);
const playersInCache = client.MPServerCache[ServerName].players;
if (!playersOnServer ?? playersOnServer === undefined) return console.log(LogPrefix('MPModule'), 'Empty array, ignoring...'); // For the love of god, stop throwing errors everytime.
if (!playersOnServer ?? playersOnServer === undefined) return Logger.forwardToConsole('log', 'MPModule', 'Array is empty, ignoring...'); // For the love of god, stop throwing errors everytime.
playersOnServer.forEach(player=>playerData.push(`**${player.name}${FormatPlayer.decoratePlayerIcons(player)}**\nFarming for ${FormatPlayer.uptimeFormat(player.uptime)}`));
// Player leaving
@ -88,7 +88,7 @@ export default async(client:TClient, Channel:string, Message:string, Server:TSer
}
} catch(err) {
msg.edit({content: err.message, embeds: [serverErrorEmbed]});
console.log(client.logTime(), LogPrefix('MPModule'),`Failed to make a request for ${ServerName}:`, err.message)
Logger.forwardToConsole('log', 'MPModule', `Failed to make a request for ${ServerName}: ${err.message}`);
}
}
HITALL();

View File

@ -1,6 +1,6 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
export default async(client:TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string)=>{
if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, 'dcmod');
@ -10,7 +10,7 @@ export default async(client:TClient, interaction: Discord.ChatInputCommandIntera
const GuildMember = interaction.options.getMember('member') ?? undefined;
const User = interaction.options.getUser('member', true);
console.log(client.logTime(), LogPrefix('PunishmentLog'), `${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in /${interaction.commandName} for ${reason}`);
Logger.forwardToConsole('log', 'PunishmentLog', `${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in /${interaction.commandName} for ${reason}`);
(client.channels.cache.get(client.config.mainServer.channels.punishment_log) as Discord.TextChannel).send({embeds:[new client.embed().setColor(client.config.embedColor).setAuthor({name: interaction?.user?.username, iconURL: interaction?.user?.displayAvatarURL({size:2048})}).setTitle('Punishment Log').setDescription(`${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(client.punishments.type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in \`/${interaction.commandName}\` for \`${reason}\``).setTimestamp()]});
if (interaction.user.id === User.id) return interaction.reply(`You cannot ${type} yourself.`);
if (!GuildMember && type != 'unban') return interaction.reply(`You cannot ${type} someone who is not in the server.`);

View File

@ -1,6 +1,6 @@
import {TextChannel} from 'discord.js';
import TClient from '../client.js';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
import MessageTool from '../helpers/MessageTool.js';
export default async(client: TClient, YTChannelID: string, YTChannelName: string, DiscordChannelID: string, DiscordRoleID: string)=>{
@ -8,7 +8,7 @@ export default async(client: TClient, YTChannelID: string, YTChannelName: string
try {
await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {signal: AbortSignal.timeout(8000), headers: {'User-Agent': 'Daggerbot - Notification/undici'}}).then(async xml=>Data = client.xjs.xml2js(await xml.text(), {compact: true}))
} catch(err){
console.log(client.logTime(), LogPrefix('YTModule'), `Failed to fetch "${YTChannelName}" from YouTube`)
Logger.forwardToConsole('log', 'YTModule', `Failed to fetch "${YTChannelName}" from YouTube`)
}
if (!Data) return;

View File

@ -1 +0,0 @@
export default (prefix:string)=>{ return `[${prefix}]` };

13
src/helpers/Logger.ts Normal file
View File

@ -0,0 +1,13 @@
import moment from 'moment';
export default class Logger {
static logTime() {
return `[${moment().format('DD/MM/YY HH:mm:ss')}]`;
}
static logPrefix(prefix:string) {
return `[${prefix}]`;
}
static forwardToConsole(logType:'log'|'error', prefix:string, message:string|any) {
console[logType](this.logTime(), this.logPrefix(prefix), message);
}
}

View File

@ -2,6 +2,7 @@ import Discord from 'discord.js';
import TClient from './client.js';
const client = new TClient;
client.init();
import Logger from './helpers/Logger.js';
import YTModule from './funcs/YTModule.js';
import MPModule from './funcs/MPModule.js';
import {Player} from 'discord-player';
@ -49,8 +50,8 @@ setInterval(async()=>{
const punishments = await client.punishments._content.find();
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(), await client.punishments.removePunishment(punishment._id, client.user.id, 'Time\'s up!'));
Logger.forwardToConsole('log', 'Punishment', `${punishment.member}\'s ${punishment.type} should expire now`);
Logger.forwardToConsole('log', 'Punishment', await client.punishments.removePunishment(punishment._id, client.user.id, 'Time\'s up!'));
});
const formattedDate = Math.floor((now - client.config.LRSstart)/1000/60/60/24);
@ -61,7 +62,7 @@ setInterval(async()=>{
if (total < yesterday) total = yesterday // messages went down.
dailyMsgs.push([formattedDate, total]);
writeFileSync('./src/database/dailyMsgs.json', JSON.stringify(dailyMsgs))
console.log(client.logTime(), `Pushed [${formattedDate}, ${total}] to dailyMsgs`);
Logger.forwardToConsole('log', 'DailyMsgs', `Pushed [${formattedDate}, ${total}]`)
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}>`));
(client.channels.resolve(client.config.mainServer.channels.thismeanswar) as Discord.TextChannel).send({files:['./src/database/dailyMsgs.json']}).catch(fileErr=>console.log(fileErr))
}

View File

@ -1,7 +1,7 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import mongoose from 'mongoose';
import LogPrefix from '../helpers/LogPrefix.js';
import Logger from '../helpers/Logger.js';
const Schema = mongoose.model('userLevels', new mongoose.Schema({
_id: {type: String},
@ -25,7 +25,7 @@ export default class userLevels extends Schema {
if (userData.messages >= this.algorithm(userData.level+2)){
while (userData.messages > this.algorithm(userData.level+1)){
const newData = await this._content.findByIdAndUpdate(userid, {level:userData.level++}, {new: true});
console.log(this.client.logTime(), LogPrefix('LevelSystem'), `${userid} extended to level ${newData.level}`);
Logger.forwardToConsole('log', 'LevelSystem', `${userid} extended to level ${newData.level}`);
}
} else if (userData.messages >= this.algorithm(userData.level+1)) {
const newData = await this._content.findByIdAndUpdate(userid, {level:userData.level+1}, {new: true});