Skip to content

Commit

Permalink
UTD
Browse files Browse the repository at this point in the history
  • Loading branch information
Yincard committed Apr 21, 2024
0 parents commit fb98df3
Show file tree
Hide file tree
Showing 15 changed files with 2,771 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: main

on:
push:
branches:
- main

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Set Node.js to 16.x
uses: actions/setup-node@v2.5.1
with:
node-version: 16.x

- name: Install dependencies
run: |
npm install
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# MMS-Commission
57 changes: 57 additions & 0 deletions bot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const { Client, GatewayIntentBits, Partials, Collection } = require("discord.js");
const { config } = require('dotenv');
config();

const client = new Client({
intents: Object.values(GatewayIntentBits),
partials: Object.values(Partials),
shards: "auto",
allowedMentions: {
parse: ["roles", "users", "everyone"],
repliedUser: false,
},
});

client.slashData = [];
client.commands = new Collection();
client.token = process.env.TOKEN;

const ResourceManager = new (require('./managers/ResourceManager'))(client);
const DatabaseManager = new (require('./managers/DatabaseManager'))();
const CacheManager = new (require('./managers/CacheManager'))();

client.login(client.token).finally(async () => {

await ResourceManager.loadCmdData('../src');
console.log(
`${ResourceManager.prefix} Loaded ${(ResourceManager.totalSize / 1024).toFixed(2)}MB of resources`
);

DatabaseManager.connect(process.env.DATABASE);
CacheManager.connect(process.env.REDIS_CACHE);

client.database = DatabaseManager;
client.cache = CacheManager;

console.log(`[DISCORD] Logged in as ${client.user.tag}`);
});

const handleErrors = (type, err, origin) => {
console.log(`\n\n\n\n\n=== ${type.toUpperCase()} ===`);
console.log(type === 'unhandledRejection' ? `Reason: ${err.stack ? String(err.stack) : String(err)}` : `Exception: ${err.stack ? err.stack : err}`);
console.log(`=== ${type.toUpperCase()} ===\n\n\n\n\n`);
};

process.on('unhandledRejection', (reason, p) => handleErrors('unhandledRejection', reason));
process.on('uncaughtException', (err, origin) => handleErrors('uncaughtException', err));
process.on('uncaughtExceptionMonitor', (err, origin) => console.log('=== uncaught Exception Monitor ==='.toUpperCase()));
process.on('beforeExit', code => {
console.log('\n\n\n\n\n=== before Exit ==='.toUpperCase());
console.log(`Code: ${code}`);
console.log('=== before Exit ===\n\n\n\n\n');
});
process.on('exit', code => {
console.log('\n\n\n\n\n=== exit ==='.toUpperCase());
console.log(`Code: ${code}`);
console.log('=== exit ===\n\n\n\n\n');
});
13 changes: 13 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { ShardingManager } = require('discord.js');
require('dotenv').config();

const shard = new ShardingManager('./bot.js', {
totalShards: 'auto',
token: process.env.TOKEN
});

shard.on("shardCreate", async (shard) => {
console.log(`[SHARDING] Launched shard #${shard.id}`)
})

shard.spawn({timeout: -1})
92 changes: 92 additions & 0 deletions managers/CacheManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
const { createClient } = require('redis');

class CacheManager {

constructor() {
this.prefix = '[CACHE]';
this.redisClient;
}

async connect(redisURI) {
this.redisClient = await createClient({
url: redisURI
})
.on('connect', () => {
console.log(`${this.prefix} Established connection with Cache`);
})
.connect();
}

async setGuildData(guildID, data) {
const key = `bot_${guildID}`;
const value = JSON.stringify(data);
await this.redisClient.set(key, value);
}

async getGuildData(guildID) {
const key = `bot_${guildID}`;
const data = await this.redisClient.get(key);
return data ? JSON.parse(data) : false;
}

async invalidate(client, keys) {
const Messages = client.database.messageSchema;

for (const key of keys) {

const startTime = Date.now();
const guildId = key.split('_')[1];
let data = await this.redisClient.get(key);
data = JSON.parse(data);
const filter = { guildId };

const bulkOperations = Object.entries(data.channels)
.flatMap(([channelID, channelDataForDates]) =>
Object.entries(channelDataForDates)
.flatMap(([date, userData]) =>
Object.entries(userData)
.map(([userId, count]) => ({
updateOne: {
filter,
update: {
$inc: {
[`channels.${channelID}.${date}.${userId}`]: count,
},
},
upsert: true,
},
}))
)
);

try {
await Messages.bulkWrite(bulkOperations);
} catch (err) {
console.error(`${this.prefix} Error Bulk Writing Operations:`, err);
}

try {
await this.redisClient.del(key);
} catch (err) {
console.error(`${this.prefix} Error deleting Redis key:`, err);
}

console.log(`${this.prefix} Redis cache invalidated successfully for: ${guildId} - ${Date.now() - startTime}ms.`);
}
}

async startInvalidationInterval(client) {
setInterval(async () => {
const keys = await this.redisClient.keys('bot_*');
if (keys.length) {
console.log(`${this.prefix} Redis cache beginning to invalidate & store.`);
await this.invalidate(client, keys);
} else {
console.log(`${this.prefix} No Queued Guilds for Cache Invalidation.`);
}
}, 10000);
}

}

