Merge pull request #385 from VermiumSifell/dev

Better managers & logging & gdpr fix for guilds & some minor changes
This commit is contained in:
Axel Olausson Holtenäs 2022-07-05 01:49:07 +02:00 committed by GitHub
commit 41c6234d81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 211 additions and 103 deletions

View file

@ -48,6 +48,7 @@
"tsconfig-paths": "^4.0.0",
"typescript": "^4.6.3",
"uuid": "^8.3.2",
"winston": "^3.8.0",
"winston-daily-rotate-file": "^4.6.1"
},
"devDependencies": {

View file

@ -7,66 +7,60 @@ import timeoutSchema from "../../models/timeout";
import logger from "../../logger";
import { Guild } from "discord.js";
import { Snowflake } from "discord.js";
export default async (guild: Guild) => {
export default async (id: Snowflake) => {
await guildSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
return logger?.silly(`Deleted guild: ${guild.id}`);
return logger?.silly(`Deleted guild: ${id}`);
})
.catch(async (error) => {
logger?.error(`Error deleting guild: ${guild.id} - ${error}`);
logger?.error(`Error deleting guild: ${id} - ${error}`);
});
await userSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
logger?.silly(`Deleted users for guild: ${guild.id} from database`);
logger?.silly(`Deleted users for guild: ${id} from database`);
})
.catch(async (error) => {
logger?.error(`Error deleting users for guild: ${guild.id} - ${error}`);
logger?.error(`Error deleting users for guild: ${id} - ${error}`);
});
await apiSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
logger?.silly(`Deleted apis for guild: ${guild.id} from database`);
logger?.silly(`Deleted apis for guild: ${id} from database`);
})
.catch(async (error) => {
logger?.error(`Error deleting apis for guild: ${guild.id} - ${error}`);
logger?.error(`Error deleting apis for guild: ${id} - ${error}`);
});
await counterSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
logger?.silly(`Deleted counters for guild: ${guild.id} from database`);
logger?.silly(`Deleted counters for guild: ${id} from database`);
})
.catch(async (error) => {
logger?.error(
`Error deleting counters for guild: ${guild.id} - ${error}`
);
logger?.error(`Error deleting counters for guild: ${id} - ${error}`);
});
await shopRoleSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
logger?.silly(`Deleted shop roles for guild: ${guild.id} from database`);
logger?.silly(`Deleted shop roles for guild: ${id} from database`);
})
.catch(async (error) => {
logger?.error(
`Error deleting shop roles for guild: ${guild.id} - ${error}`
);
logger?.error(`Error deleting shop roles for guild: ${id} - ${error}`);
});
await timeoutSchema
.deleteMany({ guildId: guild.id })
.deleteMany({ guildId: id })
.then(async () => {
logger?.silly(`Deleted timeouts for guild: ${guild.id} from database`);
logger?.silly(`Deleted timeouts for guild: ${id} from database`);
})
.catch(async (error) => {
logger?.error(
`Error deleting timeouts for guild: ${guild.id} - ${error}`
);
logger?.error(`Error deleting timeouts for guild: ${id} - ${error}`);
});
};

View file

