├── .gitignore ├── LICENSE ├── README.md ├── config.ts ├── package.json ├── src ├── @types │ └── backup │ │ └── index.d.ts ├── base │ ├── Core.ts │ └── Utils.ts ├── commands │ ├── Management.ts │ ├── Permissions.ts │ ├── SafeRole.ts │ └── Safes.ts ├── events │ ├── ChannelCreate.ts │ ├── ChannelDelete.ts │ ├── ChannelUpdate.ts │ ├── GuildBandAdd.ts │ ├── GuildMemberAdd.ts │ ├── GuildMemberUpdate.ts │ ├── GuildUpdate.ts │ ├── MemberKick.ts │ ├── MemberPrune.ts │ ├── MessageCreate.ts │ ├── Ready.ts │ ├── RoleCreate.ts │ ├── RoleDelete.ts │ ├── RoleUpdate.ts │ └── WebhookUpdate.ts ├── index.ts └── models │ ├── Channel.ts │ ├── Guild.ts │ └── Role.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log 6 | 7 | # Dependency directories 8 | node_modules/ 9 | 10 | # Optional eslint cache 11 | .eslintcache 12 | 13 | # build / generate output 14 | dist 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public 421 | licenses. Notwithstanding, Creative Commons may elect to apply one of 422 | its public licenses to material it publishes and in those instances 423 | will be considered the “Licensor.” The text of the Creative Commons 424 | public licenses is dedicated to the public domain under the CC0 Public 425 | Domain Dedication. Except for the limited purpose of indicating that 426 | material is shared under a Creative Commons public license or as 427 | otherwise permitted by the Creative Commons policies published at 428 | creativecommons.org/policies, Creative Commons does not authorize the 429 | use of the trademark "Creative Commons" or any other trademark or logo 430 | of Creative Commons without its prior written consent including, 431 | without limitation, in connection with any unauthorized modifications 432 | to any of its public licenses or any other arrangements, 433 | understandings, or agreements concerning use of licensed material. For 434 | the avoidance of doubt, this paragraph does not form part of the 435 | public licenses. 436 | 437 | Creative Commons may be contacted at creativecommons.org. 438 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to Discord Guard Bot

2 |

Discord Guard Bot for private servers.

3 | 4 | ## Usage 5 | 6 |
    7 |
  1. Clone/Download repo and enter config.ts settings. (NOTE: Minumum 2 tokens.)
  2. 8 |
  3. Install ts-node npm install -g ts-node or yarn global add ts-node. Then install all modules npm install
  4. 9 |
  5. Run yarn start or npm run start for production server, run yarn dev or npm run dev for development server.
  6. 10 |
  7. Add bot your server.
  8. 11 |
  9. If you haven't set the safes. Use !safe role/user. See all safes use !safe list.
  10. 12 |
  11. Use !guard-menu then click Backup Start button for start backup system.
  12. 13 |
  13. If you want a log, you need to edit it public updates channel from the community settings.
  14. 14 |
