aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-23 22:02:44 -0400
committerIRONM00N <64110067+IRONM00N@users.noreply.github.com>2021-07-23 22:02:44 -0400
commitb015bec7f66526ec5e959ae99865845f4db4b181 (patch)
tree67538c9549b7e0f7cd6a97e9c82db8d8462a19c7
parent5c242f597595b8db71875d92c0afe0a5947442a6 (diff)
downloadtanzanite-b015bec7f66526ec5e959ae99865845f4db4b181.tar.gz
tanzanite-b015bec7f66526ec5e959ae99865845f4db4b181.tar.bz2
tanzanite-b015bec7f66526ec5e959ae99865845f4db4b181.zip
feat: some shit
- fix breaking changes - refactored active punishments into one table - made listeners args have stricter types
-rw-r--r--.yarn/sdks/typescript/package.json2
-rw-r--r--package.json2
-rw-r--r--src/commands/dev/eval.ts2
-rw-r--r--src/commands/dev/servers.ts4
-rw-r--r--src/commands/moderation/removeReactionEmoji.ts2
-rw-r--r--src/lib/extensions/discord-akairo/BushClient.ts56
-rw-r--r--src/lib/extensions/discord-akairo/BushClientUtil.ts88
-rw-r--r--src/lib/extensions/discord-akairo/BushCommandHandler.ts41
-rw-r--r--src/lib/extensions/discord-akairo/BushListener.ts5
-rw-r--r--src/lib/extensions/discord.js/BushButtonInteraction.ts2
-rw-r--r--src/lib/extensions/discord.js/BushCommandInteraction.ts2
-rw-r--r--src/lib/extensions/discord.js/BushGuildMember.ts2
-rw-r--r--src/lib/extensions/discord.js/BushSelectMenuInteraction.ts2
-rw-r--r--src/lib/index.ts4
-rw-r--r--src/lib/models/ActivePunishment.ts (renamed from src/lib/models/PunishmentRole.ts)53
-rw-r--r--src/lib/models/Ban.ts80
-rw-r--r--src/lib/models/ModLog.ts5
-rw-r--r--src/lib/models/Mute.ts80
-rw-r--r--src/listeners/client/interactionCreate.ts4
-rw-r--r--src/listeners/commands/commandBlocked.ts4
-rw-r--r--src/listeners/commands/commandError.ts5
-rw-r--r--src/listeners/commands/commandMissingPermissions.ts10
-rw-r--r--src/listeners/commands/commandStarted.ts10
-rw-r--r--src/listeners/commands/slashBlocked.ts7
-rw-r--r--src/listeners/commands/slashCommandError.ts7
-rw-r--r--src/listeners/commands/slashMissingPermissions.ts25
-rw-r--r--src/listeners/commands/slashStarted.ts11
-rw-r--r--src/listeners/guild/syncUnban.ts13
-rw-r--r--src/listeners/message/level.ts5
-rw-r--r--src/listeners/other/consoleListener.ts16
-rw-r--r--src/tasks/removeExpiredPunishements.ts74
-rw-r--r--src/tasks/removePunishmentRole.ts37
-rw-r--r--src/tasks/unban.ts27
-rw-r--r--src/tasks/unmute.ts39
-rw-r--r--yarn.lock156
35 files changed, 334 insertions, 548 deletions
diff --git a/.yarn/sdks/typescript/package.json b/.yarn/sdks/typescript/package.json
index fbe0980..27270c7 100644
--- a/.yarn/sdks/typescript/package.json
+++ b/.yarn/sdks/typescript/package.json
@@ -1,6 +1,6 @@
{
"name": "typescript",
- "version": "0.0.0-sdk",
+ "version": "4.2.4-pnpify",
"main": "./lib/typescript.js",
"type": "commonjs"
}
diff --git a/package.json b/package.json
index 716d351..364b9a2 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"chalk": "^4.1.1",
"common-tags": "^1.8.0",
"discord-akairo": "NotEnoughUpdates/discord-akairo",
- "discord-api-types": "0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e",
+ "discord-api-types": "0.19.0",
"discord.js": "NotEnoughUpdates/discord.js",
"discord.js-minesweeper": "^1.0.6",
"fuse.js": "^6.4.6",
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts
index 0de0aa1..f8ba7e1 100644
--- a/src/commands/dev/eval.ts
+++ b/src/commands/dev/eval.ts
@@ -181,7 +181,7 @@ export default class EvalCommand extends BushCommand {
members = message.guild?.members,
roles = message.guild?.roles,
client = this.client,
- { Ban, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'),
+ { ActivePunishment, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'),
{
ButtonInteraction,
Collector,
diff --git a/src/commands/dev/servers.ts b/src/commands/dev/servers.ts
index 08f74e8..3986451 100644
--- a/src/commands/dev/servers.ts
+++ b/src/commands/dev/servers.ts
@@ -4,7 +4,7 @@ import { BushCommand, BushMessage, BushSlashMessage } from '../../lib';
export default class ServersCommand extends BushCommand {
public constructor() {
super('servers', {
- aliases: ['servers'],
+ aliases: ['servers', 'guilds'],
category: 'dev',
description: {
content: 'Displays all the severs the bot is in',
@@ -37,7 +37,7 @@ export default class ServersCommand extends BushCommand {
`**ID:** ${g.id}\n**Owner:** ${owner ? owner : g.ownerId}\n**Members:** ${g.memberCount.toLocaleString()}`,
false
)
- .setTitle('Server List')
+ .setTitle(`Server List [${this.client.guilds.cache.size}]`)
.setColor(this.client.util.colors.default);
});
embeds.push(embed);
diff --git a/src/commands/moderation/removeReactionEmoji.ts b/src/commands/moderation/removeReactionEmoji.ts
index 3d274e1..b0079c6 100644
--- a/src/commands/moderation/removeReactionEmoji.ts
+++ b/src/commands/moderation/removeReactionEmoji.ts
@@ -13,7 +13,7 @@ export default class RemoveReactionEmojiCommand extends BushCommand {
examples: ['removereactionemoji 791413052347252786 <:omegaclown:782630946435366942>']
},
clientPermissions: ['MANAGE_MESSAGES', 'SEND_MESSAGES', 'EMBED_LINKS'],
- userPermissions: ['MANAGE_MESSAGES', 'MANAGE_EMOJIS'], // Can't undo the removal of 1000s of reactions
+ userPermissions: ['MANAGE_MESSAGES', 'MANAGE_EMOJIS_AND_STICKERS'], // Can't undo the removal of 1000s of reactions
args: [
{
id: 'messageToRemoveFrom',
diff --git a/src/lib/extensions/discord-akairo/BushClient.ts b/src/lib/extensions/discord-akairo/BushClient.ts
index 66204ac..8eec3ce 100644
--- a/src/lib/extensions/discord-akairo/BushClient.ts
+++ b/src/lib/extensions/discord-akairo/BushClient.ts
@@ -18,13 +18,11 @@ import { Sequelize } from 'sequelize';
import { contentWithDurationTypeCaster } from '../../../arguments/contentWithDuration';
import { durationTypeCaster } from '../../../arguments/duration';
import { UpdateCacheTask } from '../../../tasks/updateCache';
-import { Ban } from '../../models/Ban';
+import { ActivePunishment } from '../../models/ActivePunishment';
import { Global } from '../../models/Global';
import { Guild as GuildModel } from '../../models/Guild';
import { Level } from '../../models/Level';
import { ModLog } from '../../models/ModLog';
-import { Mute } from '../../models/Mute';
-import { PunishmentRole } from '../../models/PunishmentRole';
import { StickyRole } from '../../models/StickyRole';
import { AllowedMentions } from '../../utils/AllowedMentions';
import { BushCache } from '../../utils/BushCache';
@@ -107,39 +105,23 @@ export class BushClient extends AkairoClient {
public constants = BushConstants;
public cache = BushCache;
public constructor(config: Config) {
- super(
- {
- ownerID: config.owners,
- intents: Object.values(Intents.FLAGS).reduce((acc, p) => acc | p, 0),
- presence: {
- activities: [
- {
- name: 'Beep Boop',
- type: 'WATCHING'
- }
- ],
- status: 'online'
- }
+ super({
+ ownerID: config.owners,
+ intents: Object.values(Intents.FLAGS).reduce((acc, p) => acc | p, 0),
+ presence: {
+ activities: [
+ {
+ name: 'Beep Boop',
+ type: 'WATCHING'
+ }
+ ],
+ status: 'online'
},
- {
- allowedMentions: AllowedMentions.users(), // No everyone or role mentions by default
- intents: Object.values(Intents.FLAGS).reduce((acc, p) => acc | p, 0),
- presence: {
- activities: [
- {
- name: 'Beep Boop',
- type: 'WATCHING'
- }
- ],
- status: 'online'
- }
- }
- );
+ http: { api: 'https://canary.discord.com/api' },
+ allowedMentions: AllowedMentions.users() // No everyone or role mentions by default
+ });
- // Set token
this.token = config.token;
-
- // Set config
this.config = config;
// Create listener handler
@@ -170,7 +152,7 @@ export class BushClient extends AkairoClient {
allowMention: true,
handleEdits: true,
commandUtil: true,
- commandUtilLifetime: 300_000,
+ commandUtilLifetime: 300_000, // 5 minutes
argumentDefaults: {
prompt: {
start: 'Placeholder argument prompt. If you see this please tell my developers.',
@@ -259,11 +241,9 @@ export class BushClient extends AkairoClient {
Global.initModel(this.db);
GuildModel.initModel(this.db, this);
ModLog.initModel(this.db);
- Ban.initModel(this.db);
- Mute.initModel(this.db);
+ ActivePunishment.initModel(this.db);
Level.initModel(this.db);
StickyRole.initModel(this.db);
- PunishmentRole.initModel(this.db);
await this.db.sync({ alter: true }); // Sync all tables to fix everything if updated
await this.console.success('Startup', `Successfully connected to <<database>>.`, false);
} catch (e) {
@@ -277,7 +257,7 @@ export class BushClient extends AkairoClient {
/** Starts the bot */
public async start(): Promise<void> {
- global.client = this;
+ global.client = this; // makes the client a global object
try {
await this._init();
diff --git a/src/lib/extensions/discord-akairo/BushClientUtil.ts b/src/lib/extensions/discord-akairo/BushClientUtil.ts
index 0e3a904..306e049 100644
--- a/src/lib/extensions/discord-akairo/BushClientUtil.ts
+++ b/src/lib/extensions/discord-akairo/BushClientUtil.ts
@@ -2,7 +2,6 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import {
- Ban,
BushCache,
BushClient,
BushConstants,
@@ -14,9 +13,7 @@ import {
Global,
Guild,
ModLog,
- ModLogType,
- Mute,
- PunishmentRole
+ ModLogType
} from '@lib';
import { exec } from 'child_process';
import { ClientUtil } from 'discord-akairo';
@@ -43,8 +40,8 @@ import {
} from 'discord.js';
import got from 'got';
import humanizeDuration from 'humanize-duration';
-import { Op } from 'sequelize';
import { promisify } from 'util';
+import { ActivePunishment, ActivePunishmentType } from '../../models/ActivePunishment';
import { BushNewsChannel } from '../discord.js/BushNewsChannel';
import { BushTextChannel } from '../discord.js/BushTextChannel';
import { BushUserResolvable } from './BushClient';
@@ -96,12 +93,6 @@ interface bushColors {
orange: '#E86100';
}
-interface punishmentModels {
- mute: Mute;
- ban: Ban;
- role: PunishmentRole;
-}
-
interface MojangProfile {
username: string;
uuid: string;
@@ -664,22 +655,21 @@ export class BushClientUtil extends ClientUtil {
}
public async createPunishmentEntry(options: {
- type: 'mute' | 'ban' | 'role';
+ type: 'mute' | 'ban' | 'role' | 'block';
user: BushGuildMemberResolvable;
duration: number;
guild: BushGuildResolvable;
modlog: string;
- role?: Snowflake;
- }): Promise<Mute | Ban | PunishmentRole> {
- const dbModel = this.findPunishmentModel(options.type);
+ extraInfo?: Snowflake;
+ }): Promise<ActivePunishment> {
const expires = options.duration ? new Date(new Date().getTime() + options.duration) : null;
const user = this.client.users.resolveId(options.user);
const guild = this.client.guilds.resolveId(options.guild);
+ const type = this.findTypeEnum(options.type);
- const entry =
- options.type === 'role'
- ? (dbModel as typeof PunishmentRole).build({ user, guild, expires, modlog: options.modlog, role: options.role })
- : dbModel.build({ user, guild, expires, modlog: options.modlog });
+ const entry = options.extraInfo
+ ? ActivePunishment.build({ user, type, guild, expires, modlog: options.modlog, extraInfo: options.extraInfo })
+ : ActivePunishment.build({ user, type, guild, expires, modlog: options.modlog });
return await entry.save().catch((e) => {
this.client.console.error('createPunishmentEntry', e?.stack || e);
return null;
@@ -687,28 +677,23 @@ export class BushClientUtil extends ClientUtil {
}
public async removePunishmentEntry(options: {
- type: 'mute' | 'ban' | 'role';
+ type: 'mute' | 'ban' | 'role' | 'block';
user: BushGuildMemberResolvable;
guild: BushGuildResolvable;
}): Promise<boolean> {
- const dbModel = this.findPunishmentModel(options.type);
const user = this.client.users.resolveId(options.user);
const guild = this.client.guilds.resolveId(options.guild);
+ const type = this.findTypeEnum(options.type);
let success = true;
- const entries = await dbModel
- .findAll({
- // finding all cases of a certain type incase there were duplicates or something
- where: {
- user,
- guild
- }
- })
- .catch((e) => {
- this.client.console.error('removePunishmentEntry', e?.stack || e);
- success = false;
- });
+ const entries = await ActivePunishment.findAll({
+ // finding all cases of a certain type incase there were duplicates or something
+ where: { user, guild, type }
+ }).catch((e) => {
+ this.client.console.error('removePunishmentEntry', e?.stack || e);
+ success = false;
+ });
if (entries) {
entries.forEach(async (entry) => {
await entry.destroy().catch((e) => {
@@ -720,33 +705,14 @@ export class BushClientUtil extends ClientUtil {
return success;
}
- public async findExpiredEntries<K extends keyof punishmentModels>(type: K): Promise<punishmentModels[K][]> {
- const dbModel = this.findPunishmentModel(type);
- //@ts-ignore: stfu idc
- return await dbModel.findAll({
- where: {
- [Op.and]: [
- {
- expires: {
- [Op.lt]: new Date() // Find all rows with an expiry date before now
- }
- }
- ]
- }
- });
- }
-
- private findPunishmentModel<K extends keyof punishmentModels>(type: K): typeof Mute | typeof Ban | typeof PunishmentRole {
- switch (type) {
- case 'mute':
- return Mute;
- case 'ban':
- return Ban;
- case 'role':
- return PunishmentRole;
- default:
- throw 'choose a valid punishment entry type';
- }
+ private findTypeEnum(type: 'mute' | 'ban' | 'role' | 'block') {
+ const typeMap = {
+ ['mute']: ActivePunishmentType.MUTE,
+ ['ban']: ActivePunishmentType.BAN,
+ ['role']: ActivePunishmentType.ROLE,
+ ['block']: ActivePunishmentType.BLOCK
+ };
+ return typeMap[type];
}
public humanizeDuration(duration: number): string {
@@ -782,5 +748,7 @@ export class BushClientUtil extends ClientUtil {
return arrByte[1] + ', ' + arrByte[2] + ', ' + arrByte[3];
}
+ /* eslint-disable @typescript-eslint/no-unused-vars */
public async lockdownChannel(options: { channel: BushTextChannel | BushNewsChannel; moderator: BushUserResolvable }) {}
+ /* eslint-enable @typescript-eslint/no-unused-vars */
}
diff --git a/src/lib/extensions/discord-akairo/BushCommandHandler.ts b/src/lib/extensions/discord-akairo/BushCommandHandler.ts
index 09baf2e..dacd17f 100644
--- a/src/lib/extensions/discord-akairo/BushCommandHandler.ts
+++ b/src/lib/extensions/discord-akairo/BushCommandHandler.ts
@@ -1,15 +1,26 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
-import { Category, CommandHandler, CommandHandlerOptions } from 'discord-akairo';
-import { Collection } from 'discord.js';
+import { Category, CommandHandler, CommandHandlerEvents, CommandHandlerOptions } from 'discord-akairo';
+import { Collection, PermissionString } from 'discord.js';
import { BushConstants } from '../../utils/BushConstants';
import { BushMessage } from '../discord.js/BushMessage';
import { BushClient } from './BushClient';
import { BushCommand } from './BushCommand';
+import { BushSlashMessage } from './BushSlashMessage';
export type BushCommandHandlerOptions = CommandHandlerOptions;
-const CommandHandlerEvents = BushConstants.CommandHandlerEvents;
-const BlockedReasons = BushConstants.BlockedReasons;
+const commandHandlerEvents = BushConstants.CommandHandlerEvents;
+const blockedReasons = BushConstants.BlockedReasons;
+
+export interface BushCommandHandlerEvents extends CommandHandlerEvents {
+ commandBlocked: [message: BushMessage, command: BushCommand, reason: string];
+
+ missingPermissions: [message: BushMessage, command: BushCommand, type: 'client' | 'user', missing: Array<PermissionString>];
+
+ slashBlocked: [message: BushSlashMessage, command: BushCommand, reason: string];
+
+ slashMissingPermissions: [message: BushSlashMessage, command: BushCommand, type: 'client' | 'user', missing: Array<PermissionString>];
+}
export class BushCommandHandler extends CommandHandler {
public declare client: BushClient;
@@ -24,10 +35,10 @@ export class BushCommandHandler extends CommandHandler {
const isOwner = this.client.isOwner(message.author);
if (!isOwner) {
this.emit(
- slash ? CommandHandlerEvents.SLASH_BLOCKED : CommandHandlerEvents.COMMAND_BLOCKED,
+ slash ? commandHandlerEvents.SLASH_BLOCKED : commandHandlerEvents.COMMAND_BLOCKED,
message,
command,
- BlockedReasons.OWNER
+ blockedReasons.OWNER
);
return true;
}
@@ -37,10 +48,10 @@ export class BushCommandHandler extends CommandHandler {
const isSuperUser = this.client.isSuperUser(message.author);
if (!isSuperUser) {
this.emit(
- slash ? CommandHandlerEvents.SLASH_BLOCKED : CommandHandlerEvents.COMMAND_BLOCKED,
+ slash ? commandHandlerEvents.SLASH_BLOCKED : commandHandlerEvents.COMMAND_BLOCKED,
message,
command,
- BlockedReasons.OWNER
+ blockedReasons.OWNER
);
return true;
}
@@ -48,32 +59,32 @@ export class BushCommandHandler extends CommandHandler {
if (command.channel === 'guild' && !message.guild) {
this.emit(
- slash ? CommandHandlerEvents.SLASH_BLOCKED : CommandHandlerEvents.COMMAND_BLOCKED,
+ slash ? commandHandlerEvents.SLASH_BLOCKED : commandHandlerEvents.COMMAND_BLOCKED,
message,
command,
- BlockedReasons.GUILD
+ blockedReasons.GUILD
);
return true;
}
if (command.channel === 'dm' && message.guild) {
this.emit(
- slash ? CommandHandlerEvents.SLASH_BLOCKED : CommandHandlerEvents.COMMAND_BLOCKED,
+ slash ? commandHandlerEvents.SLASH_BLOCKED : commandHandlerEvents.COMMAND_BLOCKED,
message,
command,
- BlockedReasons.DM
+ blockedReasons.DM
);
return true;
}
if (command.restrictedChannels?.length && message.channel) {
if (!command.restrictedChannels.includes(message.channel.id)) {
- this.emit(CommandHandlerEvents.COMMAND_BLOCKED, message, command, BlockedReasons.RESTRICTED_CHANNEL);
+ this.emit(commandHandlerEvents.COMMAND_BLOCKED, message, command, blockedReasons.RESTRICTED_CHANNEL);
return true;
}
}
if (command.restrictedGuilds?.length && message.guild) {
if (!command.restrictedGuilds.includes(message.guild.id)) {
- this.emit(CommandHandlerEvents.COMMAND_BLOCKED, message, command, BlockedReasons.RESTRICTED_GUILD);
+ this.emit(commandHandlerEvents.COMMAND_BLOCKED, message, command, blockedReasons.RESTRICTED_GUILD);
return true;
}
}
@@ -82,7 +93,7 @@ export class BushCommandHandler extends CommandHandler {
}
const reason = this.inhibitorHandler ? await this.inhibitorHandler.test('post', message, command) : null;
if (reason != null) {
- this.emit(CommandHandlerEvents.COMMAND_BLOCKED, message, command, reason);
+ this.emit(commandHandlerEvents.COMMAND_BLOCKED, message, command, reason);
return true;
}
return !!this.runCooldowns(message, command);
diff --git a/src/lib/extensions/discord-akairo/BushListener.ts b/src/lib/extensions/discord-akairo/BushListener.ts
index e555e89..2583e85 100644
--- a/src/lib/extensions/discord-akairo/BushListener.ts
+++ b/src/lib/extensions/discord-akairo/BushListener.ts
@@ -1,6 +1,9 @@
import { Listener } from 'discord-akairo';
+import EventEmitter from 'events';
import { BushClient } from './BushClient';
-
export class BushListener extends Listener {
public declare client: BushClient;
+ public constructor(id: string, options?:{emitter: string|EventEmitter, event: string, type?: 'on'|'once', category?: string}){
+ super(id, options)
+ }
}
diff --git a/src/lib/extensions/discord.js/BushButtonInteraction.ts b/src/lib/extensions/discord.js/BushButtonInteraction.ts
index 3a54f61..846786c 100644
--- a/src/lib/extensions/discord.js/BushButtonInteraction.ts
+++ b/src/lib/extensions/discord.js/BushButtonInteraction.ts
@@ -1,4 +1,4 @@
-import { APIInteractionGuildMember } from 'discord-api-types/v8';
+import { APIInteractionGuildMember } from 'discord-api-types/v9';
import { ButtonInteraction, PartialDMChannel } from 'discord.js';
import { BushClient } from '../discord-akairo/BushClient';
import { BushDMChannel } from './BushDMChannel';
diff --git a/src/lib/extensions/discord.js/BushCommandInteraction.ts b/src/lib/extensions/discord.js/BushCommandInteraction.ts
index 84c0707..e4fdb53 100644
--- a/src/lib/extensions/discord.js/BushCommandInteraction.ts
+++ b/src/lib/extensions/discord.js/BushCommandInteraction.ts
@@ -1,4 +1,4 @@
-import { APIInteractionGuildMember } from 'discord-api-types/v8';
+import { APIInteractionGuildMember } from 'discord-api-types/v9';
import {
ApplicationCommand,
CommandInteraction,
diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts
index 40e4a3a..50875cc 100644
--- a/src/lib/extensions/discord.js/BushGuildMember.ts
+++ b/src/lib/extensions/discord.js/BushGuildMember.ts
@@ -137,7 +137,7 @@ export class BushGuildMember extends GuildMember {
guild: this.guild,
duration: options.duration,
modlog: modlog.id,
- role: options.role.id
+ extraInfo: options.role.id
})
.catch(() => null);
if (!punishmentEntrySuccess) return 'error creating role entry';
diff --git a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts b/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts
index 1dd1638..18db8ef 100644
--- a/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts
+++ b/src/lib/extensions/discord.js/BushSelectMenuInteraction.ts
@@ -1,4 +1,4 @@
-import { APIInteractionGuildMember } from 'discord-api-types/v8';
+import { APIInteractionGuildMember } from 'discord-api-types/v9';
import { PartialDMChannel, SelectMenuInteraction } from 'discord.js';
import { BushClient } from '../discord-akairo/BushClient';
import { BushDMChannel } from './BushDMChannel';
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 1059e2b..73cee56 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -46,14 +46,12 @@ export * from './extensions/discord.js/BushThreadMemberManager';
export * from './extensions/discord.js/BushUser';
export * from './extensions/discord.js/BushVoiceChannel';
export * from './extensions/discord.js/BushVoiceState';
-export * from './models/Ban';
+export * from './models/ActivePunishment';
export * from './models/BaseModel';
export * from './models/Global';
export * from './models/Guild';
export * from './models/Level';
export * from './models/ModLog';
-export * from './models/Mute';
-export * from './models/PunishmentRole';
export * from './models/StickyRole';
export * from './utils/AllowedMentions';
export * from './utils/BushCache';
diff --git a/src/lib/models/PunishmentRole.ts b/src/lib/models/ActivePunishment.ts
index 0b54f31..9fcafbe 100644
--- a/src/lib/models/PunishmentRole.ts
+++ b/src/lib/models/ActivePunishment.ts
@@ -3,54 +3,67 @@ import { DataTypes, Sequelize } from 'sequelize';
import { v4 as uuidv4 } from 'uuid';
import { BaseModel } from './BaseModel';
-export interface PunishmentRoleModel {
+export enum ActivePunishmentType {
+ BAN = 'BAN',
+ MUTE = 'MUTE',
+ ROLE = 'ROLE',
+ BLOCK = 'BLOCK'
+}
+
+export interface ActivePunishmentModel {
id: string;
+ type: ActivePunishmentType;
user: Snowflake;
- role: Snowflake;
guild: Snowflake;
+ extraInfo: Snowflake;
expires: Date;
modlog: string;
}
-export interface PunishmentRoleModelCreationAttributes {
+export interface ActivePunishmentModelCreationAttributes {
id?: string;
+ type: ActivePunishmentType;
user: Snowflake;
- role?: Snowflake;
guild: Snowflake;
+ extraInfo?: Snowflake;
expires?: Date;
modlog: string;
}
-export class PunishmentRole
- extends BaseModel<PunishmentRoleModel, PunishmentRoleModelCreationAttributes>
- implements PunishmentRoleModel
+export class ActivePunishment
+ extends BaseModel<ActivePunishmentModel, ActivePunishmentModelCreationAttributes>
+ implements ActivePunishmentModel
{
/**
- * The ID of this punishment role (no real use just for a primary key)
+ * The ID of this punishment (no real use just for a primary key)
*/
id: string;
/**
- * The user who received a role
+ * The type of punishment.
*/
- user: Snowflake;
+ type: ActivePunishmentType;
/**
- * The role added to the user.
+ * The user who is punished.
*/
- role: Snowflake;
+ user: Snowflake;
/**
- * The guild they received a role in
+ * The guild they are punished in.
*/
guild: Snowflake;
/**
- * The date at which this role expires and should be removed (optional)
+ * Additional info about the punishment if applicable. The channel id for channel blocks and role for punishment roles.
+ */
+ extraInfo: Snowflake;
+ /**
+ * The date when this punishment expires (optional).
*/
expires: Date | null;
/**
- * The ref to the modlog entry
+ * The reference to the modlog entry.
*/
modlog: string;
static initModel(sequelize: Sequelize): void {
- PunishmentRole.init(
+ ActivePunishment.init(
{
id: {
type: DataTypes.STRING,
@@ -58,11 +71,11 @@ export class PunishmentRole
allowNull: false,
defaultValue: uuidv4
},
- user: {
+ type: {
type: DataTypes.STRING,
allowNull: false
},
- role: {
+ user: {
type: DataTypes.STRING,
allowNull: false
},
@@ -74,6 +87,10 @@ export class PunishmentRole
key: 'id'
}
},
+ extraInfo: {
+ type: DataTypes.DATE,
+ allowNull: true
+ },
expires: {
type: DataTypes.DATE,
allowNull: true
diff --git a/src/lib/models/Ban.ts b/src/lib/models/Ban.ts
deleted file mode 100644
index 1bdda6f..0000000
--- a/src/lib/models/Ban.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Snowflake } from 'discord.js';
-import { DataTypes, Sequelize } from 'sequelize';
-import { v4 as uuidv4 } from 'uuid';
-import { BaseModel } from './BaseModel';
-
-export interface BanModel {
- id: string;
- user: Snowflake;
- guild: Snowflake;
- expires: Date;
- modlog: string;
-}
-export interface BanModelCreationAttributes {
- id?: string;
- user: Snowflake;
- guild: Snowflake;
- expires?: Date;
- modlog: string;
-}
-
-export class Ban extends BaseModel<BanModel, BanModelCreationAttributes> implements BanModel {
- /**
- * The ID of this ban (no real use just for a primary key)
- */
- id: string;
- /**
- * The user who is banned
- */
- user: Snowflake;
- /**
- * The guild they are banned from
- */
- guild: Snowflake;
- /**
- * The date at which this ban expires and should be unbanned (optional)
- */
- expires: Date | null;
- /**
- * The ref to the modlog entry
- */
- modlog: string;
-
- static initModel(sequelize: Sequelize): void {
- Ban.init(
- {
- id: {
- type: DataTypes.STRING,
- primaryKey: true,
- allowNull: false,
- defaultValue: uuidv4
- },
- user: {
- type: DataTypes.STRING,
- allowNull: false
- },
- guild: {
- type: DataTypes.STRING,
- allowNull: false,
- references: {
- model: 'Guilds',
- key: 'id'
- }
- },
- expires: {
- type: DataTypes.DATE,
- allowNull: true
- },
- modlog: {
- type: DataTypes.STRING,
- allowNull: false,
- references: {
- model: 'ModLogs',
- key: 'id'
- }
- }
- },
- { sequelize: sequelize }
- );
- }
-}
diff --git a/src/lib/models/ModLog.ts b/src/lib/models/ModLog.ts
index 40dc86d..3375751 100644
--- a/src/lib/models/ModLog.ts
+++ b/src/lib/models/ModLog.ts
@@ -14,7 +14,10 @@ export enum ModLogType {
WARN = 'WARN',
PERM_PUNISHMENT_ROLE = 'PERM_PUNISHMENT_ROLE',
TEMP_PUNISHMENT_ROLE = 'TEMP_PUNISHMENT_ROLE',
- REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE'
+ REMOVE_PUNISHMENT_ROLE = 'REMOVE_PUNISHMENT_ROLE',
+ PERM_CHANNEL_BLOCK = 'PERM_CHANNEL_BLOCK',
+ TEMP_CHANNEL_BLOCK = 'TEMP_CHANNEL_BLOCK',
+ CHANNEL_UNBLOCK = 'CHANNEL_UNBLOCK'
}
export interface ModLogModel {
diff --git a/src/lib/models/Mute.ts b/src/lib/models/Mute.ts
deleted file mode 100644
index 4208d02..0000000
--- a/src/lib/models/Mute.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { Snowflake } from 'discord.js';
-import { DataTypes, Sequelize } from 'sequelize';
-import { v4 as uuidv4 } from 'uuid';
-import { BaseModel } from './BaseModel';
-
-export interface MuteModel {
- id: string;
- user: Snowflake;
- guild: Snowflake;
- expires: Date;
- modlog: string;
-}
-export interface MuteModelCreationAttributes {
- id?: string;
- user: Snowflake;
- guild: Snowflake;
- expires?: Date;
- modlog: string;
-}
-
-export class Mute extends BaseModel<MuteModel, MuteModelCreationAttributes> implements MuteModel {
- /**
- * The ID of this mute (no real use just for a primary key)
- */
- id: string;
- /**
- * The user who is muted
- */
- user: Snowflake;
- /**
- * The guild they are muted in
- */
- guild: Snowflake;
- /**
- * The date at which this Mute expires and should be unmuted (optional)
- */
- expires: Date | null;
- /**
- * The ref to the modlog entry
- */
- modlog: string;
-
- static initModel(sequelize: Sequelize): void {
- Mute.init(
- {
- id: {
- type: DataTypes.STRING,
- primaryKey: true,
- allowNull: false,
- defaultValue: uuidv4
- },
- user: {
- type: DataTypes.STRING,
- allowNull: false
- },
- guild: {
- type: DataTypes.STRING,
- allowNull: false,
- references: {
- model: 'Guilds',
- key: 'id'
- }
- },
- expires: {
- type: DataTypes.DATE,
- allowNull: true
- },
- modlog: {
- type: DataTypes.STRING,
- allowNull: false,
- references: {
- model: 'ModLogs',
- key: 'id'
- }
- }
- },
- { sequelize: sequelize }
- );
- }
-}
diff --git a/src/listeners/client/interactionCreate.ts b/src/listeners/client/interactionCreate.ts
index 1183004..0e77fea 100644
--- a/src/listeners/client/interactionCreate.ts
+++ b/src/listeners/client/interactionCreate.ts
@@ -1,5 +1,5 @@
import { BushListener } from '@lib';
-import { ButtonInteraction, CommandInteraction, Interaction, SelectMenuInteraction } from 'discord.js';
+import { ClientEvents } from 'discord.js';
export default class InteractionCreateListener extends BushListener {
public constructor() {
@@ -10,7 +10,7 @@ export default class InteractionCreateListener extends BushListener {
});
}
- async exec(interaction: Interaction | CommandInteraction | ButtonInteraction | SelectMenuInteraction): Promise<unknown> {
+ async exec([interaction]: ClientEvents['interactionCreate']): Promise<unknown> {
if (!interaction) return;
if (interaction.isCommand()) {
this.client.console.info(
diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts
index 24d46af..c02f21c 100644
--- a/src/listeners/commands/commandBlocked.ts
+++ b/src/listeners/commands/commandBlocked.ts
@@ -1,4 +1,4 @@
-import { BushCommand, BushListener, BushMessage } from '@lib';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class CommandBlockedListener extends BushListener {
public constructor() {
@@ -8,7 +8,7 @@ export default class CommandBlockedListener extends BushListener {
});
}
- public async exec(message: BushMessage, command: BushCommand, reason: string): Promise<unknown> {
+ public async exec([message, command, reason]: BushCommandHandlerEvents['commandBlocked']): Promise<unknown> {
this.client.console.info(
'CommandBlocked',
`<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`,
diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts
index 696b59b..1aaefad 100644
--- a/src/listeners/commands/commandError.ts
+++ b/src/listeners/commands/commandError.ts
@@ -1,6 +1,5 @@
-import { BushListener, BushMessage } from '@lib';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
import { stripIndents } from 'common-tags';
-import { Command } from 'discord-akairo';
import { MessageEmbed } from 'discord.js';
export default class CommandErrorListener extends BushListener {
@@ -11,7 +10,7 @@ export default class CommandErrorListener extends BushListener {
});
}
- public async exec(error: Error, message: BushMessage, command: Command | null | undefined): Promise<void> {
+ public async exec([error, message, command]: BushCommandHandlerEvents['error']): Promise<void> {
const errorNo = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number
const errorEmbed: MessageEmbed = new MessageEmbed()
.setTitle(`Error # \`${errorNo}\`: An error occurred`)
diff --git a/src/listeners/commands/commandMissingPermissions.ts b/src/listeners/commands/commandMissingPermissions.ts
index f3d2218..765bc72 100644
--- a/src/listeners/commands/commandMissingPermissions.ts
+++ b/src/listeners/commands/commandMissingPermissions.ts
@@ -1,5 +1,4 @@
-import { BushCommand, BushListener, BushMessage } from '@lib';
-import { PermissionString } from 'discord.js';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class CommandMissingPermissionsListener extends BushListener {
public constructor() {
@@ -10,12 +9,7 @@ export default class CommandMissingPermissionsListener extends BushListener {
});
}
- public async exec(
- message: BushMessage,
- command: BushCommand | null | undefined,
- type: 'client' | 'user',
- missing: Array<PermissionString>
- ): Promise<void> {
+ public async exec([message, command, type, missing]: BushCommandHandlerEvents['missingPermissions']): Promise<void> {
const niceMissing = [];
missing.forEach((missing) => {
if (this.client.consts.mappings.permissions[missing]) {
diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts
index 24bb041..dcc1d83 100644
--- a/src/listeners/commands/commandStarted.ts
+++ b/src/listeners/commands/commandStarted.ts
@@ -1,20 +1,20 @@
-import { BushCommand, BushListener } from '@lib';
-import { Message } from 'discord.js';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class CommandStartedListener extends BushListener {
public constructor() {
super('commandStarted', {
emitter: 'commandHandler',
- event: 'commandStarted'
+ event: 'commandStarted',
+ category: 'commands'
});
}
- exec(message: Message, command: BushCommand): void {
+ exec([message, command]: BushCommandHandlerEvents['commandStarted']): void {
this.client.logger.info(
'Command',
`The <<${command.id}>> command was used by <<${message.author.tag}>> in ${
message.channel.type === 'DM' ? `their <<DMs>>` : `<<#${message.channel.name}>> in <<${message.guild?.name}>>`
}.`,
- true //// I don't want to spam the log channel when people use commands
+ true
);
}
}
diff --git a/src/listeners/commands/slashBlocked.ts b/src/listeners/commands/slashBlocked.ts
index 2443efb..ecbe863 100644
--- a/src/listeners/commands/slashBlocked.ts
+++ b/src/listeners/commands/slashBlocked.ts
@@ -1,14 +1,15 @@
-import { BushCommand, BushListener, BushSlashMessage } from '@lib';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class SlashBlockedListener extends BushListener {
public constructor() {
super('slashBlocked', {
emitter: 'commandHandler',
- event: 'slashBlocked'
+ event: 'slashBlocked',
+ category: 'commands'
});
}
- public async exec(message: BushSlashMessage, command: BushCommand, reason: string): Promise<unknown> {
+ public async exec([message, command, reason]: BushCommandHandlerEvents['slashBlocked']): Promise<unknown> {
this.client.console.info(
'SlashBlocked',
`<<${message.author.tag}>> tried to run <<${message.util.parsed.command}>> but was blocked because <<${reason}>>.`,
diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts
index 8abe788..4eaf293 100644
--- a/src/listeners/commands/slashCommandError.ts
+++ b/src/listeners/commands/slashCommandError.ts
@@ -1,4 +1,4 @@
-import { BushCommand, BushListener, BushSlashMessage } from '@lib';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
import { stripIndents } from 'common-tags';
import { GuildChannel, MessageEmbed } from 'discord.js';
@@ -6,10 +6,11 @@ export default class SlashCommandErrorListener extends BushListener {
public constructor() {
super('slashError', {
emitter: 'commandHandler',
- event: 'slashError'
+ event: 'slashError',
+ category: 'commands'
});
}
- async exec(error: Error, message: BushSlashMessage, command: BushCommand): Promise<void> {
+ async exec([error, message, command]: BushCommandHandlerEvents['slashError']): Promise<void> {
const errorNo = Math.floor(Math.random() * 6969696969) + 69; // hehe funny number
const errorEmbed: MessageEmbed = new MessageEmbed()
.setTitle(`Slash Error # \`${errorNo}\`: An error occurred`)
diff --git a/src/listeners/commands/slashMissingPermissions.ts b/src/listeners/commands/slashMissingPermissions.ts
index a410bef..45966bb 100644
--- a/src/listeners/commands/slashMissingPermissions.ts
+++ b/src/listeners/commands/slashMissingPermissions.ts
@@ -1,22 +1,15 @@
-import { BushListener } from '@lib';
-import { Command } from 'discord-akairo';
-import { CommandInteraction } from 'discord.js';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class SlashMissingPermissionsListener extends BushListener {
public constructor() {
super('slashMissingPermissions', {
emitter: 'commandHandler',
event: 'slashMissingPermissions',
- category: 'slashCommands'
+ category: 'commands'
});
}
- public async exec(
- interaction: CommandInteraction,
- command: Command,
- type: 'user' | 'client',
- missing?: string[]
- ): Promise<void> {
+ public async exec([message, command, type, missing]: BushCommandHandlerEvents['slashMissingPermissions']): Promise<void> {
const niceMissing = [];
missing.forEach((missing) => {
if (this.client.consts.mappings.permissions[missing]) {
@@ -30,24 +23,22 @@ export default class SlashMissingPermissionsListener extends BushListener {
const consoleFormat = this.client.util.oxford(this.client.util.surroundArray(niceMissing, '<<', '>>'), 'and', '');
this.client.console.info(
'CommandMissingPermissions',
- `<<${interaction.user.tag}>> tried to run <<${
+ `<<${message.author.tag}>> tried to run <<${
command?.id
}>> but could not because <<${type}>> is missing the ${consoleFormat} permissions${missing.length ? 's' : ''}.`,
true
);
if (type == 'client') {
- await this.client.util
- .slashRespond(
- interaction,
+ await message.util
+ .reply(
`${this.client.util.emojis.error} I am missing the ${discordFormat} permission${
missing.length ? 's' : ''
} required for the \`${command?.id}\` command.`
)
.catch(() => {});
} else if (type == 'user') {
- await this.client.util
- .slashRespond(
- interaction,
+ await message.util
+ .reply(
`${this.client.util.emojis.error} You are missing the ${discordFormat} permission${
missing.length ? 's' : ''
} required for the \`${command?.id}\` command.`
diff --git a/src/listeners/commands/slashStarted.ts b/src/listeners/commands/slashStarted.ts
index a0201c5..6ff3c6e 100644
--- a/src/listeners/commands/slashStarted.ts
+++ b/src/listeners/commands/slashStarted.ts
@@ -1,19 +1,20 @@
-import { BushCommand, BushListener, BushSlashMessage } from '@lib';
+import { BushCommandHandlerEvents, BushListener } from '@lib';
export default class SlashStartedListener extends BushListener {
public constructor() {
super('slashStarted', {
emitter: 'commandHandler',
- event: 'slashStarted'
+ event: 'slashStarted',
+ category: 'commands'
});
}
- exec(message: BushSlashMessage, command: BushCommand): void {
- this.client.logger.info(
+ async exec([message, command]: BushCommandHandlerEvents['slashStarted']): Promise<unknown> {
+ return await this.client.logger.info(
'SlashCommand',
`The <<${command.id}>> command was used by <<${message.author.tag}>> in ${
message.channel.type === 'DM' ? `their <<DMs>>` : `<<#${message.channel.name}>> in <<${message.guild?.name}>>`
}.`,
- true //// I don't want to spam the log channel when people use commands
+ true
);
}
}
diff --git a/src/listeners/guild/syncUnban.ts b/src/listeners/guild/syncUnban.ts
index aa148f9..c9ba0cb 100644
--- a/src/listeners/guild/syncUnban.ts
+++ b/src/listeners/guild/syncUnban.ts
@@ -1,5 +1,5 @@
-import { Ban, BushListener } from '@lib';
-import { Guild, User } from 'discord.js';
+import { ActivePunishment, ActivePunishmentType, BushListener } from '@lib';
+import { ClientEvents } from 'discord.js';
export default class SyncUnbanListener extends BushListener {
public constructor() {
@@ -9,11 +9,12 @@ export default class SyncUnbanListener extends BushListener {
});
}
- public async exec(guild: Guild, user: User): Promise<void> {
- const bans = await Ban.findAll({
+ public async exec([ban]: ClientEvents['guildBanRemove']): Promise<void> {
+ const bans = await ActivePunishment.findAll({
where: {
- user: user.id,
- guild: guild.id
+ user: ban.user,
+ guild: ban.guild,
+ type: ActivePunishmentType.BAN
}
});
for (const dbBan of bans) {
diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts
index 1f57930..b06fdd2 100644
--- a/src/listeners/message/level.ts
+++ b/src/listeners/message/level.ts
@@ -1,5 +1,5 @@
import { BushListener, Level } from '@lib';
-import { Message } from 'discord.js';
+import { Message, MessageType } from 'discord.js';
export default class LevelListener extends BushListener {
private levelCooldowns: Set<string> = new Set();
@@ -17,7 +17,8 @@ export default class LevelListener extends BushListener {
if (message.util?.parsed?.command) return;
if (this.levelCooldowns.has(`${message.guild.id}-${message.author.id}`)) return;
if (this.blacklistedChannels.includes(message.channel.id)) return;
- if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc
+ const allowedMessageTypes: MessageType[] = ['DEFAULT', 'REPLY']; // this is so ts will yell at me when discord.js makes some unnecessary breaking change
+ if (!allowedMessageTypes.includes(message.type)) return; //checks for join messages, slash commands, booster messages etc
const [user] = await Level.findOrBuild({
where: {
user: message.author.id,
diff --git a/src/listeners/other/consoleListener.ts b/src/listeners/other/consoleListener.ts
index ef1efd5..90f9871 100644
--- a/src/listeners/other/consoleListener.ts
+++ b/src/listeners/other/consoleListener.ts
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { BushListener } from '@lib';
+import { exec } from 'child_process';
+import { promisify } from 'util';
export default class ConsoleListener extends BushListener {
public constructor() {
@@ -12,10 +14,12 @@ export default class ConsoleListener extends BushListener {
public async exec(line: string): Promise<void> {
if (line.startsWith('eval ') || line.startsWith('ev ')) {
- const bot = this.client,
+ const
+ sh = promisify(exec),
+ bot = this.client,
config = this.client.config,
client = this.client,
- { Ban, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'),
+ { ActivePunishment, Global, Guild, Level, ModLog, StickyRole } = await import('@lib'),
{
ButtonInteraction,
Collector,
@@ -26,12 +30,14 @@ export default class ConsoleListener extends BushListener {
MessageAttachment,
MessageButton,
MessageCollector,
- MessageComponentInteractionCollector,
+ InteractionCollector,
MessageEmbed,
MessageSelectMenu,
ReactionCollector,
- Util
- } = require('discord.js');
+ Util,
+ Collection
+ } = await import('discord.js'),
+ { Canvas } = await import('node-canvas');
try {
const input = line.replace('eval ', '').replace('ev ', '');
let output = eval(input);
diff --git a/src/tasks/removeExpiredPunishements.ts b/src/tasks/removeExpiredPunishements.ts
new file mode 100644
index 0000000..d0220ba
--- /dev/null
+++ b/src/tasks/removeExpiredPunishements.ts
@@ -0,0 +1,74 @@
+import { BushGuild, BushGuildMember, BushTask } from '@lib';
+import { Op } from 'sequelize';
+import { ActivePunishment, ActivePunishmentType } from '../lib/models/ActivePunishment';
+
+export default class RemoveExpiredPunishmentsTask extends BushTask {
+ public constructor() {
+ super('removeExpiredPunishments', {
+ delay: 15_000, // 15 seconds
+ runOnStart: true
+ });
+ }
+ async exec(): Promise<void> {
+ const expiredEntries = await ActivePunishment.findAll({
+ where: {
+ [Op.and]: [
+ {
+ expires: {
+ [Op.lt]: new Date() // Find all rows with an expiry date before now
+ }
+ }
+ ]
+ }
+ });
+
+ this.client.logger.verbose(
+ `removeExpiredPunishments`,
+ `Queried punishments, found <<${expiredEntries.length}>> expired punishments.`
+ );
+
+ for (const entry of expiredEntries) {
+ const guild = this.client.guilds.cache.get(entry.guild) as BushGuild;
+ const member = guild.members.cache.get(entry.user) as BushGuildMember;
+
+ if (!guild) {
+ await entry.destroy();
+ continue;
+ }
+
+ switch (entry.type) {
+ case ActivePunishmentType.BAN: {
+ const result = await guild.unban({ user: entry.user, reason: 'Punishment expired.' });
+ if (['success', 'user not banned'].includes(result)) await entry.destroy();
+ else throw result;
+ this.client.logger.verbose(`removeExpiredPunishments`, `Unbanned ${entry.user}.`);
+ break;
+ }
+ case ActivePunishmentType.BLOCK: {
+ //todo
+ break;
+ }
+ case ActivePunishmentType.MUTE: {
+ const result = await member.unmute({ reason: 'Punishment expired.' });
+ if (['success', 'failed to dm'].includes(result)) await entry.destroy();
+ else throw result;
+ this.client.logger.verbose(`removeExpiredPunishments`, `Unmuted ${entry.user}.`);
+ break;
+ }
+ case ActivePunishmentType.ROLE: {
+ const role = guild?.roles?.cache?.get(entry.extraInfo);
+ const result = await member.removeRole({
+ reason: 'Punishment expired.',
+ role: role,
+ addToModlog: true
+ });
+
+ if (['success', 'failed to dm'].includes(result)) await entry.destroy();
+ else throw result;
+ this.client.logger.verbose(`removeExpiredPunishments`, `Removed a punishment role from ${entry.user}.`);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/tasks/removePunishmentRole.ts b/src/tasks/removePunishmentRole.ts
deleted file mode 100644
index 9830338..0000000
--- a/src/tasks/removePunishmentRole.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { BushGuildMember, BushTask } from '@lib';
-
-export default class RemovePunishmentRole extends BushTask {
- public constructor() {
- super('removePunishmentRole', {
- delay: 30_000, // 1/2 min
- runOnStart: true
- });
- }
- async exec(): Promise<void> {
- const expiredEntries = await this.client.util.findExpiredEntries('role');
- this.client.logger.verbose(
- `RemovePunishmentRoleTask`,
- `Queried punishment roles, found <<${expiredEntries.length}>> expired punishment roles.`
- );
-
- for (const entry of expiredEntries) {
- const guild = this.client.guilds.cache.get(entry.guild);
- const role = guild?.roles?.cache?.get(entry.role);
- if (!guild || !role) {
- await entry.destroy();
- continue;
- }
-
- const member = guild.members.cache.get(entry.user) as BushGuildMember;
- const result = await member.removeRole({
- reason: 'Punishment expired.',
- role: role,
- addToModlog: true
- });
- if (['success', 'failed to dm'].includes(result)) await entry.destroy();
- else throw result;
-
- this.client.logger.verbose(`RemovePunishmentRoleTask`, `Removed a punishment role from ${entry.user}.`);
- }
- }
-}
diff --git a/src/tasks/unban.ts b/src/tasks/unban.ts
deleted file mode 100644
index 136e6c2..0000000
--- a/src/tasks/unban.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { BushGuild, BushTask } from '@lib';
-
-export default class UnbanTask extends BushTask {
- public constructor() {
- super('unban', {
- delay: 30_000, // 1/2 min
- runOnStart: true
- });
- }
- async exec(): Promise<void> {
- const rows = await this.client.util.findExpiredEntries('mute');
- this.client.logger.verbose(`UnbanTask`, `Queried bans, found <<${rows.length}>> expired bans.`);
-
- for (const row of rows) {
- const guild = this.client.guilds.cache.get(row.guild) as BushGuild;
- if (!guild) {
- await row.destroy();
- continue;
- }
-
- const result = await guild.unban({ user: row.user, reason: 'Punishment expired.' });
- if (['success', 'user not banned'].includes(result)) await row.destroy();
- else throw result;
- this.client.logger.verbose(`UnbanTask`, `Unbanned ${row.user}`);
- }
- }
-}
diff --git a/src/tasks/unmute.ts b/src/tasks/unmute.ts
deleted file mode 100644
index c61c6e9..0000000
--- a/src/tasks/unmute.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { BushGuildMember, BushTask, Mute } from '@lib';
-import { Op } from 'sequelize';
-
-export default class UnmuteTask extends BushTask {
- public constructor() {
- super('unmute', {
- delay: 30_000, // 1/2 min
- runOnStart: true
- });
- }
- async exec(): Promise<void> {
- const rows = await Mute.findAll({
- where: {
- [Op.and]: [
- {
- expires: {
- [Op.lt]: new Date() // Find all rows with an expiry date before now
- }
- }
- ]
- }
- });
- this.client.logger.verbose(`UnmuteTask`, `Queried mutes, found <<${rows.length}>> expired mutes.`);
- for (const row of rows) {
- const guild = this.client.guilds.cache.get(row.guild);
- if (!guild) {
- await row.destroy();
- continue;
- }
-
- const member = guild.members.cache.get(row.user) as BushGuildMember;
- const result = await member.unmute({ reason: 'Punishment expired.' });
- if (['success', 'failed to dm'].includes(result)) await row.destroy();
- else throw result;
-
- this.client.logger.verbose(`UnmuteTask`, `Unmuted ${row.user}`);
- }
- }
-}
diff --git a/yarn.lock b/yarn.lock
index ca5d694..dd518d7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14,10 +14,10 @@ __metadata:
languageName: node
linkType: hard
-"@babel/helper-validator-identifier@npm:^7.14.5":
- version: 7.14.5
- resolution: "@babel/helper-validator-identifier@npm:7.14.5"
- checksum: 6366bceab4498785defc083a1bd96344f788d90a1aa7a6f18d6813c1d3d134640bfc05690453c0b79bbfc820472cf5b29110dfddaca1f8e2763dfe1bd5df0b88
+"@babel/helper-validator-identifier@npm:^7.14.5, @babel/helper-validator-identifier@npm:^7.14.8":
+ version: 7.14.8
+ resolution: "@babel/helper-validator-identifier@npm:7.14.8"
+ checksum: f21ad9a9f0a66a02e0e5f62d505cbeb9e01a7ac5bd34be0af9f916f0b6d8d40718efaf51b656b41759e3454703090b4d386105f1f97f6598ee5a3f8eb98adc6a
languageName: node
linkType: hard
@@ -33,21 +33,21 @@ __metadata:
linkType: hard
"@babel/parser@npm:^7.0.0":
- version: 7.14.7
- resolution: "@babel/parser@npm:7.14.7"
+ version: 7.14.8
+ resolution: "@babel/parser@npm:7.14.8"
bin:
parser: ./bin/babel-parser.js
- checksum: 0d7acc8cf9c19ccd0e80ab0608953f32f4375f3867c080211270e7bb4bb94c551fd1fc3f49b3cc92a4eec356cf507801f5c93c4c72996968bdc4c28815fe0550
+ checksum: 9e532b2bbe690fff8cdaf8c25cfecb684ebe9e9d50d30cd775852dd711649ddb964368b26fda55786404fadf500f944043fb0f731b765104ad857d677dd29ce5
languageName: node
linkType: hard
"@babel/types@npm:^7.8.3":
- version: 7.14.5
- resolution: "@babel/types@npm:7.14.5"
+ version: 7.14.8
+ resolution: "@babel/types@npm:7.14.8"
dependencies:
- "@babel/helper-validator-identifier": ^7.14.5
+ "@babel/helper-validator-identifier": ^7.14.8
to-fast-properties: ^2.0.0
- checksum: 7c1ab6e8bdf438d44236034cab10f7d0f1971179bc405dca26733a9b89dd87dd692dc49a238a7495075bc41a9a17fb6f08b4d1da45ea6ddcce1e5c8593574aea
+ checksum: d4ebd2e0e52f05cbcb3ded434d9fb49db73c239d98c4f7bd27beaf32fcd7c81aa30618237e87d53505d5e65fd20d688cb4237b6fa927a04831129a6044f2e4b5
languageName: node
linkType: hard
@@ -319,19 +319,19 @@ __metadata:
linkType: hard
"@types/node-fetch@npm:^2":
- version: 2.5.11
- resolution: "@types/node-fetch@npm:2.5.11"
+ version: 2.5.12
+ resolution: "@types/node-fetch@npm:2.5.12"
dependencies:
"@types/node": "*"
form-data: ^3.0.0
- checksum: a52ee9a205ce3130404a1c8eb0a163aa013fb94c5c93150735dda55bd4d21a556713834b11f6f031721ad7c82d9d7f77d45436010cefc11e851f518dfeeaca3e
+ checksum: ad63c85ba6a9477b8e057ec8682257738130d98e8ece4e31141789bd99df9d9147985cc8bc0cb5c8983ed5aa6bb95d46df23d1e055f4ad5cf8b82fc69cf626c7
languageName: node
linkType: hard
"@types/node@npm:*":
- version: 16.3.3
- resolution: "@types/node@npm:16.3.3"
- checksum: fa885b835e57a4a2fd15571e5cbfe361d4ac48278196416aa2690ebdfb118a116249dd81475078c61fbf2d95920324f8f3a66ae7f19fac424ae74ab87a41c1e4
+ version: 16.4.1
+ resolution: "@types/node@npm:16.4.1"
+ checksum: 6cff78c802a1efecc900506a468a9bdaa321c7132a05e87552ab710f3d3a5cd230308317634f94b8db4833c66c72a765b28670d5ccf8c7c5a7363fcce32d1b1e
languageName: node
linkType: hard
@@ -390,20 +390,20 @@ __metadata:
linkType: hard
"@types/ws@npm:^7.4.4, @types/ws@npm:^7.4.5":
- version: 7.4.6
- resolution: "@types/ws@npm:7.4.6"
+ version: 7.4.7
+ resolution: "@types/ws@npm:7.4.7"
dependencies:
"@types/node": "*"
- checksum: 500c2f7edcb5bbcb34ecf535c6163aad6c901a04d806089931125eecd630e9f30e41a167a4b9929aaaae9658d4fc4146f3a29762857f0d18a4038b6641ec36f8
+ checksum: b4c9b8ad209620c9b21e78314ce4ff07515c0cadab9af101c1651e7bfb992d7fd933bd8b9c99d110738fd6db523ed15f82f29f50b45510288da72e964dedb1a3
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^4.14.1":
- version: 4.28.3
- resolution: "@typescript-eslint/eslint-plugin@npm:4.28.3"
+ version: 4.28.4
+ resolution: "@typescript-eslint/eslint-plugin@npm:4.28.4"
dependencies:
- "@typescript-eslint/experimental-utils": 4.28.3
- "@typescript-eslint/scope-manager": 4.28.3
+ "@typescript-eslint/experimental-utils": 4.28.4
+ "@typescript-eslint/scope-manager": 4.28.4
debug: ^4.3.1
functional-red-black-tree: ^1.0.1
regexpp: ^3.1.0
@@ -415,66 +415,66 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 2ac7113dbb0916ee911c3cee552b31b82c3c286ce4be1d2b5b8fbbe64eb05eb8c5187a64aba2fc5d46f2bc2d391887b33e00edbb65f2f25f41342efa346776ef
+ checksum: 8de0301888e7308bca45c1cbfb28693b365780e85e0d7810ec9f004fc3f81b90871d2a55ad71d5865a93ce5d382e13ca9bbdc43d4234e0e409ef65f1348fe864
languageName: node
linkType: hard
-"@typescript-eslint/experimental-utils@npm:4.28.3":
- version: 4.28.3
- resolution: "@typescript-eslint/experimental-utils@npm:4.28.3"
+"@typescript-eslint/experimental-utils@npm:4.28.4":
+ version: 4.28.4
+ resolution: "@typescript-eslint/experimental-utils@npm:4.28.4"
dependencies:
"@types/json-schema": ^7.0.7
- "@typescript-eslint/scope-manager": 4.28.3
- "@typescript-eslint/types": 4.28.3
- "@typescript-eslint/typescript-estree": 4.28.3
+ "@typescript-eslint/scope-manager": 4.28.4
+ "@typescript-eslint/types": 4.28.4
+ "@typescript-eslint/typescript-estree": 4.28.4
eslint-scope: ^5.1.1
eslint-utils: ^3.0.0
peerDependencies:
eslint: "*"
- checksum: 09b1b196318acbf6efbb9ea93fc73b18a77c1fc04efb24bc77d941666b5a0c48828f2d788079bdfd340828045d15054a4c95fba5367e7c8b1fe53de53736a1db
+ checksum: 71eb19a55efb32b28f2cf130c6a9689ac9df18d41ac0eb0351f1bd47c2ef39e8acbc20d743830ecd2f60d2b18f38a45a588f1b6e292cacf5e55b5f57c2043583
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^4.14.1":
- version: 4.28.3
- resolution: "@typescript-eslint/parser@npm:4.28.3"
+ version: 4.28.4
+ resolution: "@typescript-eslint/parser@npm:4.28.4"
dependencies:
- "@typescript-eslint/scope-manager": 4.28.3
- "@typescript-eslint/types": 4.28.3
- "@typescript-eslint/typescript-estree": 4.28.3
+ "@typescript-eslint/scope-manager": 4.28.4
+ "@typescript-eslint/types": 4.28.4
+ "@typescript-eslint/typescript-estree": 4.28.4
debug: ^4.3.1
peerDependencies:
eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
peerDependenciesMeta:
typescript:
optional: true
- checksum: 3a5d64237e7085235f3b27eece73105bd5a7b51f17c868c086a6062bd8ab6901c908270d41c055f44d9f440c6a1bb3203429693a07436905cc60d3a03cd361e9
+ checksum: 838c4fed7ad14652edd02a3649a4da2c2a0f8c3e7496657f573791114c9e0aa0278163350b349e722b2d414080c108d18e9c6571c110b229bb17ff089c8ebda3
languageName: node
linkType: hard
-"@typescript-eslint/scope-manager@npm:4.28.3":
- version: 4.28.3
- resolution: "@typescript-eslint/scope-manager@npm:4.28.3"
+"@typescript-eslint/scope-manager@npm:4.28.4":
+ version: 4.28.4
+ resolution: "@typescript-eslint/scope-manager@npm:4.28.4"
dependencies:
- "@typescript-eslint/types": 4.28.3
- "@typescript-eslint/visitor-keys": 4.28.3
- checksum: 9ffb955581311ef46d7aebf23ac544c96164bac00f7657f963680ba7b239b1c021733318ea22ca2965ff9b8f2798dff8ae5d7add0dae4207d3dad86d5e4f0f1f
+ "@typescript-eslint/types": 4.28.4
+ "@typescript-eslint/visitor-keys": 4.28.4
+ checksum: 75ff460989d334dcef513b8ea06d8c9039731092f65790abee90f4251d1f145f9496894d5fe076b1f26612a1dce29acbddfdb73472d49fa8a0dd63451a8eb2b5
languageName: node
linkType: hard
-"@typescript-eslint/types@npm:4.28.3":
- version: 4.28.3
- resolution: "@typescript-eslint/types@npm:4.28.3"
- checksum: 15f052f92ee429056e7bdd4b0ff3750d72a6ab6c202a46f394dcd7490da7f670d7b516fc5febc96332cced39fdf3fd20c4893ee58fb93ce9b58e2f1e1766d7bb
+"@typescript-eslint/types@npm:4.28.4":
+ version: 4.28.4
+ resolution: "@typescript-eslint/types@npm:4.28.4"
+ checksum: be565692cc42ce387fe8a1cc9ad44edaa8dea45728a33f3ad8cc8ee664bbd3ef220d529fdc7a2165b60cd1eb5280b767fbb7f351e56f2c0b90c99d2ccf24ad06
languageName: node
linkType: hard
-"@typescript-eslint/typescript-estree@npm:4.28.3, @typescript-eslint/typescript-estree@npm:^4.8.2":
- version: 4.28.3
- resolution: "@typescript-eslint/typescript-estree@npm:4.28.3"
+"@typescript-eslint/typescript-estree@npm:4.28.4, @typescript-eslint/typescript-estree@npm:^4.8.2":
+ version: 4.28.4
+ resolution: "@typescript-eslint/typescript-estree@npm:4.28.4"
dependencies:
- "@typescript-eslint/types": 4.28.3
- "@typescript-eslint/visitor-keys": 4.28.3
+ "@typescript-eslint/types": 4.28.4
+ "@typescript-eslint/visitor-keys": 4.28.4
debug: ^4.3.1
globby: ^11.0.3
is-glob: ^4.0.1
@@ -483,17 +483,17 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
- checksum: 674cdd5e3c24306f906d6479ede454d5f5a2100e22cd5aa08f9bc27953cdb4930384ad3b4434937e31e8e92e6b3cc867f8d56c3b65e2254f7042662deafef583
+ checksum: 526f41028d63ddb506586abe6ca5ffd6cee54b2773fc70e803d61988682f4528228bef8a6773ea5219e7412887e4c74732736620a4e904e51239b7acab4441b9
languageName: node
linkType: hard
-"@typescript-eslint/visitor-keys@npm:4.28.3":
- version: 4.28.3
- resolution: "@typescript-eslint/visitor-keys@npm:4.28.3"
+"@typescript-eslint/visitor-keys@npm:4.28.4":
+ version: 4.28.4
+ resolution: "@typescript-eslint/visitor-keys@npm:4.28.4"
dependencies:
- "@typescript-eslint/types": 4.28.3
+ "@typescript-eslint/types": 4.28.4
eslint-visitor-keys: ^2.0.0
- checksum: b570740ae16901df85febde13b5e34b95dfa13fc51b035875bc087fef356f2db1284ff045812ace06b0e8f821171a7ddaf7606cee672d35df486989e79a0c7df
+ checksum: d0b359dc0aaf0f6c3396bf7bee31f9ec7a6d90b7f69890478a002af6eb9ab4a7be466b4ddb050af87ce5f1d01384ce41fd976d472cbe587787b2ee21d977e8f6
languageName: node
linkType: hard
@@ -804,7 +804,7 @@ __metadata:
chalk: ^4.1.1
common-tags: ^1.8.0
discord-akairo: NotEnoughUpdates/discord-akairo
- discord-api-types: 0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e
+ discord-api-types: 0.19.0
discord.js: NotEnoughUpdates/discord.js
discord.js-minesweeper: ^1.0.6
esbuild: ^0.12.11
@@ -1328,15 +1328,15 @@ __metadata:
discord-akairo@NotEnoughUpdates/discord-akairo:
version: 8.2.2
- resolution: "discord-akairo@https://github.com/NotEnoughUpdates/discord-akairo.git#commit=7164ae3af00266b0ac357e3e65ef5d4101308c40"
- checksum: 63a85f0de4d018b83ecd537b42dadafdc471d90c67559281ea8e202527f4b11eef6a1b88cb43b229e883f9ed52041994df7a5653077ba8e3dae6bf8fea1c9c75
+ resolution: "discord-akairo@https://github.com/NotEnoughUpdates/discord-akairo.git#commit=f31c8e8b27e1ca8c918cf10e720d36ba31fdd7dc"
+ checksum: 193b6ca28637f542a1ee10090e3b54a83c36fdd188f746eb86881dee94b8b5d85c74a544c6febba274e49898f504ea84e224fa82ac41e60b06a98c8744c1258e
languageName: node
linkType: hard
-"discord-api-types@npm:0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e, discord-api-types@npm:^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e":
- version: 0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e
- resolution: "discord-api-types@npm:0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e"
- checksum: 0b3f2cad00edd005bf6542bb6ce712777b09e917f955f8d4f69a292ed5da2f60721e709cc11ab226879658e839056e1aff16d724b3b1c6689217eb34c2b4d3f5
+"discord-api-types@npm:0.19.0, discord-api-types@npm:^0.19.0":
+ version: 0.19.0
+ resolution: "discord-api-types@npm:0.19.0"
+ checksum: 83d9363772a0ac0fcfa9df61da1c510c5427c2d82cc4b1c2e0a8ab50c31b2ae297a416cd1559b21b039dcc8065e5dca962dbac12522ee5e3447e9f4ebcb8a846
languageName: node
linkType: hard
@@ -1356,7 +1356,7 @@ discord-akairo@NotEnoughUpdates/discord-akairo:
discord.js@NotEnoughUpdates/discord.js:
version: 13.0.0-dev
- resolution: "discord.js@https://github.com/NotEnoughUpdates/discord.js.git#commit=8551402ddbac89a53133385f953c595e8be16d98"
+ resolution: "discord.js@https://github.com/NotEnoughUpdates/discord.js.git#commit=d0edf2a15b62e3b739d3f0ee8fc4ffd9c569fce5"
dependencies:
"@discordjs/builders": ^0.2.0
"@discordjs/collection": ^0.1.6
@@ -1364,10 +1364,10 @@ discord.js@NotEnoughUpdates/discord.js:
"@sapphire/async-queue": ^1.1.4
"@types/ws": ^7.4.5
abort-controller: ^3.0.0
- discord-api-types: ^0.19.0-next.f393ba520d7d6d2aacaca7b3ca5d355fab614f6e
+ discord-api-types: ^0.19.0
node-fetch: ^2.6.1
ws: ^7.5.1
- checksum: f5a41b02faa95d7d5efafd734c8af9b063badcce257fe87c3399fcafab083026ba2334793d46d01553670cd57081a4650c68a6c05a417bf2640591b05c54f82c
+ checksum: 44a2c3f49b0a553af63b80dad1b6c5df2ae5f289dd61b980a1a342d446727d07f0703750e92d932d04430f74ed281a5d7d11dd0b2499045e15aa9b65ef7c6294
languageName: node
linkType: hard
@@ -3104,13 +3104,13 @@ discord.js@NotEnoughUpdates/discord.js:
linkType: hard
"postcss@npm:^8.1.7, postcss@npm:^8.2.13":
- version: 8.3.5
- resolution: "postcss@npm:8.3.5"
+ version: 8.3.6
+ resolution: "postcss@npm:8.3.6"
dependencies:
colorette: ^1.2.2
nanoid: ^3.1.23
source-map-js: ^0.6.2
- checksum: c73fc4825ed27396d453a942628cd8e34dd43c11b724f43f65f376d3900037736013b6446f1d9947ce5a847837cf96649e9a3f200ca2bd94a884e91e56ee1ceb
+ checksum: ff55b91bea21f42c2a94d77fd05c3f66dd15889c68506cf1dbb9cdee8c3b9e9d0e219bcbc6e61a107bd63e3cac0670176486e2a5794c106a4e1b9babceb79317
languageName: node
linkType: hard
@@ -3383,9 +3383,9 @@ discord.js@NotEnoughUpdates/discord.js:
linkType: hard
"resolve-alpn@npm:^1.0.0":
- version: 1.1.2
- resolution: "resolve-alpn@npm:1.1.2"
- checksum: 99ffb597caf1cbe811a6cab283a9a0cd74198e4439c8c91f444d426a81b880e29829c7293b349f3647fd82b226a63424bc492d59ef7d8070a180e2c13f402721
+ version: 1.2.0
+ resolution: "resolve-alpn@npm:1.2.0"
+ checksum: a38b5bf2084d384586fe15b31735396cff4640cbe137c87ffd1dcc94dcdc0c743e6875ec4baa7a9eed460355a46e3f9ebafa4cdbb8122264ed5f4e3260f69d0c
languageName: node
linkType: hard
@@ -3889,8 +3889,8 @@ resolve@^1.19.0:
linkType: hard
"tar@npm:^6.0.2, tar@npm:^6.1.0":
- version: 6.1.0
- resolution: "tar@npm:6.1.0"
+ version: 6.1.1
+ resolution: "tar@npm:6.1.1"
dependencies:
chownr: ^2.0.0
fs-minipass: ^2.0.0
@@ -3898,7 +3898,7 @@ resolve@^1.19.0:
minizlib: ^2.1.1
mkdirp: ^1.0.3
yallist: ^4.0.0
- checksum: 0638a405b625263e0c47e97f0ea5e871b1a549da4593e31bf1792bcc83d97c28065ed172669f186744526637ea627a424d519ddd99f3fd52b17ac75f58f43519
+ checksum: ffd7d67d5bfa6f8ef0024c0c7c6da575f2257988f022e61299372db1d50b78868cfaf04d13abe41f480d593ace5628b44d2998ac7b0bda883d1385b578877514
languageName: node
linkType: hard