Added database files.

This commit is contained in:
Toastie 2024-07-05 00:20:16 +12:00
parent 6303a1b715
commit 2073e6eb14
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
11 changed files with 783 additions and 0 deletions

32
src/database/mongoose.js Normal file
View file

@ -0,0 +1,32 @@
const mongoose = require("mongoose");
const { log, success, error } = require("../helpers/Logger");
mongoose.set("strictQuery", true);
module.exports = {
async initializeMongoose() {
log(`Connecting to MongoDb...`);
try {
await mongoose.connect(process.env.MONGO_CONNECTION);
success("Mongoose: Database connection established");
return mongoose.connection;
} catch (err) {
error("Mongoose: Failed to connect to database", err);
process.exit(1);
}
},
schemas: {
Giveaways: require("./schemas/Giveaways"),
Guild: require("./schemas/Guild"),
Member: require("./schemas/Member"),
ReactionRoles: require("./schemas/ReactionRoles").model,
ModLog: require("./schemas/ModLog").model,
TranslateLog: require("./schemas/TranslateLog").model,
User: require("./schemas/User"),
Suggestions: require("./schemas/Suggestions").model,
},
};

View file

@ -0,0 +1,39 @@
const mongoose = require("mongoose");
const reqString = {
type: String,
required: true,
}
const Schema = new mongoose.Schema(
{
guild_id: reqString,
member_id: reqString,
content: String,
reason: String,
strikes: Number,
},
{
versionKey: false,
autoIndex: false,
timestamps: {
createdAt: "created_at",
updatedAt: false,
},
}
);
const Model = mongoose.Model("automod-logs", Schema);
module.exports = {
addAutoModLogToDb: async (member, content, reason, strikes) => {
if (!member) throw new Error("Member is undefined");
await new Model({
guild_id: member.guild.id,
member_id: member.id,
content,
reason,
strikes,
}).save();
},
};

View file

@ -0,0 +1,64 @@
const mongoose = require("mongoose");
const Schema = new mongoose.Schema(
{
messageId: String,
channelId: String,
guildId: String,
startAt: Number,
endAt: Number,
ended: Boolean,
winnerCount: Number,
prize: String,
messages: {
giveaway: String,
giveawayEnded: String,
inviteToParticipate: String,
drawing: String,
dropMessage: String,
winMessage: mongoose.Mixed,
embedFooter: mongoose.Mixed,
noWinner: String,
winners: String,
endedAt: String,
hostedBy: String,
},
thumbnail: String,
hostedBy: String,
winnerIds: { type: [String], default: undefined },
reaction: mongoose.Mixed,
botsCanWin: Boolean,
embedColor: mongoose.Mixed,
embedColorEnd: mongoose.Mixed,
exemptPermissions: { type: [], default: undefined },
exemptMembers: String,
bonusEntries: String,
extraData: mongoose.Mixed,
lastChance: {
enabled: Boolean,
content: String,
threshold: Number,
embedColor: mongoose.Mixed,
},
pauseOptions: {
isPaused: Boolean,
content: String,
unPauseAfter: Number,
embedColor: mongoose.Mixed,
durationAfterPause: Number,
},
isDrop: Boolean,
allowedMentions: {
parse: { type: [String], default: undefined },
users: { type: [String], default: undefined },
roles: { type: [String], default: undefined },
},
},
{
id: false,
autoIndex: false,
}
);
const Model = mongoose.model("giveaways", Schema);
module.exports = Model;

View file

