feat: guild bans

This commit is contained in:
Isaac 2024-12-20 02:24:32 +00:00
parent ca38235309
commit 30cd5413c4
No known key found for this signature in database
GPG Key ID: 17700D08381EA590
6 changed files with 113 additions and 37 deletions

View File

@ -3,6 +3,7 @@ const {
GatewayIntentBits, GatewayIntentBits,
Partials, Partials,
} = require('discord.js'); } = require('discord.js');
const logger = require('./lib/logger');
const { PrismaClient } = require('@prisma/client'); const { PrismaClient } = require('@prisma/client');
const Keyv = require('keyv'); const Keyv = require('keyv');
const I18n = require('@eartharoid/i18n'); const I18n = require('@eartharoid/i18n');
@ -14,7 +15,7 @@ const sqliteMiddleware = require('./lib/middleware/prisma-sqlite');
const ms = require('ms'); const ms = require('ms');
module.exports = class Client extends FrameworkClient { module.exports = class Client extends FrameworkClient {
constructor(config, log) { constructor() {
super( super(
{ {
intents: [ intents: [
@ -39,6 +40,12 @@ module.exports = class Client extends FrameworkClient {
{ baseDir: __dirname }, { baseDir: __dirname },
); );
this.config = {};
this.log = {};
this.init();
}
async init(reload = false) {
const locales = {}; const locales = {};
fs.readdirSync(join(__dirname, 'i18n')) fs.readdirSync(join(__dirname, 'i18n'))
.filter(file => file.endsWith('.yml')) .filter(file => file.endsWith('.yml'))
@ -48,30 +55,57 @@ module.exports = class Client extends FrameworkClient {
locales[name] = YAML.parse(data); locales[name] = YAML.parse(data);
}); });
this.keyv = new Keyv();
/** @type {I18n} */ /** @type {I18n} */
this.i18n = new I18n('en-GB', locales); this.i18n = new I18n('en-GB', locales);
/** @type {TicketManager} */
this.tickets = new TicketManager(this); // to maintain references, these shouldn't be reassigned
this.config = config; Object.assign(this.config, YAML.parse(fs.readFileSync('./user/config.yml', 'utf8')));
this.log = log; Object.assign(this.log, logger(this.config));
this.supers = (process.env.SUPER ?? '').split(',');
/** @param {import('discord.js/typings').Interaction} interaction */ this.banned_guilds = new Set(
this.commands.interceptor = async interaction => { (() => {
if (!interaction.inGuild()) return; let array = fs.readFileSync('./user/banned-guilds.txt', 'utf8').trim().split(/\r?\n/);
const id = interaction.guildId; if (array[0] === '') array = [];
const cacheKey = `cache/known/guild:${id}`; return array;
if (await this.keyv.has(cacheKey)) return; })(),
await this.prisma.guild.upsert({ );
create: { this.log.info(`${this.banned_guilds.size} guilds are banned`);
id,
locale: this.i18n.locales.find(locale => locale === interaction.guild.preferredLocale), // undefined if not supported if (reload) {
}, await this.initAfterLogin();
update: {}, } else {
where: { id }, this.keyv = new Keyv();
});
await this.keyv.set(cacheKey, true); 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) { async login(token) {
@ -86,7 +120,7 @@ module.exports = class Client extends FrameworkClient {
}; };
if (process.env.DB_PROVIDER === 'sqlite' && !process.env.DB_CONNECTION_URL) { 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} */ /** @type {PrismaClient} */

View File

@ -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 guildMember = await guild.members.fetch(userId);
const isAdmin = await getPrivilegeLevel(guildMember) >= 2; const isAdmin = await getPrivilegeLevel(guildMember) >= 2;
if (!isAdmin) { if (!isAdmin) {

View File

@ -37,6 +37,7 @@ if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
process.exit(1); process.exit(1);
} }
// check cwd
const base_dir = path.resolve(path.join(__dirname, '../')); const base_dir = path.resolve(path.join(__dirname, '../'));
const cwd = path.resolve(process.cwd()); const cwd = path.resolve(process.cwd());
if (base_dir !== cwd) { if (base_dir !== cwd) {
@ -49,26 +50,17 @@ if (base_dir !== cwd) {
console.log(colours.blueBright(' Learn more at https://lnk.earth/dt-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 process.env.NODE_ENV ??= 'production'; // make sure NODE_ENV is set
require('./env').load(); // load and check environment variables require('./env').load(); // load and check environment variables
const fs = require('fs'); const fs = require('fs');
const YAML = require('yaml'); const YAML = require('yaml');
const logger = require('./lib/logger'); 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 // create a Logger using the default config
// so the config file could be missing even though the directory exists // and set listeners as early as possible.
if (!fs.existsSync('./user/config.yml')) { let config = YAML.parse(fs.readFileSync(path.join(__dirname, 'user/config.yml'), 'utf8'));
console.log('The config file does not exist, copying defaults...'); let log = logger(config);
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);
process.on('uncaughtException', (error, origin) => { process.on('uncaughtException', (error, origin) => {
log.notice(`Discord Tickets v${pkg.version} on Node.js ${process.version} (${process.platform})`); log.notice(`Discord Tickets v${pkg.version} on Node.js ${process.version} (${process.platform})`);
@ -78,7 +70,26 @@ process.on('uncaughtException', (error, origin) => {
process.on('warning', warning => log.warn(warning.stack || warning)); 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); 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(() => { client.login().then(() => {
http(client); http(client);
}); });

View File

@ -33,6 +33,8 @@ module.exports = class extends Listener {
process.title = 'tickets'; process.title = 'tickets';
client.log.success('Connected to Discord as "%s" over %d shards', client.user.tag, client.ws.shards.size); client.log.success('Connected to Discord as "%s" over %d shards', client.user.tag, client.ws.shards.size);
await client.initAfterLogin();
// fill cache // fill cache
await sync(client); await sync(client);

21
src/stdin/reload.js Normal file
View File

@ -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');
}
};

View File