├── index.ts ├── .gitignore ├── lib ├── index.d.ts ├── src │ ├── interfaces.js │ ├── constants.d.ts │ ├── events │ │ ├── role │ │ │ ├── roleCreate.d.ts │ │ │ ├── roleDelete.d.ts │ │ │ ├── roleCreate.js │ │ │ └── roleDelete.js │ │ ├── guild │ │ │ ├── guildBanAdd.d.ts │ │ │ ├── guildBanRemove.d.ts │ │ │ ├── guildMemberRemove.d.ts │ │ │ ├── guildBanAdd.js │ │ │ ├── guildBanRemove.js │ │ │ └── guildMemberRemove.js │ │ ├── channel │ │ │ ├── channelCreate.d.ts │ │ │ ├── channelDelete.d.ts │ │ │ ├── channelCreate.js │ │ │ └── channelDelete.js │ │ └── threads │ │ │ ├── threadCreate.d.ts │ │ │ ├── threadDelete.d.ts │ │ │ ├── threadCreate.js │ │ │ └── threadDelete.js │ ├── BaseEvent.d.ts │ ├── constants.js │ ├── interfaces.d.ts │ ├── AntiRaidManager.d.ts │ ├── BaseEvent.js │ └── AntiRaidManager.js └── index.js ├── tsconfig.json ├── CHANGELOG.md ├── src ├── events │ ├── role │ │ ├── roleCreate.ts │ │ └── roleDelete.ts │ ├── guild │ │ ├── guildBanAdd.ts │ │ ├── guildBanRemove.ts │ │ └── guildMemberRemove.ts │ ├── channel │ │ ├── channelCreate.ts │ │ └── channelDelete.ts │ └── threads │ │ ├── threadCreate.ts │ │ └── threadDelete.ts ├── Constants.ts ├── interfaces.ts ├── BaseEvent.ts └── AntiRaidManager.ts ├── .github └── workflows │ └── npm-publish.yml ├── .eslintrc.json ├── LICENSE ├── package.json └── README.md /index.ts: -------------------------------------------------------------------------------- 1 | export {AntiRaidManager} from './src/AntiRaidManager'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules 3 | package-lock.json 4 | /test -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | export { AntiRaidManager } from './src/AntiRaidManager'; 2 | -------------------------------------------------------------------------------- /lib/src/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /lib/src/constants.d.ts: -------------------------------------------------------------------------------- 1 | import { ActionType, AntiRaidManagerOptions } from './interfaces'; 2 | export declare const defaultOptions: AntiRaidManagerOptions; 3 | export declare const AuditActionsTypes: ActionType; 4 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.AntiRaidManager = void 0; 4 | var AntiRaidManager_1 = require("./src/AntiRaidManager"); 5 | Object.defineProperty(exports, "AntiRaidManager", { enumerable: true, get: function () { return AntiRaidManager_1.AntiRaidManager; } }); 6 | -------------------------------------------------------------------------------- /lib/src/events/role/roleCreate.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { Role } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(role: Role): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/role/roleDelete.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { Role } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(role: Role): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildBanAdd.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { GuildBan } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(ban: GuildBan): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildBanRemove.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { GuildBan } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(ban: GuildBan): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/channel/channelCreate.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { GuildChannel } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(channel: GuildChannel): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/channel/channelDelete.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { GuildChannel } from 'discord.js'; 4 | export default class ChannelDelete extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(channel: GuildChannel): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildMemberRemove.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { GuildMember } from 'discord.js'; 4 | export default class ChannelCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(member: GuildMember): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/threads/threadCreate.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { ThreadChannel } from 'discord.js'; 4 | export default class ThreadCreate extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(thread: ThreadChannel): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/events/threads/threadDelete.d.ts: -------------------------------------------------------------------------------- 1 | import { BaseEvent } from '../../BaseEvent'; 2 | import { AntiRaidManager } from '../../AntiRaidManager'; 3 | import { ThreadChannel } from 'discord.js'; 4 | export default class ThreadDelete extends BaseEvent { 5 | constructor(manager: AntiRaidManager); 6 | run(thread: ThreadChannel): Promise; 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "strict": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strictPropertyInitialization": false 10 | }, 11 | "include": ["src/", "index.ts"], 12 | "exclude": ["node_modules/*", "**/__tests__/*"] 13 | } 14 | 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | 3 | ### In version 3.0.0 4 | ##### -Add: 5 | * Discord.js v13.x support 6 | * Threads and MemberKick events 7 | * eslint configuration for better code quality 8 | 9 | ##### -Remove: 10 | * Docs 11 | * Useless things 12 | * 13 | 14 | ##### -Other: 15 | * Code optimization 16 | * Requests optimization 17 | * Rewriting the lib 18 | * Remade event's handler 19 | 20 | -------------------------------------------------------------------------------- /src/events/role/roleCreate.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {Role} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (role: Role) { 10 | await this.antiraid(role.guild.id, this.name, Date.now(), role.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/role/roleDelete.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {Role} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (role: Role) { 10 | await this.antiraid(role.guild.id, this.name, Date.now(), role.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/guild/guildBanAdd.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {GuildBan} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (ban: GuildBan) { 10 | await this.antiraid(ban.guild.id, this.name, Date.now(), ban.user.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/guild/guildBanRemove.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {GuildBan} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (ban: GuildBan) { 10 | await this.antiraid(ban.guild.id, this.name, Date.now(), ban.user.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/channel/channelCreate.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {GuildChannel} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (channel: GuildChannel) { 10 | await this.antiraid(channel.guild.id, this.name, Date.now(), channel.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/channel/channelDelete.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {GuildChannel} from 'discord.js'; 4 | 5 | export default class ChannelDelete extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (channel: GuildChannel) { 10 | await this.antiraid(channel.guild.id, this.name, Date.now(), channel.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/guild/guildMemberRemove.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {GuildMember} from 'discord.js'; 4 | 5 | export default class ChannelCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (member: GuildMember) { 10 | await this.antiraid(member.guild.id, this.name, Date.now(), member.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/threads/threadCreate.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {ThreadChannel} from 'discord.js'; 4 | 5 | export default class ThreadCreate extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (thread: ThreadChannel) { 10 | await this.antiraid(thread.guild.id, this.name, Date.now(), thread.id); 11 | } 12 | } -------------------------------------------------------------------------------- /src/events/threads/threadDelete.ts: -------------------------------------------------------------------------------- 1 | import {BaseEvent} from '../../BaseEvent'; 2 | import {AntiRaidManager} from '../../AntiRaidManager'; 3 | import {ThreadChannel} from 'discord.js'; 4 | 5 | export default class ThreadDelete extends BaseEvent { 6 | constructor (manager: AntiRaidManager) { 7 | super(manager, __filename); 8 | } 9 | async run (thread: ThreadChannel) { 10 | await this.antiraid(thread.guild.id, this.name, Date.now(), thread.id); 11 | } 12 | } -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | 9 | publish-npm: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 12 16 | registry-url: https://registry.npmjs.org/ 17 | - run: npm publish 18 | env: 19 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 20 | -------------------------------------------------------------------------------- /lib/src/BaseEvent.d.ts: -------------------------------------------------------------------------------- 1 | import { AntiRaidManager } from './AntiRaidManager'; 2 | import { AntiRaidManagerOptions, EventsNames } from './interfaces'; 3 | import { GuildMember } from 'discord.js'; 4 | export declare abstract class BaseEvent { 5 | protected _manager: AntiRaidManager; 6 | name: EventsNames; 7 | constructor(manager: AntiRaidManager, filename: string); 8 | protected abstract run(...args: any[]): void; 9 | protected checkEvent(options: AntiRaidManagerOptions): boolean; 10 | protected checkUser(options: AntiRaidManagerOptions, memberId: string): boolean; 11 | protected checkRoles(options: AntiRaidManagerOptions, member: GuildMember): boolean; 12 | protected antiraid(guildId: string, event: string, startAt: number, targetId: string): Promise; 13 | } 14 | -------------------------------------------------------------------------------- /src/Constants.ts: -------------------------------------------------------------------------------- 1 | import {ActionType, AntiRaidManagerOptions} from './interfaces'; 2 | import {AuditLogEvent} from 'discord-api-types/v9'; 3 | 4 | export const defaultOptions: AntiRaidManagerOptions = { 5 | enabled: true, 6 | events: [], 7 | exemptedUsers: [], 8 | exemptedRoles: [], 9 | rateLimit: 3, 10 | time: 10000, 11 | sanction: 'kick', 12 | reason: 'discord-antiraid', 13 | }; 14 | 15 | export const AuditActionsTypes: ActionType = { 16 | 'channelCreate': AuditLogEvent.ChannelCreate, 17 | 'channelDelete': AuditLogEvent.ChannelDelete, 18 | 'roleCreate': AuditLogEvent.RoleCreate, 19 | 'roleDelete': AuditLogEvent.RoleDelete, 20 | 'guildBanAdd': AuditLogEvent.MemberBanAdd, 21 | 'guildBanRemove': AuditLogEvent.MemberBanRemove, 22 | 'threadCreate': AuditLogEvent.ThreadCreate, 23 | 'threadDelete': AuditLogEvent.ThreadDelete, 24 | 'guildMemberRemove': AuditLogEvent.MemberKick, 25 | }; -------------------------------------------------------------------------------- /lib/src/constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.AuditActionsTypes = exports.defaultOptions = void 0; 4 | const v9_1 = require("discord-api-types/v9"); 5 | exports.defaultOptions = { 6 | enabled: true, 7 | events: [], 8 | exemptedUsers: [], 9 | exemptedRoles: [], 10 | rateLimit: 3, 11 | time: 10000, 12 | sanction: 'kick', 13 | reason: 'discord-antiraid', 14 | }; 15 | exports.AuditActionsTypes = { 16 | 'channelCreate': v9_1.AuditLogEvent.ChannelCreate, 17 | 'channelDelete': v9_1.AuditLogEvent.ChannelDelete, 18 | 'roleCreate': v9_1.AuditLogEvent.RoleCreate, 19 | 'roleDelete': v9_1.AuditLogEvent.RoleDelete, 20 | 'guildBanAdd': v9_1.AuditLogEvent.MemberBanAdd, 21 | 'guildBanRemove': v9_1.AuditLogEvent.MemberBanRemove, 22 | 'threadCreate': v9_1.AuditLogEvent.ThreadCreate, 23 | 'threadDelete': v9_1.AuditLogEvent.ThreadDelete, 24 | 'guildMemberRemove': v9_1.AuditLogEvent.MemberKick, 25 | }; 26 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended" 10 | ], 11 | "parser": "@typescript-eslint/parser", 12 | "parserOptions": { 13 | "ecmaVersion": "latest", 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "@typescript-eslint" 18 | ], 19 | "rules": { 20 | "indent": [ 21 | "error", 22 | "tab" 23 | ], 24 | "linebreak-style": [ 25 | "error", 26 | "unix" 27 | ], 28 | "quotes": [ 29 | "error", 30 | "single" 31 | ], 32 | "semi": [ 33 | "error", 34 | "always" 35 | ], 36 | "space-before-function-paren": [ 37 | "error", 38 | "always" 39 | ], 40 | "prefer-const": [ 41 | "error" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | export interface AntiRaidManagerOptions { 2 | enabled: boolean; 3 | events: EventsNames[]; 4 | exemptedUsers: string[]; 5 | exemptedRoles: string[]; 6 | rateLimit: number; 7 | time: number; 8 | sanction: sanctionType; 9 | reason: string; 10 | } 11 | export declare type EventsNames = 'channelCreate' | 'channelDelete' | 'guildBanAdd' | 'guildBanRemove' | 'roleCreate' | 'roleDelete' | 'threadCreate' | 'guildMemberRemove' | 'threadDelete'; 12 | export declare type ActionType = { 13 | [key in EventsNames]: number; 14 | }; 15 | export declare type sanctionType = 'ban' | 'kick' | 'removeAllRoles'; 16 | export interface Case { 17 | user: string; 18 | guild: string; 19 | event: string; 20 | startedAt: number; 21 | rate: number; 22 | } 23 | export interface AntiraidManagerEvents { 24 | 'addcase': (memberId: string, guildId: string, event: string, oldCase: Case) => void; 25 | 'sanction': (memberId: string, guildId: string) => void; 26 | 'error': (error: string) => void; 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Derp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface AntiRaidManagerOptions { 2 | enabled: boolean; 3 | events: EventsNames[]; 4 | exemptedUsers: string[]; 5 | exemptedRoles: string[]; 6 | rateLimit: number; 7 | time: number; 8 | sanction: sanctionType; 9 | reason: string; 10 | } 11 | 12 | export type EventsNames = 13 | 'channelCreate' 14 | | 'channelDelete' 15 | | 'guildBanAdd' 16 | | 'guildBanRemove' 17 | | 'roleCreate' 18 | | 'roleDelete' 19 | | 'threadCreate' 20 | | 'guildMemberRemove' 21 | | 'threadDelete'; 22 | 23 | 24 | export type ActionType = { 25 | [key in EventsNames]: number; 26 | }; 27 | 28 | export type sanctionType = 29 | 'ban' 30 | | 'kick' 31 | | 'removeAllRoles'; 32 | 33 | 34 | export interface Case { 35 | user: string; 36 | guild: string; 37 | event: string; 38 | startedAt: number; 39 | rate: number; 40 | } 41 | 42 | export interface AntiraidManagerEvents { 43 | 'addcase' : (memberId: string, guildId: string, event: string, oldCase: Case) => void; 44 | 'sanction' : (memberId: string, guildId: string) => void; 45 | 'error' : (error: string) => void; 46 | } -------------------------------------------------------------------------------- /lib/src/events/role/roleCreate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(role) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(role.guild.id, this.name, Date.now(), role.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/role/roleDelete.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(role) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(role.guild.id, this.name, Date.now(), role.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildBanAdd.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(ban) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(ban.guild.id, this.name, Date.now(), ban.user.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildBanRemove.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(ban) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(ban.guild.id, this.name, Date.now(), ban.user.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/threads/threadCreate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ThreadCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(thread) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(thread.guild.id, this.name, Date.now(), thread.id); 20 | }); 21 | } 22 | } 23 | exports.default = ThreadCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/threads/threadDelete.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ThreadDelete extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(thread) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(thread.guild.id, this.name, Date.now(), thread.id); 20 | }); 21 | } 22 | } 23 | exports.default = ThreadDelete; 24 | -------------------------------------------------------------------------------- /lib/src/events/channel/channelCreate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(channel) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(channel.guild.id, this.name, Date.now(), channel.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/events/channel/channelDelete.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelDelete extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(channel) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(channel.guild.id, this.name, Date.now(), channel.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelDelete; 24 | -------------------------------------------------------------------------------- /lib/src/events/guild/guildMemberRemove.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const BaseEvent_1 = require("../../BaseEvent"); 13 | class ChannelCreate extends BaseEvent_1.BaseEvent { 14 | constructor(manager) { 15 | super(manager, __filename); 16 | } 17 | run(member) { 18 | return __awaiter(this, void 0, void 0, function* () { 19 | yield this.antiraid(member.guild.id, this.name, Date.now(), member.id); 20 | }); 21 | } 22 | } 23 | exports.default = ChannelCreate; 24 | -------------------------------------------------------------------------------- /lib/src/AntiRaidManager.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { EventEmitter } from 'events'; 3 | import { Client, Collection, GuildMember } from 'discord.js'; 4 | import { AntiraidManagerEvents, AntiRaidManagerOptions, Case } from './interfaces'; 5 | import { AxiosInstance } from 'axios'; 6 | export declare class AntiRaidManager extends EventEmitter { 7 | _client: Client; 8 | private _options; 9 | _request: AxiosInstance; 10 | cases: Collection; 11 | constructor(client: Client, options?: AntiRaidManagerOptions); 12 | on(event: U, listener: AntiraidManagerEvents[U]): this; 13 | emit(event: U, ...args: Parameters): boolean; 14 | private _handleEvents; 15 | getOptionsFromDb(id: string): AntiRaidManagerOptions; 16 | addCase(options: AntiRaidManagerOptions, user: string, guild: string, event: string, startedAt: number, oldCase: Case): void; 17 | getCases(id: string): Case[]; 18 | search(member: GuildMember, event: string): Case | undefined; 19 | punishable(options: AntiRaidManagerOptions, caseToCheck: Case): boolean; 20 | ban(guild: string, member: string, reason: string): Promise; 21 | kick(guild: string, member: string, reason: string): Promise; 22 | removeRoles(guild: string, member: string, reason: string): Promise; 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-antiraid", 3 | "version": "3.1.3", 4 | "description": "Discord.js extention library for protect discord servers against Raids", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "files": [ 8 | "lib/" 9 | ], 10 | "eslintIgnore": [ 11 | "lib/*", 12 | "node_modules/*", 13 | "test/*" 14 | ], 15 | "scripts": { 16 | "start:dev": "tsc && node test/index.js", 17 | "build": "tsc", 18 | "lint": "eslint . --ext .ts" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/Derpinou/discord-antiraid.git" 23 | }, 24 | "keywords": [ 25 | "discord", 26 | "antiraid", 27 | "anti-raid", 28 | "raid", 29 | "protect", 30 | "raidprotect", 31 | "raid-protect", 32 | "bot", 33 | "package", 34 | "npm", 35 | "derp" 36 | ], 37 | "author": "https://github.com/Derpinou", 38 | "license": "MIT", 39 | "bugs": { 40 | "url": "https://github.com/Derpinou/discord-antiraid/issues" 41 | }, 42 | "homepage": "https://github.com/Derpinou/discord-antiraid/", 43 | "devDependencies": { 44 | "@types/node": "^14.0.5", 45 | "@types/ws": "^7.4.7", 46 | "@typescript-eslint/eslint-plugin": "^5.17.0", 47 | "@typescript-eslint/parser": "^5.17.0", 48 | "discord.js": "^13.6.0", 49 | "eslint": "^8.12.0", 50 | "typescript": "^4.0.2" 51 | }, 52 | "dependencies": { 53 | "axios": "^0.26.1", 54 | "discord-api-types": "^0.30.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | Discord.js extention library for protecting discord servers against Raids 8 | 9 | [![downloadsBadge](https://img.shields.io/npm/dt/discord-antiraid?style=for-the-badge)](https://npmjs.com/discord-antiraid) 10 | [![versionBadge](https://img.shields.io/npm/v/discord-antiraid?style=for-the-badge)](https://npmjs.com/discord-antiraid) 11 | 12 |
13 | 14 | [![discord](https://discord.com/api/guilds/848500695506223104/widget.png)](https://discord.gg/ahjFrbk2Nr) 15 | [![GitHub license](https://img.shields.io/github/license/Derpinou/discord-antiraid.svg)](https://github.com/Derpinou/discord-antiraid/blob/master/LICENSE) 16 | 17 |
18 | 19 | ## - Installation: 20 | ``` 21 | 22 | npm i discord-antiraid 23 | 24 | ``` 25 | 26 | 27 | Supported Events: 28 | (If you want to Add your Own Events, Use AntiRaid Class Methods in the documentation or follow Example [here](https://github.com/Derpinou/discord-antiraid/blob/main/example/AntiRaid/Event.js)) 29 | 30 | ```js 31 | channelCreate 32 | channelDelete 33 | 34 | roleCreate 35 | roleDelete 36 | 37 | guildBanAdd 38 | guildBanRemove 39 | guildMemberRemove 40 | 41 | threadCreate 42 | threadDelete 43 | 44 | ``` 45 | 46 | Create AntiRaid: 47 | (Example: [here](https://github.com/Derpinou/discord-antiraid/blob/main/example/AntiRaid/sample.js)) 48 | 49 | ```js 50 | const {AntiRaidManager} = require('discord-antiraid'); 51 | 52 | const antiraid = new AntiRaidManager(client, { 53 | enabled: true, 54 | events: [ 55 | "channelCreate", 56 | "channelDelete", 57 | "roleCreate", 58 | "roleDelete", 59 | "threadCreate", 60 | "threadDelete", 61 | ], 62 | exemptedRoles: [], // Ignored roles (ex: ['848500766955405332']) 63 | exemptedUsers: [], // Ignored users (ex: ['555429540613062656']) 64 | rateLimit: 2, // Number of events before sanction 65 | time: 30000, // Time in ms before case deletion 66 | sanction: 'removeAllRoles', // Sanction to apply (ex: 'removeAllRoles' / 'ban' / 'kick') 67 | reason: 'discord-antiraid' // Audit Log Reason 68 | }) 69 | ``` 70 | Using Database (Example with [quick.db](https://www.npmjs.com/package/quick.db)): 71 | 72 | ## - Changelog: 73 | 74 | See Changelog [here](https://github.com/Derpinou/discord-antiraid/blob/main/CHANGELOG.md) 75 | 76 | 77 | ## - Contributors: 78 | 79 | 80 | 81 | 82 | ## - Special Credits: 83 | Thanks to [Androz](https://github.com/Androz2091) with his repo [discord-giveaways](https://github.com/Androz2091/discord-giveaways) for doc generator and typing example 84 | 85 | Thanks to [Sayrix](https://github.com/Sayrix) for making the logo and the banner 86 | 87 | ## - License: 88 | 89 | Licensed under the MIT license. 90 | -------------------------------------------------------------------------------- /src/BaseEvent.ts: -------------------------------------------------------------------------------- 1 | import {AntiRaidManager} from './AntiRaidManager'; 2 | import {sep} from 'path'; 3 | import {AntiRaidManagerOptions, Case, EventsNames} from './interfaces'; 4 | import {Guild, GuildMember} from 'discord.js'; 5 | import {RESTGetAPIAuditLogQuery, RESTGetAPIAuditLogResult} from 'discord-api-types/v9'; 6 | import {AuditActionsTypes} from './constants'; 7 | 8 | export abstract class BaseEvent { 9 | protected _manager: AntiRaidManager; 10 | public name: EventsNames; 11 | constructor (manager: AntiRaidManager, filename: string) { 12 | this._manager = manager; 13 | this.name = filename.split(sep)[filename.split(sep).length - 1].replace('.js', '') as EventsNames; 14 | } 15 | protected abstract run(...args: any[]): void 16 | 17 | protected checkEvent (options: AntiRaidManagerOptions): boolean { 18 | return !!options.events.includes(this.name as EventsNames); 19 | } 20 | 21 | protected checkUser (options: AntiRaidManagerOptions, memberId: string): boolean { 22 | return !!options.exemptedUsers.includes(memberId); 23 | } 24 | 25 | protected checkRoles (options: AntiRaidManagerOptions, member: GuildMember): boolean { 26 | return options.exemptedRoles.some(role => member.roles.cache.has(role)); 27 | } 28 | 29 | protected async antiraid (guildId: string, event: string, startAt: number, targetId: string) { 30 | const options = await this._manager.getOptionsFromDb(guildId); 31 | const guild = this._manager._client.guilds.cache.get(guildId) as Guild; 32 | 33 | if (!guild) return undefined; 34 | 35 | if (!this.checkEvent(options)) return undefined; 36 | 37 | const auditLogsRequest = await this._manager._request.get(`/guilds/${guildId}/audit-logs`, { 38 | params: { 39 | limit: 1, 40 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 41 | //@ts-ignore 42 | action_type: AuditActionsTypes[event], 43 | } as RESTGetAPIAuditLogQuery 44 | }).then(res => res.data).catch(e => this._manager.emit('error', e)) as RESTGetAPIAuditLogResult; 45 | 46 | if (!auditLogsRequest) return undefined; 47 | 48 | if (auditLogsRequest.audit_log_entries[0].target_id !== targetId) return undefined; 49 | 50 | if (this.checkUser(options, auditLogsRequest.audit_log_entries[0].user_id as string)) return undefined; 51 | 52 | const member = guild.members.cache.get(auditLogsRequest.audit_log_entries[0].user_id as string); 53 | 54 | if (!member) return undefined; 55 | 56 | if (this.checkRoles(options, member)) return undefined; 57 | 58 | const oldCase = this._manager.search(member, event); 59 | 60 | const punishable = this._manager.punishable(options, oldCase as Case); 61 | 62 | if (punishable) { 63 | switch (options.sanction) { 64 | case 'kick': 65 | return this._manager.kick(guildId, member.id, options.reason); 66 | case 'ban': 67 | return this._manager.ban(guildId, member.id, options.reason); 68 | case 'removeAllRoles': 69 | return this._manager.removeRoles(guildId, member.id, options.reason); 70 | } 71 | } 72 | 73 | return this._manager.addCase(options, member.user.id, member.guild.id, event, startAt, oldCase as Case); 74 | } 75 | } -------------------------------------------------------------------------------- /lib/src/BaseEvent.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.BaseEvent = void 0; 13 | const path_1 = require("path"); 14 | const constants_1 = require("./constants"); 15 | class BaseEvent { 16 | constructor(manager, filename) { 17 | this._manager = manager; 18 | this.name = filename.split(path_1.sep)[filename.split(path_1.sep).length - 1].replace('.js', ''); 19 | } 20 | checkEvent(options) { 21 | return !!options.events.includes(this.name); 22 | } 23 | checkUser(options, memberId) { 24 | return !!options.exemptedUsers.includes(memberId); 25 | } 26 | checkRoles(options, member) { 27 | return options.exemptedRoles.some(role => member.roles.cache.has(role)); 28 | } 29 | antiraid(guildId, event, startAt, targetId) { 30 | return __awaiter(this, void 0, void 0, function* () { 31 | const options = yield this._manager.getOptionsFromDb(guildId); 32 | const guild = this._manager._client.guilds.cache.get(guildId); 33 | if (!guild) 34 | return undefined; 35 | if (!this.checkEvent(options)) 36 | return undefined; 37 | const auditLogsRequest = yield this._manager._request.get(`/guilds/${guildId}/audit-logs`, { 38 | params: { 39 | limit: 1, 40 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 41 | //@ts-ignore 42 | action_type: constants_1.AuditActionsTypes[event], 43 | } 44 | }).then(res => res.data).catch(e => this._manager.emit('error', e)); 45 | if (!auditLogsRequest) 46 | return undefined; 47 | if (auditLogsRequest.audit_log_entries[0].target_id !== targetId) 48 | return undefined; 49 | if (this.checkUser(options, auditLogsRequest.audit_log_entries[0].user_id)) 50 | return undefined; 51 | const member = guild.members.cache.get(auditLogsRequest.audit_log_entries[0].user_id); 52 | if (!member) 53 | return undefined; 54 | if (this.checkRoles(options, member)) 55 | return undefined; 56 | const oldCase = this._manager.search(member, event); 57 | const punishable = this._manager.punishable(options, oldCase); 58 | if (punishable) { 59 | switch (options.sanction) { 60 | case 'kick': 61 | return this._manager.kick(guildId, member.id, options.reason); 62 | case 'ban': 63 | return this._manager.ban(guildId, member.id, options.reason); 64 | case 'removeAllRoles': 65 | return this._manager.removeRoles(guildId, member.id, options.reason); 66 | } 67 | } 68 | return this._manager.addCase(options, member.user.id, member.guild.id, event, startAt, oldCase); 69 | }); 70 | } 71 | } 72 | exports.BaseEvent = BaseEvent; 73 | -------------------------------------------------------------------------------- /src/AntiRaidManager.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | import {Client, Collection, GuildMember} from 'discord.js'; 3 | import {readdir} from 'fs/promises'; 4 | import {AntiraidManagerEvents, AntiRaidManagerOptions, Case} from './interfaces'; 5 | import {sep} from 'path'; 6 | import {defaultOptions} from './constants'; 7 | import axios from 'axios'; 8 | import {AxiosInstance} from 'axios'; 9 | import {RESTPatchAPIGuildMemberJSONBody, RESTPutAPIGuildBanJSONBody} from 'discord-api-types/v9'; 10 | 11 | export class AntiRaidManager extends EventEmitter { 12 | public _client: Client; 13 | private _options: AntiRaidManagerOptions; 14 | public _request: AxiosInstance; 15 | public cases: Collection; 16 | constructor (client : Client, options?: AntiRaidManagerOptions) { 17 | super(); 18 | this._client = client; 19 | this._options = options || defaultOptions; 20 | this.cases = new Collection(); 21 | this._request = axios.create({ 22 | baseURL: `${this._client.options.http?.api}/v${this._client.options.http?.version}`, 23 | headers: { 24 | Authorization: `Bot ${this._client.token}` 25 | } 26 | }); 27 | this._handleEvents().catch(console.error); 28 | } 29 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 30 | // @ts-ignore 31 | on( 32 | event: U, listener: AntiraidManagerEvents[U] 33 | ): this; 34 | 35 | emit( 36 | event: U, ...args: Parameters 37 | ): boolean; 38 | 39 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 40 | //@ts-ignore 41 | private async _handleEvents () { 42 | const categories = await readdir(`${__dirname}${sep}events`); 43 | for (const category of categories) { 44 | const events = await readdir(`${__dirname}${sep}events${sep}${category}`); 45 | const js_files = events.filter(f => f.split('.').pop() === 'js'); 46 | 47 | for (const event of js_files) { 48 | const eventName = event.split('.')[0]; 49 | // eslint-disable-next-line @typescript-eslint/no-var-requires 50 | const evt = require(`${__dirname}${sep}events${sep}${category}${sep}${event}`)['default']; 51 | const eventConstructor = new evt(this); 52 | //console.log(eventConstructor); 53 | this._client.on(eventName, (...args) => eventConstructor.run(...args)); 54 | } 55 | } 56 | } 57 | public getOptionsFromDb (id: string) { 58 | return this._options; 59 | } 60 | 61 | public addCase (options: AntiRaidManagerOptions, user: string, guild: string, event: string, startedAt: number, oldCase: Case) { 62 | const cases = this.getCases(guild); 63 | const caseToPush: Case = { 64 | guild, 65 | startedAt, 66 | user, 67 | event, 68 | rate: oldCase && oldCase.rate ? oldCase.rate++ : 1 69 | }; 70 | cases.push(caseToPush); 71 | this.cases.set(guild, cases); 72 | const index: number = cases.indexOf(caseToPush); 73 | setTimeout(() => { 74 | cases.splice(index); 75 | }, options.time); 76 | this.cases.set(guild, cases); 77 | } 78 | 79 | public getCases (id: string): Case[] { 80 | if (!this.cases.get(id)) { 81 | this.cases.set(id, []); 82 | } 83 | return this.cases.get(id) as Case[]; 84 | } 85 | 86 | public search (member: GuildMember, event: string): Case | undefined { 87 | return this.getCases(member.guild.id).find(c => c.user === member.id && c.guild === member.guild.id && c.event === event); 88 | } 89 | 90 | public punishable (options: AntiRaidManagerOptions, caseToCheck: Case): boolean { 91 | return caseToCheck && caseToCheck.rate >= options.rateLimit -1; 92 | } 93 | 94 | public async ban (guild: string, member: string, reason: string) { 95 | await this._request.put(`/guilds/${guild}/bans/${member}`, { 96 | delete_message_days: 0, 97 | reason 98 | } as RESTPutAPIGuildBanJSONBody, { 99 | headers: { 100 | 'X-Audit-Log-Reason': reason 101 | } 102 | }).catch((err) => { 103 | this.emit('error', err); 104 | }); 105 | } 106 | 107 | public async kick (guild: string, member: string, reason: string) { 108 | await this._request.delete(`/guilds/${guild}/members/${member}`, { 109 | headers: { 110 | 'X-Audit-Log-Reason': reason 111 | } 112 | }).catch((err) => { 113 | this.emit('error', err); 114 | }); 115 | } 116 | 117 | public async removeRoles (guild: string, member: string, reason: string) { 118 | await this._request.patch(`/guilds/${guild}/members/${member}`, { 119 | roles: [] 120 | } as RESTPatchAPIGuildMemberJSONBody, { 121 | headers: { 122 | 'X-Audit-Log-Reason': reason 123 | } 124 | }).catch((err) => { 125 | this.emit('error', err); 126 | }); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/src/AntiRaidManager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.AntiRaidManager = void 0; 13 | const events_1 = require("events"); 14 | const discord_js_1 = require("discord.js"); 15 | const promises_1 = require("fs/promises"); 16 | const path_1 = require("path"); 17 | const constants_1 = require("./constants"); 18 | const axios_1 = require("axios"); 19 | class AntiRaidManager extends events_1.EventEmitter { 20 | constructor(client, options) { 21 | var _a, _b; 22 | super(); 23 | this._client = client; 24 | this._options = options || constants_1.defaultOptions; 25 | this.cases = new discord_js_1.Collection(); 26 | this._request = axios_1.default.create({ 27 | baseURL: `${(_a = this._client.options.http) === null || _a === void 0 ? void 0 : _a.api}/v${(_b = this._client.options.http) === null || _b === void 0 ? void 0 : _b.version}`, 28 | headers: { 29 | Authorization: `Bot ${this._client.token}` 30 | } 31 | }); 32 | this._handleEvents().catch(console.error); 33 | } 34 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 35 | //@ts-ignore 36 | _handleEvents() { 37 | return __awaiter(this, void 0, void 0, function* () { 38 | const categories = yield (0, promises_1.readdir)(`${__dirname}${path_1.sep}events`); 39 | for (const category of categories) { 40 | const events = yield (0, promises_1.readdir)(`${__dirname}${path_1.sep}events${path_1.sep}${category}`); 41 | const js_files = events.filter(f => f.split('.').pop() === 'js'); 42 | for (const event of js_files) { 43 | const eventName = event.split('.')[0]; 44 | // eslint-disable-next-line @typescript-eslint/no-var-requires 45 | const evt = require(`${__dirname}${path_1.sep}events${path_1.sep}${category}${path_1.sep}${event}`)['default']; 46 | const eventConstructor = new evt(this); 47 | //console.log(eventConstructor); 48 | this._client.on(eventName, (...args) => eventConstructor.run(...args)); 49 | } 50 | } 51 | }); 52 | } 53 | getOptionsFromDb(id) { 54 | return this._options; 55 | } 56 | addCase(options, user, guild, event, startedAt, oldCase) { 57 | const cases = this.getCases(guild); 58 | const caseToPush = { 59 | guild, 60 | startedAt, 61 | user, 62 | event, 63 | rate: oldCase && oldCase.rate ? oldCase.rate++ : 1 64 | }; 65 | cases.push(caseToPush); 66 | this.cases.set(guild, cases); 67 | const index = cases.indexOf(caseToPush); 68 | setTimeout(() => { 69 | cases.splice(index); 70 | }, options.time); 71 | this.cases.set(guild, cases); 72 | } 73 | getCases(id) { 74 | if (!this.cases.get(id)) { 75 | this.cases.set(id, []); 76 | } 77 | return this.cases.get(id); 78 | } 79 | search(member, event) { 80 | return this.getCases(member.guild.id).find(c => c.user === member.id && c.guild === member.guild.id && c.event === event); 81 | } 82 | punishable(options, caseToCheck) { 83 | return caseToCheck && caseToCheck.rate >= options.rateLimit - 1; 84 | } 85 | ban(guild, member, reason) { 86 | return __awaiter(this, void 0, void 0, function* () { 87 | yield this._request.put(`/guilds/${guild}/bans/${member}`, { 88 | delete_message_days: 0, 89 | reason 90 | }, { 91 | headers: { 92 | 'X-Audit-Log-Reason': reason 93 | } 94 | }).catch((err) => { 95 | this.emit('error', err); 96 | }); 97 | }); 98 | } 99 | kick(guild, member, reason) { 100 | return __awaiter(this, void 0, void 0, function* () { 101 | yield this._request.delete(`/guilds/${guild}/members/${member}`, { 102 | headers: { 103 | 'X-Audit-Log-Reason': reason 104 | } 105 | }).catch((err) => { 106 | this.emit('error', err); 107 | }); 108 | }); 109 | } 110 | removeRoles(guild, member, reason) { 111 | return __awaiter(this, void 0, void 0, function* () { 112 | yield this._request.patch(`/guilds/${guild}/members/${member}`, { 113 | roles: [] 114 | }, { 115 | headers: { 116 | 'X-Audit-Log-Reason': reason 117 | } 118 | }).catch((err) => { 119 | this.emit('error', err); 120 | }); 121 | }); 122 | } 123 | } 124 | exports.AntiRaidManager = AntiRaidManager; 125 | --------------------------------------------------------------------------------