@ -0,0 +1,153 @@
const mongoose = require("mongoose");
const { CACHE_SIZE, PREFIX_COMMANDS, STATS } = require("@root/config.js");
const FixedSizeMap = require("fixedsize-map");
const { getUser } = require("./User");
const cache = new FixedSizeMap(CACHE_SIZE.GUILDS);
const Schema = new mongoose.Schema({
_id: String,
data: {
name: String,
region: String,
owner: { type: String, ref: "users" },
joinedAt: Date,
leftAt: Date,
bots: { type: Number, default: 0 },
},
prefix: { type: String, default: PREFIX_COMMANDS.DEFAULT_PREFIX },
stats: {
enabled: Boolean,
xp: {
message: { type: String, default: STATS.DEFAULT_LVL_UP_MSG },
channel: String,
},
},
ticket: {
log_channel: String,
limit: { type: Number, default: 10 },
categories: [
{
_id: false,
name: String,
staff_roles: [String],
},
],
},
automod: {
debug: Boolean,
strikes: { type: Number, default: 10 },
action: { type: String, default: "TIMEOUT" },
wh_channels: [String],
anti_attachments: Boolean,
anti_invites: Boolean,
anti_links: Boolean,
anti_spam: Boolean,
anti_ghostping: Boolean,
anti_massmention: Number,
max_lines: Number,
},
invite: {
tracking: Boolean,
ranks: [
{
invites: { type: Number, required: true },
_id: { type: String, required: true },
},
],
},
flag_translation: {
enabled: Boolean,
},
modlog_channel: String,
max_warn: {
action: {
type: String,
enum: ["TIMEOUT", "KICK", "BAN"],
default: "KICK",
},
limit: { type: Number, default: 5 },
},
counters: [
{
_id: false,
counter_type: String,
name: String,
channel_id: String,
},
],
welcome: {
enabled: Boolean,
channel: String,
content: String,
embed: {
description: String,
color: String,
thumbnail: Boolean,
footer: String,
image: String,
},
},
farewell: {
enabled: Boolean,
channel: String,
content: String,
embed: {
description: String,
color: String,
thumbnail: Boolean,
footer: String,
image: String,
},
},
autorole: String,
suggestions: {
enabled: Boolean,
channel_id: String,
approved_channel: String,
rejected_channel: String,
staff_roles: [String],
},
});
const Model = mongoose.model("guild", Schema);
module.exports = {
/**
* @param {import('discord.js').Guild} guild
*/
getSettings: async (guild) => {
if (!guild) throw new Error("Guild is undefined");
if (!guild.id) throw new Error("Guild Id is undefined");
const cached = cache.get(guild.id);
if (cached) return cached;
let guildData = await Model.findById(guild.id);
if (!guildData) {
// save owner details
guild
.fetchOwner()
.then(async (owner) => {
const userDb = await getUser(owner);
await userDb.save();
})
.catch((ex) => {});
// create a new guild model
guildData = new Model({
_id: guild.id,
data: {
name: guild.name,
region: guild.preferredLocale,
owner: guild.ownerId,
joinedAt: guild.joinedAt,
},
});
await guildData.save();
}
cache.add(guild.id, guildData);
return guildData;
},
};

View file

@ -0,0 +1,72 @@
const mongoose = require("mongoose");
const { CACHE_SIZE } = require("@root/config.js");
const FixedSizeMap = require("fixedsize-map");
const cache = new FixedSizeMap(CACHE_SIZE.MEMBERS);
const ReqString = {
type: String,
required: true,
};
const Schema = new mongoose.Schema(
{
guild_id: ReqString,
member_id: ReqString,
strikes: { type: Number, default: 0 },
warnings: { type: Number, default: 0 },
invite_data: {
inviter: String,
code: String,
tracked: { type: Number, default: 0 },
fake: { type: Number, default: 0 },
left: { type: Number, default: 0 },
added: { type: Number, default: 0 },
},
},
{
timestamps: {
createdAt: "created_at",
updatedAt: "updated_at",
},
}
);
const Model = mongoose.model("members", Schema);
module.exports = {
getMember: async (guildId, memberId) => {
const key = `${guildId}|${memberId}`;
if (cache.contains(key)) return cache.get(key);
let member = await Model.findOne({ guild_id: guildId, member_id: memberId });
if (!member) {
member = new Model({
guild_id: guildId,
member_id: memberId,
});
}
cache.add(key, member);
return member;
},
getInvitesLb: async (guildId, limit = 10) =>
Model.aggregate([
{ $match: { guild_id: guildId } },
{
$project: {
member_id: "$member_id",
invites: {
$subtract: [
{ $add: ["$invite_data.tracked", "$invite_data.added"] },
{ $add: ["$invite_data.left", "$invite_data.fake"] },
],
},
},
},
{ $match: { invites: { $gt: 0 } } },
{ $sort: { invites: -1 } },
{ $limit: limit },
]),
};

View file

