├── .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 | })();
--------------------------------------------------------------------------------