Skip to content

Commit

Permalink
feat: guild bans
Browse files Browse the repository at this point in the history
  • Loading branch information
eartharoid committed Dec 20, 2024
1 parent ca38235 commit 30cd541
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 37 deletions.
82 changes: 58 additions & 24 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const {
GatewayIntentBits,
Partials,
} = require('discord.js');
const logger = require('./lib/logger');
const { PrismaClient } = require('@prisma/client');
const Keyv = require('keyv');
const I18n = require('@eartharoid/i18n');
Expand All @@ -14,7 +15,7 @@ const sqliteMiddleware = require('./lib/middleware/prisma-sqlite');
const ms = require('ms');

module.exports = class Client extends FrameworkClient {
constructor(config, log) {
constructor() {
super(
{
intents: [
Expand All @@ -39,6 +40,12 @@ module.exports = class Client extends FrameworkClient {
{ baseDir: __dirname },
);

this.config = {};
this.log = {};
this.init();
}

async init(reload = false) {
const locales = {};
fs.readdirSync(join(__dirname, 'i18n'))
.filter(file => file.endsWith('.yml'))
Expand All @@ -48,30 +55,57 @@ module.exports = class Client extends FrameworkClient {
locales[name] = YAML.parse(data);
});

this.keyv = new Keyv();
/** @type {I18n} */
this.i18n = new I18n('en-GB', locales);
/** @type {TicketManager} */
this.tickets = new TicketManager(this);
this.config = config;
this.log = log;
this.supers = (process.env.SUPER ?? '').split(',');
/** @param {import('discord.js/typings').Interaction} interaction */
this.commands.interceptor = async interaction => {
if (!interaction.inGuild()) return;
const id = interaction.guildId;
const cacheKey = `cache/known/guild:${id}`;
if (await this.keyv.has(cacheKey)) return;
await this.prisma.guild.upsert({
create: {
id,
locale: this.i18n.locales.find(locale => locale === interaction.guild.preferredLocale), // undefined if not supported
},
update: {},
where: { id },
});
await this.keyv.set(cacheKey, true);
};

// to maintain references, these shouldn't be reassigned
Object.assign(this.config, YAML.parse(fs.readFileSync('./user/config.yml', 'utf8')));
Object.assign(this.log, logger(this.config));

this.banned_guilds = new Set(
(() => {
let array = fs.readFileSync('./user/banned-guilds.txt', 'utf8').trim().split(/\r?\n/);
if (array[0] === '') array = [];
return array;
})(),
);
this.log.info(`${this.banned_guilds.size} guilds are banned`);

if (reload) {
await this.initAfterLogin();
} else {
this.keyv = new Keyv();

this.tickets = new TicketManager(this);

this.supers = (process.env.SUPER ?? '').split(',');

/** @param {import('discord.js/typings').Interaction} interaction */
this.commands.interceptor = async interaction => {
if (!interaction.inGuild()) return;
const id = interaction.guildId;
const cacheKey = `cache/known/guild:${id}`;
if (await this.keyv.has(cacheKey)) return;
await this.prisma.guild.upsert({
create: {
id,
locale: this.i18n.locales.find(locale => locale === interaction.guild.preferredLocale), // undefined if not supported
},
update: {},
where: { id },
});
await this.keyv.set(cacheKey, true);
};
}
}

async initAfterLogin() {
for (const id of this.banned_guilds) {
if (this.guilds.cache.has(id)) {
this.log.info(`Leaving banned guild ${id}`);
await this.guilds.cache.get(id).leave();
}
}
}

async login(token) {
Expand All @@ -86,7 +120,7 @@ module.exports = class Client extends FrameworkClient {
};

if (process.env.DB_PROVIDER === 'sqlite' && !process.env.DB_CONNECTION_URL) {
prisma_options.datasources = { db: { url:'file:' + join(process.cwd(), './user/database.db') } };
prisma_options.datasources = { db: { url: 'file:' + join(process.cwd(), './user/database.db') } };
}

/** @type {PrismaClient} */
Expand Down
8 changes: 8 additions & 0 deletions src/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ module.exports = async client => {

});
}
if (client.banned_guilds.has(guildId)) {
return res.code(451).send({
error: 'Unavailable For Legal Reasons',
message: 'This guild has been banned for breaking the terms of service.',
statusCode: 451,

});
}
const guildMember = await guild.members.fetch(userId);
const isAdmin = await getPrivilegeLevel(guildMember) >= 2;
if (!isAdmin) {
Expand Down
37 changes: 24 additions & 13 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
process.exit(1);
}

// check cwd
const base_dir = path.resolve(path.join(__dirname, '../'));
const cwd = path.resolve(process.cwd());
if (base_dir !== cwd) {
Expand All @@ -49,26 +50,17 @@ if (base_dir !== cwd) {
console.log(colours.blueBright(' Learn more at https://lnk.earth/dt-cwd.'));
}

// this could be done first, but then there would be no banner :(
process.env.NODE_ENV ??= 'production'; // make sure NODE_ENV is set
require('./env').load(); // load and check environment variables

const fs = require('fs');
const YAML = require('yaml');
const logger = require('./lib/logger');
const Client = require('./client');
const http = require('./http');

// user directory may or may not exist depending on if sqlite is being used
// so the config file could be missing even though the directory exists
if (!fs.existsSync('./user/config.yml')) {
console.log('The config file does not exist, copying defaults...');
fs.cpSync(path.join(__dirname, 'user'), './user', { recursive: true });
console.log('Created user directory at', path.join(cwd, 'user'));
}

const config = YAML.parse(fs.readFileSync('./user/config.yml', 'utf8'));
const log = logger(config);
// create a Logger using the default config
// and set listeners as early as possible.
let config = YAML.parse(fs.readFileSync(path.join(__dirname, 'user/config.yml'), 'utf8'));
let log = logger(config);

process.on('uncaughtException', (error, origin) => {
log.notice(`Discord Tickets v${pkg.version} on Node.js ${process.version} (${process.platform})`);
Expand All @@ -78,7 +70,26 @@ process.on('uncaughtException', (error, origin) => {

process.on('warning', warning => log.warn(warning.stack || warning));

const Client = require('./client');
const http = require('./http');

// the `user` directory may or may not exist depending on if sqlite is being used.
// copy any files that don't already exist
fs.cpSync(path.join(__dirname, 'user'), './user', {
force: false,
recursive: true,
});

// initialise the framework and client,
// which also loads the custom config and creates a new Logger.
const client = new Client(config, log);

// allow any config changes to affect the above listeners
// as long as these `client` properties are not reassigned.
config = client.config;
log = client.log;

// start the bot and then the web server
client.login().then(() => {
http(client);
});
2 changes: 2 additions & 0 deletions src/listeners/client/ready.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ module.exports = class extends Listener {
process.title = 'tickets';
client.log.success('Connected to Discord as "%s" over %d shards', client.user.tag, client.ws.shards.size);

await client.initAfterLogin();

// fill cache
await sync(client);

Expand Down
21 changes: 21 additions & 0 deletions src/stdin/reload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { StdinCommand } = require('@eartharoid/dbf');

module.exports = class extends StdinCommand {
constructor(client, options) {
super(client, {
...options,
id: 'reload',
});
}

async run() {
this.client.log.warn('Reloading is not the same as restarting!');
this.client.log.info('Reinitialising client...');
await this.client.init(true);
this.client.log.success('Client reinitialised');
// TODO: fix this
// this.client.log.info('Reloading module components...');
// this.client.mods.forEach(mod => mod.components.forEach(component => component.reload()));
// this.client.log.success('Components reloaded');
}
};
Empty file added src/user/banned-guilds.txt
Empty file.

0 comments on commit 30cd541

Please sign in to comment.