15 | 16 | ## For Helping 17 | 18 | - Discord Account: [Muratva Stark](https://discord.com/users/470974660264067072) 19 | - Discord Server: [My Private Server](https://discord.gg/USWySwTtsJ) 20 | 21 | ## 🤝 Contributing 22 | 23 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/muratvastark/discord-guard-bot/issues). 24 | 25 | ## Show your support 26 | 27 | Give a ⭐️ if this project helped you! 28 | 29 | ## 📝 License 30 | 31 | Copyright © 2022 [Muratva Stark](https://github.com/muratvastark).
32 | This project is [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International ("CC-BY-NC-SA-4.0")](https://github.com/muratvastark/discord-guard-bot/blob/main/LICENSE) licensed. 33 | -------------------------------------------------------------------------------- /config.ts: -------------------------------------------------------------------------------- 1 | export const CONFIG: Backup.Config = { 2 | GUILD_ID: '', 3 | PREFIX: '!', 4 | MONGO_URL: '', 5 | STATUS: '💙 Muratva Stark', 6 | TOKENS: [] 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-guard-bot", 3 | "version": "1.0.0", 4 | "main": "src/index.ts", 5 | "license": "CC-BY-NC-SA-4.0", 6 | "author": "Muratva Stark", 7 | "repository": "git+https://github.com/muratvastark/discord-guard-bot.git", 8 | "bugs": { 9 | "url": "https://github.com/muratvastark/discord-guard-bot/issues" 10 | }, 11 | "scripts": { 12 | "start": "ts-node src/index", 13 | "dev": "nodemon src/index" 14 | }, 15 | "dependencies": { 16 | "@typegoose/typegoose": "^8.3.0", 17 | "discord.js": "^13.2.0", 18 | "mongoose": "^5.13.8", 19 | "pogger": "^0.0.8" 20 | }, 21 | "devDependencies": { 22 | "@types/node": "^16.7.1", 23 | "nodemon": "^2.0.12", 24 | "ts-node": "^9.1.1", 25 | "typescript": "^4.3.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/@types/backup/index.d.ts: -------------------------------------------------------------------------------- 1 | import { ClientEvents, Message, PermissionString } from 'discord.js'; 2 | import { Core } from '../../base/Core'; 3 | 4 | export {}; 5 | 6 | declare global { 7 | namespace Backup { 8 | interface PermissionObject { 9 | [key: PermissionString]: boolean; 10 | } 11 | 12 | interface Limit { 13 | count: number; 14 | lastDate: number; 15 | } 16 | 17 | interface GuildSettings { 18 | name: string; 19 | icon: string; 20 | banner: string; 21 | } 22 | 23 | interface CommandArgs { 24 | client: Core; 25 | message: Message; 26 | args: string[]; 27 | } 28 | 29 | interface Command { 30 | usages: string[]; 31 | execute: (arguments: CommandArgs) => any ; 32 | } 33 | 34 | interface Event { 35 | name: keyof ClientEvents; 36 | execute: (client: Core, ...args: any[]) => any | Promise; 37 | } 38 | 39 | interface RoleOverwrites { 40 | id: string; 41 | permissions: PermissionObject; 42 | } 43 | 44 | interface Permission { 45 | ID: string; 46 | ALLOW: PermissionString[]; 47 | } 48 | 49 | interface Safe { 50 | developer?: boolean; 51 | owner?: boolean; 52 | role?: boolean; 53 | ban?: boolean; 54 | channel?: boolean; 55 | } 56 | 57 | interface SafeRole extends Safe { 58 | id: string; 59 | } 60 | 61 | interface Config { 62 | GUILD_ID: string; 63 | PREFIX: string; 64 | STATUS: string; 65 | TOKENS: string[]; 66 | MONGO_URL: string; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/base/Core.ts: -------------------------------------------------------------------------------- 1 | import { Client, Intents, IntentsString, User, Collection } from 'discord.js'; 2 | import { Utils } from './Utils'; 3 | import { CONFIG } from '../../config'; 4 | import { connect } from 'mongoose'; 5 | import * as pogger from 'pogger'; 6 | 7 | export class Core extends Client { 8 | public utils = new Utils(this); 9 | public commands = new Collection(); 10 | public safes = new Collection(); 11 | public config = CONFIG; 12 | public logger = pogger; 13 | 14 | constructor() { 15 | super({ 16 | intents: Object.keys(Intents.FLAGS) as IntentsString[], 17 | presence: { 18 | activities: [{ name: CONFIG.STATUS, type: 'WATCHING' }], 19 | }, 20 | }); 21 | } 22 | 23 | async connect() { 24 | this.logger.event('Loading commands...'); 25 | await this.utils.loadCommands(); 26 | 27 | this.logger.event('Loading events...'); 28 | await this.utils.loadEvents(); 29 | 30 | await this.login(CONFIG.TOKENS[0]); 31 | CONFIG.TOKENS.splice(0, 1); 32 | 33 | this.logger.info('Connecting MongoDB...'); 34 | await connect(CONFIG.MONGO_URL, { 35 | useNewUrlParser: true, 36 | useUnifiedTopology: true, 37 | useCreateIndex: true, 38 | }); 39 | 40 | this.logger.success(`The system is activated. You can start the backup using the "${CONFIG.PREFIX}guard-menu" command on the server.`); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/base/Utils.ts: -------------------------------------------------------------------------------- 1 | import { readdirSync } from 'fs'; 2 | import { resolve } from 'path'; 3 | import { Client, Collection, Intents, PermissionString, Snowflake } from 'discord.js'; 4 | import { Core } from './Core'; 5 | import { RoleModel, RoleSchema } from '../models/Role'; 6 | import { ChannelModel, ChannelSchema } from '../models/Channel'; 7 | import { GuildModel } from '../models/Guild'; 8 | 9 | enum ChannelTypes { 10 | GUILD_TEXT = 0, 11 | DM = 1, 12 | GUILD_VOICE = 2, 13 | GROUP_DM = 3, 14 | GUILD_CATEGORY = 4, 15 | GUILD_NEWS = 5, 16 | GUILD_STORE = 6, 17 | UNKNOWN = 7, 18 | GUILD_NEWS_THREAD = 10, 19 | GUILD_PUBLIC_THREAD = 11, 20 | GUILD_PRIVATE_THREAD = 12, 21 | GUILD_STAGE_VOICE = 13, 22 | } 23 | 24 | export class Utils { 25 | private readonly client: Core; 26 | public danger: Boolean = true; 27 | public indelibleRoles: string[] = [] 28 | public closingPermissions: Boolean = false; 29 | private limits = new Collection(); 30 | public guildSettings: Backup.GuildSettings; 31 | public safeRoles: Backup.SafeRole[] = []; 32 | public readonly dangerPerms: PermissionString[] = [ 33 | 'ADMINISTRATOR', 34 | 'KICK_MEMBERS', 35 | 'MANAGE_GUILD', 36 | 'BAN_MEMBERS', 37 | 'MANAGE_ROLES', 38 | 'MANAGE_WEBHOOKS', 39 | 'MANAGE_NICKNAMES', 40 | 'MANAGE_CHANNELS', 41 | ]; 42 | 43 | constructor(client) { 44 | this.client = client; 45 | this.danger = true; 46 | } 47 | 48 | startHelpers(): Promise { 49 | this.client.logger.info('The helpers is waking up.'); 50 | 51 | const promises = []; 52 | for (const TOKEN of this.client.config.TOKENS) { 53 | promises.push(new Promise((resolve) => { 54 | const helperClient = new Client({ 55 | intents: [ 56 | Intents.FLAGS.GUILDS, 57 | Intents.FLAGS.GUILD_PRESENCES 58 | ], 59 | presence: { 60 | activities: [{ name: this.client.config.STATUS, type: 'WATCHING' }], 61 | }, 62 | }); 63 | 64 | helperClient.on('ready', () => { 65 | const guild = helperClient.guilds.cache.get(this.client.config.GUILD_ID); 66 | if (!guild) { 67 | this.client.logger.warning(`WARN: ${helperClient.user.tag} is not in server!`); 68 | helperClient.destroy(); 69 | return; 70 | } 71 | 72 | this.client.safes.set(helperClient.user.id, { developer: true }); 73 | resolve(helperClient); 74 | }); 75 | 76 | helperClient.on('rateLimit', (rateLimitData) => { 77 | this.client.logger.warning(`WARN: ${helperClient.user.tag} rate limited caught. Retrying in ${Math.round(rateLimitData.timeout / 1000)} seconds.`); 78 | }); 79 | 80 | helperClient.login(TOKEN); 81 | })); 82 | } 83 | 84 | return Promise.all(promises); 85 | } 86 | 87 | async getBackup(): Promise { 88 | const guild = this.client.guilds.cache.get(this.client.config.GUILD_ID); 89 | if (!guild || (!guild.roles.cache.size && !guild.channels.cache.size)) return false; 90 | 91 | await RoleModel.deleteMany(); 92 | guild.roles.cache.sort((a, b) => a.position - b.position).filter(role => !role.managed).forEach(async (role) => { 93 | const channelOverwrites: Backup.RoleOverwrites[] = []; 94 | guild.channels.cache.forEach((channel) => { 95 | if (channel.isThread() || !channel.permissionOverwrites.cache.has(role.id)) return; 96 | 97 | const permission = channel.permissionOverwrites.cache.get(role.id); 98 | channelOverwrites.push({ 99 | id: channel.id, 100 | permissions: { ...permission.deny.serialize(), ...permission.allow.serialize() }, 101 | }); 102 | }); 103 | 104 | await RoleModel.create({ 105 | id: role.id, 106 | channelOverwrites: channelOverwrites, 107 | members: role.members.map((member) => member.id), 108 | name: role.name, 109 | color: role.color, 110 | position: role.position, 111 | permissions: role.permissions.toArray(), 112 | mentionable: role.mentionable, 113 | hoist: role.hoist 114 | }); 115 | }); 116 | 117 | await ChannelModel.deleteMany(); 118 | guild.channels.cache.forEach(async (channel) => { 119 | if (channel.isThread()) return; 120 | 121 | await ChannelModel.create({ 122 | id: channel.id, 123 | type: ChannelTypes[channel.type], 124 | parent: channel.parentId, 125 | name: channel.name, 126 | topic: channel.isText() ? channel.topic : undefined, 127 | position: channel.position, 128 | permissionOverwrites: channel.permissionOverwrites.cache.map((permission) => { 129 | return { 130 | id: permission.id, 131 | type: permission.type, 132 | allow: permission.allow.toArray(), 133 | deny: permission.deny.toArray(), 134 | }; 135 | }), 136 | nsfw: channel.isText() ? channel.nsfw : undefined, 137 | userLimit: channel.isVoice() ? channel.userLimit : undefined, 138 | }); 139 | }); 140 | return true; 141 | } 142 | 143 | async closePermissions() { 144 | if (this.closingPermissions) return; 145 | 146 | this.closingPermissions = true; 147 | 148 | const guild = this.client.guilds.cache.get(this.client.config.GUILD_ID); 149 | if (!guild) return; 150 | 151 | const permissions = []; 152 | guild.roles.cache. 153 | filter((role) => this.dangerPerms.some((perm) => role.permissions.has(perm))). 154 | forEach((role) => { 155 | permissions.push({ 156 | ID: role.id, 157 | ALLOW: role.permissions.toArray() 158 | }); 159 | role.setPermissions([]); 160 | }); 161 | await GuildModel.updateOne({ id: guild.id }, { $set: { permissions: permissions } }, { upsert: true }); 162 | } 163 | 164 | checkLimits(id: Snowflake, type: 'ban_kick' | 'channel_operations' | 'role_operations', limit: number = 5, time: number = 1000 * 60 * 15) { 165 | const now = Date.now().valueOf(); 166 | const key = `${id}_${type}`; 167 | const userLimits = this.limits.get(key); 168 | if (!userLimits) { 169 | this.limits.set(key, { count: 1, lastDate: now }); 170 | return false; 171 | } 172 | 173 | userLimits.count++; 174 | const diff = now - userLimits.lastDate; 175 | if (diff < time && userLimits.count >= limit) return true; 176 | 177 | if (diff > time) this.limits.set(key, { count: 1, lastDate: now });; 178 | return false; 179 | } 180 | 181 | async loadEvents() { 182 | const files = readdirSync(resolve(__dirname, '..', 'events')); 183 | for (const file of files) { 184 | const event = (await import(resolve(__dirname, '..', 'events', file))).default as Backup.Event; 185 | this.client.on(event.name, (...args) => event.execute(this.client, ...args)); 186 | } 187 | } 188 | 189 | async loadCommands() { 190 | const commandFiles = readdirSync(resolve(__dirname, '..', 'commands')); 191 | for (const name of commandFiles) { 192 | const command = (await import(resolve(__dirname, '..', 'commands', name))).default as Backup.Command; 193 | this.client.commands.set(command.usages[0], command); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/commands/Management.ts: -------------------------------------------------------------------------------- 1 | import { GuildChannel, Message, MessageActionRow, MessageButton } from 'discord.js'; 2 | import { RoleModel } from '../models/Role'; 3 | import { GuildModel } from '../models/Guild'; 4 | import { ChannelModel, ChannelSchema } from '../models/Channel'; 5 | import { Core } from '../base/Core'; 6 | 7 | const Management: Backup.Command = { 8 | usages: ['guard-menu', 'guard-panel', 'menu', 'panel', 'guardpanel', 'gpanel', 'gmenu', 'guardmenu'], 9 | execute: async ({ client, message }) => { 10 | const row = new MessageActionRow().addComponents([ 11 | new MessageButton().setStyle('PRIMARY').setLabel('Check roles.').setCustomId('roles'), 12 | new MessageButton().setStyle('PRIMARY').setLabel('Check channels.').setCustomId('channels'), 13 | new MessageButton() 14 | .setStyle('DANGER') 15 | .setLabel(client.utils.danger === true ? 'Backup start.' : 'Backup stop.') 16 | .setCustomId('danger'), 17 | ]); 18 | 19 | const question = await message.channel.send({ 20 | content: 'Change bot settings using the menu below!', 21 | components: [row], 22 | }); 23 | 24 | const collector = await question.createMessageComponentCollector({ 25 | componentType: 'BUTTON', 26 | filter: (component) => component.user.id === message.author.id, 27 | time: 60000, 28 | }); 29 | 30 | collector.on('collect', async (interaction) => { 31 | interaction.deferUpdate(); 32 | if (interaction.customId === 'danger') await setDanger(client, question, row); 33 | else if (interaction.customId === 'roles') await checkRoles(client, question, row); 34 | else if (interaction.customId === 'channels') await checkChannels(question, row); 35 | }); 36 | 37 | collector.on('end', () => { 38 | question.delete(); 39 | }); 40 | }, 41 | }; 42 | 43 | export default Management; 44 | 45 | async function setDanger(client: Core, question: Message, row: MessageActionRow) { 46 | client.utils.danger = !client.utils.danger; 47 | if (client.utils.danger === false) await client.utils.getBackup(); 48 | (row.components[2] as MessageButton).setLabel(client.utils.danger === true ? 'Backup start.' : 'Backup stop.'); 49 | question.edit({ 50 | content: 'Change bot settings using the menu below!', 51 | components: [row], 52 | }); 53 | } 54 | 55 | async function checkRoles(client: Core, question: Message, row: MessageActionRow) { 56 | const roles = await RoleModel.find(); 57 | const deletedRoles = roles.filter((role) => !question.guild.roles.cache.has(role.id)); 58 | if (!deletedRoles.length) return; 59 | 60 | (row.components[0] as MessageButton).setDisabled(true); 61 | await question.edit({ 62 | content: 'Change bot settings using the menu below!', 63 | components: [row], 64 | }); 65 | 66 | for (const deletedRole of deletedRoles) { 67 | const newRole = await question.guild.roles.create({ 68 | name: deletedRole.name, 69 | color: deletedRole.color, 70 | hoist: deletedRole.hoist, 71 | position: deletedRole.position, 72 | permissions: deletedRole.permissions, 73 | mentionable: deletedRole.mentionable, 74 | }); 75 | 76 | await RoleModel.updateOne({ id: deletedRole.id }, { id: newRole.id }); 77 | await ChannelModel.updateMany({ 'permissions.$.id': deletedRole.id }, { 'permissions.$.id': newRole.id }); 78 | 79 | for (const overwrite of deletedRole.channelOverwrites) { 80 | const channel = question.guild.channels.cache.get(overwrite.id) as GuildChannel; 81 | if (channel) channel.permissionOverwrites.create(newRole.id, overwrite.permissions); 82 | } 83 | 84 | const role = deletedRoles.find((role) => role.id === deletedRole.id); 85 | role.id = newRole.id; 86 | 87 | const safeRole = client.utils.safeRoles.find((sRole) => sRole.id === deletedRole.id); 88 | if (safeRole) { 89 | const operation: { [key: string]: string } = {}; 90 | if (safeRole.developer) operation['safeDevelopers'] = newRole.id; 91 | if (safeRole.owner) operation['safeOwners'] = newRole.id; 92 | if (safeRole.role) operation['safeRoles'] = newRole.id; 93 | if (safeRole.ban) operation['safeBans'] = newRole.id; 94 | if (safeRole.channel) operation['safeChannels'] = newRole.id; 95 | 96 | safeRole.id = newRole.id; 97 | await GuildModel.updateOne( 98 | { id: question.guildId }, 99 | { 100 | $push: operation, 101 | $pull: { 102 | safeDevelopers: deletedRole.id, 103 | safeOwners: deletedRole.id, 104 | safeRoles: deletedRole.id, 105 | safeBans: deletedRole.id, 106 | safeChannels: deletedRole.id 107 | } 108 | }, 109 | { upsert: true } 110 | ); 111 | } 112 | 113 | if (client.utils.indelibleRoles.includes(deletedRole.id)) { 114 | client.utils.indelibleRoles = client.utils.indelibleRoles.filter((iRole) => iRole !== deletedRole.id); 115 | client.utils.indelibleRoles.push(newRole.id); 116 | await GuildModel.updateOne({ id: question.guildId }, { $pull: { indelibleRoles: deletedRole.id }, $push: { indelibleRoles: newRole.id } }, { upsert: true }); 117 | } 118 | } 119 | 120 | const arrayMembers = [...new Set(deletedRoles.map((role) => role.members).reduce((a, b) => a.concat(b)))]; 121 | if (!arrayMembers.length) return question.channel.send('Roles have not members.'); 122 | 123 | client.utils.startHelpers().then(async (distributors) => { 124 | if (distributors.length === 0) return client.logger.error('Tokens length must be minimum 2.'); 125 | 126 | const extraMembers = arrayMembers.length % distributors.length; 127 | const perMembers = (arrayMembers.length - extraMembers) / distributors.length; 128 | for (let index = 0; index < distributors.length; index++) { 129 | const members = arrayMembers.splice(0, index === 0 ? perMembers + extraMembers : perMembers); 130 | if (members.length <= 0) break; 131 | 132 | const guild = await distributors[index].guilds.fetch(client.config.GUILD_ID); 133 | members.forEach(async (id, i) => { 134 | const roles = deletedRoles.filter((role) => role.members.includes(id)).map((role) => role.id); 135 | const member = guild.members.cache.get(id); 136 | if (member) await member.roles.add(roles.filter((role) => !member.roles.cache.has(role))); 137 | 138 | if (members.length === i + 1) distributors[index].destroy(); 139 | }); 140 | } 141 | }); 142 | } 143 | 144 | async function checkChannels(question: Message, row: MessageActionRow) { 145 | const channels = await ChannelModel.find(); 146 | const deletedChannels: ChannelSchema[] = channels.filter((channel) => !question.guild.channels.cache.has(channel.id)); 147 | if (!deletedChannels.length) return; 148 | 149 | (row.components[1] as MessageButton).setDisabled(true); 150 | question.edit({ 151 | content: 'Change bot settings using the menu below!', 152 | components: [row], 153 | }); 154 | 155 | const sortedChannels = [ 156 | ...deletedChannels.filter((channel) => channel.type === 4), 157 | ...deletedChannels.filter((channel) => channel.type !== 4), 158 | ]; 159 | for (const deletedChannel of sortedChannels) { 160 | const newChannel = (await question.guild.channels.create(deletedChannel.name, { 161 | nsfw: deletedChannel.nsfw, 162 | parent: deletedChannel.parent, 163 | type: deletedChannel.type, 164 | topic: deletedChannel.topic, 165 | position: deletedChannel.position, 166 | permissionOverwrites: deletedChannel.permissionOverwrites, 167 | userLimit: deletedChannel.userLimit, 168 | })) as GuildChannel; 169 | await RoleModel.updateMany({ 'channelOverwrites.$.id': deletedChannel.id }, { 'channelOverwrites.$.id': newChannel.id }); 170 | await ChannelModel.updateOne({ id: deletedChannel.id }, { id: newChannel.id }); 171 | 172 | if (newChannel.type === 'GUILD_CATEGORY') { 173 | for (const parentChannel of deletedChannels.filter((channel) => channel.parent === deletedChannel.id)) { 174 | parentChannel.parent = newChannel.id; 175 | } 176 | await ChannelModel.updateMany({ parent: deletedChannel.id }, { parent: newChannel.id }); 177 | 178 | const parentChannels = channels.filter((channel) => channel.parent === deletedChannel.id); 179 | for (const parentChannel of parentChannels) { 180 | const channel = question.guild.channels.cache.get(parentChannel.id) as GuildChannel; 181 | if (channel) await channel.setParent(newChannel.id, { lockPermissions: false }); 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/commands/Permissions.ts: -------------------------------------------------------------------------------- 1 | import { GuildModel } from '../models/Guild'; 2 | 3 | const Permissions: Backup.Command = { 4 | usages: ['close-permissions', 'close-perms', 'closeperms', 'cperms', 'perms', 'permissions'], 5 | execute: async ({ client, message, args }) => { 6 | const operation = args[0] ? args[0].toLowerCase() : undefined; 7 | if (!operation || !['on', 'off'].some(arg => operation === arg)) return message.channel.send('Please specified a valid argument. (`off` or `on`)'); 8 | 9 | if (client.utils.closingPermissions) client.utils.closingPermissions = false; 10 | 11 | const data = await GuildModel.findOne({ id: message.guildId }) || new GuildModel({ id: message.guildId }); 12 | if (operation === 'on') { 13 | data.permissions.forEach((permission) => { 14 | const role = message.guild.roles.cache.get(permission.ID); 15 | if (role) role.setPermissions(permission.ALLOW); 16 | }); 17 | } else { 18 | data.permissions = []; 19 | message.guild.roles.cache 20 | .filter((role) => client.utils.dangerPerms.some((perm) => role.permissions.has(perm)) && !role.managed) 21 | .forEach((role) => { 22 | data.permissions.push({ 23 | ID: role.id, 24 | ALLOW: role.permissions.toArray() 25 | }); 26 | role.setPermissions([]); 27 | }); 28 | data.save(); 29 | } 30 | 31 | message.channel.send({ content: `All perms ${operation === 'on' ? 'actived.' : 'deactived.'}` }) 32 | }, 33 | }; 34 | 35 | export default Permissions; 36 | -------------------------------------------------------------------------------- /src/commands/SafeRole.ts: -------------------------------------------------------------------------------- 1 | import { GuildModel } from '../models/Guild'; 2 | 3 | const SafeRole: Backup.Command = { 4 | usages: ['safe-role', 'saferole', 'srole', 'srol'], 5 | execute: async ({ client, message, args }) => { 6 | if (args[0] === 'list') { 7 | message.channel.send(client.utils.indelibleRoles.map(role => `\`${(message.guild.roles.cache.get(role) || { name: role }).name}\``).join(', ')); 8 | return; 9 | } 10 | 11 | const target = message.mentions.roles.first() || message.guild.roles.cache.get(args[0]); 12 | if (!target) return message.channel.send('Specify a valid role.'); 13 | 14 | let operation = 'added'; 15 | if (client.utils.indelibleRoles.includes(target.id)) { 16 | await GuildModel.updateOne({ id: message.guildId }, { $pull: { indelibleRoles: target.id } }, { upsert: true }); 17 | client.utils.indelibleRoles = client.utils.indelibleRoles.filter(role => role !== target.id); 18 | operation = 'removed'; 19 | } else { 20 | client.utils.indelibleRoles.push(target.id); 21 | await GuildModel.updateOne({ id: message.guildId }, { $push: { indelibleRoles: target.id } }, { upsert: true }); 22 | } 23 | 24 | message.channel.send({ content: `\`${target.name}\` ${operation} in list.` }); 25 | }, 26 | }; 27 | 28 | export default SafeRole; 29 | -------------------------------------------------------------------------------- /src/commands/Safes.ts: -------------------------------------------------------------------------------- 1 | import { MessageSelectMenu, MessageActionRow, MessageEmbed, Message, Role } from 'discord.js'; 2 | import { Core } from '../base/Core'; 3 | import { GuildModel } from '../models/Guild'; 4 | 5 | const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); 6 | 7 | const Safes: Backup.Command = { 8 | usages: ['safe', 'safes'], 9 | execute: async ({ client, message, args }) => { 10 | const operation = args[0] ? args[0] : undefined; 11 | if (!operation) return message.channel.send('Please specified a valid argument. (`list`, `role` or `user`)'); 12 | 13 | if (operation === 'list') return await showLists(client, message); 14 | await addSafe(client, message, args); 15 | }, 16 | }; 17 | 18 | export default Safes; 19 | 20 | async function showLists(client: Core, message: Message) { 21 | const data = await GuildModel.findOne({ id: message.guildId }); 22 | if (!data) return message.channel.send('List is not found.'); 23 | 24 | let rows: MessageActionRow[] = []; 25 | ['safeChannels', 'safeBans', 'safeRoles', 'safeOwners', 'safeDevelopers'].forEach((key) => { 26 | const array: string[] = data[key]; 27 | if (!array.length) return; 28 | rows.push( 29 | new MessageActionRow() 30 | .addComponents( 31 | new MessageSelectMenu() 32 | .setCustomId(`list-${key}`) 33 | .setPlaceholder(`Nothing selected. (${capitalize(key.slice(4))})`) 34 | .addOptions( 35 | array.map((element) => { 36 | const elementName = message.guild.roles.cache.get(element)?.name || client.users.cache.get(element)?.tag || element; 37 | return { label: elementName, value: element, description: 'Click for remove!' }; 38 | }) 39 | ) 40 | .setMaxValues(array.length === 25 ? 25 : array.length) 41 | .setMinValues(1) 42 | ) 43 | ) 44 | }); 45 | 46 | const question = await message.channel.send({ content: rows.length ? 'I show you the lists.' : 'List is not found.', components: rows }) 47 | const collector = await question.createMessageComponentCollector({ 48 | componentType: 'SELECT_MENU', 49 | filter: (component) => component.user.id === message.author.id && component.customId.startsWith('list'), 50 | time: 60000, 51 | }); 52 | 53 | collector.on('collect', async (interaction) => { 54 | interaction.deferUpdate(); 55 | 56 | const componentId = interaction.customId.split('-')[1]; 57 | const key = componentId.slice(4).slice(0, -1).toLowerCase(); 58 | interaction.values.forEach(async (value) => { 59 | const safeRole = client.utils.safeRoles.find((sRole) => sRole.id === value); 60 | if (safeRole) safeRole[key] = false; 61 | 62 | const safe = client.safes.get(value); 63 | if (safe) safe[key] = false; 64 | 65 | await GuildModel.updateOne({ id: message.guildId }, { $pull: { [componentId]: value } }) 66 | }); 67 | 68 | rows = [...rows.filter(row => row.components[0].customId !== interaction.customId)]; 69 | const array = data[componentId].filter((element) => !interaction.values.includes(element)) 70 | if (array.length) { 71 | rows.push( 72 | new MessageActionRow() 73 | .addComponents( 74 | new MessageSelectMenu() 75 | .setCustomId(`list-${interaction.customId}`) 76 | .setPlaceholder(`Nothing selected. (${capitalize(componentId.slice(4))})`) 77 | .addOptions( 78 | array.map((element) => { 79 | const elementName = message.guild.roles.cache.get(element)?.name || client.users.cache.get(element)?.tag || element; 80 | return { label: elementName, value: element, description: 'Click for remove!' }; 81 | }) 82 | ) 83 | .setMaxValues(array.length === 25 ? 25 : array.length) 84 | .setMinValues(1) 85 | ) 86 | ) 87 | } 88 | question.edit({ 89 | content: rows.length ? 'I show you the lists.' : 'List is not found.', 90 | components: rows 91 | }); 92 | }); 93 | 94 | collector.on('end', () => { 95 | question.delete(); 96 | }); 97 | } 98 | 99 | async function addSafe(client: Core, message: Message, args: string[]) { 100 | const target = args[0] ? (message.guild.members.cache.get(args[0].replace(/\D/g, '')) || message.guild.roles.cache.get(args[0].replace(/\D/g, ''))) : undefined; 101 | if (!target) return message.channel.send('Specify a valid user or role.'); 102 | 103 | const row = new MessageActionRow() 104 | .addComponents( 105 | new MessageSelectMenu() 106 | .setCustomId('safes') 107 | .setPlaceholder('Nothing selected.') 108 | .addOptions([ 109 | { label: 'Developer', value: 'safeDeveloper' }, 110 | { label: 'Owner', value: 'safeOwner' }, 111 | { label: 'Role', value: 'safeRole' }, 112 | { label: 'Ban and Kick', value: 'safeBan' }, 113 | { label: 'Channel', value: 'safeChannel' }, 114 | ]) 115 | .setMaxValues(5) 116 | .setMinValues(1) 117 | ); 118 | 119 | const question = await message.channel.send({ 120 | embeds: [ 121 | new MessageEmbed({ 122 | author: { 123 | name: message.author.tag, 124 | icon_url: message.author.displayAvatarURL({ dynamic: true }) 125 | }, 126 | description: [ 127 | `**Developer:** Everything is free.`, 128 | `**Owner:** Everything is free except deleting secure roles and everything is limited.`, 129 | `**Role:** Only operations is limited.`, 130 | `**Ban and Kick:** Only ban and kick operations is limited.`, 131 | `**Channel:** Only channel operations is limited.` 132 | ].join('\n'), 133 | color: 'RANDOM', 134 | footer: { 135 | text: 'Select tier using the menu below! To take back the permissions you have given, use the command and select the authorities you have given.' 136 | } 137 | }) 138 | ], 139 | components: [row], 140 | }); 141 | 142 | question.awaitMessageComponent({ 143 | componentType: 'SELECT_MENU', 144 | filter: (component) => component.user.id === message.author.id, 145 | time: 60000, 146 | }) 147 | .then(async (collected) => { 148 | const addedAuths = []; 149 | const removedAuths = []; 150 | for (const value of collected.values) { 151 | const newKey = value.toLowerCase().slice(4); 152 | if (target instanceof Role) { 153 | const safeRole = client.utils.safeRoles.find((sRole) => sRole.id === target.id) || { ban: false, channel: false, developer: false, owner: false, role: false }; 154 | if (!safeRole[newKey]) { 155 | await GuildModel.updateOne({ id: message.guildId }, { $push: { [`${value}s`]: target.id } }, { upsert: true }); 156 | safeRole[newKey] = true; 157 | addedAuths.push(newKey); 158 | } else { 159 | await GuildModel.updateOne({ id: message.guildId }, { $pull: { [`${value}s`]: target.id } }, { upsert: true }); 160 | safeRole[newKey] = false; 161 | removedAuths.push(newKey); 162 | } 163 | if (!client.utils.safeRoles.some((sRole) => sRole.id === target.id)) client.utils.safeRoles.push({ ...safeRole, id: target.id }) 164 | continue; 165 | } 166 | 167 | const person = client.safes.get(target.id) || { ban: false, channel: false, developer: false, owner: false, role: false }; 168 | if (!person[newKey]) { 169 | await GuildModel.updateOne({ id: message.guildId }, { $push: { [`${value}s`]: target.id } }, { upsert: true }); 170 | person[newKey] = true; 171 | addedAuths.push(newKey); 172 | } else { 173 | await GuildModel.updateOne({ id: message.guildId }, { $pull: { [`${value}s`]: target.id } }, { upsert: true }); 174 | person[newKey] = false; 175 | removedAuths.push(newKey); 176 | } 177 | client.safes.set(target.id, person); 178 | } 179 | 180 | question.delete(); 181 | collected.reply({ 182 | content: [ 183 | 'The specified operations were performed successfully.', 184 | `\`Added Authorities:\` ${addedAuths.map(auth => capitalize(auth)).join(', ') || 'None'}.`, 185 | `\`Removed Authorities:\` ${removedAuths.map(auth => capitalize(auth)).join(', ') || 'None'}.`, 186 | ].join('\n'), 187 | ephemeral: true 188 | }); 189 | }) 190 | .catch(() => question.delete()); 191 | } 192 | -------------------------------------------------------------------------------- /src/events/ChannelCreate.ts: -------------------------------------------------------------------------------- 1 | import { GuildBasedChannel } from 'discord.js'; 2 | 3 | const ChannelCreate: Backup.Event = { 4 | name: 'channelCreate', 5 | execute: async (client, channel: GuildBasedChannel) => { 6 | const entry = await channel.guild.fetchAuditLogs({ limit: 1, type: 'CHANNEL_CREATE' }).then((audit) => audit.entries.first()); 7 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | channel.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 12 | (sRole.developer || sRole.owner || sRole.channel) 13 | ); 14 | if ( 15 | safe?.developer || 16 | safeRole?.developer || 17 | ( 18 | (safe?.owner || safe?.channel || safeRole?.owner || safeRole?.channel) && 19 | !client.utils.checkLimits(entry.executor.id, 'channel_operations') 20 | ) 21 | ) 22 | return; 23 | 24 | client.utils.danger = true; 25 | await channel.guild.members.ban(entry.executor.id); 26 | await client.utils.closePermissions(); 27 | await channel.delete(); 28 | if (channel.guild.publicUpdatesChannel) channel.guild.publicUpdatesChannel.send(`[\`CREATE-CHANNEL\`] **${entry.executor.tag}**`); 29 | }, 30 | }; 31 | 32 | export default ChannelCreate; -------------------------------------------------------------------------------- /src/events/ChannelDelete.ts: -------------------------------------------------------------------------------- 1 | import { GuildBasedChannel } from 'discord.js'; 2 | 3 | const ChannelDelete: Backup.Event = { 4 | name: 'channelDelete', 5 | execute: async (client, channel: GuildBasedChannel) => { 6 | const entry = await channel.guild.fetchAuditLogs({ limit: 1, type: 'CHANNEL_DELETE' }).then((audit) => audit.entries.first()); 7 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | channel.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 12 | (sRole.developer || sRole.owner || sRole.channel) 13 | ); 14 | if ( 15 | safe?.developer || 16 | safeRole?.developer || 17 | ( 18 | (safe?.owner || safe?.channel || safeRole?.owner || safeRole?.channel) && 19 | !client.utils.checkLimits(entry.executor.id, 'channel_operations') 20 | ) 21 | ) 22 | return; 23 | 24 | client.utils.danger = true; 25 | await channel.guild.members.ban(entry.executor.id); 26 | await client.utils.closePermissions(); 27 | if (channel.guild.publicUpdatesChannel) channel.guild.publicUpdatesChannel.send(`[\`DELETE-CHANNEL\`] **${entry.executor.tag}**`); 28 | }, 29 | }; 30 | 31 | export default ChannelDelete; -------------------------------------------------------------------------------- /src/events/ChannelUpdate.ts: -------------------------------------------------------------------------------- 1 | import { GuildBasedChannel } from 'discord.js'; 2 | import { ChannelModel } from '../models/Channel'; 3 | 4 | const ChannelUpdate: Backup.Event = { 5 | name: 'channelUpdate', 6 | execute: async (client, oldChannel: GuildBasedChannel, newChannel: GuildBasedChannel) => { 7 | const entry = await newChannel.guild.fetchAuditLogs({ limit: 1, type: 'CHANNEL_UPDATE' }).then((audit) => audit.entries.first()); 8 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 9 | 10 | const safe = client.safes.get(entry.executor.id); 11 | const safeRole = client.utils.safeRoles.find((sRole) => 12 | newChannel.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 13 | (sRole.developer || sRole.owner || sRole.channel) 14 | ); 15 | if ( 16 | safe?.developer || 17 | safeRole?.developer || 18 | ( 19 | (safe?.owner || safe?.channel || safeRole?.owner || safeRole?.channel) && 20 | !client.utils.checkLimits(entry.executor.id, 'channel_operations') 21 | ) 22 | ) 23 | return; 24 | 25 | client.utils.danger = true; 26 | await newChannel.guild.members.ban(entry.executor.id); 27 | await client.utils.closePermissions(); 28 | if (newChannel.guild.publicUpdatesChannel) newChannel.guild.publicUpdatesChannel.send(`[\`UPDATE-CHANNEL\`] **${entry.executor.tag}**`); 29 | 30 | const data = await ChannelModel.findOne({ id: newChannel.id }); 31 | if (!data) return client.logger.warning(`WARN: #${oldChannel.name} (${oldChannel.id}) was not created because the data could not be found.`); 32 | 33 | newChannel.edit({ 34 | name: data.name, 35 | nsfw: data.nsfw, 36 | parent: data.parent, 37 | topic: data.topic, 38 | position: data.position, 39 | userLimit: data.userLimit, 40 | permissionOverwrites: data.permissionOverwrites, 41 | }); 42 | }, 43 | }; 44 | 45 | export default ChannelUpdate; -------------------------------------------------------------------------------- /src/events/GuildBandAdd.ts: -------------------------------------------------------------------------------- 1 | import { GuildBan } from 'discord.js'; 2 | 3 | const GuildBanAdd: Backup.Event = { 4 | name: 'guildBanAdd', 5 | execute: async (client, ban: GuildBan) => { 6 | const entry = await ban.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_BAN_ADD' }).then((audit) => audit.entries.first()); 7 | if (Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | ban.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 12 | (sRole.developer || sRole.owner || sRole.ban) 13 | ); 14 | if ( 15 | safe?.developer || 16 | safeRole?.developer || 17 | ( 18 | (safe?.owner || safe?.ban || safeRole?.owner || safeRole?.ban) && 19 | !client.utils.checkLimits(entry.executor.id, 'ban_kick') 20 | ) 21 | ) 22 | return; 23 | 24 | client.utils.danger = true; 25 | await ban.guild.members.ban(entry.executor.id); 26 | await client.utils.closePermissions(); 27 | if (ban.guild.publicUpdatesChannel) ban.guild.publicUpdatesChannel.send(`[\`MEMBER-BAN\`] **${entry.executor.tag}**`); 28 | }, 29 | }; 30 | 31 | export default GuildBanAdd; -------------------------------------------------------------------------------- /src/events/GuildMemberAdd.ts: -------------------------------------------------------------------------------- 1 | import { GuildMember } from 'discord.js'; 2 | 3 | const GuildMemberAdd: Backup.Event = { 4 | name: 'guildMemberAdd', 5 | execute: async (client, member: GuildMember) => { 6 | if (!member.user.bot) return; 7 | 8 | const entry = await member.guild.fetchAuditLogs({ limit: 1, type: 'BOT_ADD' }).then((audit) => audit.entries.first()); 9 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 10 | 11 | const safe = client.safes.get(entry.executor.id); 12 | const safeRole = client.utils.safeRoles.find((sRole) => 13 | member.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && sRole.developer 14 | ); 15 | if (safe?.developer || safeRole?.developer) return; 16 | 17 | client.utils.danger = true; 18 | await member.guild.members.ban(member.id); 19 | await member.guild.members.ban(entry.executor.id); 20 | await client.utils.closePermissions(); 21 | if (member.guild.publicUpdatesChannel) member.guild.publicUpdatesChannel.send(`[\`ADD-BOT\`] **${entry.executor.tag}**`); 22 | }, 23 | }; 24 | 25 | export default GuildMemberAdd; -------------------------------------------------------------------------------- /src/events/GuildMemberUpdate.ts: -------------------------------------------------------------------------------- 1 | import { GuildMember } from 'discord.js'; 2 | 3 | const GuildMemberUpdate: Backup.Event = { 4 | name: 'guildMemberUpdate', 5 | execute: async (client, oldMember: GuildMember, newMember: GuildMember) => { 6 | if ( 7 | oldMember.roles.cache.size === newMember.roles.cache.size || 8 | newMember.roles.cache.filter((role) => !oldMember.roles.cache.has(role.id) && client.utils.dangerPerms.some((perm) => role.permissions.has(perm))).size === 0 9 | ) 10 | return; 11 | 12 | const entry = await newMember.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_ROLE_UPDATE' }).then((audit) => audit.entries.first()); 13 | if (Date.now() - entry.createdTimestamp > 5000) return; 14 | 15 | const safe = client.safes.get(entry.executor.id); 16 | const safeRole = client.utils.safeRoles.find((sRole) => 17 | newMember.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 18 | (sRole.developer || sRole.owner || sRole.role) 19 | ); 20 | if ( 21 | safe?.developer || 22 | safeRole?.developer || 23 | ( 24 | (safe?.owner || safe?.role || safeRole?.owner || safeRole?.role) && 25 | !client.utils.checkLimits(entry.executor.id, 'role_operations') 26 | ) 27 | ) 28 | return; 29 | 30 | client.utils.danger = true; 31 | await newMember.guild.members.ban(entry.executor.id); 32 | await newMember.roles.set(oldMember.roles.cache); 33 | await client.utils.closePermissions(); 34 | if (newMember.guild.publicUpdatesChannel) newMember.guild.publicUpdatesChannel.send(`[\`UPDATE-MEMBER\`] **${entry.executor.tag}**`); 35 | }, 36 | }; 37 | 38 | export default GuildMemberUpdate; -------------------------------------------------------------------------------- /src/events/GuildUpdate.ts: -------------------------------------------------------------------------------- 1 | import { Guild } from 'discord.js'; 2 | 3 | const GuildUpdate: Backup.Event = { 4 | name: 'guildUpdate', 5 | execute: async (client, oldGuild: Guild, newGuild: Guild) => { 6 | if (oldGuild.banner === newGuild.banner && oldGuild.icon === newGuild.icon && oldGuild.name === newGuild.name) return; 7 | 8 | const entry = await newGuild.fetchAuditLogs({ limit: 1, type: 'GUILD_UPDATE' }).then((audit) => audit.entries.first()); 9 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 10 | 11 | const safe = client.safes.get(entry.executor.id); 12 | const safeRole = client.utils.safeRoles.find((sRole) => 13 | newGuild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 14 | (sRole.developer || sRole.owner || sRole.role) 15 | ); 16 | if (safe?.developer || safeRole?.developer || safe?.owner || safeRole?.owner) return; 17 | 18 | client.utils.danger = true; 19 | await newGuild.members.ban(entry.executor.id); 20 | await client.utils.closePermissions(); 21 | await newGuild.edit(client.utils.guildSettings); 22 | if (newGuild.publicUpdatesChannel) newGuild.publicUpdatesChannel.send(`[\`GUILD-UPDATE\`] **${entry.executor.tag}**`); 23 | }, 24 | }; 25 | 26 | export default GuildUpdate; -------------------------------------------------------------------------------- /src/events/MemberKick.ts: -------------------------------------------------------------------------------- 1 | import { GuildMember } from 'discord.js'; 2 | 3 | const GuildMemberRemove: Backup.Event = { 4 | name: 'guildMemberRemove', 5 | execute: async (client, member: GuildMember) => { 6 | if (!member.user.bot) return; 7 | 8 | const entry = await member.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_PRUNE' }).then((audit) => audit.entries.first()); 9 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 10 | 11 | const safe = client.safes.get(entry.executor.id); 12 | const safeRole = client.utils.safeRoles.find((sRole) => 13 | member.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 14 | (sRole.developer || sRole.owner || sRole.ban) 15 | ); 16 | if ( 17 | safe?.developer || 18 | safeRole?.developer || 19 | ( 20 | (safe?.owner || safe?.ban || safeRole?.owner || safeRole?.ban) && 21 | !client.utils.checkLimits(entry.executor.id, 'ban_kick') 22 | ) 23 | ) 24 | return; 25 | 26 | client.utils.danger = true; 27 | await member.guild.members.ban(entry.executor.id); 28 | await client.utils.closePermissions(); 29 | if (member.guild.publicUpdatesChannel) member.guild.publicUpdatesChannel.send(`[\`MEMBER-KICK\`] **${entry.executor.tag}**`); 30 | }, 31 | }; 32 | 33 | export default GuildMemberRemove; -------------------------------------------------------------------------------- /src/events/MemberPrune.ts: -------------------------------------------------------------------------------- 1 | import { GuildMember } from 'discord.js'; 2 | 3 | const GuildMemberRemove: Backup.Event = { 4 | name: 'guildMemberRemove', 5 | execute: async (client, member: GuildMember) => { 6 | if (!member.user.bot) return; 7 | 8 | const entry = await member.guild.fetchAuditLogs({ limit: 1, type: 'MEMBER_PRUNE' }).then((audit) => audit.entries.first()); 9 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 10 | 11 | const safe = client.safes.get(entry.executor.id); 12 | const safeRole = client.utils.safeRoles.find((sRole) => 13 | member.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && sRole.developer 14 | ); 15 | if (safe?.developer || safeRole?.developer) return; 16 | 17 | client.utils.danger = true; 18 | await member.guild.members.ban(entry.executor.id); 19 | await client.utils.closePermissions(); 20 | if (member.guild.publicUpdatesChannel) member.guild.publicUpdatesChannel.send(`[\`MEMBER-PRUNE\`] **${entry.executor.tag}**`); 21 | }, 22 | }; 23 | 24 | export default GuildMemberRemove; -------------------------------------------------------------------------------- /src/events/MessageCreate.ts: -------------------------------------------------------------------------------- 1 | import { Message } from 'discord.js'; 2 | 3 | const MessageCreate: Backup.Event = { 4 | name: 'messageCreate', 5 | execute: async (client, message: Message) => { 6 | if (!message.content.startsWith(client.config.PREFIX)) return; 7 | 8 | const safe = client.safes.get(message.author.id); 9 | const safeRole = client.utils.safeRoles.find((sRole) => 10 | message.guild.roles.cache.get(sRole.id)?.members.has(message.author.id) && sRole.developer 11 | ); 12 | if (!safe?.developer && !safeRole?.developer) return; 13 | 14 | const args = message.content.slice(client.config.PREFIX.length).trim().split(' '); 15 | const commandName = args.shift()?.toLowerCase() as string; 16 | const command = client.commands.find((command) => command.usages.includes(commandName)); 17 | if (command) command.execute({ client, message, args }); 18 | }, 19 | }; 20 | 21 | export default MessageCreate; 22 | -------------------------------------------------------------------------------- /src/events/Ready.ts: -------------------------------------------------------------------------------- 1 | import { GuildModel } from '../models/Guild'; 2 | import { Team } from 'discord.js'; 3 | 4 | const Ready: Backup.Event = { 5 | name: 'ready', 6 | execute: async (client) => { 7 | setInterval(async () => { 8 | if (client.utils.danger === false) await client.utils.getBackup(); 9 | }, 1000 * 60 * 60); 10 | 11 | await client.application.fetch(); 12 | const ownerID = client.application.owner instanceof Team ? (client.application.owner as Team).ownerId : client.application.owner.id; 13 | client.safes.set(ownerID, { developer: true }); 14 | client.safes.set(client.user.id, { developer: true }); 15 | 16 | const guild = client.guilds.cache.get(client.config.GUILD_ID); 17 | if (!guild) return client.logger.warning(`WARN: ${client.user.tag} is not in server!`); 18 | 19 | client.utils.guildSettings = { 20 | name: guild.name, 21 | icon: guild.iconURL({ dynamic: true }), 22 | banner: guild.bannerURL(), 23 | }; 24 | 25 | const data = await GuildModel.findOne({ id: client.config.GUILD_ID }); 26 | if (!data) return; 27 | 28 | client.utils.indelibleRoles = data.indelibleRoles; 29 | 30 | const safes = [...new Set([...data.safeBans, ...data.safeDevelopers, ...data.safeChannels, ...data.safeOwners, ...data.safeRoles])]; 31 | safes.forEach((safe) => { 32 | const role = guild.roles.cache.get(safe); 33 | const auths = { 34 | ban: data.safeBans.includes(safe), 35 | channel: data.safeChannels.includes(safe), 36 | developer: data.safeDevelopers.includes(safe), 37 | owner: data.safeOwners.includes(safe), 38 | role: data.safeRoles.includes(safe) 39 | }; 40 | 41 | if (role) client.utils.safeRoles.push({ id: safe, ...auths }); 42 | else client.safes.set(safe, auths); 43 | }); 44 | }, 45 | }; 46 | 47 | export default Ready; 48 | -------------------------------------------------------------------------------- /src/events/RoleCreate.ts: -------------------------------------------------------------------------------- 1 | import { Role } from 'discord.js'; 2 | 3 | const RoleCreate: Backup.Event = { 4 | name: 'roleCreate', 5 | execute: async (client, role: Role) => { 6 | const entry = await role.guild.fetchAuditLogs({ limit: 1, type: 'ROLE_CREATE' }).then((audit) => audit.entries.first()); 7 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | role.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 12 | (sRole.developer || sRole.owner) 13 | ); 14 | if ( 15 | safe?.developer || 16 | safeRole?.developer || 17 | ( 18 | (safe?.owner || safeRole?.owner) && 19 | !client.utils.checkLimits(entry.executor.id, 'role_operations') 20 | ) 21 | ) 22 | return; 23 | 24 | client.utils.danger = true; 25 | await role.guild.members.ban(entry.executor.id); 26 | await client.utils.closePermissions(); 27 | await role.delete(); 28 | if (role.guild.publicUpdatesChannel) role.guild.publicUpdatesChannel.send(`[\`CREATE-ROLE\`] **${entry.executor.tag}**`); 29 | }, 30 | }; 31 | 32 | export default RoleCreate; -------------------------------------------------------------------------------- /src/events/RoleDelete.ts: -------------------------------------------------------------------------------- 1 | import { Role } from 'discord.js'; 2 | 3 | const RoleDelete: Backup.Event = { 4 | name: 'roleDelete', 5 | execute: async (client, role: Role) => { 6 | const entry = await role.guild.fetchAuditLogs({ limit: 1, type: 'ROLE_DELETE' }).then((audit) => audit.entries.first()); 7 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | role.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 12 | (sRole.developer || sRole.owner) 13 | ); 14 | if ( 15 | safe?.developer || 16 | safeRole?.developer || 17 | ( 18 | !client.utils.indelibleRoles.includes(role.id) && 19 | (safe?.owner || safeRole?.owner) && 20 | !client.utils.checkLimits(entry.executor.id, 'role_operations') 21 | ) 22 | ) 23 | return; 24 | 25 | client.utils.danger = true; 26 | await role.guild.members.ban(entry.executor.id); 27 | await client.utils.closePermissions(); 28 | if (role.guild.publicUpdatesChannel) role.guild.publicUpdatesChannel.send(`[\`DELETE-ROLE\`] **${entry.executor.tag}**`); 29 | }, 30 | }; 31 | 32 | export default RoleDelete; 33 | -------------------------------------------------------------------------------- /src/events/RoleUpdate.ts: -------------------------------------------------------------------------------- 1 | import { Role } from 'discord.js'; 2 | import { RoleModel } from '../models/Role'; 3 | 4 | const RoleUpdate: Backup.Event = { 5 | name: 'roleUpdate', 6 | execute: async (client, role: Role) => { 7 | const entry = await role.guild.fetchAuditLogs({ limit: 1, type: 'ROLE_UPDATE' }).then((audit) => audit.entries.first()); 8 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 9 | 10 | const safe = client.safes.get(entry.executor.id); 11 | const safeRole = client.utils.safeRoles.find((sRole) => 12 | role.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && 13 | (sRole.developer || sRole.owner) 14 | ); 15 | if ( 16 | safe?.developer || 17 | safeRole?.developer || 18 | ( 19 | (safe?.owner || safeRole?.owner) && 20 | !client.utils.checkLimits(entry.executor.id, 'role_operations') 21 | ) 22 | ) 23 | return; 24 | 25 | client.utils.danger = true; 26 | await role.guild.members.ban(entry.executor.id); 27 | await client.utils.closePermissions(); 28 | if (role.guild.publicUpdatesChannel) role.guild.publicUpdatesChannel.send(`[\`UPDATE-ROLE\`] **${entry.executor.tag}**`); 29 | 30 | const data = await RoleModel.findOne({ id: role.id }); 31 | if (!data) return client.logger.warning(`WARN: @${role.name} (${role.id}) was not created because the data could not be found.`); 32 | 33 | role.edit({ 34 | name: data.name, 35 | color: data.color, 36 | hoist: data.hoist, 37 | permissions: data.permissions, 38 | position: data.position, 39 | mentionable: data.mentionable, 40 | }); 41 | }, 42 | }; 43 | 44 | export default RoleUpdate; -------------------------------------------------------------------------------- /src/events/WebhookUpdate.ts: -------------------------------------------------------------------------------- 1 | import { NewsChannel, TextChannel } from 'discord.js'; 2 | 3 | const WebhookDelete: Backup.Event = { 4 | name: 'webhookUpdate', 5 | execute: async (client, webhook: TextChannel | NewsChannel) => { 6 | const entry = await webhook.guild.fetchAuditLogs({ limit: 1, type: 'WEBHOOK_CREATE' }).then((audit) => audit.entries.first()); 7 | if (!entry || Date.now() - entry.createdTimestamp > 5000) return; 8 | 9 | const safe = client.safes.get(entry.executor.id); 10 | const safeRole = client.utils.safeRoles.find((sRole) => 11 | webhook.guild.roles.cache.get(sRole.id)?.members.has(entry.executor.id) && sRole.developer 12 | ); 13 | if (safe?.developer || safeRole?.developer) return; 14 | 15 | client.utils.danger = true; 16 | await webhook.guild.members.ban(entry.executor.id); 17 | await client.utils.closePermissions(); 18 | if (webhook.guild.publicUpdatesChannel) webhook.guild.publicUpdatesChannel.send(`[\`WEBHOOK-CREATE\`] **${entry.executor.tag}**`); 19 | }, 20 | }; 21 | 22 | export default WebhookDelete; -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Core } from './base/Core'; 2 | 3 | const client = new Core(); 4 | 5 | client.connect(); 6 | 7 | process.on('unhandledRejection', (error: Error) => { 8 | if (error.message === 'Missing Permissions') return; 9 | client.logger.warning(`WARN: ${error.name} | ${error.message}`); 10 | }); 11 | -------------------------------------------------------------------------------- /src/models/Channel.ts: -------------------------------------------------------------------------------- 1 | import { prop, getModelForClass, modelOptions } from '@typegoose/typegoose'; 2 | import { OverwriteData } from 'discord.js'; 3 | 4 | @modelOptions({ options: { customName: 'Channels', allowMixed: 0 } }) 5 | export class ChannelSchema { 6 | @prop({ type: () => String, required: true, unique: true }) 7 | id!: string; 8 | 9 | @prop({ type: () => String, required: true }) 10 | name!: string; 11 | 12 | @prop({ type: () => Number, required: true }) 13 | type!: number; 14 | 15 | @prop({ type: () => String, required: false, default: undefined }) 16 | parent?: string; 17 | 18 | @prop({ type: () => String, required: false }) 19 | topic!: string; 20 | 21 | @prop({ type: () => Number, required: true }) 22 | position!: number; 23 | 24 | @prop({ type: () => Number, required: false }) 25 | userLimit!: number; 26 | 27 | @prop({ type: () => Boolean, required: false }) 28 | nsfw!: boolean; 29 | 30 | @prop({ type: () => [Object], required: true }) 31 | permissionOverwrites!: OverwriteData[]; 32 | } 33 | 34 | export const ChannelModel = getModelForClass(ChannelSchema); 35 | -------------------------------------------------------------------------------- /src/models/Guild.ts: -------------------------------------------------------------------------------- 1 | import { prop, getModelForClass, modelOptions } from '@typegoose/typegoose'; 2 | 3 | @modelOptions({ options: { customName: 'Guilds', allowMixed: 0 } }) 4 | export class GuildSchema { 5 | @prop({ type: () => String, required: true, unique: true }) 6 | id!: string; 7 | 8 | @prop({ type: () => [String], default: [] }) 9 | indelibleRoles!: string[]; 10 | 11 | @prop({ type: () => [Object], default: [] }) 12 | permissions!: Backup.Permission[]; 13 | 14 | @prop({ type: () => [String], default: [] }) 15 | safeDevelopers!: string[]; 16 | 17 | @prop({ type: () => [String], default: [] }) 18 | safeOwners!: string[]; 19 | 20 | @prop({ type: () => [String], default: [] }) 21 | safeRoles!: string[]; 22 | 23 | @prop({ type: () => [String], default: [] }) 24 | safeBans!: string[]; 25 | 26 | @prop({ type: () => [String], default: [] }) 27 | safeChannels!: string[]; 28 | } 29 | 30 | export const GuildModel = getModelForClass(GuildSchema); 31 | -------------------------------------------------------------------------------- /src/models/Role.ts: -------------------------------------------------------------------------------- 1 | import { prop, getModelForClass, modelOptions } from '@typegoose/typegoose'; 2 | import { PermissionString } from 'discord.js'; 3 | 4 | @modelOptions({ options: { customName: 'Roles', allowMixed: 0 } }) 5 | export class RoleSchema { 6 | @prop({ type: () => String, required: true, unique: true }) 7 | id!: string; 8 | 9 | @prop({ type: () => String, required: true }) 10 | name!: string; 11 | 12 | @prop({ type: () => Number, required: true }) 13 | color!: number; 14 | 15 | @prop({ type: () => Number, required: true }) 16 | position!: number; 17 | 18 | @prop({ type: () => [String], required: true }) 19 | permissions!: PermissionString[]; 20 | 21 | @prop({ type: () => [Object], required: true }) 22 | channelOverwrites!: Backup.RoleOverwrites[]; 23 | 24 | @prop({ type: () => [String], required: true }) 25 | members!: string[]; 26 | 27 | @prop({ type: () => Boolean, required: true }) 28 | hoist!: boolean; 29 | 30 | @prop({ type: () => Boolean, required: true }) 31 | mentionable!: boolean; 32 | } 33 | 34 | export const RoleModel = getModelForClass(RoleSchema); 35 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "allowSyntheticDefaultImports": true, 5 | "target": "es2017", 6 | "baseUrl": "./", 7 | "outDir": "./dist", 8 | "incremental": true, 9 | "strict": false, 10 | "typeRoots": ["node_modules/@types", "src/@types"], 11 | "skipLibCheck": true, 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------