@ -0,0 +1,66 @@
const mongoose = require("mongoose");
const { CACHE_SIZE } = require("@root/config.js");
const FixedSizeMap = require("fixedsize-map");
const cache = new FixedSizeMap(CACHE_SIZE.MEMBERS);
const ReqString = {
type: String,
required: true,
};
const Schema = new mongoose.Schema(
{
guild_id: ReqString,
member_id: ReqString,
messages: { type: Number, default: 0 },
voice: {
connections: { type: Number, default: 0 },
time: { type: Number, default: 0 },
},
commands: {
prefix: { type: Number, default: 0 },
slash: { type: Number, default: 0 },
},
contexts: {
message: { type: Number, default: 0 },
user: { type: Number, default: 0 },
},
xp: { type: Number, default: 0 },
level: { type: Number, default: 1 },
},
{
timestamps: {
createdAt: "created_at",
updatedAt: "updated_at",
},
}
);
const Model = mongoose.model("member-stats", Schema);
module.exports = {
getMemberStats: async (guildId, memberId) => {
const key = `${guildId}|${memberId}`;
if (cache.contains(key)) return cache.get(key);
let member = await Model.findOne({ guild_id: guildId, member_id: memberId });
if (!member) {
member = new Model({
guild_id: guildId,
member_id: memberId,
});
}
cache.add(key, member);
return member;
},
getXpLb: async (guildId, limit = 10) =>
Model.find({
guild_id: guildId,
})
.limit(limit)
.sort({ level: -1, xp: -1 })
.lean(),
};

View file

@ -0,0 +1,78 @@
const mongoose = require("mongoose");
const reqString = {
type: String,
required: true,
};
const Schema = new mongoose.Schema(
{
guild_id: reqString,
member_id: String,
reason: String,
admin: {
id: reqString,
tag: reqString,
},
type: {
type: String,
required: true,
enum: [
"PURGE",
"WARN",
"TIMEOUT",
"UNTIMEOUT",
"KICK",
"SOFTBAN",
"BAN",
"UNBAN",
"VMUTE",
"VUNMUTE",
"DEAFEN",
"UNDEAFEN",
"DISCONNECT",
"MOVE",
],
},
},
{
versionKey: false,
autoIndex: false,
timestamps: {
createdAt: "created_at",
updatedAt: false,
},
}
);
const Model = mongoose.model("mod-logs", Schema);
module.exports = {
model: Model,
addModLogToDb: async (admin, target, reason, type) =>
await new Model({
guild_id: admin.guild.id,
member_id: target.id,
reason,
admin: {
id: admin.id,
tag: admin.user.tag,
},
type,
}).save(),
getWarningLogs: async (guildId, targetId) =>
Model.find({
guild_id: guildId,
member_id: targetId,
type: "WARN",
}).lean(),
clearWarningLogs: async (guildId, targetId) =>
Model.deleteMany({
guild_id: guildId,
member_id: targetId,
type: "WARN",
}),
};

View file

@ -0,0 +1,92 @@
const mongoose = require("mongoose");
const reqString = {
type: String,
required: true,
};
const Schema = new mongoose.Schema(
{
guild_id: reqString,
channel_id: reqString,
message_id: reqString,
roles: [
{
_id: false,
emote: reqString,
role_id: reqString,
},
],
},
{
timestamps: {
createdAt: "created_at",
updatedAt: false,
},
}
);
const Model = mongoose.model("reaction-roles", Schema);
// Cache
const rrCache = new Map();
const getKey = (guildId, channelId, messageId) => `${guildId}|${channelId}|${messageId}`;
module.exports = {
model: Model,
cacheReactionRoles: async (client) => {
// clear previous cache
rrCache.clear();
// load all docs from database
const docs = await Model.find().lean();
// validate and cache docs
for (const doc of docs) {
const guild = client.guilds.cache.get(doc.guild_id);
if (!guild) {
// await Model.deleteMany({ guild_id: doc.guild_id });
continue;
}
if (!guild.channels.cache.has(doc.channel_id)) {
// await Model.deleteMany({ guild_id: doc.guild_id, channel_id: doc.channel_id });
continue;
}
const key = getKey(doc.guild_id, doc.channel_id, doc.message_id);
rrCache.set(key, doc.roles);
}
},
getReactionRoles: (guildId, channelId, messageId) => rrCache.get(getKey(guildId, channelId, messageId)) || [],
addReactionRole: async (guildId, channelId, messageId, emote, roleId) => {
const filter = { guild_id: guildId, channel_id: channelId, message_id: messageId };
// Pull if existing configuration is present
await Model.updateOne(filter, { $pull: { roles: { emote } } });
const data = await Model.findOneAndUpdate(
filter,
{
$push: {
roles: { emote, role_id: roleId },
},
},
{ upsert: true, new: true }
).lean();
// update cache
const key = getKey(guildId, channelId, messageId);
rrCache.set(key, data.roles);
},
removeReactionRole: async (guildId, channelId, messageId) => {
await Model.deleteOne({
guild_id: guildId,
channel_id: channelId,
message_id: messageId,
});
rrCache.delete(getKey(guildId, channelId, messageId));
},
};