@ -1,5 +1,5 @@
// Dependencies
import { Guild } from "discord.js";
import { Snowflake } from "discord.js";
// Models
import guildSchema from "../../models/guild";
@ -8,18 +8,18 @@ import guildSchema from "../../models/guild";
import logger from "../../logger";
// Function
export default async (guild: Guild) => {
const guildObj = await guildSchema?.findOne({ guildId: guild.id });
export default async (id: Snowflake) => {
const guildObj = await guildSchema?.findOne({ guildId: id });
if (guildObj === null) {
const newGuildObj = new guildSchema({ guildId: guild.id });
const newGuildObj = new guildSchema({ guildId: id });
await newGuildObj
.save()
.then(async () => {
logger?.silly(`Created guild: ${guild.id}`);
logger?.silly(`Created guild: ${id}`);
})
.catch(async (error) => {
logger?.error(`Error creating guild: ${guild.id} - ${error}`);
logger?.error(`Error creating guild: ${id} - ${error}`);
});
return newGuildObj;

View file

@ -2,7 +2,5 @@ import fs from "fs";
const fsPromises = fs.promises;
export default async (path: string) => {
return fsPromises.readdir(path).catch(async (err) => {
throw new Error(`Could not list directory: ${path}`, err);
});
return fsPromises.readdir(path);
};

View file

@ -1,6 +1,6 @@
import { token, intents } from "./config/discord";
import { Client } from "discord.js"; // discord.js
import { Client, Collection } from "discord.js"; // discord.js
import * as managers from "./managers";
@ -11,6 +11,9 @@ const main = async () => {
intents,
});
// Create command collection
client.commands = new Collection();
await managers.start(client);
// Authorize with Discord's API

View file

@ -0,0 +1,35 @@
import { Client, Guild } from "discord.js";
import logger from "../../logger";
import dropGuild from "../../helpers/dropGuild";
import fetchGuild from "../../helpers/fetchGuild";
import guildSchema from "../../models/guild";
export const options = {
schedule: "0 0 1 * *", // https://crontab.guru/
};
export const execute = async (client: Client) => {
const guildsDB = await guildSchema.find();
const guildsDiscord = client.guilds.cache;
const shouldNotExist = guildsDB
.filter((x) => !guildsDiscord.some((y) => y.id === x.guildId))
.map((z) => z.guildId);
const shouldExist = guildsDiscord
.filter((x) => !guildsDB.some((y) => y.guildId === x.id))
.map((z) => z.id);
if (shouldNotExist) {
shouldNotExist.forEach(async (x) => {
await dropGuild(x);
});
}
if (shouldExist) {
shouldExist.forEach(async (x) => {
await fetchGuild(x);
});
}
};

View file

@ -5,31 +5,65 @@ import logger from "../../logger";
import { ICommand } from "../../interfaces/Command";
export const register = async (client: Client) => {
client.commands = new Collection();
// Get name of directories containing commands
const commandNames = await listDir("plugins/commands");
if (!commandNames) throw new Error("📦 No commands available");
if (!commandNames) throw new Error("Could not list commands");
const amountOfCommands = commandNames.length;
let importedCommandAmount = 0;
logger.info(`📦 Trying to load ${amountOfCommands} commands`);
logger.info(`Loading ${commandNames.length} commands`);
const importCommand = async (commandName: string) => {
// Import command from plugins/commands
const command: ICommand = await import(
`../../plugins/commands/${commandName}`
);
if (!command)
throw new Error(`📦 No command found while importing "${commandName}"`);
if (!command.builder)
throw new Error(
`📦 No command builder found while importing "${commandName}"`
);
if (!command.execute)
throw new Error(
`📦 No command execute found while importing "${commandName}"`
);
if (!command.moduleData)
throw new Error(
`📦 No command moduleData found while importing "${commandName}"`
);
await Promise.all(
commandNames.map(async (commandName) => {
const command: ICommand = await import(
`../../plugins/commands/${commandName}`
).catch(async (e) => {
throw new Error(`Could not load command: ${commandName}`, e);
// Add command to collection
client.commands.set(command.builder.name, command);
importedCommandAmount += 1;
};
// Send log message when it's done loading commands
const doneImporting = async () => {
if (importedCommandAmount !== amountOfCommands) {
return logger.warn(
`📦 Failed importing ${
amountOfCommands - importedCommandAmount
} of ${amountOfCommands} commands`
);
}
return logger.info(`📦 Managed to load all commands`);
};
// Start importing commands
commandNames.forEach(async (commandName: string, index: number) => {
await importCommand(commandName)
.then(async () => {
logger.debug(`📦 Imported the "${commandName}" command`);
})
.catch(async (err) => {
logger.error(err);
});
client.commands.set(command.builder.name, command);
logger.verbose(`${command.builder.name} loaded`);
})
)
.then(async () => {
logger.info(`Finished loading commands.`);
})
.catch(async (err) => {
throw new Error(`Could not load commands: ${err}`);
});
// If done importing
if (index + 1 === amountOfCommands) {
await doneImporting();
}
});
};

View file

@ -1,27 +1,6 @@
// 3rd party dependencies
import mongoose from "mongoose";
// Dependencies
import logger from "../../logger";
// Configuration
import { url } from "../../config/database";
export const connect = async () => {
await mongoose
.connect(url)
.then(async (connection) => {
logger.info(`Connected to database: ${connection.connection.name}`);
})
.catch(async (e) => {
logger.error("Could not connect to database", e);
});
mongoose.connection.on("error", async (error) => {
logger.error(`${error}`);
});
mongoose.connection.on("warn", async (warning) => {
logger.warn(warning);
});
await mongoose.connect(url);
};

View file

@ -6,15 +6,35 @@ import logger from "../../logger";
export const register = async (client: Client) => {
const eventNames = await listDir("plugins/events");
if (!eventNames) return;
if (!eventNames) throw new Error("📦 No events available");
for await (const eventName of eventNames) {
const amountOfEvents = eventNames.length;
let importedEventAmount = 0;
logger.info(`📦 Trying to load ${amountOfEvents} events`);
const importEvent = async (eventName: string) => {
// Import event from plugins/events
const event: IEvent = await import(`../../plugins/events/${eventName}`);
const eventExecutor = async (...args: Promise<void>[]) =>
event.execute(...args).catch(async (err) => {
if (!event)
throw new Error(`📦 No event found while importing "${eventName}"`);
if (!event.options)
throw new Error(
`📦 No event options found while importing "${eventName}"`
);
if (!event.execute)
throw new Error(
`📦 No event execute found while importing "${eventName}"`
);
// Register event
const eventExecutor = async (...args: Promise<void>[]) => {
await event.execute(...args).catch(async (err) => {
logger.error(`${err}`);
});
if (!event.options?.type) return;
};
if (!event.options?.type)
throw new Error(`📦 No event type found while importing "${eventName}"`);
switch (event.options.type) {
case "once":
@ -25,5 +45,49 @@ export const register = async (client: Client) => {
client.on(eventName, eventExecutor);
break;
}
}
importedEventAmount += 1;
};
// Send log message when it's done loading events
const doneImporting = async () => {
if (importedEventAmount !== amountOfEvents) {
return logger.warn(
`📦 Failed importing ${
amountOfEvents - importedEventAmount
} of ${amountOfEvents} events`
);
}
return logger.info(`📦 Managed to load all events`);
};
eventNames.forEach(async (eventName: string, index: number) => {
await importEvent(eventName).then(async () => {
logger.debug(`📦 Imported the "${eventName}" event`);
});
// If done importing
if (index + 1 === amountOfEvents) {
await doneImporting();
}
});
// for await (const eventName of eventNames) {
// const event: IEvent = await import(`../../plugins/events/${eventName}`);
// const eventExecutor = async (...args: Promise<void>[]) =>
// event.execute(...args).catch(async (err) => {
// logger.error(`${err}`);
// });
// if (!event.options?.type) return;
// switch (event.options.type) {
// case "once":
// client.once(eventName, eventExecutor);
// break;
// case "on":
// client.on(eventName, eventExecutor);
// break;
// }
// }
};

View file

@ -8,22 +8,19 @@ import listDir from "../../helpers/listDir";
import schedule from "node-schedule";
export const start = async (client: Client) => {
logger.info("Starting schedule manager...");
logger.info("⏰ Started job management");
const jobNames = await listDir("jobs");
if (!jobNames) return logger.info("No jobs found");
if (!jobNames) return logger.warn("No available jobs found");
await Promise.all(
jobNames.map(async (jobName) => {
const job: IJob = await import(`../../jobs/${jobName}`);
schedule.scheduleJob(job.options.schedule, async () => {
logger.info(`Executed job ${jobName}!`);
logger.verbose(`⏰ Performed the job "${jobName}"`);
await job.execute(client);
});
})
).then(async () => {
const list = schedule.scheduledJobs;
logger.silly(list);
});
);
};

View file

@ -58,7 +58,7 @@ const guildSchema = new Schema<IGuild>(
},
timeout: {
type: Number,
default: 5000,
default: 5,
},
workRate: {
type: Number,
@ -66,7 +66,7 @@ const guildSchema = new Schema<IGuild>(
},
workTimeout: {
type: Number,
default: 900000,
default: 900,
},
},
embeds: {
@ -118,7 +118,7 @@ const guildSchema = new Schema<IGuild>(
},
timeout: {
type: Number,
default: 5000,
default: 5,
},
},
welcome: {

View file

@ -43,7 +43,7 @@ export default {
return logger?.silly(`Guild is null`);
}
const guildDB = await fetchGuild(guild);
const guildDB = await fetchGuild(guild.id);
await cooldown.command(interaction, guildDB?.credits?.workTimeout);

View file

@ -11,10 +11,13 @@ export const options: IEventOptions = {
export const execute = async (guild: Guild) => {
const { client } = guild;
logger?.silly(`Added to guild: ${guild.name} (${guild.id})`);
if (!client.user)
throw new Error("Discord API client user is not available.");
await fetchGuild(guild);
logger.silly(
`${client.user.username} joined guild: ${guild.name} (${guild.id})`
);
await fetchGuild(guild.id);
await updatePresence(client);
logger.silly(`guildCreate: ${guild}`);
};

View file

@ -16,7 +16,7 @@ export const execute = async (guild: Guild) => {
logger?.silly(`Deleted from guild: ${guild.name} (${guild.id})`);
await dropGuild(guild);
await dropGuild(guild.id);
await updatePresence(client);
logger.silly(`guildDelete: ${guild}`);

View file

@ -17,7 +17,7 @@ export default {
const { id: guildId } = guild;
const { id: userId } = author;
const guildData = await fetchGuild(guild);
const guildData = await fetchGuild(guild.id);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;

View file

@ -14,7 +14,7 @@ export default {
if (author.bot) return;
if (channel?.type !== "GUILD_TEXT") return;
const guildData = await fetchGuild(guild);
const guildData = await fetchGuild(guild.id);
const userData = await fetchUser(author, guild);
if (content.length < guildData.credits.minimumLength) return;