├── .gitattributes ├── .gitignore ├── LICENSE ├── config.js ├── events ├── -ornekOlay.js └── -ornekPluginOlay.js ├── generated ├── configOther.d.ts ├── ids.d.ts ├── localeTypes.d.ts └── pluginTypes.d.ts ├── globals.d.ts ├── index.js ├── interactions ├── -ornekKomut.js ├── -ornekSağtık.js ├── autocomplateUnban.js ├── butonYolla.js ├── eval.js ├── matamatik.js ├── modal-yolla.js ├── moderasyon │ ├── -rol.js │ ├── -ses-sustur.js │ ├── -temizle.js │ ├── -yasakla.js │ └── at.js ├── ornek-modal.js ├── ornekButton.js ├── profil.js └── reload.js ├── jsconfig.json ├── locales ├── English.js └── Turkish.js ├── main.js ├── other ├── generator.js ├── patchConsoleLog.js ├── typesGenerator.js └── utils.js ├── package-lock.json ├── package.json ├── plugins ├── -mongoose.up └── -vault.up ├── publishInteractions.js ├── readme.md ├── types ├── Button.js ├── ChatInput.js ├── Config.js ├── Event.d.ts ├── Event.js ├── Interaction.d.ts ├── Interaction.js ├── Locale.d.ts ├── Locale.js ├── MemoryVariables.js ├── MessageAction.js ├── Modal.js ├── Plugin.d.ts ├── Plugin.js ├── RedisVariables.js ├── SelectMenu.js └── UserAction.js ├── v14-Upgrader.js └── watchChanges.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | # FuseBox cache 76 | .fusebox/ 77 | 78 | yarn.lock 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Armağanın Basit Altyapısı Copyright (C) 2021-2022 Kıraç Armağan Önal 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const Discord = require("discord.js"); 2 | 3 | module.exports = new (require("./types/Config"))({ 4 | // E tabi, bot tokeni buraya. 5 | clientToken: "", 6 | // Sharding özellikleri 7 | sharding: { 8 | enabled: false, 9 | // [clusterCount, shardCountPerCluster] 10 | count: [4, 4], 11 | onManager(manager) { 12 | manager.on("clusterCreate", (cluster) => { 13 | console.info(`[BILGI] ${cluster.id} idli cluster doğruldu.`); 14 | }); 15 | manager.spawn({ timeout: -1 }); 16 | } 17 | }, 18 | // Yasaklı kullanıcıların idleri. 19 | blockedUsers: new Set([]), 20 | // Geliştiricilerin idleri. 21 | developers: new Set([ 22 | "707309693449535599", 23 | "319862027571036161" 24 | ]), 25 | variables: "memory", // "memory" / "redis" 26 | // 0: No Debug, 1: Minimal Debug 2: Maximum Debug 27 | debugLevel: 2, 28 | // Discord.js client ayarları. 29 | clientOptions: { 30 | // Okumanızı tavsiye ederim: https://discordjs.guide/popular-topics/intents.html 31 | intents: [Discord.IntentsBitField.Flags.Guilds] 32 | }, 33 | // Botunuzun varsayılan dili. 34 | defaultLanguage: "tr", 35 | // Diğer ayarlar. Bunun içine ne isterseniz koyabilirsiniz. 36 | // Ulaşmak için "Underline.config.other" ve/veya "Underline.other" objesini kullanabilirsiniz. 37 | other: { 38 | plugins: { 39 | mongooseDatabase: { 40 | connectionURL: "mongodb://localhost:27017/basit-altyapi" 41 | } 42 | }, 43 | redis: { 44 | url: "redis://localhost:6379", 45 | key: "Underline:Variables:0" 46 | } 47 | }, 48 | // Otomatik olarak "Underline" objesinin içine eklenen değerler. 49 | // Eklediğiniz değerler "Underline." şeklinde ulaşabilirsiniz. 50 | // Prejeinin herhangi bir yerinde bu işlemi yapabilirsiniz. 51 | // Bu obje hiçbir filitrelemeden geçmemektedir. "Başınız yanabilir." 52 | // - Bu özellik other objesinin bi amacı kalmamasını sağlıyor 53 | globalObjects: { 54 | 55 | }, 56 | // Kullanıcı hatalarındaki uyarı mesajları/olayları. 57 | userErrors: { 58 | // Arka arkaya interaksiyon kullanma limiti aşıldığında. 59 | coolDown(interaction, uInteraction, coolDown, type, other) { 60 | interaction.reply({ 61 | ephemeral: true, 62 | content: other.locale.userErrors.coolDown[type]((coolDown / 1000).toFixed(2)) 63 | }); 64 | }, 65 | // interaksiyon kapalı olduğunda 66 | disabled(interaction, uInteraction, other) { 67 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 68 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.disabled() }); 69 | }, 70 | // Kullanıcı bottan yasaklı olduğunda. 71 | blocked(interaction, uInteraction, other) { 72 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 73 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.blocked() }); 74 | }, 75 | // interaksiyon sadece geliştiricilere özel olduğunda. 76 | developerOnly(interaction, uInteraction, other) { 77 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 78 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.developerOnly() }); 79 | }, 80 | // interaksiyon sadece sunucu sahiplerine özel olduğunda. 81 | guildOwnerOnly(interaction, uInteraction, other) { 82 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 83 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.guildOwnerOnly() }); 84 | }, 85 | // interaksiyon sadece sunuculara özel olduğunda. 86 | guildOnly(interaction, uInteraction, other) { 87 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 88 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.guildOnly() }); 89 | }, 90 | // Botun çalışmak için x yertkilerine ihtiyacı olduğunda. 91 | botPermsRequired(interaction, uInteraction, perms, other) { 92 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 93 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.botPermsRequired(perms.join(", ")) }); 94 | }, 95 | // Kullanıcının interaksiyonu kullanabilmek için x yetkilerine ihtiyacı olduğunda. 96 | userPermsRequired(interaction, uInteraction, perms, other) { 97 | if (interaction.type == Discord.InteractionType.ApplicationCommandAutocomplete) return []; 98 | interaction.reply({ ephemeral: true, content: other.locale.userErrors.userPermsRequired(perms.join(", ")) }); 99 | }, 100 | }, 101 | // Her interaksiyonun varsayılan ayarları her anahtarın ne 102 | // işe yaradığını merak ediyorsanız interactions/ornekInteraksiyon.js'e 103 | // bakabilirsiniz. 104 | interactionDefaults: { 105 | actionType: "ChatInput", 106 | description: "...", 107 | developerOnly: false, 108 | guildOnly: true, 109 | disabled: false, 110 | coolDown: -1, 111 | other: {}, 112 | perms: { 113 | bot: [], 114 | user: [] 115 | }, 116 | options: [], 117 | defaultPermission: true, 118 | autoDefer: "off", 119 | nullError: false 120 | }, 121 | // Bot ilk açıldığında daha hiçbirşey yüklenmeden önce çalışan fonksiyon. Opsiyonel. 122 | onBeforeLoad(client) { 123 | console.log("[CONFIG] Yüklemeye başlamadan önce çalıştı."); 124 | }, 125 | // Bot interaksiyonları ve olayları yükledikten sonra çalışan fonksiyon. Opsiyonel. 126 | onAfterLoad(client) { 127 | console.log("[CONFIG] Yükleme bittikten sonra çalıştı."); 128 | }, 129 | // Bot açıldıktan sonra kullanıma hazır olduktan sonra çalışan fonksiyon. Opsiyonel. 130 | async onReady(client) { 131 | console.log("[CONFIG] Discord hesabına giriş yaptıktan sonra çalıştı."); 132 | client.user.setActivity(`/help - Basit Altyapı by TheArmagan`, { type: "Watching" }); 133 | }, 134 | // interaksiyon üzerinde hiçbir kontrol yapılmadan önce çalışır. 135 | // Sadece cevap true ise işleme devam eder. 136 | // 137 | // Other objesini istediğiniz gibi modifiye edebilirsiniz. 138 | // Nasılsa altakki fonksiyon her interaksiyon çalışmadan önce çalışır. 139 | async onInteractionBeforeChecks(uInteraction, interaction, other) { 140 | return true; 141 | }, 142 | // interaksiyontaki bütün kontrolleri geçtikten sonra, interaksiyon 143 | // hemen çalıştırılmadan önce çalışır. 144 | // Sadece cevap true ise işleme devam eder. 145 | // 146 | // Other objesini istediğiniz gibi modifiye edebilirsiniz. 147 | // Nasılsa altakki fonksiyon her interaksiyon çalışmadan önce çalışır. 148 | async onInteraction(uInteraction, interaction, other) { 149 | return true; 150 | }, 151 | // İnteraksiyon hatasız bir şekilde çalıştıktan sonra tetikleniyor. (Armağan: peki.) 152 | async onAfterInteraction(uInteraction, interaction, other) { }, 153 | // eventteki bütün kontrolleri geçtikten sonra, event 154 | // hemen çalıştırılmadan önce çalışır. 155 | // Sadece cevap true ise işleme devam eder. 156 | // 157 | // Other objesini istediğiniz gibi modifiye edebilirsiniz. 158 | // Nasılsa altakki fonksiyon her event çalışmadan önce çalışır. 159 | async onEvent(eventName, args, other) { 160 | return true; 161 | }, 162 | // Olay hatasız bir şekilde çalıştıktan sonra çalışır. 163 | async onAfterEvent(eventName, args, other) { 164 | 165 | }, 166 | }) 167 | -------------------------------------------------------------------------------- /events/-ornekOlay.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.Event({ 2 | // Event idsi. Opsiyonel, boş bırakıldığında dosya ismini alır. 3 | // Boşluk içeremez. 4 | id: "örnekOlay", 5 | // Herhangi bir discord.js olay ismi. 6 | eventName: "messageCreate", 7 | // onEvent belirtiğiniz olay yaşandığında çağrılır. 8 | // interaksiyon argumentlari normal discord.js'deki gibi ideğişkendir. 9 | // Otomatik tamamlama eventName değişkenindeki tipe göre değişir. 10 | onEvent(message, other) { 11 | console.log(`[MESAJ] ${message.author.tag}: ${message.content}`); 12 | }, 13 | // Olay çalışmaya hazır olduğunda bot açılırken 14 | // sadece bir kereliğine çağrılır. Opsiyonel 15 | onLoad(client) { 16 | // Normal discord.js client objesi. 17 | client; 18 | }, 19 | // Event açıkmı kapalı mı? 20 | // Opsiyonel. Varsayılan değer false. 21 | disabled: false 22 | }); -------------------------------------------------------------------------------- /events/-ornekPluginOlay.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.Event({ 2 | eventName: "mongooseDatabase:onConnect", 3 | async onEvent(connected) { 4 | console.log(`[DATABASE] ${ connected ? "Bağlandı": "Bağlanamadı" }!`); 5 | } 6 | }) -------------------------------------------------------------------------------- /generated/configOther.d.ts: -------------------------------------------------------------------------------- 1 | export default class Other { 2 | redis: { 3 | url: string; 4 | key: string 5 | }; 6 | plugins: import('./pluginTypes').config 7 | } -------------------------------------------------------------------------------- /generated/ids.d.ts: -------------------------------------------------------------------------------- 1 | export type InteractionIds = "chat_input_buton-yolla" | "chat_input_matamatik" | "chat_input_eval" | "chat_input_unban" | "chat_input_profil" | "chat_input_reload" | "chat_input_moderasyon_at" | "chat_input_moderasyon_rol" | "chat_input_moderasyon_ses-sustur" | "chat_input_moderasyon_temizle" | "chat_input_moderasyon_yasakla" | "chat_input_interactions_guild_publish" | "chat_input_interactions_guild_clear" | "user_sağtık_üye" | "message_sağtık_mesaj"; 2 | export type EventIds = ""; -------------------------------------------------------------------------------- /generated/localeTypes.d.ts: -------------------------------------------------------------------------------- 1 | export default class Locale { 2 | locale: import("../types/Locale").LocaleString 3 | data: LocaleData 4 | } 5 | 6 | export type LocaleData = { 7 | userErrors: { 8 | coolDown: { 9 | user: (...args) => string, 10 | member: (...args) => string, 11 | guild: (...args) => string, 12 | channel: (...args) => string, 13 | message: (...args) => string, 14 | any: (...args) => string 15 | }, 16 | disabled: (...args) => string, 17 | blocked: (...args) => string, 18 | guildOnly: (...args) => string, 19 | developerOnly: (...args) => string, 20 | guildOwnerOnly: (...args) => string, 21 | botPermsRequired: (...args) => string, 22 | userPermsRequired: (...args) => string 23 | }, 24 | example: { 25 | success: (...args) => string, 26 | error: (...args) => string 27 | }, 28 | test: { 29 | bö: (...args) => string 30 | }, 31 | mongooseDatabase: {} 32 | }; -------------------------------------------------------------------------------- /generated/pluginTypes.d.ts: -------------------------------------------------------------------------------- 1 | import { IOther } from "../types/Event"; 2 | 3 | export class config { 4 | 5 | } 6 | 7 | export class Types { 8 | ["mongooseDatabase"]: import("../plugins/mongoose.up/index").Plugin; 9 | ["vault"]: import("../plugins/vault.up/index").Plugin; 10 | }; 11 | export type TEventNames = "mongooseDatabase:onConnect"; 12 | export type TEvents = mongooseDatabase_onConnect; 13 | export interface mongooseDatabase_onConnect { eventName: "mongooseDatabase:onConnect", onEvent: (arg0: boolean, other: IOther) => void } -------------------------------------------------------------------------------- /globals.d.ts: -------------------------------------------------------------------------------- 1 | // Global değişkenlerde otomatik tamamlama desteği için. 2 | 3 | interface Underline { 4 | interactions: import("discord.js").Collection 5 | events: import("discord.js").Collection 6 | locales: import("discord.js").Collection 7 | config: import("./types/Config"); 8 | other: { [key: string | number]: any; }, 9 | utils: typeof import("./other/utils.js"); 10 | client: import("discord.js").Client; 11 | Interaction: typeof import("./types/Interaction"), 12 | ChatInput: typeof import("./types/ChatInput"), 13 | MessageAction: typeof import("./types/MessageAction"), 14 | UserAction: typeof import("./types/UserAction"), 15 | Event: typeof import("./types/Event"), 16 | Button: typeof import("./types/Button"), 17 | SelectMenu: typeof import("./types/SelectMenu"), 18 | Modal: typeof import("./types/Modal"), 19 | Locale: typeof import("./types/Locale"), 20 | plugins: import("./generated/pluginTypes").Types, 21 | Plugin: typeof import("./types/Plugin"), 22 | variables: (import("./types/MemoryVariables") | import("./types/RedisVariables")) & {type: "memory"|"redis"}, 23 | 24 | reload(): Promise; 25 | 26 | [key: string | number]: any 27 | } 28 | 29 | interface Enums { 30 | ChannelType: typeof import("discord.js").ChannelType, 31 | MessageType: typeof import("discord.js").MessageType, 32 | ApplicationCommandOptionType: typeof import("discord.js").ApplicationCommandOptionType, 33 | ActivityType: typeof import("discord.js").ActivityType, 34 | AuditLogOptionsType: typeof import("discord.js").AuditLogOptionsType, 35 | InteractionType: typeof import("discord.js").InteractionType, 36 | ComponentType: typeof import("discord.js").ComponentType, 37 | ButtonStyle: typeof import("discord.js").ButtonStyle, 38 | TextInputStyle: typeof import("discord.js").TextInputStyle, 39 | } 40 | 41 | 42 | declare var Underline: Underline; 43 | declare var Enums: Enums; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require("./other/patchConsoleLog"); 2 | console.info(`[BİLGİ] Basit Altyapı v${require("./package.json").version} - by Kıraç Armağan Önal`); 3 | const config = require("./config"); 4 | const utils = require("./other/utils"); 5 | globalThis.Underline = config.globalObjects; 6 | const Discord = require("discord.js"); 7 | const { ChannelType, MessageType, ComponentType, InteractionType, ActivityType, AuditLogOptionsType, ApplicationCommandOptionType, ButtonStyle, TextInputStyle } = require("discord.js"); 8 | const RedisVariables = require("./types/RedisVariables"); 9 | const MemoryVariables = require("./types/MemoryVariables"); 10 | 11 | const chillout = require("chillout"); 12 | const path = require("path"); 13 | const fs = require("fs"); 14 | const readdirRecursive = require("recursive-readdir"); 15 | const { makeSureFolderExists } = require("stuffs"); 16 | const Cluster = require('discord-hybrid-sharding'); 17 | const client = new Discord.Client( 18 | Object.assign( 19 | {}, 20 | config.clientOptions, 21 | config.sharding.enabled ? { 22 | shards: Cluster.data.SHARD_LIST, 23 | shardCount: Cluster.data.TOTAL_SHARDS 24 | } : {} 25 | ) 26 | ); 27 | 28 | if (config.sharding.enabled) { 29 | client.cluster = new Cluster.Client(client); 30 | } 31 | 32 | const interactions = new Discord.Collection(); 33 | const events = new Discord.Collection(); 34 | const locales = new Discord.Collection(); 35 | const { quickMap, quickForEach } = require("async-and-quick"); 36 | 37 | let interactionFiles; 38 | let eventFiles; 39 | let localeFiles; 40 | let pluginFiles; 41 | let pluginEvents; 42 | let pluginInteractions; 43 | let onFunctions = { 44 | onInteractionBeforeChecks: [config.onInteractionBeforeChecks], 45 | onInteraction: [config.onInteraction], 46 | onAfterInteraction: [config.onAfterInteraction], 47 | onEvent: [config.onEvent], 48 | onAfterEvent: [config.onAfterEvent], 49 | onReady: [config.onReady], 50 | }; 51 | globalThis.Underline = { 52 | ...config.globalObjects, 53 | config, 54 | other: config.other, 55 | client, 56 | interactions, 57 | events, 58 | locales, 59 | utils, 60 | plugins: {}, 61 | variables: null, 62 | _references: new Discord.Collection(), 63 | Interaction: require('./types/Interaction'), 64 | Event: require('./types/Event'), 65 | ChatInput: require("./types/ChatInput"), 66 | MessageAction: require("./types/MessageAction"), 67 | UserAction: require("./types/UserAction"), 68 | SelectMenu: require("./types/SelectMenu"), 69 | Button: require("./types/Button"), 70 | Modal: require("./types/Modal"), 71 | Locale: require("./types/Locale"), 72 | Plugin: require("./types/Plugin"), 73 | } 74 | 75 | Underline.variables = config.variables == "redis" ? new RedisVariables() : new MemoryVariables(); 76 | 77 | globalThis.Enums = { 78 | ChannelType, 79 | MessageType, 80 | ApplicationCommandOptionType, 81 | ActivityType, 82 | AuditLogOptionsType, 83 | InteractionType, 84 | ComponentType, 85 | ButtonStyle, 86 | TextInputStyle 87 | } 88 | 89 | const extractZip = require("extract-zip"); 90 | const { copyFile } = require("fs/promises"); 91 | 92 | 93 | async function getPluginFilePaths() { 94 | let pluginsPath = path.resolve("./plugins"); 95 | await makeSureFolderExists(pluginsPath); 96 | let folderOrZips = await fs.promises.readdir(pluginsPath, { withFileTypes: true }); 97 | let result = []; 98 | for (let i = 0; i < folderOrZips.length; i++) { 99 | const folderOrZip = folderOrZips[i]; 100 | if (folderOrZip.isDirectory()) { 101 | if (folderOrZip.name.startsWith("-")) continue; 102 | result.push(path.resolve(pluginsPath, folderOrZip.name, "index.js")); 103 | } else if (folderOrZip.name.endsWith(".up.js")) { 104 | if (folderOrZip.name.startsWith("-")) continue; 105 | result.push(path.resolve(pluginsPath, folderOrZip.name)); 106 | } else if (folderOrZip.name.endsWith(".up.zip") || folderOrZip.name.endsWith(".up")) { 107 | let rawName = folderOrZip.name.replace(".up.zip", ".up"); 108 | let folderPath = path.resolve(pluginsPath, rawName); 109 | let zipPath = path.resolve(pluginsPath, folderOrZip.name); 110 | 111 | if (!folderOrZip.name.endsWith(".zip")) { 112 | await copyFile(zipPath, zipPath + ".zip").catch(() => null); 113 | await fs.promises.unlink(zipPath).catch(() => null); 114 | zipPath = zipPath + ".zip"; 115 | } 116 | 117 | await fs.promises.rm(folderPath, { recursive: true }).catch(() => { }); 118 | await makeSureFolderExists(folderPath); 119 | await extractZip(zipPath, { dir: folderPath, defaultFileMode: 0 }); 120 | fs.promises.unlink(zipPath).catch(() => null); 121 | if (folderOrZip.name.startsWith("-")) continue; 122 | result.push(path.resolve(folderPath, "index.js")); 123 | } 124 | } 125 | return result; 126 | } 127 | 128 | async function getEventFilePaths() { 129 | let eventsPath = path.resolve("./events"); 130 | await makeSureFolderExists(eventsPath); 131 | let VEventFiles = await readdirRecursive(eventsPath); 132 | VEventFiles = VEventFiles.filter(i => { 133 | let state = path.basename(i).startsWith("-"); 134 | return !state; 135 | }); 136 | return VEventFiles; 137 | } 138 | 139 | async function getInteractionFilePaths() { 140 | let interactionsPath = path.resolve("./interactions"); 141 | await makeSureFolderExists(interactionsPath); 142 | let VInteractionFiles = await readdirRecursive(interactionsPath); 143 | VInteractionFiles = VInteractionFiles.filter(i => { 144 | let state = path.basename(i).startsWith("-"); 145 | return !state; 146 | }); 147 | return VInteractionFiles; 148 | } 149 | 150 | async function getLocaleFilePaths() { 151 | let localesPath = path.resolve("./locales"); 152 | await makeSureFolderExists(localesPath); 153 | let VLocaleFiles = await readdirRecursive(localesPath); 154 | VLocaleFiles = VLocaleFiles.filter(i => { 155 | let state = path.basename(i).startsWith("-"); 156 | return !state; 157 | }); 158 | return VLocaleFiles; 159 | } 160 | 161 | /** @type {{name:string,listener:()=>any,base:any}[]} */ 162 | let eventListeners = []; 163 | 164 | async function load() { 165 | 166 | onFunctions = { 167 | onInteractionBeforeChecks: [config.onInteractionBeforeChecks], 168 | onInteraction: [config.onInteraction], 169 | onAfterInteraction: [config.onAfterInteraction], 170 | onEvent: [config.onEvent], 171 | onAfterEvent: [config.onAfterEvent], 172 | onReady: [config.onReady], 173 | }; 174 | 175 | let loadStart = Date.now(); 176 | console.debug(`[HATA AYIKLAMA] Yüklemeye başlandı!`); 177 | 178 | localeFiles = await getLocaleFilePaths(); 179 | await chillout.forEach(localeFiles, (localeFile) => { 180 | let start = Date.now(); 181 | let rltPath = path.relative(__dirname, localeFile); 182 | console.info(`[BİLGİ] "${rltPath}" konumundaki dil yükleniyor..`) 183 | /** @type {import("./types/Locale")} */ 184 | let locale = require(localeFile); 185 | 186 | if (locale._type != "locale") 187 | return console.warn(`[UYARI] "${rltPath}" dil dosyası boş. Atlanıyor..`); 188 | 189 | if (locales.has(locale.locale)) 190 | return console.warn(`[UYARI] ${locale.locale} dili zaten yüklenmiş. Atlanıyor..`); 191 | 192 | locales.set(locale.locale, locale); 193 | console.info(`[BİLGİ] "${locale.locale}" dili yüklendi. (${Date.now() - start}ms sürdü.)`); 194 | }) 195 | 196 | pluginFiles = await getPluginFilePaths(); 197 | let pluginCache = []; 198 | pluginEvents = []; 199 | pluginInteractions = []; 200 | await chillout.forEach(pluginFiles, (pluginFile) => { 201 | let start = Date.now(); 202 | let rltPath = path.relative(__dirname, pluginFile); 203 | console.info(`[BİLGİ] "${rltPath}" konumundaki plugin yükleniyor..`) 204 | /** @type {import("./types/Plugin")} */ 205 | let plugin = require(pluginFile); 206 | let isReady = false; 207 | plugin.path = pluginFile; 208 | 209 | if (plugin._type != "plugin") 210 | return console.warn(`[UYARI] "${rltPath}" plugin dosyası boş. Atlanıyor..`); 211 | 212 | if (Underline.plugins[plugin.namespace]) 213 | return console.warn(`[UYARI] ${plugin.name} plugini zaten yüklenmiş. Atlanıyor..`); 214 | 215 | pluginCache.push(plugin); 216 | console.info(`[BİLGİ] "${plugin.name}" plugini yüklenme sırasına alındı. (${Date.now() - start}ms sürdü.)`); 217 | 218 | }) 219 | 220 | let neededs = []; 221 | pluginCache.forEach(plugin => { 222 | plugin?.requires?.plugins?.forEach(dependency => { 223 | if (!pluginCache.some(x => x.namespace == dependency)) { 224 | neededs.push(`[EKSİK] "${plugin.namespace}" plugini "${dependency}" pluginine ihtiyaç duyuyor lütfen ekleyin.`); 225 | } 226 | }) 227 | }); 228 | if (neededs.length) { 229 | for (let i = 0; i < neededs.length; i++) { 230 | console.error(neededs[i]); 231 | } 232 | process.exit(0); 233 | } 234 | 235 | pluginCache = pluginCache.sort((plugin, dependency) => plugin?.requires?.plugins?.includes(dependency) ? 1 : 0); 236 | 237 | let pluginSort = utils.sortDependant(pluginCache.map(i => i.namespace), Object.fromEntries(pluginCache.map(i => [i.namespace, i?.requires?.plugins || []]))) 238 | 239 | await chillout.forEach(pluginSort, async (pluginNamespace) => { 240 | let start = Date.now(); 241 | 242 | const plugin = pluginCache.find(x => x.namespace === pluginNamespace); 243 | 244 | const pluginApi = {}; 245 | let isReady = false; 246 | 247 | pluginApi.setPluginReady = async () => { 248 | if (isReady) throw new Error("Plugin is already ready!") 249 | isReady = true; 250 | } 251 | 252 | Underline.plugins[plugin.namespace] = {}; 253 | 254 | pluginApi.define = (name, value) => { 255 | Underline.plugins[plugin.namespace][name] = value; 256 | } 257 | 258 | pluginApi.emit = (name, ...args) => { 259 | client.emit(`${plugin.namespace}:${name}`, ...args); 260 | } 261 | 262 | pluginApi.onInteractionBeforeChecks = onFunctions.onInteractionBeforeChecks.push; 263 | pluginApi.onInteraction = onFunctions.onInteraction.push; 264 | pluginApi.onAfterInteraction = onFunctions.onAfterInteraction.push; 265 | 266 | pluginApi.onEvent = onFunctions.onEvent.push; 267 | pluginApi.onAfterEvent = onFunctions.onAfterEvent.push; 268 | pluginApi.onBotReady = onFunctions.onReady.push; 269 | 270 | pluginApi.client = client; 271 | 272 | plugin.onLoad(pluginApi); 273 | console.info(`[BİLGİ] "${plugin.name}" pluginin yüklenmesi bekleniyor!`); 274 | console.log(plugin.path, "-path"); 275 | if (plugin.path?.match(new RegExp(`plugins\\${path.sep}(.|[şçğüiÇŞİĞÜIıöÖ])*\\${path.sep}index\\.js$`))) { 276 | 277 | let interPlPath = plugin.path.replace(new RegExp(`\\${path.sep}index\\.js$`), path.sep + "interactions") 278 | let evntPlPath = plugin.path.replace(new RegExp(`\\${path.sep}index\\.js$`), path.sep + "events") 279 | 280 | if (fs.existsSync(interPlPath)) { 281 | 282 | let interactionsPath = path.resolve(interPlPath); 283 | let VInteractionFiles = await readdirRecursive(interactionsPath); 284 | VInteractionFiles = VInteractionFiles.filter(i => { 285 | let state = path.basename(i).startsWith("-"); 286 | return !state; 287 | }); 288 | 289 | 290 | await chillout.forEach(VInteractionFiles, (interactionFile) => { 291 | let start = Date.now(); 292 | let rltPath = path.relative(__dirname, interactionFile); 293 | console.info(`[BİLGİ] "${rltPath}" konumundaki interaksiyon yükleniyor..`) 294 | /** @type {import("./types/Interaction")} */ 295 | let uInter = require(interactionFile); 296 | 297 | if (uInter?._type != "interaction" && uInter?._type != "ComponentInteraction") { 298 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyası boş. Atlanıyor..`); 299 | return; 300 | } 301 | 302 | if (!uInter.id) { 303 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının bir idsi bulunmuyor. Atlanıyor..`); 304 | return; 305 | } 306 | 307 | if (uInter.name.length > 3) { 308 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının isim listesi çok uzun. (>3) Atlanıyor..`); 309 | return; 310 | } 311 | 312 | if (!uInter.name?.length) { 313 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının bir ismi bulunmuyor. Atlanıyor..`); 314 | return; 315 | } 316 | 317 | if (Underline.interactions.has(uInter.id)) { 318 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${uInter.id}" idli bir interaksiyon daha önceden zaten yüklenmiş. Atlanıyor.`) 319 | return; 320 | } 321 | 322 | if (typeof uInter.onInteraction != "function") { 323 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" interaksiyon dosyası geçerli bir onInteraction fonksiyonuna sahip değil! Atlanıyor.`); 324 | return; 325 | }; 326 | 327 | uInter.calculated = { 328 | developerOnly: false, 329 | guildOwnerOnly: false 330 | } 331 | 332 | if (uInter.developerOnly) { 333 | uInter.calculated.developerOnly = true; 334 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${uInter.id}" idli interaksiyon'da developerOnly seçeneğini kullanmışsınız, bu seçenek ilerki sürümlerde kaldırılacaktır lütfen bunu yapmak yerine perms.user kısmına "Developer"'ı koyunuz.`) 335 | } 336 | 337 | { 338 | let devOnlyIndex = uInter.perms.user.findIndex(p => p == "Developer"); 339 | if (devOnlyIndex > -1) { 340 | uInter.calculated.developerOnly = true; 341 | uInter.perms.user.splice(devOnlyIndex, 1); 342 | } 343 | 344 | let gOwnerOnlyIndex = uInter.perms.user.findIndex(p => p == "GuildOwner"); 345 | if (gOwnerOnlyIndex > -1) { 346 | uInter.calculated.guildOwnerOnly = true; 347 | uInter.perms.user.splice(gOwnerOnlyIndex, 1); 348 | } 349 | } 350 | 351 | if (!uInter.guildOnly && (uInter.perms.bot.length != 0 || uInter.perms.user.length != 0)) { 352 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyası sunuculara özel olmamasına rağmen özel perm kullanıyor.`); 353 | } 354 | 355 | if (!uInter.guildOnly && uInter.calculated.guildOwnerOnly) { 356 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" interaksiyon dosyası sunuculara özel olmamasına rağmen sunucu sahibine özel! Atlanıyor.`); 357 | return; 358 | } 359 | uInter.pluginApi = pluginApi; 360 | Underline.interactions.set(uInter.id, uInter); 361 | uInter.onLoad(client); 362 | console.info(`[BİLGİ] "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" (${uInter.id}) adlı plugin interaksiyonu yüklendi. (${Date.now() - start}ms sürdü.)`); 363 | }); 364 | 365 | } 366 | if (fs.existsSync(evntPlPath)) { 367 | 368 | let interactionsPath = path.resolve(evntPlPath); 369 | let VEventFiles = await readdirRecursive(interactionsPath); 370 | VEventFiles = VEventFiles.filter(i => { 371 | let state = path.basename(i).startsWith("-"); 372 | return !state; 373 | }); 374 | 375 | 376 | await chillout.forEach(VEventFiles, (eventFile) => { 377 | let start = Date.now(); 378 | let rltPath = path.relative(__dirname, eventFile); 379 | console.info(`[BİLGİ] "${rltPath}" event yükleniyor..`); 380 | 381 | /** @type {import("./types/Event")} */ 382 | let event = require(eventFile); 383 | 384 | if (event?._type != "event") { 385 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" event dosyası boş. Atlanıyor..`); 386 | return; 387 | } 388 | 389 | if (typeof event.id != "string") event.id = path.basename(eventFile).slice(0, -3).replace(/ /g, ""); 390 | 391 | if (Underline.events.has(event.id)) { 392 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${event.id}" adlı bir event daha önceden zaten yüklenmiş. Atlanıyor.`); 393 | return; 394 | } 395 | 396 | if (typeof event.onEvent != "function") { 397 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" olay dosyası geçerli bir onEvent fonksiyonuna sahip değil! Atlanıyor.`); 398 | return; 399 | }; 400 | 401 | event.pluginApi = pluginApi; 402 | Underline.events.set(event.id, event); 403 | event.onLoad(client); 404 | console.info(`[BİLGİ] ("${rltPath}") "${event.id}" adlı plugin eventi yüklendi. (${Date.now() - start}ms sürdü.)`); 405 | }); 406 | 407 | } 408 | } 409 | 410 | await chillout.waitUntil(() => { 411 | if (isReady) return chillout.StopIteration; 412 | }) 413 | console.info(`[BİLGİ] "${plugin.name}" plugini yüklendi! (${Date.now() - start}ms sürdü.)`); 414 | }) 415 | 416 | 417 | for (let ind in onFunctions) onFunctions[ind] = onFunctions[ind].filter(x => typeof x === "function"); 418 | 419 | interactionFiles = await getInteractionFilePaths(); 420 | await chillout.forEach(interactionFiles, (interactionFile) => { 421 | let start = Date.now(); 422 | let rltPath = path.relative(__dirname, interactionFile); 423 | console.info(`[BİLGİ] "${rltPath}" konumundaki interaksiyon yükleniyor..`) 424 | /** @type {import("./types/Interaction")} */ 425 | let uInter = require(interactionFile); 426 | 427 | if (uInter?._type != "interaction" && uInter?._type != "ComponentInteraction") { 428 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyası boş. Atlanıyor..`); 429 | return; 430 | } 431 | 432 | if (!uInter.id) { 433 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının bir idsi bulunmuyor. Atlanıyor..`); 434 | return; 435 | } 436 | 437 | if (uInter.name.length > 3) { 438 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının isim listesi çok uzun. (>3) Atlanıyor..`); 439 | return; 440 | } 441 | 442 | if (!uInter.name?.length) { 443 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyasının bir ismi bulunmuyor. Atlanıyor..`); 444 | return; 445 | } 446 | 447 | if (Underline.interactions.has(uInter.id)) { 448 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${uInter.id}" idli bir interaksiyon daha önceden zaten yüklenmiş. Atlanıyor.`) 449 | return; 450 | } 451 | 452 | if (typeof uInter.onInteraction != "function") { 453 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" interaksiyon dosyası geçerli bir onInteraction fonksiyonuna sahip değil! Atlanıyor.`); 454 | return; 455 | }; 456 | 457 | uInter.calculated = { 458 | developerOnly: false, 459 | guildOwnerOnly: false 460 | } 461 | 462 | if (uInter.developerOnly) { 463 | uInter.calculated.developerOnly = true; 464 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${uInter.id}" idli interaksiyon'da developerOnly seçeneğini kullanmışsınız, bu seçenek ilerki sürümlerde kaldırılacaktır lütfen bunu yapmak yerine perms.user kısmına "Developer"'ı koyunuz.`) 465 | } 466 | 467 | { 468 | let devOnlyIndex = uInter.perms.user.findIndex(p => p == "Developer"); 469 | if (devOnlyIndex > -1) { 470 | uInter.calculated.developerOnly = true; 471 | uInter.perms.user.splice(devOnlyIndex, 1); 472 | } 473 | 474 | let gOwnerOnlyIndex = uInter.perms.user.findIndex(p => p == "GuildOwner"); 475 | if (gOwnerOnlyIndex > -1) { 476 | uInter.calculated.guildOwnerOnly = true; 477 | uInter.perms.user.splice(gOwnerOnlyIndex, 1); 478 | } 479 | } 480 | 481 | if (!uInter.guildOnly && (uInter.perms.bot.length != 0 || uInter.perms.user.length != 0)) { 482 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" interaksiyon dosyası sunuculara özel olmamasına rağmen özel perm kullanıyor.`); 483 | } 484 | 485 | if (!uInter.guildOnly && uInter.calculated.guildOwnerOnly) { 486 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" interaksiyon dosyası sunuculara özel olmamasına rağmen sunucu sahibine özel! Atlanıyor.`); 487 | return; 488 | } 489 | 490 | Underline.interactions.set(uInter.id, uInter); 491 | uInter.onLoad(client); 492 | console.info(`[BİLGİ] "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" (${uInter.id}) adlı plugin interaksiyonu yüklendi. (${Date.now() - start}ms sürdü.)`); 493 | }); 494 | 495 | if (Underline.interactions.size) { 496 | console.info(`[BİLGİ] ${Underline.interactions.size} interaksiyon yüklendi.`); 497 | } else { 498 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] Hiçbir interaksiyon yüklenmedi, herşey yolunda mı?`); 499 | } 500 | 501 | eventFiles = await getEventFilePaths(); 502 | 503 | await chillout.forEach(eventFiles, async (eventFile) => { 504 | let start = Date.now(); 505 | let rltPath = path.relative(__dirname, eventFile); 506 | console.info(`[BİLGİ] "${rltPath}" event yükleniyor..`); 507 | 508 | /** @type {import("./types/Event")} */ 509 | let event = require(eventFile); 510 | 511 | if (event?._type != "event") { 512 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${rltPath}" event dosyası boş. Atlanıyor..`); 513 | return; 514 | } 515 | 516 | if (typeof event.id != "string") event.id = path.basename(eventFile).slice(0, -3).replace(/ /g, ""); 517 | 518 | if (Underline.events.has(event.id)) { 519 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${event.id}" adlı bir event daha önceden zaten yüklenmiş. Atlanıyor.`); 520 | return; 521 | } 522 | 523 | if (typeof event.onEvent != "function") { 524 | if (Underline.config.debugLevel >= 1) console.error(`[HATA] "${rltPath}" olay dosyası geçerli bir onEvent fonksiyonuna sahip değil! Atlanıyor.`); 525 | return; 526 | }; 527 | 528 | Underline.events.set(event.id, event); 529 | event.onLoad(client); 530 | console.info(`[BİLGİ] ("${rltPath}") "${event.id}" adlı event yüklendi. (${Date.now() - start}ms sürdü.)`); 531 | }) 532 | 533 | // Önce ismi daha uzun olanlar test edilsin diye. 534 | Underline.interactions.sort((a, b) => b.name.length - a.name.length); 535 | 536 | if (Underline.events.size) { 537 | console.info(`[BİLGİ] ${Underline.events.size} event yüklendi.`); 538 | } else { 539 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] Hiçbir olay yüklenmedi, herşey yolunda mı?`); 540 | } 541 | 542 | { 543 | /** @type {Map} */ 544 | let eventsMapped = Underline.events.reduce((all, cur) => { 545 | if (!all.has(cur.eventName)) all.set(cur.eventName, []); 546 | all.get(cur.eventName).push(cur); 547 | return all; 548 | }, new Map()); 549 | 550 | await chillout.forEach( 551 | [...eventsMapped.entries()], 552 | /** 553 | * @param {[string, (import("./types/Event"))[]>]} param0 554 | */ 555 | ([eventName, events]) => { 556 | console.info(`[BİLGİ] Event "${eventName}" için ${events.length} dinleyici yüklendi!`); 557 | 558 | let listener = (...args) => { 559 | 560 | setTimeout(async () => { 561 | 562 | let other = {}; 563 | 564 | let guild_locale_id = (args[0].guild || args[0])?.preferredLocale?.split("-")[0]; 565 | other.guildLocale = (Underline.locales.get(guild_locale_id) || Underline.locales.get(Underline.config.defaultLanguage)).data; 566 | 567 | let before = (await quickMap(onFunctions.onEvent, async (func) => { return await func(eventName, args, other); })).findIndex(v => v === false); 568 | if (before != -1) return; 569 | chillout.forEach(events, 570 | /** @param {import("./types/Event")} event */ 571 | (event) => { 572 | if (!event.disabled) { 573 | try { 574 | let sOther = { ...other }; 575 | sOther.pluginApi = event.pluginApi; 576 | event.onEvent(...args, sOther); 577 | quickForEach(onFunctions.onAfterEvent, async (func) => { func(...args); }); 578 | } catch (err) { 579 | if (Underline.config.debugLevel >= 1) { 580 | console.error(`[HATA] "${event.id}" idli ve "${eventName}" isimli olayda bir hata oluştu!`); 581 | if (err.message) console.error(`[HATA] ${err.message}`); 582 | if (err.stack) { 583 | `${err.stack}`.split("\n").forEach((line) => { 584 | console.error(`[HATA] ${line}`); 585 | }); 586 | } 587 | } 588 | } 589 | } 590 | }); 591 | 592 | }, 0) 593 | } 594 | client.on(eventName, listener); 595 | eventListeners.push({ name: eventName, base: client, listener }); 596 | } 597 | ) 598 | } 599 | 600 | console.debug(`[HATA AYIKLAMA] Herşey ${Date.now() - loadStart}ms içerisinde yüklendi!`); 601 | 602 | loadStart = 0; 603 | } 604 | 605 | async function unloadModule(modulePath) { 606 | let nodeModule = require.cache[modulePath]; 607 | if (nodeModule) { 608 | if (nodeModule.children.length) await chillout.forEach(nodeModule.children, async (child) => { 609 | if (child.filename) unloadModule(child.filename); 610 | }); 611 | } 612 | delete require.cache[modulePath]; 613 | } 614 | 615 | async function unload() { 616 | console.debug(`[HATA AYIKLAMA] Önbellek temizle işlemi başladı.`); 617 | let unloadStart = Date.now(); 618 | 619 | console.info(`[BILGI] Plugin listesi temizleniyor..`); 620 | Underline.plugins = {}; 621 | 622 | console.info(`[BILGI] İnteraksiyon listesi temizleniyor..`); 623 | Underline.interactions.clear(); 624 | 625 | console.info(`[BILGI] Olay listesi temizleniyor..`); 626 | Underline.events.clear(); 627 | 628 | console.info(`[BILGI] Olay dinleyicileri temizleniyor..`); 629 | await chillout.forEach(eventListeners, (el) => { 630 | el.base.off(el.name, el.listener); 631 | }) 632 | eventListeners.length = 0; 633 | 634 | console.info(`[BILGI] Dil listesi temizleniyor..`); 635 | Underline.locales.clear(); 636 | 637 | let pathsToUnload = [...interactionFiles, ...eventFiles, ...localeFiles, ...pluginFiles]; 638 | 639 | await chillout.forEach(pathsToUnload, async (pathToUnload) => { 640 | console.info(`[BILGI] Modül "${path.relative(__dirname, pathToUnload)}" önbellekten kaldırılıyor!`); 641 | await unloadModule(pathToUnload); 642 | }); 643 | 644 | console.debug(`[HATA AYIKLAMA] Önbellek temizleme ${Date.now() - unloadStart}ms içerisinde tamamlandı!`); 645 | 646 | unloadStart = 0; 647 | pathsToUnload = 0; 648 | 649 | } 650 | 651 | async function reload() { 652 | await unload(); 653 | await load(); 654 | } 655 | 656 | client.on("interactionCreate", async (interaction) => { 657 | let subCommandName = ""; 658 | try { subCommandName = interaction.options.getSubcommand(); } catch { }; 659 | let subCommandGroupName = ""; 660 | try { subCommandGroupName = interaction.options.getSubcommandGroup(); } catch { }; 661 | 662 | let data = []; 663 | 664 | if (interaction.isButton() || interaction.isSelectMenu() || interaction.type == InteractionType.ModalSubmit) { 665 | data = interaction.customId.split("—"); 666 | interaction.customId = data.shift(); 667 | data = data.map(key => { 668 | if (key.startsWith("π") && !isNaN(key.slice(1))) return Number(key.slice(1)); 669 | if (key.startsWith("¤")) return Underline._references.get(key.slice(1)) || null; 670 | return key; 671 | }) 672 | } 673 | 674 | let uInter = Underline.interactions.find(uInter => { 675 | switch (uInter.name.length) { 676 | case 1: return (uInter.name[0] == interaction.commandName) || ((uInter.id == interaction.customId) && ( 677 | (uInter.actionType == "ChatInput" && (interaction.isChatInputCommand() || interaction.type == InteractionType.ApplicationCommandAutocomplete)) || 678 | (uInter.actionType == "SelectMenu" && interaction.isSelectMenu()) || 679 | (uInter.actionType == "Button" && interaction.isButton()) || 680 | (uInter.actionType == "Modal" && interaction.type == InteractionType.ModalSubmit) || 681 | ((uInter.actionType == "User" || uInter.actionType == "Message") && interaction.isContextMenuCommand()) 682 | )); 683 | case 2: return uInter.name[0] == interaction.commandName && uInter.name[1] == subCommandName && (interaction.type == InteractionType.ApplicationCommand || interaction.type == InteractionType.ApplicationCommandAutocomplete); 684 | case 3: return uInter.name[0] == interaction.commandName && uInter.name[1] == subCommandGroupName && uInter.name[2] == subCommandName && (interaction.type == InteractionType.ApplicationCommand || interaction.type == InteractionType.ApplicationCommandAutocomplete); 685 | } 686 | }); 687 | 688 | if (!uInter) return; 689 | 690 | let other = { 691 | data 692 | }; 693 | other.pluginApi = uInter.pluginApi; 694 | 695 | if (interaction.type == InteractionType.ApplicationCommandAutocomplete) { 696 | if (uInter.disabled) { 697 | let r = await config.userErrors.disabled(interaction, uInter, other); 698 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 699 | return; 700 | } 701 | if (config.blockedUsers.has(interaction.user.id)) { 702 | let r = await config.userErrors.blocked(interaction, uInter, other); 703 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 704 | return; 705 | } 706 | if (uInter.guildOnly && !interaction.guildId) { 707 | let r = await config.userErrors.guildOnly(interaction, uInter, other); 708 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 709 | return; 710 | } 711 | if (uInter.calculated.developerOnly && !config.developers.has(interaction.user.id)) { 712 | let r = await config.userErrors.developerOnly(interaction, uInter, other); 713 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 714 | return; 715 | } 716 | if (uInter.calculated.guildOwnerOnly && !config.developers.has(interaction.user.id) && interaction.guild.ownerId != interaction.user.id) { 717 | let r = await config.userErrors.guildOwnerOnly(interaction, uInter, other); 718 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 719 | return; 720 | } 721 | if (uInter.guildOnly && (!config.developers.has(interaction.user.id)) && uInter.perms.user.length != 0 && !uInter.perms.user.every(perm => interaction.member.permissions.has(perm))) { 722 | let r = await config.userErrors.userPermsRequired(interaction, uInter, uInter.perms.user, other); 723 | interaction.respond(r).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }) 724 | return; 725 | } 726 | /** @type {Discord.ApplicationCommandOptionChoice} */ 727 | let focussed = null; 728 | try { focussed = interaction.options.getFocused(true) } catch { }; 729 | let option = uInter.options.find(i => i.autocomplete && i.name == focussed?.name); 730 | if (option) { 731 | try { 732 | let completeResponse = await option.onComplete(interaction, focussed.value); 733 | interaction.respond(completeResponse).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }); 734 | } catch (err) { 735 | if (Underline.config.debugLevel >= 1) { 736 | console.error(`[HATA] "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" adlı interaksiyon için otomatik tamamlama çalıştırılırken bir hata ile karşılaşıldı!`) 737 | if (err.message) console.error(`[HATA] ${err.message}`); 738 | if (err.stack) { 739 | `${err.stack}`.split("\n").forEach((line) => { 740 | console.error(`[HATA] ${line}`); 741 | }); 742 | } 743 | } 744 | } 745 | } 746 | return; 747 | } 748 | 749 | { 750 | const locale_id = (interaction.user.locale || interaction.locale)?.split("-")[0]; 751 | const guild_locale_id = interaction.guild?.preferredLocale?.split("-")[0]; 752 | other.locale = (Underline.locales.get(locale_id) || Underline.locales.get(Underline.config.defaultLanguage)).data; 753 | if (!guild_locale_id) other.guildLocale = other.locale; 754 | else other.guildLocale = (Underline.locales.get(guild_locale_id) || Underline.locales.get(Underline.config.defaultLanguage)).data; 755 | } 756 | 757 | { 758 | let shouldRun1 = (await quickMap(onFunctions.onInteractionBeforeChecks, async (func) => { return await func(uInter, interaction, other); })).findIndex(v => v === false); 759 | if (shouldRun1 != -1) return; 760 | } 761 | 762 | if (uInter.disabled) { 763 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 764 | config.userErrors.disabled(interaction, uInter, other); 765 | return; 766 | } 767 | 768 | if (config.blockedUsers.has(interaction.user.id)) { 769 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 770 | config.userErrors.blocked(interaction, uInter, other); 771 | return; 772 | } 773 | 774 | if (uInter.guildOnly && !interaction.guildId) { 775 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 776 | config.userErrors.guildOnly(interaction, uInter, other); 777 | return; 778 | } 779 | 780 | if (uInter.calculated.developerOnly && !config.developers.has(interaction.user.id)) { 781 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 782 | config.userErrors.developerOnly(interaction, uInter, other); 783 | return; 784 | } 785 | 786 | if (uInter.calculated.guildOwnerOnly && !config.developers.has(interaction.user.id) && interaction.guild.ownerId != interaction.user.id) { 787 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 788 | config.userErrors.guildOwnerOnly(interaction, uInter, other); 789 | return; 790 | } 791 | 792 | if (uInter.guildOnly && uInter.perms.bot.length != 0 && !uInter.perms.bot.every(perm => interaction.guild.members.me.permissions.has(perm))) { 793 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 794 | config.userErrors.botPermsRequired(interaction, uInter, uInter.perms.bot, other); 795 | return; 796 | } 797 | 798 | if (uInter.guildOnly && (!config.developers.has(interaction.user.id)) && uInter.perms.user.length != 0 && !uInter.perms.user.every(perm => interaction.member.permissions.has(perm))) { 799 | if (uInter.nullError) return interaction.update ? (await interaction.update().catch(Underline.config.debugLevel >= 2 ? console.error : () => { })) : null; 800 | config.userErrors.userPermsRequired(interaction, uInter, uInter.perms.user, other); 801 | return; 802 | } 803 | 804 | if (uInter.autoDefer && uInter.autoDefer !== "off") { 805 | const newDefer = () => { 806 | if (Underline.config.debugLevel >= 1) console.warn(`[UYARI] "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" adlı interaksiyon için "deferReply" umursanmadı, interaksiyon zaten otomatik olarak bekleme moduna alınmış.`); 807 | }; 808 | if ( 809 | interaction.type == InteractionType.ApplicationCommand || interaction.isContextMenuCommand() 810 | ) { 811 | await interaction.deferReply(uInter.autoDefer == "ephemeral" ? { ephemeral: true } : null).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }); 812 | interaction.deferReply = newDefer; 813 | interaction.reply = interaction.editReply; 814 | interaction.update = interaction.editReply; 815 | } else if ( 816 | interaction.isButton() || interaction.isSelectMenu() || interaction.type == InteractionType.ModalSubmit 817 | ) { 818 | if (uInter.autoDefer == "update") await interaction.deferUpdate().catch(Underline.config.debugLevel >= 2 ? console.error : () => { }); 819 | else await interaction.deferReply(uInter.autoDefer == "ephemeral" ? { ephemeral: true } : null).catch(Underline.config.debugLevel >= 2 ? console.error : () => { }); 820 | interaction.deferReply = newDefer; 821 | interaction.reply = interaction.editReply; 822 | interaction.update = interaction.editReply; 823 | } 824 | } 825 | 826 | if (typeof uInter.coolDown == "number") uInter.coolDown = [{ 827 | type: "user", 828 | amount: uInter.coolDown, 829 | }]; 830 | 831 | if (typeof uInter.coolDown == "object" && !Array.isArray(uInter.coolDown)) uInter.coolDown = [uInter.coolDown]; 832 | 833 | let converter = { 834 | "user": interaction.user.id, 835 | "member": interaction.user.id + "" + interaction.guild?.id, 836 | "channel": interaction.channelId || interaction.user.id + " _c", 837 | "guild": interaction.guildId || interaction.user.id + "_g", 838 | "message": interaction.message?.id || (interaction.channelId + "_m") || (interaction.user.id + "_m"), 839 | "any": "any" 840 | } 841 | 842 | let now = Date.now(); 843 | 844 | for (let k in converter) { 845 | let keyCooldown = uInter.coolDowns.get(k); 846 | if (now < keyCooldown) { 847 | config.userErrors.coolDown(interaction, uInter, keyCooldown - now, k, other); 848 | return; 849 | } 850 | } 851 | 852 | { 853 | let shouldReturn = false; 854 | await quickForEach(Object.entries(converter), async ([key, value]) => { 855 | if (shouldReturn) return; 856 | let keyCooldown = await Underline.variables.get(`_.cooldowns["${value}"]`); 857 | if (now < keyCooldown) { 858 | config.userErrors.coolDown(interaction, uInter, keyCooldown - now, key, other); 859 | shouldReturn = true; 860 | return; 861 | } 862 | }); 863 | if (shouldReturn) return; 864 | } 865 | 866 | async function setCoolDown(duration = 0, type = "user") { 867 | let ckey = converter[type] || interaction.user.id; 868 | if (typeof duration == "number" && duration > 0) { 869 | return await Underline.variables.set(`_.cooldowns["${ckey}"]`, Date.now() + duration); 870 | } else { 871 | return await Underline.variables.unset(`_.cooldowns["${ckey}"]`); 872 | } 873 | } 874 | 875 | other.setCoolDown = setCoolDown; 876 | for (let index = 0; index < uInter.coolDown.length; index++) { 877 | 878 | let cld = uInter.coolDown[index] 879 | if (cld && !cld.amount) cld.amount = 0; 880 | 881 | if (cld?.amount > 0) { 882 | setCoolDown(cld?.amount, cld?.type); 883 | } 884 | 885 | } 886 | 887 | (async () => { 888 | 889 | { 890 | let shouldRun2 = (await quickMap(onFunctions.onInteraction, async (func) => { try { return await func(uInter, interaction, other); } catch (e) { onfig.debugLevel > 2 ? console.error(e) : null; } })).findIndex(v => v === false); 891 | if (shouldRun2 != -1) return; 892 | } 893 | 894 | try { 895 | 896 | await uInter.onInteraction(interaction, other); 897 | quickForEach(onFunctions.onAfterInteraction, async (func) => { try { func(uInter, interaction, other)?.catch(config.debugLevel > 2 ? console.error : () => null); } catch (err) { (config.debugLevel > 2 ? console.error : () => null)(err) } }) 898 | 899 | } catch (err) { 900 | if (Underline.config.debugLevel >= 1) { 901 | console.error(`[HATA] "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" adlı interaksiyon çalıştırılırken bir hata ile karşılaşıldı!`) 902 | if (err.message) console.error(`[HATA] ${err.message}`); 903 | if (err.stack) { 904 | `${err.stack}`.split("\n").forEach((line) => { 905 | console.error(`[HATA] ${line}`); 906 | }); 907 | } 908 | } 909 | } 910 | })(); 911 | 912 | return; 913 | }); 914 | 915 | (async () => { 916 | await client.login(!config.sharding.enabled ? config.clientToken : undefined); 917 | 918 | await config.onBeforeLoad(client); 919 | await load(); 920 | await config.onAfterLoad(client); 921 | 922 | quickForEach(onFunctions.onReady, async (func) => { 923 | try { 924 | func?.()?.catch?.(() => {}); 925 | } catch (err) { 926 | 927 | } 928 | }) 929 | })(); 930 | 931 | Underline.reload = reload; 932 | -------------------------------------------------------------------------------- /interactions/-ornekKomut.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | // Slash komutun kullanıcıya gözüken ismi. Bu isim bir arraydır (liste) 3 | // Bu liste en fazla 3 uzunlukta olabilir. Örneğin; 4 | // ["muüzik", "çal"] kullanıcıya /müzik çal olarak gözükecektir. 5 | // veya ["muüzik", "liste", "temizle"] kullanıcıya /müzik liste temizle 6 | // olarak gözükecektir. 7 | name: ["örnekiteraksiyon"], 8 | // onInteraction fonksiyonu her interaksiyon kullanıldığında çağrılır. 9 | onInteraction(interaction, other) { 10 | // Discord.js CommandInteraction objesi. 11 | interaction; 12 | 13 | // Tek seferlik olarak coolDown değiştirmek için kullanılır. 14 | other.setCoolDown(5000); 15 | 16 | interaction.reply("Merhaba!"); 17 | }, 18 | // interaksiyon çalışmaya hazır olduğunda sadece bot açılırken bir 19 | // kereliğine çağrılır. Opsiyonel. 20 | onLoad(client) { 21 | // Normal discord.js Client objesi 22 | client; 23 | }, 24 | // interaksiyon açıklaması, Gerekli. 25 | description: "Örnek interaksiyon.", 26 | // Sadece bot geliştiricilerine özelmi değil mi? 27 | // Opsiyonel. Varsayılan olarak false. 28 | developerOnly: true, 29 | // interaksiyon kullanıma genel olarak kapalı mı? 30 | // Opsiyonel. Varsayılan olarak false. 31 | disabled: false, 32 | // Arka arkaya varsayılan interaksiyon kullanma süre limiti. 33 | // Opsiyonel. Varsayılan olarak 0. 34 | // Bu değer other.setCoolDown(1000, "user") fonksiyonu olarak işlem başına değiştirilebilir. 35 | coolDown: { 36 | amount: 1000, 37 | type: "user" 38 | }, 39 | // İstediğiniz interaksiyon ile alakalı diğer bütün dataları burada tutabilirsiniz. 40 | // Opsiyonel. Varsayılan olarak {}. 41 | other: {}, 42 | // interaksiyon yetkileri 43 | // Opsiyonel. Varsayılan olarak {bot: [], user: []}. 44 | perms: { 45 | // interaksiyonun çalışması için bot'a gerekli olan yetkiler. 46 | bot: ["SendMessages"], 47 | // interaksiyonun çalışması için kullanıcıya gerekli olan yetkiler. 48 | user: [] 49 | }, 50 | // Slash interaksiyon opsiyonları. 51 | // Daha fazla örnek için diğer interaksiyon dosylarına bakabilirsiniz. 52 | options: [], 53 | // Slash interaksiyon varsayılan olarak sunucudaki 54 | // gözüksün mü gözükmesin mi? Opsiyonel. 55 | // Bunun false olması durumunda interaksiyonlar dmlerde 56 | // kullanılmaz hale gelir. Ek olarak sunucunun admini 57 | // olsanız bile kullanamazsınız. Daha fazla bilgi için 58 | // discord.js guilde sitesine bakabilirsiniz: 59 | // https://discordjs.guide/interactions/slash-command-permissions.html 60 | defaultPermission: true 61 | }) -------------------------------------------------------------------------------- /interactions/-ornekSağtık.js: -------------------------------------------------------------------------------- 1 | // Sağtık menüsünün nerede çıkacağını 2 | // Class tipini değiştirerek değiştirebilirsiniz. 3 | // Örneğin; Underline.UserAction üyeye sağ tıklama ve 4 | // Underline.MessageAction mesaja sağ tıklama interaksiyonudur. 5 | module.exports = new Underline.UserAction({ 6 | // Sağtık menüsünde kullanıcıya gözüken isimdir. Bu isim bir stringdir (yazı) 7 | name: "Örnek İsim", 8 | // Örnek komuttaki bütün değerleri buradada kullanabilirsiniz! 9 | }) -------------------------------------------------------------------------------- /interactions/autocomplateUnban.js: -------------------------------------------------------------------------------- 1 | const { ApplicationCommandOptionType } = require("discord.js"); 2 | 3 | module.exports = new Underline.ChatInput({ 4 | description: "Banlı bir kullanıcının banını açmanızı sağlar.", 5 | name: ["unban"], 6 | async onInteraction(inter, other) { 7 | other.setCoolDown(30000, "channel") 8 | other.setCoolDown(10000, "guild") 9 | let targetId = inter.options.getString("id", false) 10 | await inter.guild.bans.fetch({ cache: false }); 11 | if (!inter.guild.bans.cache.has(targetId)) return inter.reply("Yasağını açacak kişiyi bulamadım!"); 12 | await inter.deferReply(); 13 | let user = inter.guild.bans.cache.get(targetId).user; 14 | await inter.guild.bans.remove(targetId); 15 | inter.editReply(`**${user.tag} (${user.id})** adlı kullanıcının yasağı açıldı!`); 16 | }, 17 | options: [ 18 | { 19 | name: "id", 20 | type: ApplicationCommandOptionType.String, 21 | description: "...", 22 | autocomplete: true, 23 | async onComplete(inter, value) { 24 | let bans = await inter.guild.bans.fetch({ cache: false }); 25 | return [...bans.values()].slice(0, 19) 26 | .map(i => ({ name: i.user.tag, value: i.user.id })); 27 | }, 28 | required: true 29 | } 30 | ], 31 | guildOnly: true, 32 | coolDown: [ 33 | { 34 | type: "member", 35 | amount: 20000 36 | }, 37 | { 38 | type: "guild", 39 | amount: 5000 40 | } 41 | ], 42 | perms: { 43 | bot: ["BanMembers"], 44 | user: ["BanMembers"] 45 | } 46 | }); -------------------------------------------------------------------------------- /interactions/butonYolla.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["buton-yolla"], 3 | description: "Buton yollar", 4 | async onInteraction(inter, other) { 5 | inter.reply({ 6 | content: "Düğme!", 7 | components: [ 8 | { 9 | type: Enums.ComponentType.ActionRow, 10 | components: [ 11 | Underline.interactions.get("ornek").toJSON([inter.user]) 12 | ] 13 | } 14 | ] 15 | }) 16 | }, 17 | coolDown: { 18 | amount: 10000, 19 | type: "user" 20 | }, 21 | guildOnly: false, 22 | developerOnly: false 23 | }); -------------------------------------------------------------------------------- /interactions/eval.js: -------------------------------------------------------------------------------- 1 | const { Util, ApplicationCommandOptionType } = require("discord.js"); 2 | const util = require("util"); 3 | 4 | module.exports = new Underline.ChatInput({ 5 | name: ["eval"], 6 | description: "Bot yetkilileri için JavaScript çalıştırma komutu.", 7 | async onInteraction(interaction) { 8 | let codeString = interaction.options.getString("code", true); 9 | await interaction.deferReply(); 10 | let result; 11 | try { 12 | result = await eval(codeString); 13 | } catch (err) { 14 | result = err; 15 | } 16 | result = Util.splitMessage(`\`\`\`${util.inspect(result, 0, 3, 0).replaceAll(interaction.client.token, "")}\`\`\``, { append: "```", prepend: "```" }); 17 | for (let index = 0; index < result.length; index++) { 18 | const part = result[index]; 19 | if (index == 0) { 20 | await interaction.editReply({ 21 | ephemeral: true, 22 | content: part 23 | }) 24 | } else { 25 | await interaction.followUp({ 26 | ephemeral: true, 27 | content: part 28 | }) 29 | } 30 | } 31 | }, 32 | perms: { 33 | user: ["Developer"], 34 | }, 35 | options: [ 36 | { 37 | description: "JavaScript kodu.", 38 | name: "code", 39 | type: ApplicationCommandOptionType.String, 40 | required: true 41 | } 42 | ] 43 | }) -------------------------------------------------------------------------------- /interactions/matamatik.js: -------------------------------------------------------------------------------- 1 | const { ButtonBuilder } = require("discord.js"); 2 | 3 | const expressionMap = { 4 | "add": "+", 5 | "subtract": "-", 6 | "multiply": "*", 7 | "divide": "/" 8 | }; 9 | 10 | module.exports = new Underline.ChatInput({ 11 | name: ["matamatik"], 12 | onInteraction(interaction, other) { 13 | let numberOne = interaction.options.getNumber("number_one"); 14 | let expressionName = interaction.options.getString("expression_type"); 15 | let numberTwo = interaction.options.getNumber("number_two"); 16 | 17 | let expressionOperator = expressionMap[expressionName]; 18 | let result = eval(`${numberOne}${expressionOperator}${numberTwo}`); 19 | 20 | interaction.reply(`\`${numberOne} ${expressionOperator} ${numberTwo}\` işleminin cevabı: \`${result}\``); 21 | }, 22 | description: "Basit 4 işlem sorularını yapmanızı sağlar.", 23 | developerOnly: false, 24 | options: [ 25 | { 26 | name: "number_one", 27 | type: Enums.ApplicationCommandOptionType.Number, 28 | description: "Sayı Bir", 29 | required: true 30 | }, { 31 | name: "expression_type", 32 | type: Enums.ApplicationCommandOptionType.String, 33 | description: "İşlem tipi", 34 | required: true, 35 | choices: [ 36 | { 37 | name: "Toplama (+)", 38 | value: "add" 39 | }, 40 | { 41 | name: "Çıkarma (-)", 42 | value: "subtract" 43 | }, 44 | { 45 | name: "Çarpma (*)", 46 | value: "multiply" 47 | }, 48 | { 49 | name: "Bölme (/)", 50 | value: "divide" 51 | } 52 | ] 53 | }, { 54 | name: "number_two", 55 | type: Enums.ApplicationCommandOptionType.Number, 56 | description: "Sayı İki", 57 | required: true, 58 | }, 59 | ], 60 | disabled: false 61 | }) -------------------------------------------------------------------------------- /interactions/modal-yolla.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["modal","yolla"], 3 | description: "..", 4 | async onInteraction(inter, other) { 5 | let modal = Underline.interactions.get("doldur_beni").toJSON(); 6 | inter.showModal(modal); 7 | }, 8 | options: [], 9 | guildOnly: true 10 | }); -------------------------------------------------------------------------------- /interactions/moderasyon/-rol.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["moderasyon", "rol"], 3 | description: "Bir kullanıcıdan rol alamanızı sağlar.", 4 | options: [ 5 | { 6 | type: Enums.ApplicationCommandOptionType.User, 7 | name: "uye", 8 | description: "Rol alacağanız kişi.", 9 | required: true 10 | }, 11 | { 12 | type: Enums.ApplicationCommandOptionType.Role, 13 | name: "rol", 14 | description: "Alınacak rol.", 15 | required: true 16 | }, 17 | { 18 | type: Enums.ApplicationCommandOptionType.String, 19 | name: "durum", 20 | description: "Rol verilsinmi alınsınmı?", 21 | required: true, 22 | choices: [ 23 | { 24 | name: "Rol Ver", 25 | value: "true" 26 | }, 27 | { 28 | name: "Rol Al", 29 | value: "false" 30 | } 31 | ] 32 | } 33 | ], 34 | async onInteraction(interaction, other) { 35 | /** @type {import("discord.js").GuildMember} */ 36 | let targetMember = interaction.options.getMember("uye", true); 37 | let targetRole = interaction.options.getRole("rol", true); 38 | let stateString = interaction.options.getString("durum", true); 39 | let state = stateString == "true" ? true : false; 40 | 41 | try { 42 | if (state) { 43 | if (!targetMember.roles.cache.has(targetRole)) await targetMember.roles.add([targetRole]); 44 | interaction.reply(`**${targetMember.user.tag}** adlı üyeye **${targetRole.name}** rülü verildi.`); 45 | } else { 46 | if (targetMember.roles.cache.has(targetRole)) await targetMember.roles.remove([targetRole]); 47 | interaction.reply(`**${targetMember.user.tag}** adlı üyeden **${targetRole.name}** rülü alındı.`); 48 | } 49 | } catch (err) { 50 | interaction.reply(`**${targetMember.user.tag}** adlı üyenin rol durumu değiştirilken bir sorun ile karşılaşıldı. \`${err}\``); 51 | } 52 | }, 53 | perms: { 54 | bot: ["ManageRoles"], 55 | user: ["ManageRoles"] 56 | } 57 | }) -------------------------------------------------------------------------------- /interactions/moderasyon/-ses-sustur.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["moderasyon", "ses-sustur"], 3 | description: "Bir kullanıcının ses susturmasını açıp kapamanızı sağlar.", 4 | options: [ 5 | { 6 | type: Enums.ApplicationCommandOptionType.User, 7 | name: "uye", 8 | description: "Rol verceğeniz kişi.", 9 | required: true 10 | }, 11 | { 12 | type: Enums.ApplicationCommandOptionType.String, 13 | name: "durum", 14 | description: "Susturulma durumu.", 15 | required: true, 16 | choices: [ 17 | { 18 | name: "Sustur (Mute)", 19 | value: "true" 20 | }, 21 | { 22 | name: "Susturma Aç (Unmute)", 23 | value: "false" 24 | } 25 | ] 26 | } 27 | ], 28 | async onInteraction(interaction, other) { 29 | /** @type {import("discord.js").GuildMember} */ 30 | let targetMember = interaction.options.getMember("uye", true); 31 | let stateString = interaction.options.getString("durum", true); 32 | let state = stateString == "true" ? true : false; 33 | 34 | try { 35 | await targetMember.voice.setMute(state); 36 | if (state) { 37 | interaction.reply(`**${targetMember.user.tag}** adlı üye sesli kanallarda **susturuldu**.`); 38 | } else { 39 | interaction.reply(`**${targetMember.user.tag}** adlı üyenin ses kanallarında **susuturması açıldı**.`); 40 | } 41 | } catch (err) { 42 | interaction.reply(`**${targetMember.user.tag}** adlı üyenin susuturma durumu değiştirelemedi. \`${err}\``); 43 | } 44 | 45 | }, 46 | perms: { 47 | bot: ["ManageChannels"], 48 | user: ["ManageChannels"] 49 | } 50 | }) -------------------------------------------------------------------------------- /interactions/moderasyon/-temizle.js: -------------------------------------------------------------------------------- 1 | const sleep = require('stuffs/lib/sleep'); 2 | 3 | module.exports = new Underline.ChatInput({ 4 | name: ["moderasyon", "temizle"], 5 | description: "Belli bir miktar mesaj silemenizi sağlar.", 6 | options: [ 7 | { 8 | type: Enums.ApplicationCommandOptionType.Integer, 9 | name: "miktar", 10 | description: "Silinecek mesaj miktarı. Maximum 100.", 11 | required: true 12 | } 13 | ], 14 | async onInteraction(interaction, other) { 15 | let amount = interaction.options.getInteger("miktar"); 16 | if (amount < 1 || amount > 100) return interaction.reply("Miktar 1 ila 100 arasında olmalıdır."); 17 | 18 | try { 19 | await interaction.channel.bulkDelete(amount); 20 | 21 | interaction.reply(`**${amount} adet** mesaj **silindi**.`); 22 | await sleep(3000); 23 | interaction.deleteReply(); 24 | } catch (err) { 25 | interaction.channel.send(`Birşeyler yanlış gitti! \`${err}\``); 26 | } 27 | }, 28 | perms: { 29 | bot: ["ManageMessages"], 30 | user: ["ManageMessages"] 31 | } 32 | }) -------------------------------------------------------------------------------- /interactions/moderasyon/-yasakla.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["moderasyon", "yasakla"], 3 | description: "Sunucudan üye yasaklamanızı sağlar.", 4 | options: [ 5 | { 6 | type: Enums.ApplicationCommandOptionType.User, 7 | name: "uye", 8 | description: "Yasaklanacak üye.", 9 | required: true 10 | }, 11 | { 12 | type: Enums.ApplicationCommandOptionType.String, 13 | name: "sebep", 14 | description: "Yasaklanma sebebi", 15 | required: false 16 | } 17 | ], 18 | async onInteraction(interaction, other) { 19 | let targetMember = interaction.options.getMember("uye"); 20 | let reason = interaction.options.getString("sebep") || ""; 21 | if (!targetMember.bannable) return interaction.reply("Bu üyeyi sunucudan yasaklamaya gücüm yetmiyor."); 22 | 23 | await targetMember.ban({ reason }); 24 | 25 | interaction.reply(`**${targetMember.user.tag}** sunucudan **${reason ? reason : "Sebep belirtilmemiş."}** sebebi ile **yasaklandı**!`); 26 | }, 27 | perms: { 28 | bot: ["BanMembers"], 29 | user: ["BanMembers"] 30 | } 31 | }) -------------------------------------------------------------------------------- /interactions/moderasyon/at.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["moderasyon", "at"], 3 | description: "Sunucudan üye atmanızı sağlar.", 4 | options: [ 5 | { 6 | type: Enums.ApplicationCommandOptionType.User, 7 | name: "uye", 8 | description: "Atılacak üye.", 9 | required: true 10 | }, 11 | { 12 | type: Enums.ApplicationCommandOptionType.String, 13 | name: "sebep", 14 | description: "Atılma sebebi", 15 | required: false 16 | } 17 | ], 18 | async onInteraction(interaction, other) { 19 | let targetMember = interaction.options.getMember("uye"); 20 | let reason = interaction.options.getString("sebep") || ""; 21 | if (!targetMember.kickable) return interaction.reply("Bu üyeyi sunucudan atmaya gücüm yetmiyor."); 22 | 23 | await targetMember.kick(reason); 24 | 25 | interaction.reply(`**${targetMember.user.tag}** sunucudan **${reason ? reason : "Sebep belirtilmemiş."}** sebebi ile **atıldı**!`); 26 | }, 27 | perms: { 28 | bot: ["BanMembers"], 29 | user: ["BanMembers"] 30 | } 31 | }) -------------------------------------------------------------------------------- /interactions/ornek-modal.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.Modal({ 2 | id: "doldur_beni", 3 | name: "doldur_beni", 4 | description: "...", 5 | async onInteraction(inter, other) { 6 | let firstRow = inter.components[0]; 7 | let firstInput = firstRow.components.find(x => x.customId == "test").value; 8 | inter.reply({ 9 | content: firstInput 10 | }) 11 | }, 12 | options: { 13 | title: "Test-123", 14 | rows: [ 15 | [ 16 | { 17 | type: "TextInput", 18 | data: { 19 | customId: "test", 20 | label: "Mesajınız", 21 | style: 2, 22 | placeholder: "Chate düşücek mesajı giriniz", 23 | } 24 | } 25 | ], 26 | [ 27 | { 28 | type: "SelectMenu", 29 | data: { 30 | customId: "gender", 31 | minValues: 1, 32 | maxValues: 1, 33 | placeholder: "Cinsiyet seçiniz", 34 | options: [ 35 | { 36 | label: "Erkek", 37 | value: "male" 38 | }, 39 | { 40 | label: "Kadın", 41 | value: "female" 42 | } 43 | ] 44 | } 45 | } 46 | ] 47 | ] 48 | }, 49 | guildOnly: true 50 | }); -------------------------------------------------------------------------------- /interactions/ornekButton.js: -------------------------------------------------------------------------------- 1 | const { ButtonStyle } = require("discord.js"); 2 | 3 | module.exports = new Underline.Button({ 4 | name: "ornek", 5 | id: "ornek", 6 | description: "...", 7 | onInteraction(inter, other) { 8 | let user = other.data[0]; 9 | console.log(user); 10 | if (!user) return inter.reply(`tıklayanı bulamadım.`); 11 | inter.reply(`düğme sahibi: ${user.tag} ${user.id}`); 12 | // Eğer düğmeye tıklayan kişi düğmenin sahibi ise sahip referansı ram'den sil. 13 | // bu sayede tekrardan tıklayamayacak. 14 | if (user.id == inter.user.id) user.$unRef(); 15 | }, 16 | perms: { 17 | bot: ["CreateInstantInvite"], 18 | user: ["KickMembers", "GuildOwner"] 19 | }, 20 | options: { 21 | style: ButtonStyle.Primary, 22 | label: "sa" 23 | } 24 | }); -------------------------------------------------------------------------------- /interactions/profil.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | description: "Bir kullanıcı hakkına bilgilere bakmanızı sağlar.", 3 | name: ["profil"], 4 | async onInteraction(interaction, other) { 5 | 6 | /** @type {import("discord.js").GuildMember} */ 7 | const member = interaction.options.getMember("kullanici", true); 8 | let avatarURL = await getUserBannerURL(member.user.id, Underline.client.token); 9 | interaction.reply({ 10 | embeds: [{ 11 | title: member.user.tag, 12 | color: 0x00afee, 13 | image: avatarURL ? { 14 | url: avatarURL 15 | } : undefined, 16 | thumbnail: { 17 | url: member.user.displayAvatarURL({ dynamic: true }) 18 | }, 19 | description: `**Açıldığı Tarih:** \n**Katıldığı Tarih:** \n**ID:** \`${member.user.id}\`**Dil:** ${member.user.locale}`, 20 | createdAt: Date.now(), 21 | footer: { 22 | icon_url: interaction.user.displayAvatarURL({ dynamic: true }), 23 | text: `${interaction.user.tag} tarafından istendi.` 24 | } 25 | }] 26 | }) 27 | }, 28 | options: [ 29 | { 30 | name: "kullanici", 31 | type: Enums.ApplicationCommandOptionType.User, 32 | description: "Bilgisini almak istediğiniz kullanıcı.", 33 | required: true 34 | } 35 | ], 36 | guildOnly: true, 37 | coolDown: 2000 38 | }); 39 | 40 | function getUserBannerURL(t, r, e = 4096) { return new Promise((n, s) => { require("https").get({ hostname: "discord.com", path: `/api/users/${t}`, port: 443, headers: { Authorization: `Bot ${r}` } }, r => { let o = ""; r.on("data", t => { o += t }), r.on("end", () => { r.destroy(); let { banner: s } = JSON.parse(o); n(s ? `https://cdn.discordapp.com/banners/${t}/${s}.${s.startsWith("a_") ? "gif" : "png"}${e ? `?size=${e}` : ""}` : null) }), r.once("error", t => { t && s(t) }) }) }) } -------------------------------------------------------------------------------- /interactions/reload.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.ChatInput({ 2 | name: ["reload"], 3 | description: "Bot yetkilileri için Underline reload komutu.", 4 | async onInteraction(interaction) { 5 | await interaction.deferReply(); 6 | await Underline.reload(); 7 | await interaction.editReply("✔ Yeniden yükleme işlemi başarılı."); 8 | }, 9 | coolDown: { 10 | type: "any", 11 | amount: 180000 12 | }, 13 | perms: { 14 | user: ["Developer"] 15 | }, 16 | publishType: "guildOnly" 17 | }) -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2020", 5 | "jsx": "preserve" 6 | }, 7 | "exclude": [ 8 | "node_modules", 9 | "**/node_modules/*" 10 | ] 11 | } -------------------------------------------------------------------------------- /locales/English.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.Locale({ 2 | locale: "en", 3 | data: { 4 | userErrors: { 5 | coolDown: { 6 | user: "You can use this interaction in {0} seconds.", 7 | member: "You can use this interaction in {0} seconds in this guild.", 8 | guild: "This interaction can be used again in {0} seconds in this guild.", 9 | channel: "This interaction can be used again in {0} seconds in this channel.", 10 | message: "This interaction can be used again in {0} seconds in this message.", 11 | any: "You can use this interaction in {0} seconds." 12 | }, 13 | disabled: "This interaction is disabled.", 14 | guildOnly: "This is a guild only interaction.", 15 | blocked: "You are banned from the bot.", 16 | developerOnly: "This is a developer only interaction.", 17 | guildOwnerOnly: "This is a guild owner only interaction.", 18 | botPermsRequired: "Bot needs {0} permissions to run this interaction.", 19 | userPermsRequired: "You need {0} permissions to run this interaction." 20 | }, 21 | example: { 22 | success: "Succesfuly done.", 23 | error: "Sorry for guild {0}, i couldnt publish..." 24 | }, 25 | mongooseDatabase: {} 26 | }, 27 | commands: [ 28 | { 29 | originalName: ["profil"], 30 | name: ["profile"], 31 | description: "Shows the profile of the user." 32 | }, 33 | { 34 | originalName: ["unban"], 35 | name: ["unban"], 36 | description: "Banlı bir kullanıcının banını açmanızı sağlar." 37 | }, 38 | { 39 | originalName: ["buton-yolla"], 40 | name: ["buton-yolla"], 41 | description: "Buton yollar" 42 | }, 43 | { 44 | originalName: ["eval"], 45 | name: ["eval"], 46 | description: "Bot yetkilileri için JavaScript çalıştırma komutu." 47 | }, 48 | { 49 | originalName: ["matamatik"], 50 | name: ["matamatik"], 51 | description: "Basit 4 işlem sorularını yapmanızı sağlar." 52 | }, 53 | { 54 | originalName: ["modal","yolla"], 55 | name: ["modal","yolla"], 56 | description: ".." 57 | }, 58 | { 59 | originalName: ["reload"], 60 | name: ["reload"], 61 | description: "Bot yetkilileri için Underline reload komutu." 62 | }, 63 | { 64 | originalName: ["moderasyon","at"], 65 | name: ["moderasyon","at"], 66 | description: "Sunucudan üye atmanızı sağlar." 67 | } 68 | ] 69 | }); -------------------------------------------------------------------------------- /locales/Turkish.js: -------------------------------------------------------------------------------- 1 | module.exports = new Underline.Locale({ 2 | locale: "tr", 3 | data: { 4 | userErrors: { 5 | coolDown: { 6 | user: "Bu interaksiyonu sen tekrardan {0} saniye içerisinde kullanabilirsin.", 7 | member: "Bu interaksiyonu sen bu sunucuda tekrardan {0} saniye içerisinde kullanabilirsin.", 8 | guild: "Bu interaksiyonu bu sunucuda tekrardan {0} saniye içerisinde kullanabilirsin.", 9 | channel: "Bu interaksiyonu bu kanalda tekrardan {0} saniye içerisinde kullanabilirsin.", 10 | message: "Bu interaksiyonu bu mesajda tekrardan {0} saniye içerisinde kullanabilirsin.", 11 | any: "Bu interaksiyonu tekrardan {0} saniye içerisinde kullanabilirsin." 12 | }, 13 | disabled: "Bu interkasiyon kapalı", 14 | blocked: "Bottan yasaklısınız.", 15 | guildOnly: "Bu sunuculara özel bir interaksiyon.", 16 | developerOnly: "Bu interaksiyon sadece bot geliştiricilerine özel", 17 | guildOwnerOnly: "Bu interaksiyon sadece sunucu sahipleri kullanabilir", 18 | botPermsRequired: "Botun bu komutu kullanması için {0} yetkilerine ihtiyacı vardır.", 19 | userPermsRequired: "Bu komutu kullanmak için {0} yetkilerine ihtiyacın var." 20 | }, 21 | example: { 22 | success: "✔ Yeniden yükleme işlemi başarılı.", 23 | error: "Malesef {0} sunucusunda paylaşılamadı..." 24 | }, 25 | mongooseDatabase: {} 26 | }, 27 | commands: [ 28 | { 29 | originalName: ["profil"], 30 | name: ["profilim"], 31 | description: "Kullanıcının profilini gösterir." 32 | }, 33 | { 34 | originalName: ["unban"], 35 | name: ["unban"], 36 | description: "Banlı bir kullanıcının banını açmanızı sağlar." 37 | }, 38 | { 39 | originalName: ["buton-yolla"], 40 | name: ["buton-yolla"], 41 | description: "Buton yollar" 42 | }, 43 | { 44 | originalName: ["eval"], 45 | name: ["eval"], 46 | description: "Bot yetkilileri için JavaScript çalıştırma komutu." 47 | }, 48 | { 49 | originalName: ["matamatik"], 50 | name: ["matamatik"], 51 | description: "Basit 4 işlem sorularını yapmanızı sağlar." 52 | }, 53 | { 54 | originalName: ["modal","yolla"], 55 | name: ["modal","yolla"], 56 | description: ".." 57 | }, 58 | { 59 | originalName: ["reload"], 60 | name: ["reload"], 61 | description: "Bot yetkilileri için Underline reload komutu." 62 | }, 63 | { 64 | originalName: ["moderasyon","at"], 65 | name: ["moderasyon","at"], 66 | description: "Sunucudan üye atmanızı sağlar." 67 | } 68 | ] 69 | }); -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | require("./other/patchConsoleLog"); 2 | const Cluster = require('discord-hybrid-sharding'); 3 | const config = require("./config"); 4 | 5 | const { plsParseArgs } = require('plsargs'); 6 | const argv = plsParseArgs(process.argv.slice(2)); 7 | let customToken = argv.get("clientToken"); 8 | if (customToken) config.clientToken = customToken; 9 | 10 | if (argv.has("shardingOn")) config.sharding?.enabled = true; 11 | if (argv.has("shardingOff")) config.sharding?.enabled = false; 12 | 13 | if (config.sharding.enabled) { 14 | const manager = new Cluster.Manager(`${__dirname}/index.js`, { 15 | totalShards: config.sharding.count[0], 16 | shardsPerClusters: config.sharding.count[1], 17 | mode: 'process', 18 | token: config.clientToken, 19 | respawn: true, 20 | execArgv: ["--expose-gc"], 21 | keepAlive: { 22 | interval: 5000, 23 | maxMissedHeartbeats: 12 24 | } 25 | }); 26 | config.sharding.onManager(manager); 27 | } else { 28 | require("./index.js"); 29 | } 30 | -------------------------------------------------------------------------------- /other/generator.js: -------------------------------------------------------------------------------- 1 | require("./patchConsoleLog"); 2 | 3 | try { 4 | require("enquirer"); 5 | } catch { 6 | console.warn("Bu işlemi yapmadan önce gerekli modülleri indirmeniz gerekiyor.") 7 | console.warn("-> yarn install"); 8 | process.exit(-1); 9 | } 10 | 11 | const DISCORD_PERMS = ["CreateInstantInvite", "KickMembers", "BanMembers", "Administrator", "ManageChannels", "ManageGuild", "AddReactions", "ViewAuditLog", "PrioritySpeaker", "Stream", "ViewChannel", "SendMessages", "SendTtsMessages", "ManageMessages", "EmbedLinks", "AttachFiles", "ReadMessageHistory", "MentionEveryone", "UseExternalEmojis", "ViewGuildInsights", "Connect", "Speak", "MuteMembers", "DeafenMembers", "MoveMembers", "UseVad", "ChangeNickname", "ManageNicknames", "ManageRoles", "ManageWebhooks", "ManageEmojis"]; 12 | const { prompt, AutoComplete, Toggle, Select } = require("enquirer"); 13 | const fs = require("fs"); 14 | const path = require("path"); 15 | const chalk = require("chalk"); 16 | const makeSureFolderExistsSync = require("stuffs/lib/makeSureFolderExistsSync"); 17 | const mode = process.argv[2]; 18 | 19 | async function permissionPrompt(message = "") { 20 | let perms = new Set(); 21 | 22 | async function _do() { 23 | console.clear(); 24 | let perm = await (new Select({ 25 | limit: 7, 26 | message: `${message}\n${perms.size ? `${[...perms].join(", ")}\n` : ""}? Yukarı (^) ve Aşağı (v) | Yön oklarını kullanarak seçim yap.`, 27 | choices: ["[BİTTİ]", ...DISCORD_PERMS.map(i => [...perms].includes(i) ? `- ${i}` : `+ ${i}`)] 28 | })).run(); 29 | 30 | if (perm == "[BİTTİ]") { 31 | return [...perms]; 32 | } else if (perm.startsWith("+")) { 33 | perms.add(perm.split(" ")[1]); 34 | return _do(); 35 | } else if (perm.startsWith("-")) { 36 | perms.delete(perm.split(" ")[1]); 37 | return _do(); 38 | } 39 | } 40 | 41 | return _do(); 42 | } 43 | 44 | makeSureFolderExistsSync("./interactions"); 45 | makeSureFolderExistsSync("./events"); 46 | 47 | (async () => { 48 | switch (mode) { 49 | case "interaction": { 50 | console.clear(); 51 | let interFileName = (await prompt({ 52 | type: "input", 53 | name: 'value', 54 | message: "İnteraksiyon dosya adınız ne olsun?", 55 | result(val) { 56 | if (val.endsWith(".js")) val = val.slice(0, -3); 57 | return val; 58 | }, 59 | required: true 60 | })).value; 61 | console.clear(); 62 | let interActionType = (await (new AutoComplete({ 63 | name: 'value', 64 | message: 'Ne tip bir interaksiyon istiyorsunuz?', 65 | limit: 10, 66 | initial: 0, 67 | choices: [ 68 | "Slash Komut", 69 | "Üye Sağtık", 70 | "Mesaj Sağtık", 71 | "Seç Menüsü", 72 | "Form", 73 | "Buton" 74 | ], 75 | result(val) { 76 | let l = { 77 | "Slash Komut": "ChatInput", 78 | "Üye Sağtık": "User", 79 | "Mesaj Sağtık": "Message", 80 | "Seç Menüsü": "SelectMenu", 81 | "Form": "Modal", 82 | "Buton": "Button" 83 | }; 84 | return l[val]; 85 | }, 86 | required: true 87 | })).run()); 88 | console.clear(); 89 | 90 | let interName = []; 91 | let interDesc = "..."; 92 | switch (interActionType) { 93 | case "ChatInput": { 94 | console.clear(); 95 | interName = (await prompt({ 96 | type: "input", 97 | name: 'value', 98 | message: "Slash komutunuz neye banzesin? (Örn; /music ve /music set veya /music set volume)", 99 | required: true, 100 | result(val) { 101 | val = val.trim(); 102 | if (val.startsWith("/")) val = val.slice(1); 103 | return val.split(" "); 104 | } 105 | })).value; 106 | 107 | console.clear(); 108 | interDesc = (await prompt({ 109 | type: "input", 110 | name: 'value', 111 | message: "Slash komutunuzun açıklaması ne olsun?", 112 | required: true 113 | })).value; 114 | break; 115 | }; 116 | case "User": 117 | case "Message": { 118 | console.clear(); 119 | interName = (await prompt({ 120 | type: "input", 121 | name: 'value', 122 | message: "Sağ tıkladığınız yerde ne yazsın? (Örn; Yasakla)", 123 | required: true 124 | })).value; 125 | break; 126 | } 127 | case "Button": { 128 | console.clear(); 129 | interName = (await prompt({ 130 | type: "input", 131 | name: 'value', 132 | message: "Butonunuzun custom id'si ne olsun? (Örn; bas_bana)", 133 | required: true 134 | })).value; 135 | break; 136 | } 137 | case "SelectMenu": { 138 | console.clear(); 139 | interName = (await prompt({ 140 | type: "input", 141 | name: 'value', 142 | message: "Menünüzün custom id'si ne olsun? (Örn; sec_beni)", 143 | required: true 144 | })).value; 145 | break; 146 | } 147 | case "Modal": { 148 | console.clear(); 149 | interName = (await prompt({ 150 | type: "input", 151 | name: 'value', 152 | message: "Formunuzun custom id'si ne olsun? (Örn; doldur_beni)", 153 | required: true 154 | })).value; 155 | break; 156 | } 157 | } 158 | console.clear(); 159 | 160 | let interDeveloperOnly = await (new Toggle({ 161 | message: "Bu interaksiyon geliştiricilere özel mi?", 162 | enabled: "Evet", 163 | disabled: "Hayır", 164 | initial: false 165 | })).run(); 166 | console.clear(); 167 | 168 | let interGuildOnly = await (new Toggle({ 169 | message: "Bu interaksiyon sadece sunuculara özel mi?", 170 | enabled: "Evet", 171 | disabled: "Hayır", 172 | initial: true 173 | })).run(); 174 | console.clear(); 175 | 176 | let interGuildOwnerOnly; 177 | if (interGuildOnly) interGuildOwnerOnly = await (new Toggle({ 178 | message: "Bu interaksiyon sunucu sahibine özel mi?", 179 | enabled: "Evet", 180 | disabled: "Hayır", 181 | initial: false 182 | })).run(); 183 | console.clear(); 184 | 185 | let interCoolDown = parseInt((await prompt({ 186 | type: "input", 187 | name: "value", 188 | message: "Bu interaksiyon kaç milisaniye yavaşlatma kullanıyor? Kullanmıyorsa 0 koy.", 189 | initial: "0", 190 | validate(val) { 191 | if (isNaN(Number(val))) return false; 192 | if (Number(val) < 0) return false; 193 | return true 194 | } 195 | })).value); 196 | console.clear(); 197 | 198 | let interBotPerms = []; 199 | let interUserPerms = []; 200 | 201 | if (interGuildOnly) { 202 | console.clear(); 203 | if (await (new Toggle({ 204 | message: "Bu interaksiyonun çalışması için botun X yetkilerine ihtiyacı var mı?", 205 | enabled: "Evet", 206 | disabled: "Hayır", 207 | initial: false 208 | })).run()) { 209 | console.clear(); 210 | interBotPerms = await permissionPrompt("İnteraksiyionun çalışması için bota gerekli olan yetkileri seç."); 211 | } 212 | 213 | console.clear(); 214 | if (await (new Toggle({ 215 | message: "Bu interaksiyonu kullanabilmek için kullanıcının X yetkilerine ihtiyacı var mı?", 216 | enabled: "Evet", 217 | disabled: "Hayır", 218 | initial: false 219 | })).run()) { 220 | console.clear(); 221 | interUserPerms = await permissionPrompt("Bu interaksiyonu kullanabilmek için kullanıcıya gerekli olan yetkileri seç."); 222 | } 223 | } 224 | if (interDeveloperOnly) interUserPerms.push("Developer"); 225 | if (interGuildOwnerOnly) interUserPerms.push("GuildOwner"); 226 | console.clear(); 227 | 228 | console.log(`! Dosyanız oluşturuluyor..`); 229 | let transformer = { "User": "UserAction", "Message": "MessageAction" } 230 | let filePath = path.resolve("./interactions", `${interFileName}.js`); 231 | let resultText = ` 232 | module.exports = new Underline.${transformer[interActionType] || interActionType || ""}({ 233 | ${interActionType == "Button" || interActionType == "SelectMenu" || interActionType == "Modal" ? `id: ${JSON.stringify(interName)},` : ""} 234 | name: ${JSON.stringify(interName)}, 235 | ${interDesc ? `description: ${JSON.stringify(interDesc)},` : ""} 236 | async onInteraction(inter, other) { 237 | // Kodunuz bruh, kolay gelsin! 238 | }, 239 | ${interActionType == "Message" || interActionType == "User" ? "" : `options: ${interActionType == "Button" || interActionType == "SelectMenu"|| interActionType == "Modal" ? "{}" : "[]"},`} 240 | ${interCoolDown ? `coolDown: ${interCoolDown},` : ""} 241 | ${interActionType == "Message" || interActionType == "User" ? "publishType: \"all\"," : ""} 242 | guildOnly: ${interGuildOnly}${interBotPerms.length > 0 || interUserPerms.length > 0 ? `,` : ""} 243 | ${interBotPerms.length > 0 || interUserPerms.length > 0 ? `perms: {` : ""} 244 | ${interBotPerms.length > 0 ? ` bot: ${JSON.stringify(interBotPerms)},` : ""} 245 | ${interUserPerms.length > 0 ? ` user: ${JSON.stringify(interUserPerms)}` : ""} 246 | ${interBotPerms.length > 0 || interUserPerms.length > 0 ? `}` : ""} 247 | }); 248 | `.split("\n").filter(i => !!i.trim()).join("\n"); 249 | 250 | console.log(chalk.blueBright(resultText)); 251 | fs.writeFileSync(filePath, resultText, "utf8"); 252 | console.log(`! Dosyanız "${chalk.green(filePath)}" konumuna kaydedildi!`); 253 | break; 254 | } 255 | case "event": { 256 | console.clear(); 257 | let eventFileName = (await prompt({ 258 | type: "input", 259 | name: 'value', 260 | message: "Olay dosya adınız ne olsun?", 261 | result(val) { 262 | if (val.endsWith(".js")) val = val.slice(0, -3); 263 | return val; 264 | }, 265 | required: true 266 | })).value; 267 | console.clear(); 268 | let eventName = await (new AutoComplete({ 269 | required: true, 270 | message: "Hangi olayı dinleyeceksiniz?", 271 | name: "value", 272 | limit: 10, 273 | index: 0, 274 | choices: ["applicationCommandCreate", "applicationCommandDelete", "applicationCommandUpdate", "channelCreate", "channelDelete", "channelPinsUpdate", "channelUpdate", "debug", "emojiCreate", "emojiDelete", "emojiUpdate", "error", "guildBanAdd", "guildBanRemove", "guildCreate", "guildDelete", "guildIntegrationsUpdate", "guildMemberAdd", "guildMemberAvailable", "guildMemberRemove", "guildMembersChunk", "guildMemberUpdate", "guildUnavailable", "guildUpdate", "interaction", "interactionCreate", "invalidated", "invalidRequestWarning", "inviteCreate", "inviteDelete", "message", "messageCreate", "messageDelete", "messageDeleteBulk", "messageReactionAdd", "messageReactionRemove", "messageReactionRemoveAll", "messageReactionRemoveEmoji", "messageUpdate", "presenceUpdate", "rateLimit", "ready", "roleCreate", "roleDelete", "roleUpdate", "shardDisconnect", "shardError", "shardReady", "shardReconnecting", "shardResume", "stageInstanceCreate", "stageInstanceDelete", "stageInstanceUpdate", "stickerCreate", "stickerDelete", "stickerUpdate", "threadCreate", "threadDelete", "threadListSync", "threadMembersUpdate", "threadMemberUpdate", "threadUpdate", "typingStart", "userUpdate", "voiceStateUpdate", "warn", "webhookUpdate"] 275 | })).run(); 276 | console.clear(); 277 | 278 | console.log(`! Dosyanız oluşturuluyor..`); 279 | 280 | let filePath = path.resolve("./events", `${eventFileName}.js`); 281 | 282 | let resultText = ` 283 | module.exports = new Underline.Event({ 284 | eventName: "${eventName}", 285 | async onEvent(...args) { 286 | // Kodunuz buraya, kolay gelsin! 287 | } 288 | }); 289 | `.split("\n").filter(i => !!i.trim()).join("\n"); 290 | 291 | console.log(chalk.blueBright(resultText)); 292 | fs.writeFileSync(filePath, resultText, "utf8"); 293 | console.log(`! Dosyanız "${chalk.green(filePath)}" konumuna kaydedildi!`); 294 | 295 | break; 296 | } 297 | default: { 298 | console.error("Geçersiz seçenek girdiniz! Geçerli seçenekler: interaction, event"); 299 | break; 300 | } 301 | } 302 | })(); 303 | -------------------------------------------------------------------------------- /other/patchConsoleLog.js: -------------------------------------------------------------------------------- 1 | try { 2 | require("chalk"); 3 | } catch { 4 | console.warn("Bu işlemi yapmadan önce gerekli modülleri indirmeniz gerekiyor.") 5 | console.warn("-> yarn install"); 6 | process.exit(-1); 7 | } 8 | 9 | const chalk = require("chalk"); 10 | 11 | { 12 | let originalLog = console.log; 13 | 14 | let log = (...args)=>{ 15 | originalLog(chalk.blackBright(`[${new Date().toLocaleTimeString()}]`), ...args) 16 | } 17 | 18 | console.info = function (...args) { 19 | args = args.map(i => { 20 | if (typeof i == "string") return chalk.blueBright(i) 21 | return i 22 | }) 23 | log(...args); 24 | } 25 | 26 | console.warn = function (...args) { 27 | args = args.map(i => { 28 | if (typeof i == "string") return chalk.yellowBright(i) 29 | return i 30 | }) 31 | log(...args); 32 | } 33 | 34 | console.error = function (...args) { 35 | args = args.map(i => { 36 | if (typeof i == "string") return chalk.redBright(i) 37 | return i 38 | }) 39 | log(...args); 40 | } 41 | 42 | console.debug = function(...args) { 43 | args = args.map(i => { 44 | if (typeof i == "string") return chalk.magentaBright(i) 45 | return i 46 | }) 47 | log(...args); 48 | } 49 | 50 | console.log = function(...args) { 51 | log(...args); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /other/typesGenerator.js: -------------------------------------------------------------------------------- 1 | require("./patchConsoleLog"); 2 | 3 | const chillout = require("chillout"); 4 | const fs = require("fs"); 5 | const extractZip = require("extract-zip"); 6 | const { makeSureFolderExists } = require("stuffs"); 7 | const path = require("path"); 8 | const readdirRecursive = require("recursive-readdir"); 9 | const config = require("../config"); 10 | const { Collection, ChannelType, MessageType, ComponentType, InteractionType, ActivityType, AuditLogOptionsType, ApplicationCommandOptionType, ButtonStyle, TextInputStyle } = require("discord.js"); 11 | 12 | globalThis.Underline = { 13 | Plugin: require("../types/Plugin"), 14 | Interaction: require('../types/Interaction'), 15 | Event: require('../types/Event'), 16 | ChatInput: require("../types/ChatInput"), 17 | MessageAction: require("../types/MessageAction"), 18 | UserAction: require("../types/UserAction"), 19 | SelectMenu: require("../types/SelectMenu"), 20 | Button: require("../types/Button"), 21 | Locale: require("../types/Locale"), 22 | Modal: require("../types/Modal"), 23 | config 24 | }; 25 | 26 | globalThis.Enums = { 27 | ChannelType, 28 | MessageType, 29 | ApplicationCommandOptionType, 30 | ActivityType, 31 | AuditLogOptionsType, 32 | InteractionType, 33 | ComponentType, 34 | ButtonStyle, 35 | TextInputStyle 36 | } 37 | 38 | async function getEventFilePaths() { 39 | let eventsPath = path.resolve("./events"); 40 | await makeSureFolderExists(eventsPath); 41 | let VEventFiles = await readdirRecursive(eventsPath); 42 | VEventFiles = VEventFiles.filter(i => { 43 | let state = path.basename(i).startsWith("-"); 44 | return !state; 45 | }); 46 | return VEventFiles; 47 | } 48 | 49 | async function getLocaleFilePaths() { 50 | let localesPath = path.resolve("./locales"); 51 | await makeSureFolderExists(localesPath); 52 | let VLocaleFiles = await readdirRecursive(localesPath); 53 | 54 | return VLocaleFiles; 55 | } 56 | 57 | async function getInteractionFilePaths() { 58 | let interactionsPath = path.resolve("./interactions"); 59 | await makeSureFolderExists(interactionsPath); 60 | let VInteractionFiles = await readdirRecursive(interactionsPath); 61 | VInteractionFiles = VInteractionFiles.filter(i => { 62 | let state = path.basename(i).startsWith("-"); 63 | return !state; 64 | }); 65 | return VInteractionFiles; 66 | } 67 | 68 | (async () => { 69 | let pluginFiles = await getPluginFilePaths(); 70 | let eventFiles = await getEventFilePaths(); 71 | let interactionFiles = await getInteractionFilePaths(); 72 | let pluginTypes = []; 73 | let pluginConfigTypes = []; 74 | let interactionIds = []; 75 | let eventIds = []; 76 | let TEventNames = []; 77 | let TEvents = []; 78 | let TInterfaces = []; 79 | let loadedNamespaces = []; 80 | 81 | 82 | let inters = []; 83 | localeFiles = await getLocaleFilePaths(); 84 | 85 | const locales = new Collection(); 86 | 87 | { 88 | let plFilePaths = await readdirRecursive(path.resolve("./plugins")); 89 | 90 | let plInteractionsPaths = plFilePaths.filter(i => { 91 | if (!i.match(new RegExp(`plugins\\${path.sep}(.|[şçğüiÇŞİĞÜIıöÖ])*\\${path.sep}interactions`))?.length) return false; 92 | if (i.match(new RegExp(`plugins\\${path.sep}-(.|[şçğüiÇŞİĞÜIıöÖ])*\\${path.sep}interactions`))?.length) return false; 93 | let state = path.basename(i).startsWith("-"); 94 | return !state; 95 | }); 96 | 97 | interactionFiles = [...interactionFiles, ...plInteractionsPaths]; 98 | } 99 | 100 | await chillout.forEach(interactionFiles, (interactionFile) => { 101 | /** @type {import("..MessageActions/Interaction")} */ 102 | let uInter = require(interactionFile); 103 | 104 | if (!uInter?._type?.toLowerCase().includes("interaction")) return; 105 | 106 | if (!uInter.id) return; 107 | interactionIds.push(uInter.id); 108 | inters.push(uInter); 109 | }); 110 | 111 | await chillout.forEach(localeFiles, (localeFile) => { 112 | /** @type {import("./types/Locale")} */ 113 | let locale = require(localeFile); 114 | locale.path = localeFile; 115 | locales.set(locale.locale, locale); 116 | }); 117 | 118 | { 119 | let configOtherType = {}; 120 | delete Underline.config.other.plugins; 121 | function fixConfigIn(fixingObj, tempObj) { 122 | for (let key in fixingObj) { 123 | if (typeof fixingObj[key] != "object") { 124 | tempObj[key] = typeof fixingObj[key]; 125 | configEdit = true; 126 | } 127 | else if (typeof fixingObj[key] == "object") { 128 | tempObj[key] = {}; 129 | fixConfigIn(fixingObj[key], tempObj[key]); 130 | } 131 | } 132 | } 133 | fixConfigIn(Underline.config.other, configOtherType); 134 | configOtherType["plugins"] = "import('./pluginTypes').config"; 135 | fs.writeFileSync(path.resolve(__dirname, "../generated/configOther.d.ts"), `export default class Other ${JSON.stringify(configOtherType, null, 2).replace(/("([^"]*[^\\]?)")/g, "$2").replace(/,$/mg, ";")}`); 136 | }; 137 | 138 | await chillout.forEach(pluginFiles, async (pluginFile) => { 139 | /** @type {import("../types/Plugin")} */ 140 | let plugin = require(pluginFile); 141 | 142 | if (plugin._type != "plugin") return 143 | 144 | if (loadedNamespaces.find(x => x == plugin.namespace)) return 145 | 146 | loadedNamespaces.push(plugin.namespace); 147 | 148 | let parsedPluginPath = path.parse(pluginFile); 149 | 150 | let dtsPath = ""; 151 | 152 | switch (parsedPluginPath.dir.split(path.sep).pop()) { 153 | case "plugins": { 154 | dtsPath = path.resolve(parsedPluginPath.dir, parsedPluginPath.base.replace(".up.js", ".up.d.ts")); 155 | break; 156 | } 157 | default: { 158 | dtsPath = path.resolve(parsedPluginPath.dir, "index.d.ts"); 159 | break; 160 | } 161 | } 162 | 163 | let isDTS = fs.existsSync(dtsPath); 164 | 165 | if (isDTS) { 166 | pluginTypes.push(`["${plugin.namespace}"]: import("../${path.relative(process.cwd(), dtsPath).replace(".d.ts", "").replaceAll(path.sep, "/")}").Plugin`); 167 | } else { 168 | if (!plugin?.implements?.properties) return; 169 | let result = [] 170 | for (let property in plugin.implements.properties) { 171 | let returns = plugin.implements.properties[property]; 172 | let returnsString = generateReturn(returns); 173 | result.push(`${property}: ${returnsString}`) 174 | } 175 | result = `{ ${result.join(", ")} }`; 176 | pluginTypes.push(`["${plugin.namespace}"]: ${result}`); 177 | } 178 | 179 | if (plugin?.implements?.events) { 180 | for (let eventName in plugin.implements.events) { 181 | let args = plugin.implements.events[eventName].split(/ *, */).map(x => x.includes(":") ? (x.split(":")[0] + ":" + generateReturn(x.split(":")[1])) : generateReturn(x)); 182 | TEventNames.push(`"${plugin.namespace}:${eventName}"`); 183 | args.push("other: IOther"); 184 | TInterfaces.push(`export interface ${plugin.namespace}_${eventName.replace(/ |-/, "")} { eventName: "${plugin.namespace}:${eventName}", onEvent: (${!args?.[0] ? "" : args.map((v, i) => v.includes(":") ? v : `arg${i}: ${v}`).join(", ")}) => void }`); 185 | TEvents.push(`${plugin.namespace}_${eventName.replace(/ |-/, "")}`); 186 | 187 | } 188 | } 189 | 190 | if (plugin.locale) { 191 | locales.forEach((locale) => { 192 | if (!locale.inConstructor) locale.inConstructor = { locale: locale.locale, data: locale._data, commands: locale.commands }; 193 | if (!locale.inConstructor.data[plugin.namespace]) { 194 | locale.inConstructor.data[plugin.namespace] = plugin.locale; 195 | locale.overwrite = true; 196 | } else { 197 | function fixO(fixingObj, tempObj) { 198 | for (let key in tempObj) { 199 | if (!fixingObj[key]) { 200 | fixingObj[key] = tempObj[key]; 201 | locale.overwrite = true; 202 | } 203 | else if (typeof fixingObj[key] == "object") fixO(fixingObj[key], tempObj[key]); 204 | } 205 | } 206 | fixO(locale.inConstructor.data[plugin.namespace], plugin.locale); 207 | } 208 | }) 209 | } 210 | 211 | if (plugin.requires.config) { 212 | let result = [] 213 | for (let property in plugin.requires.config) { 214 | let returns = plugin.requires.config[property]; 215 | let returnsString = generateReturn(returns); 216 | result.push(`${property}: ${returnsString}`) 217 | } 218 | result = `{ ${result.join(", ")} }`; 219 | pluginConfigTypes.push(`["${plugin.namespace}"]: ${result}`); 220 | } 221 | 222 | }); 223 | 224 | let defaultLocale = locales.get(Underline.config.defaultLanguage); 225 | 226 | locales.forEach((locale) => { 227 | if (!locale.inConstructor) locale.inConstructor = { locale: locale.locale, data: locale._data, commands: locale.commands }; 228 | 229 | // function commentThemAll(fixingObj, tempObj) { 230 | // for (let key in tempObj) { 231 | // if (!fixingObj[key]) { 232 | // if (typeof tempObj[key] == "string") { 233 | // fixingObj[key] = ""; 234 | // if (!locale.comments) locale.comments = {}; 235 | // locale.comments[key] = tempObj[key] 236 | // } 237 | 238 | // locale.overwrite = true; 239 | // } 240 | // else if (typeof fixingObj[key] == "object") fillDataWithDefault(fixingObj[key], tempObj[key]); 241 | // } 242 | // } 243 | let lastTriggers = []; 244 | 245 | function fillDataWithDefaultWithComment(fixingObj, tempObj) { 246 | for (let key in tempObj) { 247 | if (!fixingObj[key]) { 248 | if (typeof tempObj[key] == "string") { 249 | fixingObj[key] = ""; 250 | if (!locale.comments) locale.comments = {}; 251 | locale.comments[key] = tempObj[key] 252 | } else if (typeof tempObj[key] == "object") { 253 | if (!locale.comments) locale.comments = {}; 254 | fixingObj[key] = tempObj[key]; 255 | fillDataWithDefaultWithComment(fixingObj[key], tempObj[key]); 256 | } 257 | locale.overwrite = true; 258 | } else if (typeof fixingObj[key] == "object") fillDataWithDefaultWithComment(fixingObj[key], tempObj[key]); 259 | else if (typeof fixingObj[key] == "string") { 260 | locale.comments[key] = tempObj[key] 261 | fixingObj[key] = ""; 262 | } 263 | } 264 | } 265 | 266 | function fillDataWithDefault(fixingObj, tempObj) { 267 | for (let key in tempObj) { 268 | if (!fixingObj[key] && fixingObj[key] !== "") { 269 | if (typeof tempObj[key] == "string") { 270 | fixingObj[key] = ""; 271 | if (!locale.comments) locale.comments = {}; 272 | locale.comments[key] = tempObj[key] 273 | } else if (typeof tempObj[key] == "object") { 274 | if (!locale.comments) locale.comments = {}; 275 | fixingObj[key] = tempObj[key]; 276 | lastTriggers.push({ f: fillDataWithDefaultWithComment, args: [fixingObj[key], tempObj[key]] }); 277 | } 278 | locale.overwrite = true; 279 | } else if (typeof fixingObj[key] == "object") fillDataWithDefault(fixingObj[key], tempObj[key]); 280 | } 281 | } 282 | fillDataWithDefault(locale.inConstructor.data, defaultLocale._data); 283 | lastTriggers.forEach((obj) => obj.f(...obj.args)); 284 | 285 | inters.forEach(inter => { 286 | if (inter.actionType != "ChatInput") return; 287 | if (!Array.isArray(locale.inConstructor.commands)) locale.inConstructor.commands = []; 288 | let exists = locale.inConstructor.commands.some(lInter => lInter.originalName[0] == inter.name[0] && lInter.originalName[1] == inter.name[1] && lInter.originalName[2] == inter.name[2]); 289 | if (!exists) { 290 | locale.overwrite = true; 291 | locale.inConstructor.commands.push({ 292 | originalName: inter.name, 293 | name: inter.name, 294 | description: inter.description 295 | }) 296 | } 297 | }) 298 | 299 | }); 300 | 301 | locales.forEach(locale => { 302 | if (locale.overwrite) { 303 | locale.inConstructor = JSON.stringify(locale.inConstructor, (key, value) => { 304 | if (key == "commands") return value; 305 | if (Array.isArray(value)) return JSON.stringify(value, null , 0); 306 | else return value; 307 | }, 2).replace(/("([^"]*[^\\]?)"|`([^`]*[^\\]?)`|'([^']*[^\\]?)')\:/g, "$2$4$3:") 308 | .replace(/"\[\\(")([a-z-]+)\\(")((,)\\(")([a-z-]+)\\("))?((,)\\(")([a-z-]+)\\("))?\]"/g, '[$1$2$3$5$6$7$8$10$11$12$13]') 309 | if (locale.comments) for (let key in locale.comments) { 310 | locale.inConstructor = locale.inConstructor.replace(new RegExp(`( *)(${key}: ")`), `$1// ${locale.comments[key]}\n$1$2`) 311 | } 312 | fs.writeFileSync(locale.path, `module.exports = new Underline.Locale(${locale.inConstructor});`); 313 | } 314 | }); 315 | 316 | 317 | 318 | if (defaultLocale) { 319 | 320 | let localeData = JSON.stringify(defaultLocale._data, null, 2).replace(/("([^"]*[^\\]?)"|`([^`]*[^\\]?)`|'([^']*[^\\]?)')\:/g, "$2$4$3:").replace(/"[^"]*[^\\]?"|`[^`]*[^\\]?`|'[^']*[^\\]?'/g, "(...args) => string"); 321 | 322 | let localeOutput = 323 | `export default class Locale { 324 | locale: import("../types/Locale").LocaleString 325 | data: LocaleData 326 | } 327 | 328 | export type LocaleData = ${localeData};`; 329 | await fs.promises.writeFile(path.resolve(__dirname, "../generated/localeTypes.d.ts"), localeOutput); 330 | } 331 | 332 | 333 | 334 | await chillout.forEach(eventFiles, (eventFile) => { 335 | /** @type {import("../types/Event")} */ 336 | let uEvent = require(eventFile); 337 | if (uEvent?._type != "event") return; 338 | if (typeof uEvent.id != "string") uEvent.id = path.basename(eventFile).slice(0, -3).replace(/ /g, ""); 339 | eventIds.push(uEvent.id); 340 | }); 341 | 342 | await makeSureFolderExists(path.resolve(__dirname, "../generated")); 343 | let pluginTypesResult = `import { IOther } from "../types/Event";\n\nexport class config {\n${pluginConfigTypes.map(i => ` ${i};`).join("\n")}\n}\n\nexport class Types {\n${pluginTypes.map(i => ` ${i};`).join("\n")}\n};\n${`export type TEventNames = ${TEventNames.join(" | ").trim() || '""'};`}\n${`export type TEvents = ${TEvents.join(" | ").trim() || "[]"};`}\n${TInterfaces.join("\n")}\n`.trim(); 344 | await fs.promises.writeFile(path.resolve(__dirname, "../generated/pluginTypes.d.ts"), pluginTypesResult); 345 | 346 | let idsResult = `export type InteractionIds = ${interactionIds.map(i => `"${i}"`).join(" | ").trim() || '""'};\nexport type EventIds = ${eventIds.map(i => `"${i}"`).join(" | ").trim() || '""'};`; 347 | await fs.promises.writeFile(path.resolve(__dirname, "../generated/ids.d.ts"), idsResult); 348 | 349 | })(); 350 | 351 | function generateReturn(returns) { 352 | let returnsString = ""; 353 | switch (returns) { 354 | case "function": { 355 | returnsString = "(...args: any[]) => any"; 356 | break; 357 | } 358 | case "object": { 359 | returnsString = "{ [key:string|number]: any }"; 360 | break; 361 | } 362 | case "array": { 363 | returnsString = "any[]"; 364 | break; 365 | } 366 | default: { 367 | returnsString = returns; 368 | } 369 | } 370 | return returnsString; 371 | } 372 | 373 | async function getPluginFilePaths() { 374 | let pluginsPath = path.resolve(__dirname, "../plugins"); 375 | await makeSureFolderExists(pluginsPath); 376 | let folderOrZips = await fs.promises.readdir(pluginsPath, { withFileTypes: true }); 377 | let result = []; 378 | for (let i = 0; i < folderOrZips.length; i++) { 379 | const folderOrZip = folderOrZips[i]; 380 | if (folderOrZip.isDirectory() && folderOrZip.name.endsWith(".up")) { 381 | if (folderOrZip.name.startsWith("-")) continue; 382 | result.push(path.resolve(pluginsPath, folderOrZip.name, "index.js")); 383 | } else if (folderOrZip.name.endsWith(".up.js")) { 384 | if (folderOrZip.name.startsWith("-")) continue; 385 | result.push(path.resolve(pluginsPath, folderOrZip.name)); 386 | } else if (folderOrZip.name.endsWith(".up.zip")) { 387 | let folderPath = path.resolve(pluginsPath, folderOrZip.name.replace(".up.zip", ".up")); 388 | let zipPath = path.resolve(pluginsPath, folderOrZip.name); 389 | 390 | await fs.promises.rm(folderPath, { recursive: true }).catch(() => { }); 391 | await makeSureFolderExists(folderPath); 392 | await extractZip(zipPath, { dir: folderPath }); 393 | fs.promises.unlink(zipPath).catch(() => null); 394 | if (folderOrZip.name.startsWith("-")) continue; 395 | result.push(path.resolve(folderPath, "index.js")); 396 | } 397 | } 398 | 399 | return result; 400 | } -------------------------------------------------------------------------------- /other/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | async nullDefer(interaction) { 3 | await interaction.client.api.interactions(interaction.id, interaction.token).callback.post({ 4 | data: { 5 | type: 6, 6 | data: { 7 | flags: null, 8 | }, 9 | }, 10 | }); 11 | return true; 12 | }, 13 | /** 14 | * @param {string[]} names 15 | * @param {{[key: string]: string[]}} obj 16 | * @param {string[]?} start 17 | * @param {number?} depth 18 | * @returns {string[]} 19 | */ 20 | sortDependant(names, obj, start=[], depth = 0) { 21 | const processed = names.reduce((a, b, i) => { 22 | if (obj[b].every(Array.prototype.includes, a)) a.push(b) 23 | return a 24 | }, start) 25 | const nextNames = names.filter(n => !processed.includes(n)), 26 | goAgain = nextNames.length && depth <= names.length 27 | return goAgain ? this.sortDependant(nextNames, obj, processed, depth + 1) : processed 28 | } 29 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@discordjs/builders": "^0.14.0-dev.1650672503-3617093", 4 | "@discordjs/rest": "^0.4.1", 5 | "async-and-quick": "^1.0.1", 6 | "chalk": "^4.1.1", 7 | "chillout": "^5.0.0", 8 | "chokidar": "^3.5.3", 9 | "discord-api-types": "^0.23.1", 10 | "discord-hybrid-sharding": "^1.6.8", 11 | "discord.js": "^14.0.3", 12 | "enquirer": "^2.3.6", 13 | "extract-zip": "^2.0.1", 14 | "lodash": "^4.17.21", 15 | "plsargs": "^0.1.6", 16 | "recursive-readdir": "^2.2.2", 17 | "redis": "^4.2.0", 18 | "stuffs": "^0.1.11" 19 | }, 20 | "devDependencies": { 21 | "@types/chalk": "^2.2.0", 22 | "@types/lodash": "^4.14.178", 23 | "@types/node": "^15.12.2", 24 | "@types/recursive-readdir": "^2.2.0" 25 | }, 26 | "name": "armagan-basit-altyapi", 27 | "version": "3.0.0", 28 | "description": "Kullanımı basit ancak bir yandanda içinde birçek özellik barındıran discord bot altyapısı.", 29 | "main": "index.js", 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/BasitAltyapi/basit-altyapi/tree/v14.git" 33 | }, 34 | "author": "Kıraç Armağan Önal", 35 | "license": "GPL-v3-or-later", 36 | "private": true, 37 | "scripts": { 38 | "interaksiyon": "node ./other/generator.js interaction", 39 | "interaction": "node ./other/generator.js interaction", 40 | "olay": "node ./other/generator.js event", 41 | "event": "node ./other/generator.js event", 42 | "baslat": "node --expose-gc ./main.js", 43 | "start": "node --expose-gc ./main.js", 44 | "tipler": "node ./other/typesGenerator.js", 45 | "types": "node ./other/typesGenerator.js", 46 | "gelistirici": "node ./watchChanges.js", 47 | "dev": "node ./watchChanges.js" 48 | }, 49 | "bugs": { 50 | "url": "https://github.com/BasitAltyapi/basit-altyapi/tree/v14/issues" 51 | }, 52 | "homepage": "https://github.com/BasitAltyapi/basit-altyapi/tree/v14#readme", 53 | "keywords": [], 54 | "engines": { 55 | "node": "^18.0.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /plugins/-mongoose.up: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasitAltyapi/basit-altyapi/008a4720a9d8bbaea8501b61d4fe256f9991f47c/plugins/-mongoose.up -------------------------------------------------------------------------------- /plugins/-vault.up: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasitAltyapi/basit-altyapi/008a4720a9d8bbaea8501b61d4fe256f9991f47c/plugins/-vault.up -------------------------------------------------------------------------------- /publishInteractions.js: -------------------------------------------------------------------------------- 1 | require("./other/patchConsoleLog"); 2 | const { plsParseArgs } = require('plsargs'); 3 | const argv = plsParseArgs(process.argv.slice(2)); 4 | const chillout = require("chillout"); 5 | const { makeSureFolderExists, sleep } = require("stuffs"); 6 | const path = require("path"); 7 | const readdirRecursive = require("recursive-readdir"); 8 | const config = require("./config"); 9 | const { REST } = require('@discordjs/rest'); 10 | const { Routes } = require('discord-api-types/v9'); 11 | const Discord = require('discord.js'); 12 | 13 | globalThis.Underline = { 14 | config, 15 | Interaction: require('./types/Interaction'), 16 | Event: require('./types/Event'), 17 | ChatInput: require("./types/ChatInput"), 18 | MessageAction: require("./types/MessageAction"), 19 | UserAction: require("./types/UserAction"), 20 | SelectMenu: require("./types/SelectMenu"), 21 | Button: require("./types/Button"), 22 | Locale: require("./types/Locale"), 23 | Modal: require("./types/Modal"), 24 | }; 25 | 26 | globalThis.Enums = { 27 | ChannelType: Discord.ChannelType, 28 | MessageType: Discord.MessageType, 29 | ApplicationCommandOptionType: Discord.ApplicationCommandOptionType, 30 | ActivityType: Discord.ActivityType, 31 | AuditLogOptionsType: Discord.AuditLogOptionsType, 32 | InteractionType: Discord.InteractionType, 33 | ComponentType: Discord.ComponentType, 34 | ButtonStyle: Discord.ButtonStyle, 35 | TextInputStyle: Discord.TextInputStyle 36 | } 37 | 38 | let ogLangs = ["da", "de", "en-GB", "en-US", "es-ES", "fr", "hr", "it", "lt", "hu", "nl", "no", "pl", "pt-BR", "ro", "fi", "sv-SE", "vi", "tr", "cs", "el", "bg", "ru", "uk", "hi", "th", "zh-CN", "ja", "zh-TW", "ko"]; 39 | 40 | 41 | 42 | let interactionTypes = { "ChatInput": 1, "Message": 3, "User": 2 }; 43 | let optionTypes = { "SubCommand": 1, "SubCommandGroup": 2, "String": 3, "Integer": 4, "Boolean": 5, "User": 6, "Channel": 7, "Role": 8, "Mentionable": 9, "Number": 10, "Attachment": 11 }; 44 | 45 | async function getLocaleFilePaths() { 46 | let localesPath = path.resolve("./locales"); 47 | await makeSureFolderExists(localesPath); 48 | let VLocaleFiles = await readdirRecursive(localesPath); 49 | VLocaleFiles = VLocaleFiles.filter(i => { 50 | let state = path.basename(i).startsWith("-"); 51 | return !state; 52 | }); 53 | return VLocaleFiles; 54 | } 55 | 56 | /** @type {[import("./types/Locale").LocaleString, import("./types/Locale").Locale][]} */ 57 | const locales = []; 58 | 59 | (async () => { 60 | 61 | await chillout.forEach(await getLocaleFilePaths(), (localeFile) => { 62 | let locale = require(localeFile); 63 | if (locale._type != "locale") return; 64 | locales.push([locale.locale, locale]); 65 | }) 66 | 67 | 68 | let dcInters = []; 69 | 70 | let isClearMode = argv.get(0) == "guild" ? argv.get(2) == "clear" : (argv.get(0) == "global" ? argv.get(1) == "clear" : false); 71 | 72 | let publishMode = argv.get(0) == "guild" ? "guild" : argv.get(0) == "global" ? "global" : null; 73 | 74 | let publishSpecialType = publishMode == "guild" ? (isClearMode ? "guildOnly" : (argv.get(2) || "guildOnly")) : "globalOnly"; 75 | 76 | console.log(publishSpecialType) 77 | 78 | if (!publishMode) { 79 | console.error(`Geçersiz paylaşım modu! Geçerli modlar: guild, global`); 80 | console.error(`Kullanım örneği: node publishInteractions.js guild [clear]`); 81 | console.error(`Kullanım örneği: node publishInteractions.js global [clear]`); 82 | return process.exit(1); 83 | } 84 | 85 | if (!isClearMode) { 86 | let interactionsFolder = path.resolve("./interactions"); 87 | 88 | await makeSureFolderExists(interactionsFolder); 89 | 90 | /** @type {(import("./types/Interaction"))[]} */ 91 | let uInters = []; 92 | 93 | console.info("Interaksiyon dosyaları okunuyor..") 94 | 95 | let interactionFilePaths = await readdirRecursive(interactionsFolder); 96 | interactionFilePaths = interactionFilePaths.filter(i => { 97 | let state = path.basename(i).startsWith("-"); 98 | return !state; 99 | }); 100 | 101 | let plFilePaths = await readdirRecursive(path.resolve("./plugins")); 102 | 103 | let plInteractionsPaths = plFilePaths.filter(i => { 104 | if (!i.match(new RegExp(`plugins\\${path.sep}(.|[şçğüiÇŞİĞÜIıöÖ])*\\${path.sep}interactions`))?.length) return false; 105 | if (i.match(new RegExp(`plugins\\${path.sep}-(.|[şçğüiÇŞİĞÜIıöÖ])*\\${path.sep}interactions`))?.length) return false; 106 | let state = path.basename(i).startsWith("-"); 107 | return !state; 108 | }); 109 | 110 | interactionFilePaths = [...interactionFilePaths, ...plInteractionsPaths]; 111 | 112 | await chillout.forEach(interactionFilePaths, (interactionFilePath) => { 113 | /** @type {import("./types/Interaction")} */ 114 | let uInter = require(interactionFilePath); 115 | if (uInter?._type != "interaction") return; 116 | if (!uInter.publishType) uInter.publishType = "globalOnly"; 117 | if (!((uInter.publishType == "all" && ["guildOnly", "globalOnly"].includes(publishSpecialType)) || publishSpecialType == uInter.publishType)) { 118 | console.warn(`Interaksiyon "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" dönüştürülme listesine eklenmedi çünkü interaksiyon paylaşılma tipi(${uInter.publishType}) ile paylaşma modu(${publishMode}) uyumsuz!`); 119 | return; 120 | } 121 | console.info(`Interaksiyon "${uInter.actionType == "ChatInput" ? `/${uInter.name.join(" ")}` : `${uInter.name[0]}`}" dönüştürülme listesine eklendi!`); 122 | uInters.push(uInter); 123 | }); 124 | 125 | console.info("Interaksiyonlar discordun anlayacağı dile dönüştürülüyor..") 126 | uInters = uInters.sort((a, b) => a.name.length - b.name.length) 127 | 128 | dcInters = uInters.reduce((all, current) => { 129 | switch (current.name.length) { 130 | case 1: { 131 | let localeData = findLocales(current.name); 132 | all.push({ 133 | type: interactionTypes[current.actionType] ?? current.actionType, 134 | name: current.name[0], 135 | description: current.description, 136 | defaultMemberPermissions: current.defaultPermission ? null : ["Administrator"], 137 | dmPermission: !current.guildOnly, 138 | options: current.options, 139 | nameLocalizations: localeData.names(0), 140 | descriptionLocalizations: localeData.descriptions, 141 | }); 142 | break; 143 | } 144 | case 2: { 145 | let baseItem = all.find((i) => { 146 | return i.name == current.name[0] && (interactionTypes[i.type] ?? i.type) == (interactionTypes[current.actionType] ?? current.actionType) 147 | }); 148 | let localeData = findLocales(current.name); 149 | if (!baseItem) { 150 | all.push({ 151 | type: interactionTypes[current.actionType] ?? current.actionType, 152 | name: current.name[0], 153 | nameLocalizations: localeData.names(0), 154 | descriptionLocalizations: localeData.descriptions, 155 | description: current.description, 156 | defaultMemberPermissions: current.defaultPermission ? null : ["Administrator"], 157 | dmPermission: !current.guildOnly, 158 | options: [ 159 | { 160 | type: 1, 161 | description: current.description, 162 | name: current.name[1], 163 | nameLocalizations: localeData.names(1), 164 | descriptionLocalizations: localeData.descriptions, 165 | options: current.options 166 | } 167 | ] 168 | }); 169 | } else { 170 | baseItem.options.push({ 171 | type: 1, 172 | description: current.description, 173 | name: current.name[1], 174 | nameLocalizations: localeData.names(1), 175 | descriptionLocalizations: localeData.descriptions, 176 | options: current.options 177 | }) 178 | } 179 | break; 180 | } 181 | case 3: { 182 | let level1Item = all.find((i) => { 183 | return i.name == current.name[0] && (interactionTypes[i.type] ?? i.type) == (interactionTypes[current.actionType] ?? current.actionType) 184 | }); 185 | let localeData = findLocales(current.name); 186 | if (!level1Item) { 187 | all.push({ 188 | type: interactionTypes[current.actionType] ?? current.actionType, 189 | name: current.name[0], 190 | defaultMemberPermissions: current.defaultPermission ? null : ["Administrator"], 191 | dmPermission: !current.guildOnly, 192 | nameLocalizations: localeData.names(0), 193 | descriptionLocalizations: localeData.descriptions, 194 | description: current.description, 195 | options: [ 196 | { 197 | type: 2, 198 | name: current.name[1], 199 | nameLocalizations: localeData.names(1), 200 | descriptionLocalizations: localeData.descriptions, 201 | description: current.description, 202 | options: [ 203 | { 204 | type: 1, 205 | description: current.description, 206 | name: current.name[2], 207 | options: current.options, 208 | nameLocalizations: localeData.names(2), 209 | descriptionLocalizations: localeData.descriptions, 210 | } 211 | ] 212 | } 213 | ] 214 | }); 215 | } else { 216 | let level2Item = level1Item.options.find(i => { 217 | return i.name == current.name[1] && (interactionTypes[i.type] ?? i.type) == 2 218 | }); 219 | if (!level2Item) { 220 | level1Item.options.push({ 221 | type: 2, 222 | name: current.name[1], 223 | nameLocalizations: localeData.names(1), 224 | descriptionLocalizations: localeData.descriptions, 225 | description: current.description, 226 | options: [ 227 | { 228 | type: 1, 229 | description: current.description, 230 | name: current.name[2], 231 | options: current.options, 232 | nameLocalizations: localeData.names(2), 233 | descriptionLocalizations: localeData.descriptions, 234 | } 235 | ] 236 | }) 237 | } else { 238 | level2Item.options.push({ 239 | type: 1, 240 | description: current.description, 241 | name: current.name[2], 242 | options: current.options, 243 | nameLocalizations: localeData.names(2), 244 | descriptionLocalizations: localeData.descriptions, 245 | }) 246 | } 247 | } 248 | } 249 | break; 250 | } 251 | 252 | return all; 253 | }, []); 254 | 255 | function fixOptions(inter = {}) { 256 | inter.type = optionTypes[inter.type] ?? inter.type; 257 | if (inter?.options) { 258 | inter.options.map(fixOptions) 259 | return inter; 260 | }; 261 | return inter; 262 | } 263 | 264 | for (let i = 0; i < dcInters.length; i++) { 265 | dcInters[i] = fixOptions(dcInters[i]); 266 | } 267 | 268 | require("util").inspect(dcInters, false, 9999, true) 269 | 270 | // return; 271 | 272 | dcInters = dcInters.map(i => Discord.ApplicationCommandManager.transformCommand(i)); 273 | } else { 274 | console.info("Hiçbir interaksiyon okunmadı, bütün var olanlar temizlenicek..."); 275 | } 276 | 277 | const rest = new REST({ version: "9" }).setToken(config.clientToken); 278 | 279 | console.info("Hesap bilgileri alınıyor!"); 280 | /** @type {import("discord-api-types/rest/v9/user").RESTGetAPIUserResult} */ 281 | const me = await rest.get(Routes.user()); 282 | console.info(`Hesap bilgileri alındı! ${me.username}#${me.discriminator} (${me.id})`); 283 | 284 | console.info(`İnteraksiyonlar discorda gönderiliyor!`); 285 | 286 | switch (publishMode) { 287 | case "guild": { 288 | let guildId = argv.get(1); 289 | console.info(`Paylaşma modu: sunucu (${guildId})`); 290 | 291 | await rest.put(Routes.applicationGuildCommands(me.id, guildId), { body: dcInters }); 292 | 293 | console.info(`Paylaşılan komutların gelmesi 3-5 saniye kadar sürebilir.`); 294 | break; 295 | } 296 | case "global": { 297 | console.info(`Paylaşma modu: global`); 298 | 299 | await rest.put(Routes.applicationCommands(me.id), { body: dcInters }); 300 | 301 | console.info(`Paylaşılan komutların gelmesi 1 saat kadar sürebilir. Eğer hemen gelmesini istiyorsanız botunuzu sunucunuzdan atıp geri alabilirsiniz.`); 302 | break; 303 | } 304 | } 305 | 306 | console.info(`İnteraksiyonlar paylaşıldı!`); 307 | })(); 308 | 309 | 310 | /** 311 | * @param {string[]} commandName 312 | */ 313 | function findLocales(commandName) { 314 | let commandNameJoined = commandName.join(" "); 315 | let descriptions = {}; 316 | let names = {}; 317 | for (let i = 0; i < locales.length; i++) { 318 | const [localeString, locale] = locales[i]; 319 | let commandLocale = locale.commands.find(i => i.originalName.join(" ") == commandNameJoined); 320 | if (commandLocale) { 321 | let aliases = ogLangs.filter(i => i.startsWith(localeString)); 322 | for (let j = 0; j < aliases.length; j++) { 323 | const alias = aliases[j]; 324 | descriptions[alias] = commandLocale.description; 325 | names[alias] = commandLocale.name; 326 | } 327 | } 328 | } 329 | return { 330 | descriptions, 331 | names(idx) { 332 | return Object.fromEntries(Object.entries(names).map(i => [i[0], i[1][idx]])); 333 | } 334 | }; 335 | } 336 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # ⚠️ BU ALTYAPI ARTIK GELİŞTİRİLMİYOR - VE YARDIMCI OLMUYORUM YENİ ALT YAPIYI İZLEMEDE KALIN [MostFeatured/DiscordBotInfrastructure](https://github.com/MostFeatured/DiscordBotInfrastructure) 2 | 3 | --- 4 | 5 | 6 | # Basit Altyapı (Versiyon 3.0) (v14.x) # 7 | 8 | Profesyoneller tarafından geliştirilen ve "*Yeryüzünde her zaman ücretsiz ve değerli bir şeyler olacaktır.*" felsefesiyle hazırlanan açık kaynaklı, güncel, stabil ve gelişmiş Discord bot altyapısı. 9 | 10 | # Özellikler # 11 | 12 | - ✅ Plugin sistemi. (Altyapıya istediğiniz özellikleri parça parça ekleyin!) 13 | - ✅ Pluginler için TAM tip desteği. 14 | - ✅ Çoklu dil desteği. 15 | - ✅ Sunucuya özel komut paylaşma tipleri. 16 | - ✅ SelectMenu ve Button desteği. 17 | - ✅ SelectMenu ve Button tam component ve options desteği. 18 | - ✅ Autocomplate (otomatik tamamlama) desteği. 19 | - ✅ Sağtık menü desteği. 20 | - ✅ Slash command desteği. 21 | - ✅ Slash subcommand desteği. 22 | - ✅ Slash subcommandgroup desteği. 23 | - ✅ Async ve aşırı hızlı. (interaksiyon başına 1ms kadar sürüyor!) 24 | - ✅ interaksiyon başına değişken yavaşlatma desteği. 25 | - ✅ interaksiyon başına bot gerekli yetki desteği. 26 | - ✅ interaksiyon başına kullanıcı gerekli yetki desteği. 27 | - ✅ Özelleştirilebilir hata mesajları. 28 | - ✅ Mantık hatası uyarı sistemleri. 29 | - ✅ Bottan kullanıcı yasaklama. 30 | - ✅ interaksiyonlarda otomatik tamamlama. 31 | - ✅ Global Underline objesi. (interactions, events, config, client vb.) 32 | - ✅ İç içe klasör desteği. 33 | - ✅ interaksiyon açıp kapama desteği. 34 | - ✅ Sadece geliştiricilerin kullanabildiği interaksiyon desteği. 35 | - ✅ Genel event desteği. 36 | - ✅ Event otomatik tamamlama desteği. 37 | - ✅ Event kapatabilme. 38 | - ✅ Gelişmiş config dosyası. (Artık index.js dosyasını modifiye etmenize gerek yok!) 39 | - ✅ interaksiyon varsayılanlarını değiştirebilme. 40 | - ✅ Kolay bir şekilde interaksiyon öncesi işlem ekleyebilme. 41 | - ✅ Kolay bir şekilde interaksiyon veya olay altyapsı oluşturabilme. 42 | - ✅ Kolayca dosya devre dışı bırakılabilme. (İsmi tire (-) ile başlayan interaksiyonlar ve olaylar umursanmaz.) 43 | - ✅ Düğme ve Seç menülerde değer/referans taşıyabilme. 44 | - ✅ Other objesi ile her işlemden önce değer belirtebilme. 45 | - ✅ Full otomatik dil desteği. (Komutu kullanan kişinin dilini otomatik belirleme.) 46 | - ✅ Redis ve clustering kullanarak 100% sharding desteği. Normal bir altyapıya kıyasla %80 daha az ram kullanımı! 47 | - ✅ Modal desteği. 48 | - ✅ Geliştrici modu desteği. (Bütün tip dosyaları otomatik olarak güncellenecektir.) 49 | 50 | # Kurulum Şeması # 51 | 52 | # İndirme # 53 | - Altyapının en son versiyonunu [buradan indirebilirsiniz.](https://github.com/TheArmagan/basit-altyapi/releases/latest) 54 | 55 | # Gerekenler # 56 | - Node.js; `v18.x`. 57 | - Yarn; `npm install -g yarn`. (Yarn, bizlerin kullandığı ve npm'e göre daha hızlı ve stabil olan bir paket yöneticisidir.) 58 | 59 | # Kurulum # 60 | - Proje dosya konumuna gelinip `yarn install` yazılması gerekmektedir ve yeterlidir. 61 | - Proje [kurulumunu yap](#kullanım)tıktan sonra `yarn start` yazarak projeyi başlatabilirsiniz. 62 | 63 | # Kullanım # 64 | - Botun genel ayarlarını, kullanıcı hata mesajlarını ve diğer olaylarda düzenleme yapmak için [`config.js`](./config.js) config dosyasını kullanabilirsiniz. 65 | - İnteraksiyonlar için `interactions` klasörünün içindeki [`ornekKomut.js`](./interactions/-ornekKomut.js) ve [`ornekSağtık.js`](./interactions/-ornekSağtık.js) dosyasını kullanabilirsiniz. 66 | - Yeni bir interaksiyon dosyası oluşturmak isterseniz `yarn interaksiyon` interaksiyonunu kullanabilsiniz. Bu sayede sizi ilk interaksiyon altyapısını yazma derdinden kurtaracak ve interaksiyon hakkında her türlü soruyu soracaktır. 67 | - Slash interaksiyonlarını Discord üzerinde global olarak yayınlamak için `node publishInteractions.js global` interaksiyonunu kullanabilirsiniz. 68 | - Slash interaksiyonlarını sadece bir sunucu için yayınlamak istiyorsanız `node publishInteractions.js guild ` interaksiyonunu kullanabilirsiniz. 69 | - Bütün interaksiyonları temizlemek için `node publishInteractions.js global clear` veya `node publishInteractions.js guild clear` interaksiyonunu kullanabilirsiniz. 70 | 71 | Bölüm Bilgilendirmesi: Global interaksiyonların sunuculara gönderilmesi yaklaşık 1 saat kadar sürebilmektedir. Sunucuya özel interaksiyonlar ise 5-10 saniye içerisinde aktif olmaktadır. 72 | *Not: Botunuzu test ederken global interaksiyonlar yerine sunucu interaksiyonlarını kullanarak debug etmenizi tavsiye ederiz. Global interaksiyonları spam halinde yayınlarsanız ratelimite takılabilirsiniz.* 73 | 74 | - Olaylar için `events` klasörünün içindeki [`-ornekOlay.js`](./events/-ornekOlay.js) dosyasını kullanabilirsiniz. 75 | - Yeni bir olay dosyası oluşturmak isterseniz `yarn olay` interaksiyonunu kullanabilsiniz. Bu sayede sizi ilk olay altyapısını yazma derdinden kurtaracak ve olay hakkında her türlü soruyu soracaktır. 76 | 77 | # Yardım # 78 | - Bu rehber üzerindeki bilgiler size yetersiz geldiyse ve geliştiricilerden destek almak isterseniz aşağıdaki hesaplar üzerinden ilgili kişilere erişebilirsiniz. 79 | 80 | - Geliştiriciler: `Armagan#4869` & `Erdm#8310` 81 | - Düzenleme ve Destek: `Maschera#6666` 82 | 83 | --- 84 | 85 | # Basit Altyapı (Basic infrastructure) (Version 3.0) (v14.x) 86 | 87 | Developed by professionals with the philosophy "*There will always be something free and valuable in the world.*" open source, up-to-date, stable and advanced Discord bot infrastructure. 88 | 89 | # Features 90 | 91 | - ✅ Plugin system. (Add features to the infrastructure piece by piece!) 92 | - ✅ FULL type support for plugins. 93 | - ✅ Multiple language support. 94 | - ✅ Server specific command sharing types. 95 | - ✅ SelectMenu and Button support. 96 | - ✅ SelectMenu and Button full component and options support. 97 | - ✅ Autocomplate (autocomplete) support. 98 | - ✅ Right menu support. 99 | - ✅ Slash command support. 100 | - ✅ Slash subcommand support. 101 | - ✅ Slash subcommandgroup support. 102 | - ✅ Async and extremely fast (takes up to 1ms per interaction!) 103 | - ✅ Support for variable slowdown per interaction. 104 | - ✅ Bot required authorization support per interaction. 105 | - ✅ User required authorization support per interaction. 106 | - ✅ Customizable error messages. 107 | - ✅ Logic error warning systems. 108 | - ✅ User banning from bot. 109 | - ✅ Autocompletion in interactions. 110 | - ✅ Global Underline object (interactions, events, config, client etc.) 111 | - ✅ Nested folder support. 112 | - ✅ Support for opening and closing interactions. 113 | - ✅ Interaction support only available to developers. 114 | - ✅ General event support. 115 | - ✅ Event auto-completion support. 116 | - ✅ Event closure. 117 | - ✅ Advanced config file. (No need to modify index.js anymore!) 118 | - ✅ Ability to change interaction defaults. 119 | - ✅ Ability to add pre-interaction actions in an easy way. 120 | - ✅ Easily create an interaction or event substructure. 121 | - ✅ Easily disable files (interactions and events with a hyphen (-) are ignored). 122 | - ✅ Ability to move value/reference in Button and Select menus. 123 | - ✅ Ability to specify value before each operation with Other object. 124 | - ✅ Fully automatic language support (automatic determination of the language of the person using the command). 125 | - ✅ 100% sharding support using Redis and Clustering. 80% less ram usage compared to a normal infrastructure! 126 | - ✅ Modal support. 127 | - ✅ Developer mode support. (All type files will be updated automatically.) 128 | 129 | # Installation Diagram 130 | 131 | # Download 132 | - The latest version of the infrastructure [download here](https://github.com/TheArmagan/basit-altyapi/releases/latest). 133 | 134 | # What's needed 135 | - Node.js; `v18.x`. 136 | - Yarn; `npm install -g yarn`. (Yarn is a package manager that we use and is faster and more stable than npm). 137 | 138 | # Installation 139 | - Navigate to the project file location and type `yarn install` and that's enough. 140 | - After clicking on the project [install](#usage) you can start the project by typing `yarn start`. 141 | 142 | # Use 143 | - You can use the config file [`config.js`](./config.js) to edit the general settings of the bot, user error messages and other events. 144 | - For interactions, you can use the [`ornekKomut.js`](./interactions/-ornekKomut.js) and [`ornekSağtık.js`](./interactions/-ornekSağtık.js) files in the `interactions` folder. 145 | - If you want to create a new interaction file, you can use the `yarn interaction` interaction. This will save you the hassle of writing the initial interaction engine and will ask you all kinds of questions about the interaction. 146 | - To publish Slash interactions globally on Discord, you can use the `node publishInteractions.js global` interaction. 147 | - If you want to publish Slash interactions for only one server, you can use the `node publishInteractions.js guild ` interaction. 148 | - To clear all interactions, you can use the `node publishInteractions.js global clear` or `node publishInteractions.js guild clear` interaction. 149 | 150 | Section Information: Global interactions can take about 1 hour to be sent to the servers. Server specific interactions are active within 5-10 seconds. 151 | *Note: When testing your bot, we recommend debugging using server interactions instead of global interactions. If you spam global interactions, you may get stuck in ratelimit. 152 | 153 | - For events you can use the file [`-ornekOlay.js`](./events/-ornekOlay.js) inside the `events` folder. 154 | - If you want to create a new event file, you can use the `yarn event` interaction. This will save you the trouble of writing the initial event engine and will ask you all kinds of questions about the event. 155 | 156 | # Help 157 | - If you find the information in this guide insufficient and would like to get support from the developers, you can reach them through the accounts below. 158 | 159 | - Developers: `Armagan#4869` & `Erdm#8310` 160 | - Editing and Support: `Maschera#6666` 161 | -------------------------------------------------------------------------------- /types/Button.js: -------------------------------------------------------------------------------- 1 | const { ButtonBuilder } = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | const stuffs = require("stuffs"); 4 | 5 | class Button extends Interaction { 6 | /** @param {Interaction.TOmittedInteraction & Interaction.Button} arg */ 7 | constructor (arg = { }) { 8 | super({ 9 | _type: "ComponentInteraction", 10 | actionType: "Button", 11 | ...arg 12 | }) 13 | } 14 | isModal() { return false; } 15 | isButton() { return true; } 16 | /** 17 | * @param {Array} data 18 | * @returns {SelectMenuBuilder} 19 | */ 20 | toJSON(data = []) { 21 | if (!Array.isArray(data)) throw Error(`SelectMenu#toJSON data type must be an array.`); 22 | data = data.map((key) => { 23 | if (typeof key === "string") return key; 24 | if (typeof key === "number") return `π${key}`; 25 | let referenceId = stuffs.randomString(16); 26 | if (key.$key) return key.$key; 27 | key.$key = `¤${referenceId}`; 28 | key.$unRef = () => { 29 | return Underline._references.delete(referenceId); 30 | } 31 | Underline._references.set(referenceId, key); 32 | return key.$key; 33 | }); 34 | data.unshift(this.id); 35 | let customId = data.join("—"); 36 | if (customId.length > 100) throw Error(`SelectMenu#toJSON id and data length must be less than 100.`); 37 | let button = new ButtonBuilder() 38 | .setCustomId(customId) 39 | .setStyle(this.options.style); 40 | if (this.options.emoji) button.setEmoji(this.options.emoji); 41 | if (this.options.label) button.setLabel(this.options.label); 42 | if (this.options.url) button.setURL(this.options.url); 43 | return button; 44 | } 45 | isSelectMenu() { return false; } 46 | isChatActionCommand() { return false; } 47 | isUserActionCommand() { return false; } 48 | isMessageActionCommand() { return false; } 49 | } 50 | 51 | module.exports = Button; 52 | -------------------------------------------------------------------------------- /types/ChatInput.js: -------------------------------------------------------------------------------- 1 | const { CommandInteraction } = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | 4 | class ChatInput extends Interaction { 5 | 6 | /** @param {Interaction.TOmittedInteraction & Interaction.ActionChatCommand} arg */ 7 | constructor (arg = { }) { 8 | super({ 9 | actionType: "ChatInput", 10 | ...arg 11 | }) 12 | } 13 | isChatActionCommand() { return true; } 14 | isSelectMenu() { return false; } 15 | isButton() { return false; } 16 | isUserActionCommand() { return false; } 17 | isMessageActionCommand() { return false; } 18 | isModal() { return false; } 19 | toJSON() {} 20 | } 21 | 22 | module.exports = ChatInput; -------------------------------------------------------------------------------- /types/Config.js: -------------------------------------------------------------------------------- 1 | const Discord = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | class Config { 4 | 5 | /** @private */ 6 | _type = "config"; 7 | 8 | /** @type {string} */ 9 | clientToken = ""; 10 | 11 | /** @type {Number} */ 12 | debugLevel = 0; 13 | 14 | /** @type {Discord.ClientOptions} */ 15 | clientOptions = {}; 16 | 17 | /** @type {"memory"|"redis"} */ 18 | variables = "memory"; 19 | 20 | /** @type {{coolDown(interaction: Discord.CommandInteraction, interaction: Interaction, timeout: number, type: "user" | "member" | "channel" | "guild" | "message" | "any", other: {[key:string|number]: any}): void, disabled(interaction: Discord.CommandInteraction, interaction: Interaction, other: {[key:string|number]: any}): void, blocked(interaction: Discord.CommandInteraction, interaction: Interaction, other: {[key:string|number]: any}): void, botPermsRequired(interaction: Discord.CommandInteraction, interaction: Interaction, perms: string[], other: {setCoolDown(duration:number): void, [key:string|number]: any}): void, userPermsRequired(interaction: Discord.CommandInteraction, interaction: Interaction, perms: string[], other: {setCoolDown(duration:number): void, [key:string|number]: any}): void, developerOnly(interaction: Discord.CommandInteraction, interaction: Interaction, other: {[key:string|number]: any}): void, guildOnly(interaction: Discord.CommandInteraction, interaction: Interaction, other: {[key:string|number]: any}): void, guildOwnerOnly(interaction: Discord.CommandInteraction, interaction: Interaction, other: {[key:string|number]: any}): void}} */ 21 | userErrors = {}; 22 | 23 | /** @type {import("../generated/configOther").default & { redis?: {url: string, key: string} }} */ 24 | other = {}; 25 | 26 | /** @type {{enabled: boolean, count: [number | "auto", number], onManager: (manager: import("discord-hybrid-sharding").Manager) => null}} */ 27 | sharding = { enabled: false, count: "auto", onManager: () => null }; 28 | 29 | /** @type {import("./Interaction").TOmittedInteraction} */ 30 | interactionDefaults = {}; 31 | 32 | /** @type {Set | Array} */ 33 | blockedUsers = new Set(); 34 | 35 | /** @type {Set | Array} */ 36 | developers = new Set(); 37 | 38 | /** @type {import("./Locale").LocaleString} */ 39 | defaultLanguage = "tr"; 40 | 41 | /** @type {(client:import("discord.js").Client)=>void} */ 42 | onBeforeLoad = () => { }; 43 | 44 | /** @type {(client:import("discord.js").Client)=>void} */ 45 | onAfterLoad = () => { }; 46 | 47 | /** @type {(client:import("discord.js").Client)=>void} */ 48 | onReady = () => { }; 49 | 50 | /** @type {(unInter: Interaction, interaction: Discord.CommandInteraction, other: {[key:string|number]: any})=>boolean} */ 51 | onInteractionBeforeChecks = async () => { return true; }; 52 | 53 | /** @type {(unInter: Interaction, interaction: Discord.CommandInteraction, other: {setCoolDown(duration:number): void, [key:string|number]: any})=>boolean} */ 54 | onInteraction = async () => { return true; }; 55 | 56 | /** @type {(unInter: Interaction, interaction: Discord.CommandInteraction, other: {setCoolDown(duration:number): void, [key:string|number]: any})=>boolean} */ 57 | onAfterInteraction = async () => { return true; }; 58 | 59 | /** @type {(eventName: import("./Event").TEventNames, args: [], other: {[key:string|number]: any})=>boolean} */ 60 | onEvent = async () => { return true; }; 61 | 62 | /** @type {(eventName: import("./Event").TEventNames, args: [], other: {[key:string|number]: any})=>any} */ 63 | onAfterEvent = async () => { return; }; 64 | 65 | /** @type {{[key: string|number]: any}} */ 66 | globalObjects = {}; 67 | 68 | /** 69 | * @param {Config} arg 70 | */ 71 | constructor(arg = {}) { 72 | this.debugLevel = arg.debugLevel ?? 0; 73 | 74 | if (typeof arg.clientToken != "string" || arg.clientToken.length == 0) { 75 | console.error("[HATA] Ayarlar dosayasında geçersiz bot tokeni girişi yapılmış."); 76 | process.exit(-1); 77 | }; 78 | 79 | this.clientToken = arg.clientToken; 80 | this.clientOptions = typeof arg.clientOptions == "object" ? arg.clientOptions : {}; 81 | 82 | this.variables = arg.variables || "memory"; 83 | 84 | this.sharding = typeof arg.sharding == "object" ? arg.sharding : { enabled: false, count: "auto" }; 85 | 86 | let messageTypes = [ 87 | "coolDown", 88 | "disabled", 89 | "blocked", 90 | "botPermsRequired", 91 | "userPermsRequired", 92 | "developerOnly", 93 | "guildOnly" 94 | ]; 95 | let loadedMessageTypes = Object.keys(arg.userErrors || {}); 96 | if ( 97 | !messageTypes.every(i => loadedMessageTypes.some(j => j == i)) || 98 | Object.values(arg.userErrors || {}).some(i => typeof i != "function") 99 | ) { 100 | console.error("[HATA] Ayarlar dosyasında kullanıcı hataları (userErrors) kısmı düzgün bir şekilde ayarlanmamış!"); 101 | process.exit(-1); 102 | } 103 | 104 | this.userErrors = arg.userErrors; 105 | this.other = arg.other || {}; 106 | 107 | this.defaultLanguage = arg.defaultLanguage || "tr"; 108 | 109 | this.interactionDefaults = typeof arg.interactionDefaults == "object" ? arg.interactionDefaults : { 110 | actionType: "ChatInput", 111 | description: "...", 112 | developerOnly: false, 113 | guildOnly: true, 114 | disabled: false, 115 | coolDown: -1, 116 | other: {}, 117 | perms: { 118 | bot: [], 119 | user: [] 120 | }, 121 | options: [], 122 | defaultPermission: true, 123 | autoDefer: "off", 124 | nullError: false 125 | }; 126 | 127 | if ( 128 | Array.isArray(arg.blockedUsers) || 129 | arg.blockedUsers instanceof Set 130 | ) this.blockedUsers = new Set([...arg.blockedUsers]); 131 | 132 | if ( 133 | Array.isArray(arg.developers) || 134 | arg.developers instanceof Set 135 | ) this.developers = new Set([...arg.developers]); 136 | 137 | this.globalObjects = arg.globalObjects; 138 | 139 | if (typeof arg.onBeforeLoad == "function") this.onBeforeLoad = arg.onBeforeLoad; 140 | if (typeof arg.onAfterLoad == "function") this.onAfterLoad = arg.onAfterLoad; 141 | if (typeof arg.onReady == "function") this.onReady = arg.onReady; 142 | 143 | if (typeof arg.onInteractionBeforeChecks == "function") this.onInteractionBeforeChecks = arg.onInteractionBeforeChecks; 144 | if (typeof arg.onInteraction == "function") this.onInteraction = arg.onInteraction; 145 | 146 | if (typeof arg.onEvent == "function") this.onEvent = arg.onEvent; 147 | if (typeof arg.onAfterEvent == "function") this.onAfterEvent = arg.onAfterEvent; 148 | } 149 | } 150 | 151 | module.exports = Config; -------------------------------------------------------------------------------- /types/Event.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApplicationInteraction, 3 | Client, 4 | Collection, 5 | DMChannel, 6 | Guild, 7 | GuildBan, 8 | GuildChannel, 9 | GuildEmoji, 10 | GuildMember, 11 | Interaction, 12 | InvalidRequestWarningData, 13 | Invite, 14 | Message, 15 | MessageReaction, 16 | NewsChannel, 17 | PartialDMChannel, 18 | PartialGuildMember, 19 | PartialMessage, 20 | PartialMessageReaction, 21 | PartialUser, 22 | Presence, 23 | RateLimitData, 24 | Role, 25 | Snowflake, 26 | StageInstance, 27 | Sticker, 28 | TextChannel, 29 | ThreadChannel, 30 | ThreadMember, 31 | Typing, 32 | VoiceState, 33 | } from "discord.js"; 34 | import { LocaleData } from "../generated/localeTypes"; 35 | import { PluginAPI } from "./Plugin"; 36 | 37 | export interface IOther { 38 | pluginApi?: PluginAPI; 39 | guildLocale: LocaleData; 40 | [key: string | number]: any 41 | } 42 | 43 | export class BaseEvent { 44 | private _type: string; 45 | 46 | id?: string; 47 | 48 | eventName!: string; 49 | pluginApi?: PluginAPI; 50 | 51 | onLoad?: (client: Client) => void; 52 | 53 | onEvent!: (...args: any[]) => {}; 54 | 55 | disabled?: boolean; 56 | 57 | constructor(arg: Omit & TEvents) 58 | } 59 | 60 | export = BaseEvent; 61 | 62 | export type TEvents = ApplicationInteractionCreateEvent | ApplicationInteractionDeleteEvent | ApplicationInteractionUpdateEvent | ChannelCreateEvent | ChannelDeleteEvent | ChannelPinsUpdateEvent | ChannelUpdateEvent | DebugEvent | WarnEvent | EmojiCreateEvent | EmojiDeleteEvent | EmojiUpdateEvent | ErrorEvent | GuildBanAddEvent | GuildBanRemoveEvent | GuildCreateEvent | GuildDeleteEvent | GuildUnavailableEvent | GuildIntegrationsUpdateEvent | GuildMemberAddEvent | GuildMemberAvailableEvent | GuildMemberRemoveEvent | GuildMembersChunkEvent | GuildMemberUpdateEvent | GuildUpdateEvent | InviteCreateEvent | InviteDeleteEvent | MessageEvent | MessageCreateEvent | MessageDeleteEvent | MessageReactionRemoveAllEvent | MessageReactionRemoveEmojiEvent | MessageDeleteBulkEvent | MessageReactionAddEvent | MessageReactionRemoveEvent | MessageUpdateEvent | PresenceUpdateEvent | RateLimitEvent | InvalidRequestWarningEvent | ReadyEvent | RoleCreateEvent | RoleDeleteEvent | RoleUpdateEvent | ThreadCreateEvent | ThreadDeleteEvent | ThreadListSyncEvent | ThreadMemberUpdateEvent | ThreadMembersUpdateEvent | ThreadUpdateEvent | TypingStartEvent | UserUpdateEvent | VoiceStateUpdateEvent | WebhookUpdateEvent | InteractionEvent | InteractionCreateEvent | ShardDisconnectEvent | ShardErrorEvent | ShardReadyEvent | ShardReconnectingEvent | ShardResumeEvent | StageInstanceCreateEvent | StageInstanceUpdateEvent | StageInstanceDeleteEvent | StickerCreateEvent | StickerDeleteEvent | StickerUpdateEvent | ScheduledEventCreateEvent | ScheduledEventDeleteEvent | ScheduledEventUpdateEvent | ScheduledEventUserAddEvent | import("../generated/pluginTypes").TEvents; 63 | 64 | export type TEventNames = "applicationCommandCreate" | "applicationCommandDelete" | "applicationCommandUpdate" | "channelCreate" | "channelDelete" | "channelPinsUpdate" | "channelUpdate" | "debug" | "emojiCreate" | "emojiDelete" | "emojiUpdate" | "error" | "guildBanAdd" | "guildBanRemove" | "guildCreate" | "guildDelete" | "guildIntegrationsUpdate" | "guildMemberAdd" | "guildMemberAvailable" | "guildMemberRemove" | "guildMembersChunk" | "guildMemberUpdate" | "guildUnavailable" | "guildUpdate" | "interaction" | "interactionCreate" | "invalidated" | "invalidRequestWarning" | "inviteCreate" | "inviteDelete" | "message" | "messageCreate" | "messageDelete" | "messageDeleteBulk" | "messageReactionAdd" | "messageReactionRemove" | "messageReactionRemoveAll" | "messageReactionRemoveEmoji" | "messageUpdate" | "presenceUpdate" | "rateLimit" | "ready" | "roleCreate" | "roleDelete" | "roleUpdate" | "shardDisconnect" | "shardError" | "shardReady" | "shardReconnecting" | "shardResume" | "stageInstanceCreate" | "stageInstanceDelete" | "stageInstanceUpdate" | "stickerCreate" | "stickerDelete" | "stickerUpdate" | "threadCreate" | "threadDelete" | "threadListSync" | "threadMembersUpdate" | "threadMemberUpdate" | "threadUpdate" | "typingStart" | "userUpdate" | "voiceStateUpdate" | "warn" | "webhookUpdate" | "guildScheduledEventCreate" | "guildScheduledEventDelete" | "guildScheduledEventUpdate" | "guildScheduledEventUserAdd" | import("../generated/pluginTypes").TEventNames; 65 | 66 | export interface ApplicationInteractionCreateEvent { eventName: "applicationInteractionCreate"; onEvent: (command: ApplicationInteraction, other: IOther) => any }; 67 | export interface ApplicationInteractionDeleteEvent { eventName: "applicationInteractionDelete"; onEvent: (command: ApplicationInteraction, other: IOther) => any }; 68 | export interface ApplicationInteractionUpdateEvent { eventName: "applicationInteractionUpdate"; onEvent: (oldCommand: ApplicationInteraction | null, newCommand: ApplicationInteraction, other: IOther) => any }; 69 | export interface ChannelCreateEvent { eventName: "channelCreate"; onEvent: (channel: GuildChannel, other: IOther) => any }; 70 | export interface ChannelDeleteEvent { eventName: "channelDelete"; onEvent: (channel: DMChannel | GuildChannel, other: IOther) => any }; 71 | export interface ChannelPinsUpdateEvent { eventName: "channelPinsUpdate"; onEvent: (channel: TextChannel | NewsChannel | DMChannel | PartialDMChannel, date: Date, other: IOther) => any }; 72 | export interface ChannelUpdateEvent { eventName: "channelUpdate"; onEvent: (oldChannel: DMChannel | GuildChannel, newChannel: DMChannel | GuildChannel, other: IOther) => any }; 73 | export interface DebugEvent { eventName: "debug"; onEvent: (message: string, other: IOther) => any }; 74 | export interface WarnEvent { eventName: "warn"; onEvent: (message: string, other: IOther) => any }; 75 | export interface EmojiCreateEvent { eventName: "emojiCreate"; onEvent: (emoji: GuildEmoji, other: IOther) => any }; 76 | export interface EmojiDeleteEvent { eventName: "emojiDelete"; onEvent: (emoji: GuildEmoji, other: IOther) => any }; 77 | export interface EmojiUpdateEvent { eventName: "emojiUpdate"; onEvent: (oldEmoji: GuildEmoji, newEmoji: GuildEmoji, other: IOther) => any }; 78 | export interface ErrorEvent { eventName: "error"; onEvent: (error: Error, other: IOther) => any }; 79 | export interface GuildBanAddEvent { eventName: "guildBanAdd"; onEvent: (ban: GuildBan, other: IOther) => any }; 80 | export interface GuildBanRemoveEvent { eventName: "guildBanRemove"; onEvent: (ban: GuildBan, other: IOther) => any }; 81 | export interface GuildCreateEvent { eventName: "guildCreate"; onEvent: (guild: Guild, other: IOther) => any }; 82 | export interface GuildDeleteEvent { eventName: "guildDelete"; onEvent: (guild: Guild, other: IOther) => any }; 83 | export interface GuildUnavailableEvent { eventName: "guildUnavailable"; onEvent: (guild: Guild, other: IOther) => any }; 84 | export interface GuildIntegrationsUpdateEvent { eventName: "guildIntegrationsUpdate"; onEvent: (guild: Guild, other: IOther) => any }; 85 | export interface GuildMemberAddEvent { eventName: "guildMemberAdd"; onEvent: (member: GuildMember, other: IOther) => any }; 86 | export interface GuildMemberAvailableEvent { eventName: "guildMemberAvailable"; onEvent: (member: GuildMember | PartialGuildMember, other: IOther) => any }; 87 | export interface GuildMemberRemoveEvent { eventName: "guildMemberRemove"; onEvent: (member: GuildMember | PartialGuildMember, other: IOther) => any }; 88 | export interface GuildMembersChunkEvent { eventName: "guildMembersChunk"; onEvent: (members: Collection, guild: Guild, data: { count: number; index: number; nonce: string | undefined }, other: IOther) => any }; 89 | export interface GuildMemberUpdateEvent { eventName: "guildMemberUpdate"; onEvent: (oldMember: GuildMember | PartialGuildMember, newMember: GuildMember, other: IOther) => any }; 90 | export interface GuildUpdateEvent { eventName: "guildUpdate"; onEvent: (oldGuild: Guild, newGuild: Guild, other: IOther) => any }; 91 | export interface InviteCreateEvent { eventName: "inviteCreate"; onEvent: (invite: Invite, other: IOther) => any }; 92 | export interface InviteDeleteEvent { eventName: "inviteDelete"; onEvent: (invite: Invite, other: IOther) => any }; 93 | export interface MessageEvent { eventName: "message"; onEvent: (message: Message, other: IOther) => any }; 94 | export interface MessageCreateEvent { eventName: "messageCreate"; onEvent: (message: Message, other: IOther) => any }; 95 | export interface MessageDeleteEvent { eventName: "messageDelete"; onEvent: (message: Message | PartialMessage, other: IOther) => any }; 96 | export interface MessageReactionRemoveAllEvent { eventName: "messageReactionRemoveAll"; onEvent: (message: Message | PartialMessage, other: IOther) => any }; 97 | export interface MessageReactionRemoveEmojiEvent { eventName: "messageReactionRemoveEmoji"; onEvent: (reaction: MessageReaction | PartialMessageReaction, other: IOther) => any }; 98 | export interface MessageDeleteBulkEvent { eventName: "messageDeleteBulk"; onEvent: (messages: Collection, other: IOther) => any }; 99 | export interface MessageReactionAddEvent { eventName: "messageReactionAdd"; onEvent: (message: MessageReaction | PartialMessageReaction, user: User | PartialUser, other: IOther) => any }; 100 | export interface MessageReactionRemoveEvent { eventName: "messageReactionRemove"; onEvent: (reaction: MessageReaction | PartialMessageReaction, user: User | PartialUser, other: IOther) => any }; 101 | export interface MessageUpdateEvent { eventName: "messageUpdate"; onEvent: (oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage, other: IOther) => any }; 102 | export interface PresenceUpdateEvent { eventName: "presenceUpdate"; onEvent: (oldPresence: Presence | null, newPresence: Presence, other: IOther) => any }; 103 | export interface RateLimitEvent { eventName: "rateLimit"; onEvent: (rateLimitData: RateLimitData, other: IOther) => any }; 104 | export interface InvalidRequestWarningEvent { eventName: "invalidRequestWarning"; onEvent: (invalidRequestWarningData: InvalidRequestWarningData, other: IOther) => any }; 105 | export interface ReadyEvent { eventName: "ready"; onEvent: (client: Client, other: IOther) => any }; 106 | export interface RoleCreateEvent { eventName: "roleCreate"; onEvent: (role: Role, other: IOther) => any }; 107 | export interface RoleDeleteEvent { eventName: "roleDelete"; onEvent: (role: Role, other: IOther) => any }; 108 | export interface RoleUpdateEvent { eventName: "roleUpdate"; onEvent: (oldRole: Role, newRole: Role, other: IOther) => any }; 109 | export interface ThreadCreateEvent { eventName: "threadCreate"; onEvent: (thread: ThreadChannel, other: IOther) => any }; 110 | export interface ThreadDeleteEvent { eventName: "threadDelete"; onEvent: (thread: ThreadChannel, other: IOther) => any }; 111 | export interface ThreadListSyncEvent { eventName: "threadListSync"; onEvent: (threads: Collection, other: IOther) => any }; 112 | export interface ThreadMemberUpdateEvent { eventName: "threadMemberUpdate"; onEvent: (oldMember: ThreadMember, newMember: ThreadMember, other: IOther) => any }; 113 | export interface ThreadMembersUpdateEvent { eventName: "threadMembersUpdate"; onEvent: (oldMembers: Collection, newMembers: Collection, other: IOther) => any }; 114 | export interface ThreadUpdateEvent { eventName: "threadUpdate"; onEvent: (oldThread: ThreadChannel, newThread: ThreadChannel, other: IOther) => any }; 115 | export interface TypingStartEvent { eventName: "typingStart"; onEvent: (typing: Typing, other: IOther) => any }; 116 | export interface UserUpdateEvent { eventName: "userUpdate"; onEvent: (oldUser: User | PartialUser, newUser: User, other: IOther) => any }; 117 | export interface VoiceStateUpdateEvent { eventName: "voiceStateUpdate"; onEvent: (oldState: VoiceState, newState: VoiceState, other: IOther) => any }; 118 | export interface WebhookUpdateEvent { eventName: "webhookUpdate"; onEvent: (channel: TextChannel, other: IOther) => any }; 119 | export interface InteractionEvent { eventName: "interaction"; onEvent: (interaction: Interaction, other: IOther) => any }; 120 | export interface InteractionCreateEvent { eventName: "interactionCreate"; onEvent: (interaction: Interaction, other: IOther) => any }; 121 | export interface ShardDisconnectEvent { eventName: "shardDisconnect"; onEvent: (closeEvent: CloseEvent, shardId: number, other: IOther) => any }; 122 | export interface ShardErrorEvent { eventName: "shardError"; onEvent: (error: Error, shardId: number, other: IOther) => any }; 123 | export interface ShardReadyEvent { eventName: "shardReady"; onEvent: (shardId: number, unavailableGuilds: Set | undefined, other: IOther) => any }; 124 | export interface ShardReconnectingEvent { eventName: "shardReconnecting"; onEvent: (shardId: number, other: IOther) => any }; 125 | export interface ShardResumeEvent { eventName: "shardResume"; onEvent: (shardId: number, replayedEvents: number, other: IOther) => any }; 126 | export interface StageInstanceCreateEvent { eventName: "stageInstanceCreate"; onEvent: (stageInstance: StageInstance, other: IOther) => any }; 127 | export interface StageInstanceUpdateEvent { eventName: "stageInstanceUpdate"; onEvent: (oldStageInstance: StageInstance | null, newStageInstance: StageInstance, other: IOther) => any }; 128 | export interface StageInstanceDeleteEvent { eventName: "stageInstanceDelete"; onEvent: (stageInstance: StageInstance, other: IOther) => any }; 129 | export interface StickerCreateEvent { eventName: "stickerCreate"; onEvent: (sticker: Sticker, other: IOther) => any }; 130 | export interface StickerDeleteEvent { eventName: "stickerDelete"; onEvent: (sticker: Sticker, other: IOther) => any }; 131 | export interface StickerUpdateEvent { eventName: "stickerUpdate"; onEvent: (oldSticker: Sticker, newSticker: Sticker, other: IOther) => any }; 132 | export interface ScheduledEventCreateEvent { eventName: "guildScheduledEventCreate"; onEvent: (event: GuildScheduledEvent<"Scheduled" | "Active" | "Completed" | "Canceled">, other: IOther) => any }; 133 | export interface ScheduledEventDeleteEvent { eventName: "guildScheduledEventDelete"; onEvent: (event: GuildScheduledEvent<"Scheduled" | "Active" | "Completed" | "Canceled">, other: IOther) => any }; 134 | export interface ScheduledEventUpdateEvent { eventName: "guildScheduledEventUpdate"; onEvent: (oldEvent: GuildScheduledEvent<"Scheduled" | "Active" | "Completed" | "Canceled">, newEvent: GuildScheduledEvent<"Scheduled" | "Active" | "Completed" | "Canceled">, other: IOther) => any }; 135 | export interface ScheduledEventUserAddEvent { eventName: "guildScheduledEventUserAdd"; onEvent: (event: GuildScheduledEvent<"Scheduled" | "Active" | "Completed" | "Canceled">, user: User, other: IOther) => any }; -------------------------------------------------------------------------------- /types/Event.js: -------------------------------------------------------------------------------- 1 | class Event { 2 | 3 | /** @private */ 4 | _type = "event"; 5 | 6 | /** @type {string} */ 7 | id = ""; 8 | 9 | /** @type {keyof import("discord.js").ClientEvents} */ 10 | eventName = ""; 11 | 12 | /** 13 | * @param {...any} args 14 | */ 15 | onEvent(...args) { 16 | 17 | } 18 | 19 | /** 20 | * @param {import("discord.js").Client} client 21 | */ 22 | onLoad(client) { 23 | 24 | } 25 | 26 | disabled = false; 27 | 28 | /** 29 | * @param {Omit} arg 30 | */ 31 | constructor(arg) { 32 | this.id = arg.id; 33 | this.eventName = arg.eventName; 34 | this.onEvent = arg.onEvent; 35 | if (typeof arg.onLoad == "function") this.onLoad = arg.onLoad; 36 | this.disabled = Boolean(arg.disabled); 37 | } 38 | } 39 | 40 | module.exports = Event; -------------------------------------------------------------------------------- /types/Interaction.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | // ApplicationCommandChannelOptionData, 3 | // ApplicationCommandChoicesData, 4 | // ApplicationCommandNonOptionsData, 5 | ApplicationCommandChannelOptionData, 6 | ChannelTypeEnumResolvable, 7 | ChannelType, 8 | CommandOptionNonChoiceResolvableType, 9 | CommandOptionChannelResolvableType, 10 | CommandOptionDataTypeResolvable, 11 | CommandOptionChoiceResolvableType, 12 | CommandInteractionOption, 13 | ApplicationCommandOptionChoice, 14 | ApplicationCommandOptionData, 15 | ApplicationCommandType, 16 | AutocompleteInteraction, 17 | CacheType, 18 | Client, 19 | CommandInteraction, 20 | ContextMenuInteraction, 21 | PermissionResolvable, 22 | SelectMenuInteraction, 23 | SelectMenuComponentOptionData, 24 | ButtonInteraction, 25 | SelectMenuBuilder, 26 | ButtonBuilder, 27 | ButtonStyleEnumResolvable, 28 | EmojiResolvable, 29 | ModalSubmitInteraction, 30 | ModalBuilder, 31 | TextInputComponentData, 32 | ApplicationCommandOptionType, 33 | PermissionResolvable, 34 | ButtonStyle, 35 | SelectMenuComponentData, 36 | } from "discord.js"; 37 | import { LocaleData } from "../generated/localeTypes"; 38 | import { PluginAPI } from "./Plugin"; 39 | interface CustomSelectMenuOptions { 40 | min?: number; 41 | max?: number; 42 | choices: SelectMenuComponentOptionData[]; 43 | placeholder?: string; 44 | } 45 | 46 | interface CustomModalOptions { 47 | title?: string; 48 | rows: ( 49 | { type: "TextInput", data: TextInputComponentData } | 50 | { type: "SelectMenu", data: SelectMenuComponentData } 51 | )[][] 52 | } 53 | 54 | interface CustomButtonOptions { 55 | emoji?: EmojiResolvable; 56 | label?: string; 57 | style: ButtonStyleEnumResolvable; 58 | url?: string; 59 | } 60 | 61 | export type CustomApplicationCommandOptionData = { 62 | name: string, 63 | description: string, 64 | choices: { name: string, value: string }[], 65 | type: ApplicationCommandOptionType, 66 | autocomplete: boolean, 67 | required: boolean, 68 | channelTypes: (ChannelType)[], 69 | onComplete( 70 | interaction: AutocompleteInteraction, 71 | value: string | number, 72 | other: IOther 73 | ): { name: string, value: string }[]; 74 | }; 75 | 76 | type Cooldown = { 77 | type: CooldownType; 78 | amount: number; 79 | }; 80 | 81 | type UserPermString = PermissionResolvable | "Developer" | "GuildOwner"; 82 | 83 | export class BaseInteraction { 84 | private _type: string; 85 | name: string[]; 86 | id?: string; 87 | pluginApi?: PluginAPI; 88 | perms?: { bot: PermissionResolvable[]; user: UserPermString[] }; 89 | onInteraction( 90 | interaction: CommandInteraction | ContextMenuInteraction, 91 | other: IOther 92 | ): void; 93 | toJSON( 94 | data: Array 95 | ): ButtonBuilder | SelectMenuBuilder | ModalBuilder | undefined; 96 | publishType?: "globalOnly" | "guildOnly" | "all" | string; 97 | onLoad?(client: Client): void; 98 | coolDowns: Map; 99 | description!: string; 100 | disabled?: boolean; 101 | other?: { [key: string | number]: any }; 102 | coolDown?: Cooldown[] | Cooldown | number; 103 | guildOnly?: boolean; 104 | options?: CustomApplicationCommandOptionData[]; 105 | defaultPermission?: boolean; 106 | actionType?: ApplicationCommandType | "SelectMenu" | "Button" | "Modal"; 107 | autoDefer?: "off" | "on" | "ephemeral"; 108 | nullError?: boolean; 109 | calculated?: { [key: string | number]: any }; 110 | isSelectMenu(): this is import("./SelectMenu"); 111 | isButton(): this is import("./Button"); 112 | isChatActionCommand(): this is import("./ChatInput"); 113 | isUserActionCommand(): this is import("./UserAction"); 114 | isMessageActionCommand(): this is import("./MessageAction"); 115 | constructor(arg: TInteractionConstructor); 116 | } 117 | 118 | export type TOmittedInteraction = Omit< 119 | BaseInteraction, 120 | | "_type" 121 | | "coolDowns" 122 | | "name" 123 | | "onInteraction" 124 | | "actionType" 125 | | "options" 126 | | "toJSON" 127 | | "calculated" 128 | >; 129 | export type TInteractionConstructor = TOmittedInteraction & 130 | (ActionChatCommand | ActionRightClickCommand | SelectMenu | Button); 131 | type CooldownType = "user" | "member" | "channel" | "guild" | "message" | "any"; 132 | export interface IOther { 133 | setCoolDown(durations: number, type: CooldownType): void; 134 | locale: LocaleData; 135 | guildLocale: LocaleData; 136 | pluginApi?: PluginAPI; 137 | data: (string | number | { [string | number]: any; $unRef(): boolean })[]; 138 | [key: string | number]: any; 139 | } 140 | 141 | export interface ActionChatCommand { 142 | name: string[]; 143 | actionType: "ChatInput"; 144 | onInteraction(interaction: CommandInteraction, other: IOther): void; 145 | options: CustomApplicationCommandOptionData[]; 146 | toJSON(): undefined; 147 | } 148 | 149 | export interface ActionRightClickCommand { 150 | name: string; 151 | actionType: "Message" | "User"; 152 | onInteraction(interaction: ContextMenuInteraction, other: IOther): void; 153 | options: undefined; 154 | toJSON(): undefined; 155 | } 156 | 157 | export interface SelectMenu { 158 | name: string; 159 | actionType: "SelectMenu"; 160 | onInteraction(interaction: SelectMenuInteraction, other: IOther): void; 161 | options?: CustomSelectMenuOptions; 162 | toJSON(data: Array): SelectMenuBuilder; 163 | nullError?: Boolean; 164 | } 165 | 166 | export interface Button { 167 | name: string; 168 | actionType: "Button"; 169 | onInteraction(interaction: ButtonInteraction, other: IOther): void; 170 | options?: { 171 | emoji?: EmojiResolvable; 172 | label?: string; 173 | style: ButtonStyle; 174 | url?: string; 175 | }; 176 | toJSON(data: Array): ButtonBuilder; 177 | nullError?: Boolean; 178 | } 179 | 180 | export interface Modal { 181 | name: string; 182 | actionType: "Modal"; 183 | onInteraction(interaction: ModalSubmitInteraction, other: IOther): void; 184 | options?: CustomModalOptions; 185 | toJSON(data: Array): ModalBuilder; 186 | nullError?: Boolean; 187 | } 188 | 189 | export = BaseInteraction; 190 | -------------------------------------------------------------------------------- /types/Interaction.js: -------------------------------------------------------------------------------- 1 | const {defaultify} = require("stuffs"); 2 | 3 | class Interaction { 4 | 5 | /** 6 | * @private 7 | */ 8 | _type = "interaction"; 9 | 10 | name = ""; 11 | 12 | id = ""; 13 | 14 | perms = {bot: [], user: []}; 15 | 16 | onInteraction = () => { }; 17 | 18 | onLoad = () => { }; 19 | 20 | coolDowns = new Map(); 21 | 22 | description = "" 23 | 24 | disabled = false; 25 | 26 | developerOnly = false; 27 | 28 | other = {}; 29 | 30 | coolDown = 0; 31 | 32 | options = []; 33 | 34 | guildOnly = true; 35 | 36 | defaultPermission = true; 37 | 38 | actionType = "ChatInput" 39 | 40 | autoDefer = "off"; 41 | 42 | calculated = {}; 43 | 44 | constructor(arg = {}) { 45 | this.name = Array.isArray(arg.name) ? arg.name : [arg.name]; 46 | this.actionType = arg.actionType || Underline.config.interactionDefaults.actionType; 47 | 48 | this.id = arg.id || `${this.actionType}_${this.name.join("")}`.toLowerCase().replace(/\s+/g, ""); 49 | this._type = arg._type ?? this._type; 50 | this.nullError = (this._type === "ComponentInteraction") ? (arg.nullError ?? false) : false; 51 | this.perms.bot = Array.isArray(arg.perms?.bot) && arg.perms.bot.length != 0 ? arg.perms.bot : Underline.config.interactionDefaults.perms.bot; 52 | this.perms.user = Array.isArray(arg.perms?.user) && arg.perms.user.length != 0 ? arg.perms.user : Underline.config.interactionDefaults.perms.user; 53 | this.onInteraction = arg.onInteraction; 54 | if (typeof arg.onLoad == "function") this.onLoad = arg.onLoad; 55 | this.guildOnly = Boolean(arg.guildOnly ?? Underline.config.interactionDefaults.guildOnly); 56 | this.description = this.actionType == "ChatInput" ? (arg.description || Underline.config.interactionDefaults.description) : null; 57 | this.disabled = Boolean(arg.disabled ?? Underline.config.interactionDefaults.disabled); 58 | this.developerOnly = Boolean(arg.developerOnly ?? Underline.config.interactionDefaults.developerOnly); 59 | this.other = defaultify(typeof arg.other == "object" ? arg.other : {}, Underline.config.interactionDefaults.other); 60 | this.coolDown = arg.coolDown ?? Underline.config.interactionDefaults.coolDown; 61 | this.options = arg.options; 62 | this.publishType = arg.publishType ?? "all"; 63 | this.defaultPermission = Boolean(arg.defaultPermission ?? Underline.config.interactionDefaults.defaultPermission); 64 | this.autoDefer = arg.autoDefer ?? Underline.config.interactionDefaults.autoDefer; 65 | } 66 | } 67 | 68 | module.exports = Interaction; 69 | -------------------------------------------------------------------------------- /types/Locale.d.ts: -------------------------------------------------------------------------------- 1 | export class Locale { 2 | private _type: string; 3 | locale: LocaleString; 4 | private _data: object; 5 | data: Data; 6 | commands: {originalName: string[], description: string, name:string[]}[]; 7 | 8 | constructor(obj: TLocaleConstructor); 9 | } 10 | 11 | export type LocaleString = "en" | "bg" | "zh" | "hr" | "cs" | "da" | "nl" | "fi" | "fr" | "de" | "el" | "hi" | "hu" | "it" | "ja" | "ko" | "no" | "pl" | "pt" | "ro" | "ru" | "es" | "sv" | "th" | "tr" | "uk" | "vi"; 12 | 13 | type TLocaleConstructor = Omit & { data: object }; 14 | 15 | export interface Data { 16 | [key: string]: (...args: any[]) => string; 17 | } 18 | 19 | export = Locale; -------------------------------------------------------------------------------- /types/Locale.js: -------------------------------------------------------------------------------- 1 | const stuffs = require('stuffs'); 2 | class Locale { 3 | _type = "locale"; 4 | locale = "tr"; 5 | _data = {}; 6 | data = {}; 7 | commands = []; 8 | 9 | /** @param {{locale:string,data:object}} obj */ 10 | constructor (obj={}) { 11 | this.locale = obj.locale?.split("-")[0] ?? this.locale; 12 | this._data = obj.data ?? this._data; 13 | this.data = convert(this._data); 14 | this.commands = obj.commands ?? []; 15 | } 16 | } 17 | 18 | function convert(data) { 19 | return Object.fromEntries(Object.entries(data).map(([key, value]) => { 20 | if (typeof value === "string") { 21 | return [key, (...args) => { 22 | return stuffs.mapReplace(value, Object.fromEntries(args.map((t, i) => [`{${i}}`, t]))) 23 | }] 24 | } else { 25 | return [key, convert(value)]; 26 | } 27 | })) 28 | } 29 | 30 | module.exports = Locale; 31 | -------------------------------------------------------------------------------- /types/MemoryVariables.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | 3 | class MemoryVariables { 4 | constructor () { 5 | this.data = {}; 6 | } 7 | 8 | get type() { return "memory"; } 9 | 10 | async get(path, defVal) { 11 | return _.get(this.data, path, defVal); 12 | } 13 | 14 | async set(path, val) { 15 | return this.data = _.set(this.data, path, val); 16 | } 17 | 18 | async unset(path) { 19 | return _.unset(this.data, path); 20 | } 21 | 22 | } 23 | 24 | module.exports = MemoryVariables; -------------------------------------------------------------------------------- /types/MessageAction.js: -------------------------------------------------------------------------------- 1 | const { ContextMenuInteraction } = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | 4 | class MessageAction extends Interaction { 5 | 6 | /** @param {Interaction.TOmittedInteraction & {name: string, onInteraction(interaction: ContextMenuInteraction, other: Interaction.IOther)}} arg */ 7 | constructor (arg = { }) { 8 | super({ 9 | type: "Command", 10 | actionType: "Message", 11 | ...arg 12 | }) 13 | } 14 | isMessageActionCommand() { return true; } 15 | isSelectMenu() { return false; } 16 | isButton() { return false; } 17 | isChatActionCommand() { return false; } 18 | isUserActionCommand() { return false; } 19 | toJSON() {} 20 | } 21 | 22 | module.exports = MessageAction; 23 | -------------------------------------------------------------------------------- /types/Modal.js: -------------------------------------------------------------------------------- 1 | const { ModalBuilder, ActionRowBuilder, TextInputBuilder, SelectMenuBuilder } = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | const stuffs = require("stuffs"); 4 | 5 | class Modal extends Interaction { 6 | /** @param {Interaction.TOmittedInteraction & Interaction.Modal} arg */ 7 | constructor(arg = {}) { 8 | super({ 9 | _type: "ComponentInteraction", 10 | actionType: "Modal", 11 | ...arg 12 | }) 13 | } 14 | isModal() { return true; } 15 | isButton() { return false; } 16 | /** 17 | * @param {Array} data 18 | * @returns {ModalBuilder} 19 | */ 20 | toJSON(data = []) { 21 | if (!Array.isArray(data)) throw Error(`Modal#toJSON data type must be an array.`); 22 | data = data.map((key) => { 23 | if (typeof key === "string") return key; 24 | if (typeof key === "number") return `π${key}`; 25 | let referenceId = stuffs.randomString(16); 26 | if (key.$key) return key.$key; 27 | key.$key = `¤${referenceId}`; 28 | key.$unRef = () => { 29 | return Underline._references.delete(referenceId); 30 | } 31 | Underline._references.set(referenceId, key); 32 | return key.$key; 33 | }); 34 | data.unshift(this.id); 35 | let customId = data.join("—"); 36 | if (customId.length > 100) throw Error(`Modal#toJSON id and data length must be less than 100.`); 37 | let modal = new ModalBuilder() 38 | .setCustomId(customId) 39 | .setTitle(this.options.title || this.id.slice(0, 32)) 40 | 41 | let rows = []; 42 | 43 | for (let i = 0; i < this.options.rows.length; i++) { 44 | let components = []; 45 | /** @type {{type: "TextInput", data: TextInputComponentData}[]} */ 46 | let rawComponents = this.options.rows[i]; 47 | 48 | for (let j = 0; j < rawComponents.length; j++) { 49 | let uComponent = rawComponents[j]; 50 | switch (uComponent.type) { 51 | case "TextInput": { 52 | let input = new TextInputBuilder({ ...uComponent.data, type: 4 }); 53 | components.push(input); 54 | break; 55 | } 56 | case "SelectMenu": { 57 | let input = new SelectMenuBuilder({ ...uComponent.data, type: 3 }); 58 | components.push(input); 59 | break; 60 | } 61 | } 62 | } 63 | 64 | let row = new ActionRowBuilder().addComponents(components); 65 | rows.push(row); 66 | }; 67 | 68 | modal.addComponents(rows); 69 | 70 | return modal; 71 | } 72 | isSelectMenu() { return false; } 73 | isChatActionCommand() { return false; } 74 | isUserActionCommand() { return false; } 75 | isMessageActionCommand() { return false; } 76 | } 77 | 78 | module.exports = Modal; 79 | -------------------------------------------------------------------------------- /types/Plugin.d.ts: -------------------------------------------------------------------------------- 1 | class Plugin { 2 | name: string; 3 | 4 | version: string; 5 | 6 | namespace: string; 7 | 8 | locale: Object; 9 | 10 | requires?: { 11 | modules?: { 12 | [key: string]: string 13 | }, 14 | plugins?: string[], 15 | config?: { [key: string]: any }, 16 | } 17 | 18 | implements?: { 19 | events?: { 20 | [key: string]: string 21 | }, 22 | properties?: { 23 | [key: string]: "number"|"string"|"function"|"array"|"boolean"|string 24 | } 25 | } 26 | 27 | onLoad(api: PluginAPI): void; 28 | 29 | constructor(obj: Plugin): Plugin; 30 | } 31 | 32 | export class PluginAPI { 33 | define(name: string, value: any): void; 34 | emit(name: string, ...args: any[]): void; 35 | setPluginReady(): void; 36 | 37 | onInteractionBeforeChecks(cb: (uInter: import("./Interaction"), interaction: import("discord.js").Interaction, other: import("./Interaction").IOther) => boolean): void; 38 | onInteraction(cb: (uInter: import("./Interaction"), interaction: import("discord.js").Interaction, other: import("./Interaction").IOther)=>boolean): void; 39 | onAfterInteraction(cb: (uInter: import("./Interaction"), interaction: import("discord.js").Interaction, other: import("./Interaction").IOther) => any): void; 40 | 41 | onEvent(cb: (eventName: import("./Event").TEventNames, args: any[], other: import("./Event").IOther) => boolean): void; 42 | onAfterEvent(cb: (eventName: import("./Event").TEventNames, args: any[], other: import("./Event").IOther) => any): void; 43 | 44 | onBotReady(cb: (client: import("discord.js").Client) => any): void; 45 | 46 | client: import("discord.js").Client; 47 | } 48 | 49 | export = Plugin; -------------------------------------------------------------------------------- /types/Plugin.js: -------------------------------------------------------------------------------- 1 | 2 | class Plugin { 3 | _type = "plugin"; 4 | 5 | /** 6 | * @param {} obj 7 | */ 8 | constructor(obj = {}) { 9 | 10 | if (!obj.name) { 11 | console.error("[HATA] Plugin dosyasında isim alanı boş bırakılamaz!"); 12 | process.exit(-1); 13 | } 14 | this.name = obj.name; 15 | this.version = (obj.version || "0.0.1").replace("v", ""); 16 | if (!obj.namespace) { 17 | console.error("[HATA] Plugin dosyasında namespace alanı boş bırakılamaz!"); 18 | process.exit(-1); 19 | } 20 | this.namespace = obj.namespace; 21 | this.requires = obj.requires; 22 | if (obj.requires?.modules) { 23 | let names = Object.keys(obj.requires.modules); 24 | for (let i = 0; i < names.length; i++) { 25 | const moduleName = names[i]; 26 | try { 27 | require(moduleName) 28 | } catch (e) { 29 | console.error(`[HATA] "${obj.name}" adlı plugin, "${moduleName}" (${obj.requires.modules[moduleName]}) adlı modülü istiyor!`); 30 | process.exit(-1); 31 | } 32 | } 33 | } 34 | 35 | this.implements = obj.implements; 36 | this.locale = obj.locale; 37 | this.onLoad = obj.onLoad; 38 | } 39 | } 40 | 41 | 42 | 43 | module.exports = Plugin; -------------------------------------------------------------------------------- /types/RedisVariables.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const { createClient } = require("redis"); 3 | 4 | class RedisVariables { 5 | constructor () { 6 | this.redis = createClient({ 7 | url: Underline.config.other.redis.url 8 | }); 9 | this.redis.connect(); 10 | this.key = Underline.config.other.redis.key; 11 | } 12 | 13 | get type() { return "redis"; } 14 | 15 | async get(path, defVal) { 16 | let firstKey = Object.keys(_.set({}, path, 1))[0]; 17 | let redisKey = `${this.key}:${firstKey}`; 18 | let data = JSON.parse(await this.redis.get(redisKey)); 19 | let value = _.get(data, path) 20 | if (!value) { 21 | value = defVal; 22 | await this.set(path, defVal); 23 | } 24 | return value; 25 | } 26 | 27 | async set(path, val) { 28 | let firstKey = Object.keys(_.set({}, path, 1))[0]; 29 | let redisKey = `${this.key}:${firstKey}`; 30 | let data = JSON.parse(await this.redis.get(redisKey)); 31 | data = _.set(data, path, val); 32 | return await this.redis.set(redisKey, JSON.stringify(data)); 33 | } 34 | 35 | async unset(path) { 36 | let firstKey = Object.keys(_.set({}, path, 1))[0]; 37 | let redisKey = `${this.key}:${firstKey}`; 38 | let data = JSON.parse(await this.redis.get(redisKey)); 39 | _.unset(data, path); 40 | if (typeof data[firstKey] == "undefined") { 41 | return await this.redis.del(redisKey); 42 | } else { 43 | return await this.redis.set(redisKey, JSON.stringify(data)); 44 | } 45 | } 46 | } 47 | 48 | module.exports = RedisVariables; -------------------------------------------------------------------------------- /types/SelectMenu.js: -------------------------------------------------------------------------------- 1 | const { SelectMenuBuilder } = require("discord.js"); 2 | const Interaction = require("./Interaction"); 3 | const stuffs = require("stuffs"); 4 | 5 | class SelectMenu extends Interaction { 6 | /** @param {Interaction.TOmittedInteraction & Interaction.SelectMenu} arg */ 7 | constructor (arg = {}) { 8 | super({ 9 | _type: "ComponentInteraction", 10 | actionType: "SelectMenu", 11 | ...arg 12 | }) 13 | } 14 | isSelectMenu() { return true; } 15 | isButton() { return false; } 16 | isChatActionCommand() { return false; } 17 | isUserActionCommand() { return false; } 18 | isModal() { return false; } 19 | isMessageActionCommand() { return false; } 20 | /** 21 | * @param {Array} data 22 | * @returns {SelectMenuBuilder} 23 | */ 24 | toJSON(data = []) { 25 | if (!Array.isArray(data)) throw Error(`SelectMenu#toJSON data type must be an array.`); 26 | data = data.map((key) => { 27 | if (typeof key === "string") return key; 28 | if (typeof key === "number") return `π${key}`; 29 | if (key.$key) return key.$key; 30 | let referenceId = stuffs.randomString(16); 31 | key.$unRef = () => { 32 | return Underline._references.delete(referenceId); 33 | } 34 | key.$key = `¤${referenceId}`; 35 | Underline._references.set(referenceId, key); 36 | return key.$key; 37 | }); 38 | data.unshift(this.id); 39 | let customId = data.join("—"); 40 | if (customId.length > 100) throw Error(`SelectMenu#toJSON id and data length must be less than 100.`); 41 | let menu = new SelectMenuBuilder() 42 | .addOptions(this.options?.choices ?? []) 43 | .setMinValues(this.options.min ?? 1) 44 | .setMaxValues(this.options.max ?? this.options.choices.length) 45 | .setCustomId(customId); 46 | if(this.options.placeholder) menu.setPlaceholder(this.options.placeholder) 47 | return menu; 48 | } 49 | } 50 | 51 | module.exports = SelectMenu; 52 | 53 | -------------------------------------------------------------------------------- /types/UserAction.js: -------------------------------------------------------------------------------- 1 | const Interaction = require("./Interaction"); 2 | 3 | class MessageAction extends Interaction { 4 | 5 | /** @param {Interaction.TOmittedInteraction & Interaction.ActionRightClickCommand} arg */ 6 | constructor (arg = { }) { 7 | super({ 8 | type: "Command", 9 | actionType: "User", 10 | ...arg 11 | }) 12 | } 13 | isUserActionCommand() { return true; } 14 | isSelectMenu() { return false; } 15 | isButton() { return false; } 16 | isChatActionCommand() { return false; } 17 | isMessageActionCommand() { return false; } 18 | toJSON() {} 19 | } 20 | 21 | module.exports = MessageAction; 22 | -------------------------------------------------------------------------------- /v14-Upgrader.js: -------------------------------------------------------------------------------- 1 | // const { Embed, ButtonComponent, SelectMenuComponent } = require("discord.js"); 2 | const { readFileSync, writeFileSync } = require("fs"); 3 | const readdirRecursive = require("recursive-readdir"); 4 | 5 | readdirRecursive(process.cwd()).then(async (paths) => { 6 | paths = paths.filter(x => !x.includes("jsconfig") && !x.includes("package") && !x.includes("LICENSE") && !x.includes("v14-") && !x.includes("yarn.lock") && !x.includes("node_modules") && !x.includes(".git")); 7 | firstLoop: for (let i = 0; i < paths.length; i++) { 8 | let path = paths[i]; 9 | let content = readFileSync(path).toString(); 10 | let upperCaseList = content.match(/\"[A-Z_]+\"|\'[A-Z_]+\'|\`[A-Z_]+\`/g); 11 | let fixSize = 0; 12 | secondLoop: for (let j = 0; j < upperCaseList?.length; j++) { 13 | fixSize++; 14 | let oldWord = upperCaseList[j]; 15 | let newWord = "\"" + upperToCamel(oldWord.match(/[A-Z_]+/)?.shift()) + "\""; 16 | content = content.replace(oldWord, newWord); 17 | } 18 | content = content.replace(/PermissionString/g, "PermissionResolvable"); 19 | content = content.replace(/MessageEmbed|EmbedBuilder|Embed/g, "EmbedBuilder"); 20 | content = content.replace(/MessageButton|ButtonBuilder|ButtonComponent/g, "ButtonBuilder"); 21 | content = content.replace(/MessageSelectMenu|SelectMenuBuilder|SelectMenuComponent/g, "SelectMenuBuilder"); 22 | writeFileSync(path, content); 23 | console.log(path.replace(process.cwd(), ""), `${i}/${paths.length}`, fixSize) 24 | } 25 | 26 | }); 27 | 28 | function upperToCamel(word) { 29 | 30 | let nextUp = true; 31 | let newWord = ""; 32 | for (let i = 0; i < word.length; i++) { 33 | 34 | let char = word[i]; 35 | 36 | if (char == "_") { 37 | nextUp = true; 38 | } else if (nextUp) { 39 | newWord += char.toUpperCase(); 40 | nextUp = false; 41 | } else { 42 | newWord += char.toLowerCase(); 43 | } 44 | 45 | } 46 | return newWord 47 | } -------------------------------------------------------------------------------- /watchChanges.js: -------------------------------------------------------------------------------- 1 | { require("./other/patchConsoleLog"); require("./config"); }; 2 | 3 | const chokidar = require('chokidar'); 4 | const { resolve } = require('path'); 5 | const { makeSureFolderExists, execAsync } = require('stuffs'); 6 | 7 | class FunctionQueue { 8 | /** 9 | * 10 | * @param {Number} interval 11 | * @param {Function} cb 12 | */ 13 | constructor(interval, cb) { 14 | this.interval = interval; 15 | this.cb = cb; 16 | this.lastCall = 0; 17 | this.willTrigger = false; 18 | } 19 | 20 | async trigger(...args) { 21 | 22 | let now = Date.now(); 23 | 24 | if (this.lastCall < now) { 25 | this.lastCall = now + this.interval; 26 | await this.cb(...args); 27 | } else { 28 | if (this.lastCall < now - 1500) return; 29 | if (!this.willTrigger) { 30 | this.willTrigger = true; 31 | setTimeout(async () => { 32 | this.lastCall = Date.now() + this.interval; 33 | this.willTrigger = false; 34 | await this.cb(...args); 35 | }, this.lastCall - now); 36 | } 37 | } 38 | } 39 | }; 40 | let TypeQueue = new FunctionQueue(15000, async () => { 41 | let { stderr, stdout } = await execAsync("yarn tipler", process.cwd()); 42 | console.log(stdout.toLowerCase().includes("[hata]") ? stdout : "Tipler yüklendi."); 43 | }); 44 | let wait = (s) => new Promise((resolve => setTimeout(() => resolve(true), s))); 45 | (async () => { 46 | await wait(1000); 47 | 48 | [ 49 | { 50 | path: "./events", 51 | name: "Events", 52 | text: "Eventler izleniyor.", 53 | }, 54 | { 55 | path: "./interactions", 56 | name: "Interactions", 57 | text: 'Interaksiyonlar izleniyor', 58 | }, 59 | { 60 | path: "./plugins", 61 | name: "Plugins", 62 | text: 'Pluginler izleniyor', 63 | }, 64 | { 65 | path: "./locales", 66 | name: "Locales", 67 | text: 'Dil dosyaları izleniyor', 68 | } 69 | ].forEach(async (ctx) => { 70 | let path = resolve(ctx.path); 71 | await makeSureFolderExists(path); 72 | const theWatcher = chokidar.watch(path, { persistent: true }); 73 | theWatcher 74 | .on('add', () => TypeQueue.trigger()) 75 | .on('change', () => TypeQueue.trigger()) 76 | .on('unlink', () => TypeQueue.trigger()) 77 | .on('error', error => console.log(`${ctx.name} Watcher Error: ${error}`)) 78 | .on('ready', () => console.log(ctx.text)) 79 | }); 80 | })(); --------------------------------------------------------------------------------