module.exports = CacheManager;
29 changes: 29 additions & 0 deletions managers/DatabaseManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const mongoose = require('mongoose');
const messageSchema = require('../models/Messages')

class DatabaseManager {
constructor() {
this.prefix = '[DATABASE]';
this.messageSchema = messageSchema;
this.localCache = {
guildId: String,
channels: {
[String]: {
[String]: {
[String]: Number,
},
},
},
}
}
async connect(mongoURI) {
mongoose.connect(mongoURI)
mongoose.connection.once('error', console.error);
mongoose.connection.on('open', () => {
console.log(`${this.prefix} Established connection with database`);
});
}

}

module.exports = DatabaseManager;
104 changes: 104 additions & 0 deletions managers/ResourceManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
const { REST } = require("@discordjs/rest");
const { Routes } = require("discord-api-types/v10");
const fs = require('fs');
const path = require('path');
const Command = require('../structures/Command.js');
const Event = require('../structures/Event.js');

function formatBytes(x) {
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
let l = 0, n = parseInt(x, 10) || 0;
while (n >= 1024 && ++l) {
n = n / 1024;
}
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l];
}

class ResourceManager {
constructor(client = null) {
this.client = client;
this.prefix = '[RESOURCE]';
this.totalSize = 0;
}

async loadCmdData(dir = '') {
const SlashCommands = [];
const rest = new REST({ version: "10" }).setToken(this.client.token);
const _path = path.join(__dirname, dir);
const files = fs.readdirSync(_path);

for (const file of files) {
const fullPath = path.join(_path, file);
const stat = fs.lstatSync(fullPath);
this.totalSize += parseFloat(stat.size);

if (stat.isDirectory()) {
await this.loadCmdData(path.join(dir, file));
}

if (file.endsWith('.js')) {
let _file = require(fullPath);

if (_file.prototype instanceof Command) {
let command = new _file();
if (!command.name || !command.description) {
console.log(`${this.prefix} Invalid command : ❌ ${file}`);
continue;
}

this.client.commands.set(command.name, command);
try {
SlashCommands.push(command);
await rest.put(Routes.applicationCommands(this.client.user.id), { body: SlashCommands });
} catch (err) {
console.error(err);
}

this.client.commands.categories = command => {
let cats = command ? '' : [];
const dir = '../src/modules/';
const _path = path.join(__dirname, dir);
const categories = fs.readdirSync(_path);

categories.forEach((module, key) => {
const cmdDir = `${dir}/${module}`;
const cmdPath = path.join(__dirname, cmdDir);
command ? cats : (cats[module] = []);
const files = fs.readdirSync(cmdPath).filter(g => g.endsWith('js'));

for (const file of files) {
const Command = require(path.join(cmdPath, file));
const cmd = new Command();
if (!command) cats[module].push(cmd);
if (command && command.name == cmd.name) {
return (cats = module);
}
}
});
return cats;
};

console.log(`${this.prefix} Registered Slash : ✔️ ${command.name} (${formatBytes(stat.size)})`);
}

if (_file.prototype instanceof Event) {
let event = new _file();
if (!event.name || event.once === undefined) {
console.log(`${this.prefix} Invalid event : ❌ ${file}`);
continue;
}

if (event.once) {
this.client.once(event.name, event.execute.bind(event, this.client));
} else {
this.client.on(event.name, event.execute.bind(event, this.client));
}

console.log(`${this.prefix} Loaded event : ✔️ ${file} (${formatBytes(stat.size)})`);
}
}
}
}
}

module.exports = ResourceManager;
8 changes: 8 additions & 0 deletions models/Messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const mongoose = require('mongoose');

const messageSchema = new mongoose.Schema({
guildId: String,
channels: Object,
}, { versionKey: false });

module.exports = mongoose.model('Message', messageSchema);
Loading

0 comments on commit fb98df3

Please sign in to comment.