From 30da26504fb119fdc2aa70db664d8f0dd75219c3 Mon Sep 17 00:00:00 2001 From: flifloo Date: Fri, 25 Aug 2023 00:16:33 +0200 Subject: [PATCH] Total rework of the bot, starting base --- .gitignore | 119 +- LICENSE | 687 +++++++++- README.md | 2 +- administrator/Administrator.py | 27 + administrator/__init__.py | 0 administrator/__main__.py | 33 + administrator/db/UserSettings.py | 14 + administrator/db/__init__.py | 18 + administrator/modules/__init__.py | 0 administrator/modules/test/__init__.py | 15 + administrator/settings/__init__.py | 7 + administrator/settings/global_settings.py | 4 + config.json.example | 3 - package-lock.json | 1272 ------------------ package.json | 41 - requirements.txt | 3 + setup.py | 21 + src/index.ts | 47 - src/lib/AdministratorClient.ts | 8 - src/lib/Command.ts | 17 - src/lib/Component.ts | 21 - src/lib/Event.ts | 5 - src/lib/Logger.ts | 77 -- src/lib/Module.ts | 82 -- src/lib/Modules.ts | 115 -- src/modules/Music/commands/disconnect.ts | 37 - src/modules/Music/commands/flush.ts | 41 - src/modules/Music/commands/pause.ts | 42 - src/modules/Music/commands/play.ts | 71 - src/modules/Music/commands/queue.ts | 66 - src/modules/Music/commands/resume.ts | 42 - src/modules/Music/commands/skip.ts | 42 - src/modules/Music/commands/stop.ts | 41 - src/modules/Music/events/VoiceStateUpdate.ts | 36 - src/modules/Music/index.ts | 14 - src/modules/Music/lib/Player.ts | 131 -- src/modules/Music/lib/Track.ts | 79 -- src/modules/Utils/commands/about.ts | 31 - src/modules/Utils/commands/info.ts | 95 -- src/modules/Utils/commands/ping.ts | 20 - src/modules/Utils/index.ts | 9 - tsconfig.json | 15 - 42 files changed, 819 insertions(+), 2631 deletions(-) create mode 100644 administrator/Administrator.py create mode 100644 administrator/__init__.py create mode 100644 administrator/__main__.py create mode 100644 administrator/db/UserSettings.py create mode 100644 administrator/db/__init__.py create mode 100644 administrator/modules/__init__.py create mode 100644 administrator/modules/test/__init__.py create mode 100644 administrator/settings/__init__.py create mode 100644 administrator/settings/global_settings.py delete mode 100644 config.json.example delete mode 100644 package-lock.json delete mode 100644 package.json create mode 100644 requirements.txt create mode 100644 setup.py delete mode 100644 src/index.ts delete mode 100644 src/lib/AdministratorClient.ts delete mode 100644 src/lib/Command.ts delete mode 100644 src/lib/Component.ts delete mode 100644 src/lib/Event.ts delete mode 100644 src/lib/Logger.ts delete mode 100644 src/lib/Module.ts delete mode 100644 src/lib/Modules.ts delete mode 100644 src/modules/Music/commands/disconnect.ts delete mode 100644 src/modules/Music/commands/flush.ts delete mode 100644 src/modules/Music/commands/pause.ts delete mode 100644 src/modules/Music/commands/play.ts delete mode 100644 src/modules/Music/commands/queue.ts delete mode 100644 src/modules/Music/commands/resume.ts delete mode 100644 src/modules/Music/commands/skip.ts delete mode 100644 src/modules/Music/commands/stop.ts delete mode 100644 src/modules/Music/events/VoiceStateUpdate.ts delete mode 100644 src/modules/Music/index.ts delete mode 100644 src/modules/Music/lib/Player.ts delete mode 100644 src/modules/Music/lib/Track.ts delete mode 100644 src/modules/Utils/commands/about.ts delete mode 100644 src/modules/Utils/commands/info.ts delete mode 100644 src/modules/Utils/commands/ping.ts delete mode 100644 src/modules/Utils/index.ts delete mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 9aa7638..340d611 100644 --- a/.gitignore +++ b/.gitignore @@ -1,113 +1,6 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# JetBrain stuff -.idea - -# TypeScript compiled sources -build/ - -# Configuration -config.json +/.vscode +/venv +/administrator/settings/local_settings.py +__pycache__ +/discord.log +/*.db diff --git a/LICENSE b/LICENSE index 7264410..b3c3815 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,674 @@ -MIT License + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2021 Florian Charlaix + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Preamble -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 2023 Ethanell + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Administrator Copyright (C) 2023 Ethanell + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 1f1ea4a..5c042d2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # Administrator -A Discord bot for administration +All purpose Discord bot diff --git a/administrator/Administrator.py b/administrator/Administrator.py new file mode 100644 index 0000000..59cb541 --- /dev/null +++ b/administrator/Administrator.py @@ -0,0 +1,27 @@ +from sqlalchemy.ext.asyncio import AsyncSession + +from discord.ext.commands import Bot +from discord import Object + + +class Administrator(Bot): + def __init__(self, *args, settings, session_maker, **kwargs): + super().__init__(*args, **kwargs) + self.settings = settings + self.session_maker = session_maker + + async def setup_hook(self) -> None: + for extension in self.settings.initial_extensions: + await self.load_extension(extension) + + if self.settings.testing_guild_id: + guild = Object(self.settings.testing_guild_id) + self.tree.copy_global_to(guild=guild) + await self.tree.sync(guild=guild) + + @property + def session(self) -> AsyncSession: + return self.session_maker() + + async def on_ready(self): + print(f'We have logged in as {self.user}') diff --git a/administrator/__init__.py b/administrator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/administrator/__main__.py b/administrator/__main__.py new file mode 100644 index 0000000..6188b8a --- /dev/null +++ b/administrator/__main__.py @@ -0,0 +1,33 @@ +import asyncio +from logging import getLogger, Formatter, INFO +from logging.handlers import RotatingFileHandler + +from discord import Intents +from discord.ext.commands import when_mentioned + +import settings +from Administrator import Administrator +from db import init as init_db + + +async def main(): + logger = getLogger('discord') + logger.setLevel(INFO) + + handler = RotatingFileHandler( + filename='discord.log', + encoding='utf-8', + maxBytes=32 * 1024 * 1024, # 32 MiB + backupCount=5, # Rotate through 5 files + ) + dt_fmt = '%Y-%m-%d %H:%M:%S' + formatter = Formatter('[{asctime}] [{levelname:<8}] {name}: {message}', dt_fmt, style='{') + handler.setFormatter(formatter) + logger.addHandler(handler) + + session_maker = await init_db(settings) + + async with Administrator(when_mentioned, intents=Intents.default(), settings=settings, session_maker=session_maker) as bot: + await bot.start(settings.token) + +asyncio.run(main()) diff --git a/administrator/db/UserSettings.py b/administrator/db/UserSettings.py new file mode 100644 index 0000000..fe921cd --- /dev/null +++ b/administrator/db/UserSettings.py @@ -0,0 +1,14 @@ +from sqlalchemy import Column +from sqlalchemy import Integer +from sqlalchemy import String +from sqlalchemy.orm import relationship + +from . import Base + + +class UserSettings(Base): + __tablename__ = "user_settings" + id = Column(Integer, primary_key=True) + uid = Column(String(30)) + app = Column(String(30)) + value = Column(String(30)) diff --git a/administrator/db/__init__.py b/administrator/db/__init__.py new file mode 100644 index 0000000..b4ffa22 --- /dev/null +++ b/administrator/db/__init__.py @@ -0,0 +1,18 @@ +from sqlalchemy.orm import declarative_base +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.ext.asyncio import create_async_engine +from sqlalchemy.orm import sessionmaker + + +Base = declarative_base() + + +from . import UserSettings + + +async def init(settings): + engine = create_async_engine(settings.db_url) + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + + return sessionmaker(engine, class_=AsyncSession) diff --git a/administrator/modules/__init__.py b/administrator/modules/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/administrator/modules/test/__init__.py b/administrator/modules/test/__init__.py new file mode 100644 index 0000000..e807b01 --- /dev/null +++ b/administrator/modules/test/__init__.py @@ -0,0 +1,15 @@ +from discord import Interaction, app_commands, Object +from discord.ext.commands import Cog, Bot + + +class Test(Cog): + def __init__(self, bot: Bot) -> None: + self.bot = bot + + @app_commands.command(name="ping") + async def ping(self, interaction: Interaction) -> None: + await interaction.response.send_message("Pong !", ephemeral=True) + + +async def setup(bot: Bot) -> None: + await bot.add_cog(Test(bot)) diff --git a/administrator/settings/__init__.py b/administrator/settings/__init__.py new file mode 100644 index 0000000..1a3a475 --- /dev/null +++ b/administrator/settings/__init__.py @@ -0,0 +1,7 @@ +from pathlib import Path +from .global_settings import * + +local_settings = Path(__file__).parent / Path(f"local_settings.py") + +if local_settings.exists(): + from .local_settings import * diff --git a/administrator/settings/global_settings.py b/administrator/settings/global_settings.py new file mode 100644 index 0000000..93e9449 --- /dev/null +++ b/administrator/settings/global_settings.py @@ -0,0 +1,4 @@ +token = None +db_url = "sqlite+aiosqlite:///:memory" +initial_extensions = ["modules.test"] +testing_guild_id = None diff --git a/config.json.example b/config.json.example deleted file mode 100644 index de197e3..0000000 --- a/config.json.example +++ /dev/null @@ -1,3 +0,0 @@ -{ - "token": "" -} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 48079fc..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1272 +0,0 @@ -{ - "name": "administrator", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "administrator", - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "@discordjs/builders": "^0.6.0", - "@discordjs/rest": "^0.1.0-canary.0", - "@discordjs/voice": "^0.7.5", - "colors": "^1.4.0", - "discord-api-types": "^0.23.1", - "discord.js": "^13.1.0", - "libsodium-wrappers": "^0.7.9", - "youtube-dl-exec": "^2.0.0", - "ytdl-core": "^4.9.1" - }, - "devDependencies": { - "typescript": "^4.5.2" - } - }, - "node_modules/@discordjs/builders": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.6.0.tgz", - "integrity": "sha512-mH3Gx61LKk2CD05laCI9K5wp+a3NyASHDUGx83DGJFkqJlRlSV5WMJNY6RS37A5SjqDtGMF4wVR9jzFaqShe6Q==", - "dependencies": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@discordjs/builders/node_modules/discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/@discordjs/collection": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz", - "integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@discordjs/rest": { - "version": "0.1.0-canary.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz", - "integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==", - "dependencies": { - "@discordjs/collection": "^0.1.6", - "@sapphire/async-queue": "^1.1.4", - "@sapphire/snowflake": "^1.3.5", - "abort-controller": "^3.0.0", - "discord-api-types": "^0.18.1", - "form-data": "^4.0.0", - "node-fetch": "^2.6.1", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@discordjs/rest/node_modules/@discordjs/collection": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", - "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" - }, - "node_modules/@discordjs/rest/node_modules/discord-api-types": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz", - "integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg==", - "deprecated": "No longer supported. Install the latest release (0.20.2)", - "engines": { - "node": ">=12" - } - }, - "node_modules/@discordjs/voice": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.7.5.tgz", - "integrity": "sha512-lUk+CmIXNKslT6DkC9IF9rpsqhzlTiedauUCPBzepjd4XWxwBZiyVIzR6QpbAirxkAwCoAbbje+3Ho71PGLEAw==", - "dependencies": { - "@types/ws": "^8.2.0", - "discord-api-types": "^0.24.0", - "prism-media": "^1.3.2", - "tiny-typed-emitter": "^2.1.0", - "tslib": "^2.3.1", - "ws": "^8.2.3" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/@discordjs/voice/node_modules/@types/ws": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.0.tgz", - "integrity": "sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@discordjs/voice/node_modules/discord-api-types": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.24.0.tgz", - "integrity": "sha512-X0uA2a92cRjowUEXpLZIHWl4jiX1NsUpDhcEOpa1/hpO1vkaokgZ8kkPtPih9hHth5UVQ3mHBu/PpB4qjyfJ4A==", - "engines": { - "node": ">=12" - } - }, - "node_modules/@discordjs/voice/node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@sapphire/async-queue": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz", - "integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==", - "engines": { - "node": ">=14", - "npm": ">=6" - } - }, - "node_modules/@sapphire/snowflake": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz", - "integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg==", - "deprecated": "This version has been automatically deprecated by @favware/npm-deprecate. Please use a newer version.", - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@types/node": { - "version": "16.9.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", - "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==" - }, - "node_modules/@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/discord-api-types": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.23.1.tgz", - "integrity": "sha512-igWmn+45mzXRWNEPU25I/pr8MwxHb767wAr51oy3VRLRcTlp5ADBbrBR0lq3SA1Rfw3MtM4TQu1xo3kxscfVdQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/discord.js": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz", - "integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==", - "dependencies": { - "@discordjs/builders": "^0.5.0", - "@discordjs/collection": "^0.2.1", - "@discordjs/form-data": "^3.0.1", - "@sapphire/async-queue": "^1.1.4", - "@types/ws": "^7.4.7", - "discord-api-types": "^0.22.0", - "node-fetch": "^2.6.1", - "ws": "^7.5.1" - }, - "engines": { - "node": ">=16.6.0", - "npm": ">=7.0.0" - } - }, - "node_modules/discord.js/node_modules/@discordjs/builders": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz", - "integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==", - "dependencies": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.0" - }, - "engines": { - "node": ">=14.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/discord.js/node_modules/discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unix": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-unix/-/is-unix-2.0.1.tgz", - "integrity": "sha512-RyKp5JtlRnfOvnKtfBMPLw9ocqDe1NlPQ8Bt+geVzKGfMnLGj8z/Y2HOmk/aMf47P4EbrEt9dN6YGTT1fx4mZA==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/libsodium": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz", - "integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==" - }, - "node_modules/libsodium-wrappers": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz", - "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==", - "dependencies": { - "libsodium": "^0.7.0" - } - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "node_modules/m3u8stream": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.8.4.tgz", - "integrity": "sha512-sco80Db+30RvcaIOndenX6E6oQNgTiBKeJbFPc+yDXwPQIkryfboEbCvXPlBRq3mQTCVPQO93TDVlfRwqpD35w==", - "dependencies": { - "miniget": "^4.0.0", - "sax": "^1.2.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", - "dependencies": { - "mime-db": "1.49.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/miniget": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/miniget/-/miniget-4.2.1.tgz", - "integrity": "sha512-O/DduzDR6f+oDtVype9S/Qu5hhnx73EDYGyZKwU/qN82lehFZdfhoa4DT51SpsO+8epYrB3gcRmws56ROfTIoQ==", - "engines": { - "node": ">=10" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ow": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz", - "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==", - "dependencies": { - "@sindresorhus/is": "^4.0.1", - "callsites": "^3.1.0", - "dot-prop": "^6.0.1", - "lodash.isequal": "^4.5.0", - "type-fest": "^1.2.1", - "vali-date": "^1.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/prism-media": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.2.tgz", - "integrity": "sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==", - "peerDependencies": { - "@discordjs/opus": "^0.5.0", - "ffmpeg-static": "^4.2.7 || ^3.0.0 || ^2.4.0", - "node-opus": "^0.3.3", - "opusscript": "^0.0.8" - }, - "peerDependenciesMeta": { - "@discordjs/opus": { - "optional": true - }, - "ffmpeg-static": { - "optional": true - }, - "node-opus": { - "optional": true - }, - "opusscript": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/tiny-typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "node_modules/ts-mixer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz", - "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ==" - }, - "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/youtube-dl-exec": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/youtube-dl-exec/-/youtube-dl-exec-2.0.0.tgz", - "integrity": "sha512-wD5BSkxC1o3rzj+1ktXdWObvkorW3Zvl1E+l56JQqYFTek3d9SR7o1RbjLiQZYQpXpoCPw0zV16SNDXjhVksXQ==", - "hasInstallScript": true, - "dependencies": { - "dargs": "~7.0.0", - "execa": "~5.1.0", - "is-unix": "~2.0.1", - "mkdirp": "~1.0.4", - "node-fetch": "~2.6.5" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ytdl-core": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.9.1.tgz", - "integrity": "sha512-6Jbp5RDhUEozlaJQAR+l8oV8AHsx3WUXxSyPxzE6wOIAaLql7Hjiy0ZM58wZoyj1YEenlEPjEqcJIjKYKxvHtQ==", - "dependencies": { - "m3u8stream": "^0.8.3", - "miniget": "^4.0.0", - "sax": "^1.1.3" - }, - "engines": { - "node": ">=10" - } - } - }, - "dependencies": { - "@discordjs/builders": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.6.0.tgz", - "integrity": "sha512-mH3Gx61LKk2CD05laCI9K5wp+a3NyASHDUGx83DGJFkqJlRlSV5WMJNY6RS37A5SjqDtGMF4wVR9jzFaqShe6Q==", - "requires": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.1" - }, - "dependencies": { - "discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==" - } - } - }, - "@discordjs/collection": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.2.1.tgz", - "integrity": "sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==" - }, - "@discordjs/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "@discordjs/rest": { - "version": "0.1.0-canary.0", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz", - "integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==", - "requires": { - "@discordjs/collection": "^0.1.6", - "@sapphire/async-queue": "^1.1.4", - "@sapphire/snowflake": "^1.3.5", - "abort-controller": "^3.0.0", - "discord-api-types": "^0.18.1", - "form-data": "^4.0.0", - "node-fetch": "^2.6.1", - "tslib": "^2.3.0" - }, - "dependencies": { - "@discordjs/collection": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz", - "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==" - }, - "discord-api-types": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz", - "integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg==" - } - } - }, - "@discordjs/voice": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.7.5.tgz", - "integrity": "sha512-lUk+CmIXNKslT6DkC9IF9rpsqhzlTiedauUCPBzepjd4XWxwBZiyVIzR6QpbAirxkAwCoAbbje+3Ho71PGLEAw==", - "requires": { - "@types/ws": "^8.2.0", - "discord-api-types": "^0.24.0", - "prism-media": "^1.3.2", - "tiny-typed-emitter": "^2.1.0", - "tslib": "^2.3.1", - "ws": "^8.2.3" - }, - "dependencies": { - "@types/ws": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.0.tgz", - "integrity": "sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==", - "requires": { - "@types/node": "*" - } - }, - "discord-api-types": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.24.0.tgz", - "integrity": "sha512-X0uA2a92cRjowUEXpLZIHWl4jiX1NsUpDhcEOpa1/hpO1vkaokgZ8kkPtPih9hHth5UVQ3mHBu/PpB4qjyfJ4A==" - }, - "ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "requires": {} - } - } - }, - "@sapphire/async-queue": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.4.tgz", - "integrity": "sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==" - }, - "@sapphire/snowflake": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz", - "integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg==" - }, - "@sindresorhus/is": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz", - "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==" - }, - "@types/node": { - "version": "16.9.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.6.tgz", - "integrity": "sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ==" - }, - "@types/ws": { - "version": "7.4.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz", - "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==", - "requires": { - "@types/node": "*" - } - }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==" - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "discord-api-types": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.23.1.tgz", - "integrity": "sha512-igWmn+45mzXRWNEPU25I/pr8MwxHb767wAr51oy3VRLRcTlp5ADBbrBR0lq3SA1Rfw3MtM4TQu1xo3kxscfVdQ==" - }, - "discord.js": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.1.0.tgz", - "integrity": "sha512-gxO4CXKdHpqA+WKG+f5RNnd3srTDj5uFJHgOathksDE90YNq/Qijkd2WlMgTTMS6AJoEnHxI7G9eDQHCuZ+xDA==", - "requires": { - "@discordjs/builders": "^0.5.0", - "@discordjs/collection": "^0.2.1", - "@discordjs/form-data": "^3.0.1", - "@sapphire/async-queue": "^1.1.4", - "@types/ws": "^7.4.7", - "discord-api-types": "^0.22.0", - "node-fetch": "^2.6.1", - "ws": "^7.5.1" - }, - "dependencies": { - "@discordjs/builders": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.5.0.tgz", - "integrity": "sha512-HP5y4Rqw68o61Qv4qM5tVmDbWi4mdTFftqIOGRo33SNPpLJ1Ga3KEIR2ibKofkmsoQhEpLmopD1AZDs3cKpHuw==", - "requires": { - "@sindresorhus/is": "^4.0.1", - "discord-api-types": "^0.22.0", - "ow": "^0.27.0", - "ts-mixer": "^6.0.0", - "tslib": "^2.3.0" - } - }, - "discord-api-types": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.22.0.tgz", - "integrity": "sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==" - } - } - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" - }, - "is-unix": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-unix/-/is-unix-2.0.1.tgz", - "integrity": "sha512-RyKp5JtlRnfOvnKtfBMPLw9ocqDe1NlPQ8Bt+geVzKGfMnLGj8z/Y2HOmk/aMf47P4EbrEt9dN6YGTT1fx4mZA==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "libsodium": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.9.tgz", - "integrity": "sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==" - }, - "libsodium-wrappers": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz", - "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==", - "requires": { - "libsodium": "^0.7.0" - } - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "m3u8stream": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/m3u8stream/-/m3u8stream-0.8.4.tgz", - "integrity": "sha512-sco80Db+30RvcaIOndenX6E6oQNgTiBKeJbFPc+yDXwPQIkryfboEbCvXPlBRq3mQTCVPQO93TDVlfRwqpD35w==", - "requires": { - "miniget": "^4.0.0", - "sax": "^1.2.4" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "mime-db": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz", - "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" - }, - "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", - "requires": { - "mime-db": "1.49.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "miniget": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/miniget/-/miniget-4.2.1.tgz", - "integrity": "sha512-O/DduzDR6f+oDtVype9S/Qu5hhnx73EDYGyZKwU/qN82lehFZdfhoa4DT51SpsO+8epYrB3gcRmws56ROfTIoQ==" - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "ow": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz", - "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==", - "requires": { - "@sindresorhus/is": "^4.0.1", - "callsites": "^3.1.0", - "dot-prop": "^6.0.1", - "lodash.isequal": "^4.5.0", - "type-fest": "^1.2.1", - "vali-date": "^1.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "prism-media": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.2.tgz", - "integrity": "sha512-L6UsGHcT6i4wrQhFF1aPK+MNYgjRqR2tUoIqEY+CG1NqVkMjPRKzS37j9f8GiYPlD6wG9ruBj+q5Ax+bH8Ik1g==", - "requires": {} - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "tiny-typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", - "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==" - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "ts-mixer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz", - "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ==" - }, - "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - }, - "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", - "dev": true - }, - "vali-date": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz", - "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} - }, - "youtube-dl-exec": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/youtube-dl-exec/-/youtube-dl-exec-2.0.0.tgz", - "integrity": "sha512-wD5BSkxC1o3rzj+1ktXdWObvkorW3Zvl1E+l56JQqYFTek3d9SR7o1RbjLiQZYQpXpoCPw0zV16SNDXjhVksXQ==", - "requires": { - "dargs": "~7.0.0", - "execa": "~5.1.0", - "is-unix": "~2.0.1", - "mkdirp": "~1.0.4", - "node-fetch": "~2.6.5" - } - }, - "ytdl-core": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/ytdl-core/-/ytdl-core-4.9.1.tgz", - "integrity": "sha512-6Jbp5RDhUEozlaJQAR+l8oV8AHsx3WUXxSyPxzE6wOIAaLql7Hjiy0ZM58wZoyj1YEenlEPjEqcJIjKYKxvHtQ==", - "requires": { - "m3u8stream": "^0.8.3", - "miniget": "^4.0.0", - "sax": "^1.1.3" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 7ee03dc..0000000 --- a/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "administrator", - "version": "1.0.0", - "description": "A Discord bot for administration", - "main": "src/index.ts", - "directories": { - "test": "test" - }, - "scripts": { - "build": "tsc", - "run": " node build/index.js", - "dev": "rm -fr build && tsc && node build/index.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/flifloo/Administrator.git" - }, - "keywords": [ - "discord-bot" - ], - "author": "flifloo ", - "license": "MIT", - "bugs": { - "url": "https://github.com/flifloo/Administrator/issues" - }, - "homepage": "https://github.com/flifloo/Administrator#readme", - "dependencies": { - "@discordjs/builders": "^0.6.0", - "@discordjs/rest": "^0.1.0-canary.0", - "@discordjs/voice": "^0.7.5", - "colors": "^1.4.0", - "discord-api-types": "^0.23.1", - "discord.js": "^13.1.0", - "libsodium-wrappers": "^0.7.9", - "youtube-dl-exec": "^2.0.0", - "ytdl-core": "^4.9.1" - }, - "devDependencies": { - "typescript": "^4.5.2" - } -} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..90dfb45 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +discord.py==2.3.2 +SQLAlchemy==2.0.20 +aiosqlite==0.19.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..1a48e7e --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +from setuptools import setup, find_packages + + +with open("README.md") as f: + readme = f.read() + +with open("LICENSE") as f: + license = f.read() + + +setup( + name='administrator', + version='0.1.0', + description='All purpose Discord bot', + long_description=readme, + author='Ethanell', + author_email='ethanell@flifloo.fr', + url='https://git.flifloo.fr/flifloo/Administrator', + license=license, + packages=find_packages(exclude=('tests', 'docs')) +) diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 6fc4620..0000000 --- a/src/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import "fs"; -import {Intents, Message} from "discord.js"; -import "./lib/Modules"; -import {AdministratorClient} from "./lib/AdministratorClient"; - -const config = require("../config.json"); -const client = new AdministratorClient({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_VOICE_STATES] }); - - -client.once("ready", async () => { - await client.application?.fetch(); - if (client.user?.username) - client.logger.name = client.user.username; - await client.modules.loadAllModules(); - client.logger.info("Started !"); -}); - -client.on("interactionCreate", async interaction => { - if (!interaction.isCommand()) return; - - const command = client.modules.getCommand(interaction.commandName); - - if (!command) return; - - try { - await command.execute(interaction); - } catch (error) { - client.logger.err(error); - const msg = {content: "There was an error while executing this command !", ephemeral: true}; - try { - await interaction.reply(msg); - } catch { - try { - await interaction.followUp(msg); - } catch { - try { - await (await interaction.fetchReply() as Message).reply(msg); - } catch { - client.logger.warn("Cant send error message to the user :/"); - } - } - } - } -}); - - -void client.login(config.token); diff --git a/src/lib/AdministratorClient.ts b/src/lib/AdministratorClient.ts deleted file mode 100644 index 61d8a5e..0000000 --- a/src/lib/AdministratorClient.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {Client} from "discord.js"; -import {Modules} from "./Modules"; -import {Logger} from "./Logger"; - -export class AdministratorClient extends Client { - logger: Logger = new Logger("Core"); - modules: Modules = new Modules(this); -} diff --git a/src/lib/Command.ts b/src/lib/Command.ts deleted file mode 100644 index c3b87da..0000000 --- a/src/lib/Command.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {ApplicationCommandData, CommandInteraction} from "discord.js"; -import {Module} from "./Module"; -import {Component} from "./Component"; - - -export abstract class Command extends Component { - data: ApplicationCommandData; - - protected constructor(module: Module, data: ApplicationCommandData) { - super(module, data.name); - this.module = module; - this.data = data; - this.logger = this.module.logger.createChild(this.data.name); - } - - abstract execute(interaction: CommandInteraction): void; -} diff --git a/src/lib/Component.ts b/src/lib/Component.ts deleted file mode 100644 index 1688015..0000000 --- a/src/lib/Component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import {Module} from "./Module"; -import {Logger} from "./Logger"; - - -export abstract class Component { - module: Module; - logger: Logger; - - protected constructor(module: Module, name: string | null = null) { - this.module = module; - this.logger = this.module.logger.createChild(name || this.constructor.name); - } - - load(): any { - - }; - - unload(): any { - - }; -} diff --git a/src/lib/Event.ts b/src/lib/Event.ts deleted file mode 100644 index eece8a2..0000000 --- a/src/lib/Event.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {Component} from "./Component"; - -export abstract class Event extends Component { - -} diff --git a/src/lib/Logger.ts b/src/lib/Logger.ts deleted file mode 100644 index ed632b9..0000000 --- a/src/lib/Logger.ts +++ /dev/null @@ -1,77 +0,0 @@ -import "colors" - -export enum LoggerLevel { - INFO = "Info", - LOG = "Log", - WARN = "Warn", - ERR = "Error" -} - -export class Logger { - private _name: string; - private parent: Logger | null = null; - children: Logger[] = []; - - constructor(name: string) { - this._name = name; - } - - public createChild(name: string): Logger { - const child = new Logger(name); - child.parent = this; - this.children.push(child); - return child; - } - - get name(): string { - if (this.parent) - return `${this.parent.name} - ${this._name}`; - return this._name; - } - - set name(name: string) { - this._name = name; - } - - get date(): string { - return new Date().toLocaleDateString(); - } - - private print(level: LoggerLevel, msg: any) { - - const message = `[${this.date}] {${level}} ${this.name}: ${msg.toString()}`; - - switch (level) { - case LoggerLevel.INFO: - console.info(message); - break; - case LoggerLevel.LOG: - console.log(message.gray); - break; - case LoggerLevel.WARN: - console.warn(message.yellow); - break; - case LoggerLevel.ERR: - console.error(message.red); - } - - if (msg instanceof Error) - console.error(msg); - } - - public info(msg: any) { - this.print(LoggerLevel.INFO, msg); - } - - public log(msg: any) { - this.print(LoggerLevel.LOG, msg); - } - - public warn(msg: any) { - this.print(LoggerLevel.WARN, msg); - } - - public err(msg: any) { - this.print(LoggerLevel.ERR, msg); - } -} diff --git a/src/lib/Module.ts b/src/lib/Module.ts deleted file mode 100644 index 887e78a..0000000 --- a/src/lib/Module.ts +++ /dev/null @@ -1,82 +0,0 @@ -import {Command} from "./Command"; -import {Modules} from "./Modules"; -import {Logger} from "./Logger"; -import {readdirSync, existsSync} from "fs"; -import {Event} from "./Event"; - - -export abstract class Module { - modules: Modules; - logger: Logger; - loadedCommands: Command[] = []; - loadedEvents: Event[] = []; - - protected constructor(modules: Modules, name: string) { - this.modules = modules; - this.logger = this.modules.client.logger.createChild(name); - } - - private getComponent(name: string) { - const folder = `${__dirname}/../modules/${this.constructor.name}/${name}`; - if (existsSync(folder)) - return readdirSync(folder, {withFileTypes: true}) - .filter(file => file.isDirectory() || file.name.endsWith(".js")) - .map(file => require(`${folder}/${file.name}`)[file.name.charAt(0).toUpperCase() + file.name.replace(/\.js$/, "").slice(1)+name.charAt(0).toUpperCase() + name.slice(1).replace(/s$/, "")]); - else - return []; - } - - get commands() { - return this.getComponent("commands"); - } - - get events() { - return this.getComponent("events"); - } - - async load() { - await this.loadCommands(); - await this.loadEvents(); - } - - async loadCommands() { - const commands: Command[] = []; - - for (const command of this.commands) { - try { - const cmd: Command = new command(this); - await cmd.load(); - commands.push(cmd); - } catch (error) { - this.logger.err(`Fail to load command ${command}`); - this.logger.err(error); - } - } - - this.loadedCommands = this.loadedCommands.concat(commands); - } - - async loadEvents() { - const events: Event[] = []; - - for (const event of this.events) { - try { - const env: Event = new event(this); - await env.load(); - events.push(env); - } catch (error) { - this.logger.err(`Fail to load event ${event}`); - this.logger.err(error); - } - } - - this.loadedEvents = this.loadedEvents.concat(events); - } - - async unload() { - await Promise.all(this.loadedCommands.map(cmd => cmd.unload())); - this.loadedCommands = []; - await Promise.all(this.loadedEvents.map(cmd => cmd.unload())); - this.loadedEvents = []; - } -} diff --git a/src/lib/Modules.ts b/src/lib/Modules.ts deleted file mode 100644 index 6baab78..0000000 --- a/src/lib/Modules.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { readdirSync } from "fs"; -import {Module} from "./Module"; -import {Command} from "./Command"; -import {AdministratorClient} from "./AdministratorClient"; -import {ApplicationCommandData, ApplicationCommandManager} from "discord.js"; - -export class Modules { - modules: Map = new Map(); - client: AdministratorClient; - - constructor(client: AdministratorClient) { - this.client = client; - } - - async load(name: string, createCommand: boolean = true) { - try { - const module: Module = new (require(__dirname+`/../modules/${name}`)[name])(this); - await module.load(); - this.modules.set(name, module); - - if (createCommand) - await this.registerCommand(module.loadedCommands.map(c => c.data)); - - module.logger.info("loaded"); - } catch (error) { - this.client.logger.err(`Fail to load module ${name}`); - this.client.logger.err(error); - return false - } - return true; - } - - async unload(name: string) { - try { - const module = this.modules.get(name); - if (!module) { - this.client.logger.err(`Module ${name} not found`); - return false; - } - await module.unload(); - this.modules.delete(name); - this.client.logger.info(`Module ${name} unloaded`) - } catch (error) { - this.client.logger.err(`Fail to unload module ${name}`); - this.client.logger.err(error); - return false - } - return true; - } - - async reload(name: string) { - if (await this.unload(name)) - return await this.load(name); - return false - } - - async allModules() { - return readdirSync(__dirname+"/../modules", {withFileTypes: true}) - .filter(file => file.isDirectory()) - } - - async loadAllModules() { - for (const module of await this.allModules()) - await this.load(module.name, false); - - await this.registerCommand(this.allCommands().map(c => c.data), true); - } - - async unloadAllModules() { - for (const module of await this.allModules()) - await this.unload(module.name); - } - - async reloadAllModules() { - for (const module of await this.allModules()) - await this.reload(module.name); - } - - async registerCommand(data: ApplicationCommandData | ApplicationCommandData[], set: boolean = false) { - if (!this.client.application) { - this.client.logger.err("Fail to register command, client application is undefined !"); - return; - } - - let commands: ApplicationCommandManager = this.client.application.commands; - - if ("DEV" in process.env && process.env["DEV"] == "true") { - await commands.set([]); - commands = (await this.client?.guilds.fetch(process.env["DEVGUILD"] as any)).commands as any; - } - - if (Array.isArray(data)) - if (set) - await commands.set(data); - else - for (const d of data) - await commands.create(d); - else - if (set) - await commands.set([data]); - else - await commands.create(data); - } - - allCommands() : Command[] { - return Array.from(this.modules.values()).map(m => m.loadedCommands).reduce((l, m) => l.concat(m)); - } - - getCommand(name: string): Command | null { - for (const command of this.allCommands()) - if (command.data.name == name) - return command; - return null; - } -} diff --git a/src/modules/Music/commands/disconnect.ts b/src/modules/Music/commands/disconnect.ts deleted file mode 100644 index fcf32ab..0000000 --- a/src/modules/Music/commands/disconnect.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; - - -export class DisconnectCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "disconnect", - description: "Stop the music" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } - - player.disconnect(); - await interaction.followUp("Bot disconnected"); - } -} diff --git a/src/modules/Music/commands/flush.ts b/src/modules/Music/commands/flush.ts deleted file mode 100644 index b5dcc7c..0000000 --- a/src/modules/Music/commands/flush.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; - - -export class FlushCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "flush", - description: "Flush the music queue" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if (!player.queue.length) { - await interaction.editReply("Can't flush queue, there is no music left"); - return; - } - - player.flush(); - await interaction.followUp("Queue flushed"); - } -} diff --git a/src/modules/Music/commands/pause.ts b/src/modules/Music/commands/pause.ts deleted file mode 100644 index 8ecaa84..0000000 --- a/src/modules/Music/commands/pause.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; -import {AudioPlayerStatus} from "@discordjs/voice"; - - -export class PauseCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "pause", - description: "Pause the music" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if ([AudioPlayerStatus.Playing, AudioPlayerStatus.Buffering].includes(player.audio.state.status)) { - await interaction.editReply(`Can't pause, the music is ${player.audio.state.status}`); - return; - } - - player.pause(); - await interaction.followUp("Music paused"); - } -} diff --git a/src/modules/Music/commands/play.ts b/src/modules/Music/commands/play.ts deleted file mode 100644 index 459c40d..0000000 --- a/src/modules/Music/commands/play.ts +++ /dev/null @@ -1,71 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember, VoiceChannel} from "discord.js"; -import {Music} from "../index"; -import {Player} from "../lib/Player"; -import {Track} from "../lib/Track"; -import {entersState, VoiceConnectionStatus} from "@discordjs/voice"; -const {Constants: { ApplicationCommandOptionTypes }} = require("discord.js"); - -export class PlayCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "play", - description: "Play a music", - options: [{ - type: ApplicationCommandOptionTypes.STRING, - name: "music", - description: "The music to play", - required: true - }] - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - if (! interaction.member.voice.channel || ! (interaction.member.voice.channel instanceof VoiceChannel)) { - await interaction.editReply("You must be connected into a voice channel !"); - return; - } else if (!interaction.member.voice.channel.joinable) { - await interaction.editReply("The bot doesn't have the permission to join this voice channel :/"); - return; - } - - player = new Player(interaction.member.voice.channel); - this.module.players.set(interaction.guild.id, player); - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } - - try { - await entersState(player.connexion, VoiceConnectionStatus.Ready, 20e3); - } catch (error) { - this.logger.warn("Fail to enter state Ready !"); - await interaction.followUp("Failed to join voice channel within 20 seconds, please try again later !"); - return; - } - - const url = interaction.options.get("music")!.value! as string; - - try { - const track = await Track.from(url, interaction); - player.enqueue(track); - await interaction.followUp(`${track.info.videoDetails.title} added to queue`); - } catch (error) { - this.logger.err(error); - await interaction.followUp("Fail to add to queue") - } - } -} diff --git a/src/modules/Music/commands/queue.ts b/src/modules/Music/commands/queue.ts deleted file mode 100644 index 2853829..0000000 --- a/src/modules/Music/commands/queue.ts +++ /dev/null @@ -1,66 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; -import {AudioPlayerStatus} from "@discordjs/voice"; - - -function millisecondsToTime(milli: number): string { - const seconds = Math.floor((milli / 1000) % 60); - const minutes = Math.floor((milli / (60 * 1000)) % 60); - - return ('0' + minutes).slice(-2) + ":" + ('0' + seconds).slice(-2); -} - -export class QueueCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "queue", - description: "Display the current queue" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if (player.audio.state.status == AudioPlayerStatus.Idle) { - await interaction.editReply("There is no queue to display"); - return; - } - - let queue = ""; - - if (player.queue.length) { - queue = player.queue.map((m, n) => `${n}. ${m.info.videoDetails.title}`).join("\n") + "\n"; - } - - let barr = ""; - if ([AudioPlayerStatus.Playing, AudioPlayerStatus.Paused, AudioPlayerStatus.AutoPaused].includes(player.audio.state.status)) { - // @ts-ignore - const duration: number = player.current?.info.videoDetails.lengthSeconds * 1000; - // @ts-ignore - const current: number = player.audio.state.playbackDuration; - - const maxSize = 35; - const progress = Math.ceil((current/duration)*maxSize); - barr = `\n${player.current?.info.videoDetails.title}\n${millisecondsToTime(current)} [${"=".repeat(progress)}${"-".repeat(maxSize-progress)}] ${millisecondsToTime(duration)}\n` - } - - await interaction.followUp(`\`\`\`md\n${queue}${barr}\`\`\``); - } -} diff --git a/src/modules/Music/commands/resume.ts b/src/modules/Music/commands/resume.ts deleted file mode 100644 index 1028f4c..0000000 --- a/src/modules/Music/commands/resume.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; -import {AudioPlayerStatus} from "@discordjs/voice"; - - -export class ResumeCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "resume", - description: "Resume the music" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if ([AudioPlayerStatus.Paused, AudioPlayerStatus.AutoPaused].includes(player.audio.state.status)) { - await interaction.editReply(`Can't resume, the music is ${player.audio.state.status}`); - return; - } - - player.resume(); - await interaction.followUp("Music resumed"); - } -} diff --git a/src/modules/Music/commands/skip.ts b/src/modules/Music/commands/skip.ts deleted file mode 100644 index 6dc5e73..0000000 --- a/src/modules/Music/commands/skip.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; -import {AudioPlayerStatus} from "@discordjs/voice"; - - -export class SkipCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "skip", - description: "Skip the music" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if (player.audio.state.status == AudioPlayerStatus.Idle) { - await interaction.editReply("Can't skip, there is no music"); - return; - } - - await player.skip(); - await interaction.followUp("Music skipped"); - } -} diff --git a/src/modules/Music/commands/stop.ts b/src/modules/Music/commands/stop.ts deleted file mode 100644 index dc27e71..0000000 --- a/src/modules/Music/commands/stop.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, GuildMember} from "discord.js"; -import {Music} from "../index"; -import {AudioPlayerStatus} from "@discordjs/voice"; - - -export class StopCommand extends Command { - module: Music; - - constructor(module: Music) { - super(module, { - name: "stop", - description: "Stop the music" - }); - this.module = module; - } - - async execute(interaction: CommandInteraction) { - await interaction.deferReply(); - if (!interaction.guild || ! (interaction.member instanceof GuildMember)) { - await interaction.editReply("This command is only usable in a guild :/"); - return; - } - - let player = this.module.players.get(interaction.guild.id); - - if (!player) { - await interaction.editReply("No music currently playing !"); - return; - } else if (interaction.member.voice.channelId != player.connexion.joinConfig.channelId) { - await interaction.editReply("You must be in the same voice channel !"); - return; - } else if (player.audio.state.status == AudioPlayerStatus.Idle) { - await interaction.editReply("Can't stop, there is no music"); - return; - } - - player.stop(); - await interaction.followUp("Music stopped"); - } -} diff --git a/src/modules/Music/events/VoiceStateUpdate.ts b/src/modules/Music/events/VoiceStateUpdate.ts deleted file mode 100644 index 2f3b0ee..0000000 --- a/src/modules/Music/events/VoiceStateUpdate.ts +++ /dev/null @@ -1,36 +0,0 @@ -import {Event} from "../../../lib/Event"; -import {VoiceState} from "discord.js"; -import {Music} from "../index"; - -export class VoiceStateUpdateEvent extends Event { - module: Music; - private readonly callback; - - constructor(module: Music) { - super(module); - this.module = module; - this.callback = async (oldState: VoiceState, newState: VoiceState) => { - const player = this.module.players.get(oldState.guild.id); - - if (!player) - return; - - if (!oldState.channel || oldState.channelId != player.connexion.joinConfig.channelId || (newState.channelId == oldState.channelId)) - return; - - await oldState.channel.fetch(true); - if (oldState.channel.members.size == 1) { - player.disconnect(); - this.module.players.delete(oldState.guild.id); - } - } - } - - load() { - this.module.modules.client.on("voiceStateUpdate", this.callback); - } - - unload() { - this.module.modules.client.removeListener("voiceStateUpdate", this.callback); - } -} diff --git a/src/modules/Music/index.ts b/src/modules/Music/index.ts deleted file mode 100644 index 4cef0e8..0000000 --- a/src/modules/Music/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Module} from "../../lib/Module"; -import {Modules} from "../../lib/Modules"; -import {Snowflake} from "discord-api-types"; -import {Player} from "./lib/Player"; - - -export class Music extends Module { - players: Map = new Map(); - - constructor(modules: Modules) { - super(modules, "Music"); - // ToDo: stop if nobody in the channel - } -} diff --git a/src/modules/Music/lib/Player.ts b/src/modules/Music/lib/Player.ts deleted file mode 100644 index 62abe66..0000000 --- a/src/modules/Music/lib/Player.ts +++ /dev/null @@ -1,131 +0,0 @@ -import {VoiceChannel} from "discord.js"; -import { - AudioPlayer, - AudioPlayerState, - AudioPlayerStatus, - AudioResource, - createAudioPlayer, - entersState, - joinVoiceChannel, - VoiceConnection, - VoiceConnectionDisconnectReason, - VoiceConnectionState, - VoiceConnectionStatus -} from '@discordjs/voice'; -import {promisify} from "util"; -import {Track} from "./Track"; - -const wait = promisify(setTimeout); - - -export class Player { - readonly connexion: VoiceConnection; - readonly audio: AudioPlayer; - current: Track | null = null; - queue: Track[] = []; - readyLock: boolean = false; - queueLock: boolean = false; - - constructor(voiceChanel: VoiceChannel) { - this.connexion = joinVoiceChannel({channelId: voiceChanel.id, guildId: voiceChanel.guildId, selfDeaf: true, selfMute: false, adapterCreator: voiceChanel.guild.voiceAdapterCreator as any}); - this.audio = createAudioPlayer(); - - this.connexion.on("error", console.warn); - this.audio.on('error', (error: { resource: any; }) => (error.resource as AudioResource).metadata.onError(error as any)); - this.connexion.on("stateChange", async (_: VoiceConnectionState, newState: VoiceConnectionState) => { - if (newState.status === VoiceConnectionStatus.Disconnected) { - if (newState.reason === VoiceConnectionDisconnectReason.WebSocketClose && newState.closeCode === 4014) { - try { - await entersState(this.connexion, VoiceConnectionStatus.Connecting, 5_000); - } catch { - this.connexion.destroy(); - } - } else if (this.connexion.rejoinAttempts < 5) { - await wait((this.connexion.rejoinAttempts + 1) * 5_000); - this.connexion.rejoin(); - } else { - this.connexion.destroy(); - } - } else if (newState.status === VoiceConnectionStatus.Destroyed) { - this.stop(); - } else if ( - !this.readyLock && - (newState.status === VoiceConnectionStatus.Connecting || newState.status === VoiceConnectionStatus.Signalling) - ) { - this.readyLock = true; - try { - await entersState(this.connexion, VoiceConnectionStatus.Ready, 20_000); - } catch (e) { - if (this.connexion.state.status !== VoiceConnectionStatus.Destroyed) this.connexion.destroy(); - } finally { - this.readyLock = false; - } - } - }); - this.audio.on('stateChange', async (oldState: AudioPlayerState, newState: AudioPlayerState) => { - if (newState.status === AudioPlayerStatus.Idle && oldState.status !== AudioPlayerStatus.Idle) { - await (oldState.resource as AudioResource).metadata.onFinish(); - await this.processQueue(); - } else if (newState.status === AudioPlayerStatus.Playing) { - await (newState.resource as AudioResource).metadata.onStart(); - } - }); - - this.connexion.subscribe(this.audio); - } - - public enqueue(track: Track) { - this.queue.push(track); - void this.processQueue(); - } - - public pause() { - this.audio.pause(); - } - - public resume() { - this.audio.unpause(); - } - - public async skip() { - this.audio.stop(true); - await this.processQueue(); - } - - public stop() { - this.queueLock = true; - this.queue = []; - this.audio.stop(true); - } - - public disconnect() { - if (this.audio.state.status != AudioPlayerStatus.Idle) - this.stop(); - this.connexion.disconnect(); - } - - public flush() { - this.queue = []; - } - - private async processQueue(): Promise { - if (this.queueLock || this.audio.state.status !== AudioPlayerStatus.Idle || this.queue.length === 0) { - return; - } - - this.queueLock = true; - - const nextTrack = this.queue.shift()!; - try { - const resource = await nextTrack.createAudioResource(); - this.audio.play(resource); - this.queueLock = false; - this.current = nextTrack; - } catch (error) { - await nextTrack.onError(error as Error); - this.queueLock = false; - this.current = null; - return this.processQueue(); - } - } -} diff --git a/src/modules/Music/lib/Track.ts b/src/modules/Music/lib/Track.ts deleted file mode 100644 index 8ca0207..0000000 --- a/src/modules/Music/lib/Track.ts +++ /dev/null @@ -1,79 +0,0 @@ -import {getInfo, videoInfo} from "ytdl-core"; -import { AudioResource, createAudioResource, demuxProbe } from "@discordjs/voice"; -import {exec as ytdl} from "youtube-dl-exec"; -import {CommandInteraction, Message} from "discord.js"; - -// eslint-disable-next-line @typescript-eslint/no-empty-function -const noop = async () => {}; - - -export class Track { - public readonly info: videoInfo; - private readonly interaction: CommandInteraction; - - private constructor(info: videoInfo, interaction: CommandInteraction) { - this.info = info; - this.interaction = interaction - } - - private async replyInteraction(message: string) { - try { - await this.interaction.followUp(message); - } catch { - await (await this.interaction.fetchReply() as Message).reply(message); - } - } - - async onStart() { - this.onStart = noop; - await this.replyInteraction("Now playing"); - } - - async onFinish() { - this.onStart = noop; - } - - async onError(error: Error) { - this.onStart = noop; - console.error(error); - await this.replyInteraction("Error with this song, sorry :/"); - } - - public createAudioResource(): Promise> { - return new Promise((resolve, reject) => { - const process = ytdl( - this.info.videoDetails.video_url, - { - output: "-", - quiet: true, - format: 'bestaudio[ext=webm+acodec=opus+asr=48000]/bestaudio', - limitRate: '100K' - }, - { stdio: ['ignore', 'pipe', 'ignore'] }, - ); - if (!process.stdout) { - reject(new Error('No stdout')); - return; - } - const stream = process.stdout; - const onError = (error: Error) => { - if (!process.killed) process.kill(); - stream.resume(); - reject(error); - }; - process - .once('spawn', () => { - demuxProbe(stream) - .then((probe: { stream: any; type: any; }) => resolve(createAudioResource(probe.stream, { metadata: this, inputType: probe.type }))) - .catch(onError); - }) - .catch(onError); - }); - } - - public static async from(url: string, interaction: CommandInteraction): Promise { - const info = await getInfo(url); - - return new Track(info, interaction); - } -} diff --git a/src/modules/Utils/commands/about.ts b/src/modules/Utils/commands/about.ts deleted file mode 100644 index fbba452..0000000 --- a/src/modules/Utils/commands/about.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction, MessageEmbed} from "discord.js"; -import {Module} from "../../../lib/Module"; - -export class AboutCommand extends Command { - - constructor(module: Module) { - super(module, { - name: "about", - description: "Show information about the bot" - }); - } - - async execute(interaction: CommandInteraction) { - const flifloo = await interaction.client.users.fetch("177393521051959306"); - - await interaction.client.application?.fetch(); - - // @ts-ignore - const embed = new MessageEmbed().setTitle(interaction.guild ? interaction.guild.me.displayName : `${interaction.client.user.username}#${interaction.client.user.discriminator}`) - .setDescription(interaction.client.application?.description || '') // @ts-ignore - .setAuthor("Administrator", interaction.client.user.avatarURL(), "https://github.com/flifloo") // @ts-ignore - .setFooter(`Made with ❤️ by ${flifloo.username}#${flifloo.discriminator}`, flifloo.avatarURL()) // @ts-ignore - .addField("Owned by", interaction.client.application?.owner.toString()) - .addField("Guilds", (await interaction.client.guilds.fetch()).size.toString()) - .addField("Modules", this.module.modules.modules.size.toString()) - .addField("Commands", Array.from(this.module.modules.modules.values()).map(m => m.loadedCommands.length).reduce((sum, current) => sum+current).toString()); - - await interaction.reply({embeds: [embed]}); - } -} diff --git a/src/modules/Utils/commands/info.ts b/src/modules/Utils/commands/info.ts deleted file mode 100644 index caf2463..0000000 --- a/src/modules/Utils/commands/info.ts +++ /dev/null @@ -1,95 +0,0 @@ -import {Command} from "../../../lib/Command"; -import { - CategoryChannel, - CommandInteraction, - GuildMember, - MessageEmbed, - TextChannel, - VoiceChannel -} from "discord.js"; -import {Module} from "../../../lib/Module"; - -const {Constants: { ApplicationCommandOptionTypes }} = require("discord.js"); - - -export class InfoCommand extends Command { - - constructor(module: Module) { - super(module, { - name: "info", - description: "Show information of the current guild or the specified user", - options: [{ - type: ApplicationCommandOptionTypes.USER, - name: "target", - description: "The target user" - }] - }); - } - - async execute(interaction: CommandInteraction) { - let embed = new MessageEmbed(); - let target = interaction.options.get("target"); - - if (target) { - if (target.member && target.member instanceof GuildMember) { - embed = embed.addField("DisplayName", target.member.displayName) - .addField("Joined at", target.member.joinedAt?.toString() || 'N/A'); - if (target.member.premiumSince) - embed = embed.addField("Guild premium since", target.member.premiumSince.toString()); - } - - embed = embed.setTitle(`${target.user?.username}#${target.user?.discriminator}`) - .setAuthor("User info", target.user?.avatarURL() || '') - .addField("Created at", target.user?.createdAt.toString() || 'N/A') - .addField("ID", target.user?.id || 'N/A'); - - //if (target.user?.bot) - // embed = embed.addField("Owner", (await target. .fetch()).owner.toString()) - } else { - if (interaction.channel instanceof TextChannel) { - embed = embed.setTitle(interaction.guild?.name || 'N/A') - .setAuthor("Guild infos", interaction.guild?.iconURL() || ''); - //.addField("Region", interaction.guild.voiceRegions()) - const owner = await interaction.guild?.fetchOwner(); - embed.addField("Owner", `${owner?.toString()}`); - if (interaction.guild?.maximumPresences) - embed = embed.addField("Max presences", interaction.guild?.maximumPresences.toString() || 'N/A'); - if (interaction.guild?.description) - embed = embed.addField("Description", interaction.guild.description); - embed = embed.addField("Two factor authorisation level", interaction.guild?.mfaLevel || 'N/A', true) - .addField("Verification level", interaction.guild?.verificationLevel || 'N/A', true) - .addField("Explicit content filter", interaction.guild?.explicitContentFilter || 'N/A', true) - .addField("Default notifications", interaction.guild?.defaultMessageNotifications.toString() || 'N/A', true); - //if (interaction.guild.features) - // embed = embed.addField("Features", interaction.guild.features.length); - if (interaction.guild?.large) - embed = embed.addField("Large", interaction.guild?.large ? "Yes" : "No", true); - if (interaction.guild?.preferredLocale) - embed = embed.addField("Preferred locale", interaction.guild.preferredLocale, true); - embed = embed.addField("Premium", `Tier: ${interaction.guild?.premiumTier} | Boosts: ${interaction.guild?.premiumSubscriptionCount}`); - - const channels = await interaction.guild?.channels.fetch(); - // ToDo news and store channels support - embed = embed.addField("Channels", `Text: ${channels?.filter(c => c instanceof TextChannel).size} | Voice: ${channels?.filter(c => c instanceof VoiceChannel).size}\nCategories: ${channels?.filter(c => c instanceof CategoryChannel).size} | Total: ${channels?.size}`); - - - embed = embed.addField("Members", `${interaction.guild?.memberCount}${interaction.guild?.maximumMembers ? "/"+interaction.guild.maximumMembers: ""}`, true) - .addField("Roles", (await interaction.guild?.roles.fetch())?.size.toString() || 'N/A', true) - .addField("Invites", (await interaction.guild?.invites.fetch())?.size.toString() || 'N/A', true) - .addField("Emojis", (await interaction.guild?.emojis.fetch())?.size.toString() || 'N/A', true) - .addField("Addons", `Webhooks: ${(await interaction.guild?.fetchWebhooks())?.size} | Integrations: ${(await interaction.guild?.fetchIntegrations())?.size}`); - if (interaction.guild?.systemChannel) - embed = embed.addField("System channel", interaction.guild.systemChannel.toString()); - if (interaction.guild?.rulesChannel) - embed = embed.addField("Rules channel", interaction.guild.rulesChannel.toString()); - if (interaction.guild?.publicUpdatesChannel) - embed = embed.addField("Public updates channel", interaction.guild.publicUpdatesChannel.toString()); - embed = embed.addField("Created at", interaction.guild?.createdAt.toString() || 'N/A') - } - } - - embed = embed.setFooter("Administrator", interaction.client.application?.iconURL() || ''); - - await interaction.reply({embeds: [embed]}); - } -} diff --git a/src/modules/Utils/commands/ping.ts b/src/modules/Utils/commands/ping.ts deleted file mode 100644 index 99d3378..0000000 --- a/src/modules/Utils/commands/ping.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {Command} from "../../../lib/Command"; -import {CommandInteraction} from "discord.js"; -import {Module} from "../../../lib/Module"; - -export class PingCommand extends Command { - - constructor(module: Module) { - super(module, { - name: "ping", - description: "Replies with Pong and the bot ping" - }); - } - - async execute(interaction: CommandInteraction) { - const msg = `Pong !\nReceive: ${new Date().getTime() - interaction.createdAt.getTime()}ms`; - const start = Date.now(); - await interaction.reply(msg); - await interaction.editReply(`${msg}\nSend: ${Date.now() - start}ms`); - } -} diff --git a/src/modules/Utils/index.ts b/src/modules/Utils/index.ts deleted file mode 100644 index 79e3377..0000000 --- a/src/modules/Utils/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Module} from "../../lib/Module"; -import {Modules} from "../../lib/Modules"; - -export class Utils extends Module { - - constructor(modules: Modules) { - super(modules, "Utils"); - } -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 510ea79..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "display": "Node 16", - - "compilerOptions": { - "lib": ["es2021"], - "module": "commonjs", - "target": "es2021", - "outDir": "build", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } -} \ No newline at end of file