View file

@ -0,0 +1,70 @@
const mongoose = require("mongoose");
const Schema = new mongoose.Schema(
{
guild_id: String,
channel_id: String,
message_id: String,
user_id: String,
suggestion: String,
status: {
type: String,
enum: ["PENDING", "APPROVED", "REJECTED", "DELETED"],
default: "PENDING",
},
stats: {
upvotes: { type: Number, default: 0 },
downvotes: { type: Number, default: 0 },
},
status_updates: [
{
_id: false,
user_id: String,
status: {
type: String,
enum: ["APPROVED", "REJECTED", "DELETED"],
},
reason: String,
timestamp: { type: Date, default: new Date() },
},
],
},
{
timestamps: {
createdAt: "created_at",
updatedAt: "updated_at",
},
}
);
const Model = mongoose.model("suggestions", Schema);
module.exports = {
model: Model,
addSuggestion: async (message, userId, suggestion) => {
return new Model({
guild_id: message.guildId,
channel_id: message.channelId,
message_id: message.id,
user_id: userId,
suggestion: suggestion,
}).save();
},
findSuggestion: async (guildId, messageId) => {
return Model.findOne({ guild_id: guildId, message_id: messageId });
},
deleteSuggestionDb: async (guildId, messageId, memberId, reason) => {
return Model.updateOne(
{ guild_id: guildId, message_id: messageId },
{
status: "DELETED",
$push: {
status_updates: { user_id: memberId, status: "DELETED", reason },
},
}
);
},
};

View file

@ -0,0 +1,45 @@
const mongoose = require("mongoose");
const reqString = {
type: String,
required: true,
};
const Schema = new mongoose.Schema(
{
guild_id: reqString,
channel_id: reqString,
message_id: reqString,
emoji: reqString,
},
{
versionKey: false,
autoIndex: false,
timestamps: {
createdAt: "created_at",
updatedAt: false,
},
}
);
const Model = mongoose.model("logs-translation", Schema);
module.exports = {
model: Model,
isTranslated: async (message, code) =>
Model.findOne({
guild_id: message.guildId,
channel_id: message.channelId,
message_id: message.id,
emoji: code,
}).lean(),
logTranslation: async (message, code) =>
new Model({
guild_id: message.guildId,
channel_id: message.channelId,
message_id: message.id,
emoji: code,
}).save(),
};

View file

@ -0,0 +1,72 @@
const mongoose = require("mongoose");
const { CACHE_SIZE } = require("@root/config.js");
const FixedSizeMap = require("fixedsize-map");
const cache = new FixedSizeMap(CACHE_SIZE.USERS);
const Schema = new mongoose.Schema(
{
_id: String,
username: String,
discriminator: String,
logged: Boolean,
coins: { type: Number, default: 0 },
bank: { type: Number, default: 0 },
reputation: {
received: { type: Number, default: 0 },
given: { type: Number, default: 0 },
timestamp: Date,
},
daily: {
streak: { type: Number, default: 0 },
timestamp: Date,
},
},
{
timestamps: {
createdAt: "created_at",
updatedAt: "updated_at",
},
}
);
const Model = mongoose.model("user", Schema);
module.exports = {
/**
* @param {import('discord.js').User} user
*/
getUser: async (user) => {
if (!user) throw new Error("User is required.");
if (!user.id) throw new Error("User Id is required.");
const cached = cache.get(user.id);
if (cached) return cached;
let userDb = await Model.findById(user.id);
if (!userDb) {
userDb = new Model({
_id: user.id,
username: user.username,
discriminator: user.discriminator,
});
}
// Temporary fix for users who where added to DB before v5.0.0
// Update username and discriminator in previous DB
else if (!userDb.username || !userDb.discriminator) {
userDb.username = user.username;
userDb.discriminator = user.discriminator;
}
cache.add(user.id, userDb);
return userDb;
},
getReputationLb: async (limit = 10) => {
return Model.find({ "reputation.received": { $gt: 0 } })
.sort({ "reputation.received": -1, "reputation.given": 1 })
.limit(limit)
.lean();
},
};