Merging LavalinkV4 Branch
All checks were successful
Subsonics - Web/pipeline/head This commit looks good

This commit is contained in:
Raphael 2024-04-16 16:08:21 +02:00
commit ef3d23d7b1
54 changed files with 3536 additions and 737 deletions

14
.gitignore vendored
View File

@ -41,7 +41,15 @@ bower_components
build/Release
# Dependency directories
node_modules/
# Ignorer tous les dossiers dans node_modules
node_modules/*
# Ne pas ignorer node_modules/moonlink.js et ses sous-dossiers
!node_modules/moonlink.js
!node_modules/moonlink.js/**
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
@ -91,7 +99,7 @@ out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
@ -133,5 +141,5 @@ dist
data/config.json
data
data/users.json
users.json

View File

@ -1,17 +0,0 @@
# **Easter Egg Subsonics**
> Un savant fou fait des recherches sur les gens du CLP et a caché son système et ses recherches sur le site du bot
**Etapes**
* Page caché
* Confirmation avec le bot
* Erreur assembler le programme
* Accès aux donnés
# Idéés
- Faille dans le bot (à) la recherche
- C'est moi le grand méchant et il doivent arrêter mon plan macchiavélique
- Etape : Me parler pour me soutirer des infos : Genre une histoire

View File

@ -1,8 +1,8 @@
[
{
"host": "omega.raphix.fr",
"host": "localhost",
"port": 2333,
"password": "youshallnotpass",
"retryAmount": 1
"retryAmount": 5
}
]

View File

@ -1,491 +0,0 @@
[
{
"auth": {
"token_type": "Bearer",
"access_token": "H2GnAISsS2hCMG9Sk1lsEhPUfjNKbQ",
"expires_in": 604800,
"refresh_token": "LFcrd8aXanArEM0ivavDMFCUYgZF9s",
"scope": "guilds guilds.members.read identify"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2021-05-23T20:38:29.274000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397724656548970508"
],
"user": {
"id": "226744358567804928",
"username": "alexmario5",
"avatar": "90467211fb1287f0d264c0a6a11b7861",
"discriminator": "0",
"public_flags": 0,
"flags": 0,
"banner": null,
"accent_color": 14981530,
"global_name": "alexmario5",
"avatar_decoration_data": null,
"banner_color": "#e4999a"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "bd1edc6d-351f-443f-87ed-d52d2794758b",
"admin": false,
"picture": "/userspictures/226744358567804928.png",
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "A1XKJTzNC3TmCV6XJuy8uROva6e8Dc",
"expires_in": 604800,
"refresh_token": "un7NRYkhCsWCLJhWoTlU0L6mFZV7D1",
"scope": "identify guilds.members.read guilds"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2020-03-30T21:34:43.763000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288"
],
"unusual_dm_activity_until": null,
"user": {
"id": "362983050054991872",
"username": "roman_cinemorphique",
"avatar": "d103a76a2f5a3a61e46d9b975ac0d23f",
"discriminator": "0",
"public_flags": 0,
"flags": 0,
"banner": null,
"accent_color": null,
"global_name": "Roman",
"avatar_decoration_data": null,
"banner_color": null
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "7782668f-b8ef-4ebc-8722-70206ceffe47",
"admin": false,
"picture": "/userspictures/362983050054991872.png",
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "Kr8S9MviRsO68aBii2TBXLPhhRHivK",
"expires_in": 604800,
"refresh_token": "3lmSmZeHDi169D7Vzisvu1qSAbOgZK",
"scope": "guilds identify guilds.members.read"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2019-01-13T22:43:08.924000+00:00",
"nick": "Néo",
"pending": false,
"premium_since": "2021-05-09T22:00:03.632000+00:00",
"roles": [
"397726163180716032",
"630169940640399370"
],
"unusual_dm_activity_until": null,
"user": {
"id": "428869747594231808",
"username": "neonightfr",
"avatar": "6c55374fd43613fe8f4a4d2f4b46750c",
"discriminator": "0",
"public_flags": 0,
"flags": 0,
"banner": "7421deb5e7ff351828e77f79265246aa",
"accent_color": null,
"global_name": "Néo Night FR",
"avatar_decoration_data": {
"asset": "a_fed43ab12698df65902ba06727e20c0e",
"sku_id": "1144058844004233369"
},
"banner_color": null
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "f931f59c-ec29-43cc-a5fc-7f05a8d3361e",
"picture": "/userspictures/428869747594231808.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "QMEQsujfQeJFRpx2gl61UvrmhK9k4g",
"expires_in": 604800,
"refresh_token": "iDKDGlLsCdCZkNVlMuozhPR00uKA1J",
"scope": "guilds identify guilds.members.read"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2020-04-14T08:47:38.484000+00:00",
"nick": null,
"pending": false,
"premium_since": "2021-01-08T17:00:59.380000+00:00",
"roles": [
"397725552968204288",
"630169940640399370"
],
"unusual_dm_activity_until": null,
"user": {
"id": "435462012974268426",
"username": "gabouille",
"avatar": "78790482ad52bad210a6e979847420d9",
"discriminator": "0",
"public_flags": 256,
"premium_type": 2,
"flags": 256,
"banner": "7c3d04b3afb1f624a7266b5dc42dbb29",
"accent_color": 16775056,
"global_name": "Gabouille",
"avatar_decoration_data": null,
"banner_color": "#fff790"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "4c8833ba-d02c-4661-93e8-94aa87626b36",
"picture": "/userspictures/435462012974268426.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "sVto3Z4OtZi2d1gufLjdMVSu0AqoVM",
"expires_in": 604800,
"refresh_token": "hlL3RjDyXtcNWUkiBtTzT4caoc0jFY",
"scope": "guilds.members.read identify guilds"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2021-10-04T21:39:15.519000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288"
],
"unusual_dm_activity_until": null,
"user": {
"id": "454603829556805632",
"username": ".mophie",
"avatar": "0cfb7c8d85b1b8c245b2545e3e1b75da",
"discriminator": "0",
"public_flags": 0,
"premium_type": 0,
"flags": 0,
"banner": null,
"accent_color": 3553599,
"global_name": "Mophie",
"avatar_decoration_data": null,
"banner_color": "#36393f"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "188204a0-123c-42b5-b92e-8cbbed3c975f",
"picture": "/userspictures/454603829556805632.png",
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "aaNl0ZbjqF80r38Uhh6dGj0UStCyF9",
"expires_in": 604800,
"refresh_token": "OBDdGUO1ws4ZtZf4ZOs9xJ6eeu8BoM",
"scope": "identify guilds.members.read guilds"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2020-03-06T18:17:58.037000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288"
],
"unusual_dm_activity_until": null,
"user": {
"id": "249494159629484033",
"username": "immudelki",
"avatar": "10376ec122172dd172d70876e3b532aa",
"discriminator": "0",
"public_flags": 0,
"premium_type": 0,
"flags": 0,
"banner": null,
"accent_color": 1123662,
"global_name": "Immudelki",
"avatar_decoration_data": null,
"banner_color": "#11254e"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "e635ee17-ba13-47f7-8330-7679cb724936",
"picture": "/userspictures/249494159629484033.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "9oiQMo6zqVFkDnD7iA3theiy5YjMT5",
"expires_in": 604800,
"refresh_token": "F8Cc1cftPfcajzHHthhgVFDYKRTk0p",
"scope": "guilds identify guilds.members.read"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2019-10-17T20:04:23.689000+00:00",
"nick": "Pierre, Empereur FR",
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288"
],
"unusual_dm_activity_until": null,
"user": {
"id": "158369928104116224",
"username": "pierreber",
"avatar": "b32d53187e2a0c4e419a5c038c6de1a8",
"discriminator": "0",
"public_flags": 0,
"premium_type": 0,
"flags": 0,
"banner": null,
"accent_color": 921132,
"global_name": "PierreB.",
"avatar_decoration_data": null,
"banner_color": "#0e0e2c"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "a6dfef7b-0a16-4fff-864e-1b5a165cba97",
"picture": "/userspictures/158369928104116224.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "gOP2InJLgtHkxDQVXhX81cDnTYa97m",
"expires_in": 604800,
"refresh_token": "3vBdERIVShrs6jZsYZBvQ0PPFUeGiE",
"scope": "guilds identify guilds.members.read"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2019-06-28T21:40:53.652000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397725956598530050",
"840278326210985994"
],
"unusual_dm_activity_until": null,
"user": {
"id": "172437570905571328",
"username": "iceplayer",
"avatar": "255f537528548d5def12fcf93d5ca439",
"discriminator": "0",
"public_flags": 0,
"premium_type": 1,
"flags": 0,
"banner": null,
"accent_color": null,
"global_name": "IcePlayer",
"avatar_decoration_data": null,
"banner_color": null
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "0adeb1bf-c4a9-4d39-b40c-bc844cd50f2d",
"picture": "/userspictures/172437570905571328.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "zR2x55rWjbMeKXNlGWnUXBmaATBZRO",
"expires_in": 604800,
"refresh_token": "gFu5XPe3yD5zUkSw69rOGlehRwD8B4",
"scope": "guilds guilds.members.read identify"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2018-05-17T09:35:10.139000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397726163180716032",
"840278326210985994"
],
"unusual_dm_activity_until": null,
"user": {
"id": "168086507486314496",
"username": "entreenatcho",
"avatar": "27b13203cd91c88ff80ab350a21cd49c",
"discriminator": "0",
"public_flags": 0,
"premium_type": 0,
"flags": 0,
"banner": null,
"accent_color": null,
"global_name": "EntreeNATCHO",
"avatar_decoration_data": null,
"banner_color": null
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": "2dbb9339-d54d-4a22-85c3-c24dc22c2251",
"picture": "/userspictures/168086507486314496.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "4kUniqI5SwtuZXFowjasl5WA9Qe6Xa",
"expires_in": 604800,
"refresh_token": "9fm6lraCAXgHbEBesgRSQGEdunj4QT",
"scope": "guilds.members.read identify guilds"
},
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2017-08-28T19:23:58.611000+00:00",
"nick": null,
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288",
"840278326210985994"
],
"unusual_dm_activity_until": null,
"user": {
"id": "253494094179991552",
"username": "darkguillaume",
"avatar": "a_395293ae3b4fc6c67f9ef1af5a17196f",
"discriminator": "0",
"public_flags": 128,
"premium_type": 2,
"flags": 128,
"banner": "a_256ce480b73b88f5c80d2e096ae4e9e8",
"accent_color": null,
"global_name": "DarkGuillaume",
"avatar_decoration_data": null,
"banner_color": null
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": [
"de730e3b-a000-4376-8f95-e1052c441636"
],
"picture": "/userspictures/253494094179991552.png",
"admin": false,
"banned": false
},
{
"auth": {
"token_type": "Bearer",
"access_token": "fATWAIB6UrA8FnN9xKofUyLdGXXoKk",
"expires_in": 604800,
"refresh_token": "pWF4LL5uFbbQmwwNmGWftiBMi2Rrip",
"scope": "guilds.members.read identify guilds"
},
"admin": true,
"avatar": null,
"communication_disabled_until": null,
"flags": 0,
"joined_at": "2023-09-24T18:54:49.914000+00:00",
"nick": "Raphix",
"pending": false,
"premium_since": null,
"roles": [
"397725552968204288"
],
"unusual_dm_activity_until": null,
"user": {
"id": "486943594893017119",
"username": "raphixscrap",
"avatar": "f3fa652cc177da053b27985a56d61f80",
"discriminator": "0",
"public_flags": 4194368,
"premium_type": 0,
"flags": 4194368,
"banner": null,
"accent_color": 10712214,
"global_name": "Raphix",
"avatar_decoration_data": null,
"banner_color": "#a37496"
},
"mute": false,
"deaf": false,
"bio": "",
"banner": null,
"token": [
"95e479e4-17c7-49db-9728-501cdfc49bda",
"aea7a790-e356-4988-919b-0847251f1396",
"47925247-a34f-401c-a051-76dedcf81cbe",
"e18d0907-826e-4801-9264-ef1e1f1ed630",
"7986444a-b39e-4a11-9c22-8bf5e6b65df5",
"fe9b028c-044b-4f41-8fac-857980ceabbd",
"c74fd6af-acd2-4f51-92f9-e868bfb72349",
"df8952ba-0cae-4711-bbbe-537360c6054e",
"0256465e-f8fb-4ca7-9386-de3b7f689df4",
"bb614baf-beeb-4015-af4a-ae28710536a5",
"4874e400-3640-4350-9b34-648705d9efd7",
"eedd4776-dd30-4b50-b180-c54e99ee7c53",
"39fb987a-89ee-4f84-93a1-56840ff18d6c",
"8e8cf424-05e5-4c19-8eb3-85f8aa64f8e0",
"67eab87f-8a22-4cef-bf7c-b1109a4fbfdb",
"e18b2d89-2494-4cfe-af55-dad6dfbc0c60",
"0a0b75f3-6a42-4ccf-92f9-d992a63545a4",
"419a246f-e7b3-4aa4-9fa0-816dffc22a19",
"471c4c81-b8bc-42e7-af1a-d33df30cff2b",
"b21fb001-4e6c-4530-81c4-07db0d1ef14d"
],
"picture": "/userspictures/486943594893017119.png",
"banned": false,
"oobe": true
}
]

172
node_modules/moonlink.js/LICENSE generated vendored Normal file
View File

@ -0,0 +1,172 @@
Open Software License ("OSL") v. 3.0
This Open Software License (the "License") applies to any original work of
authorship (the "Original Work") whose owner (the "Licensor") has placed the
following licensing notice adjacent to the copyright notice for the Original
Work:
Licensed under the Open Software License version 3.0
1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, for the duration of the copyright, to do
the following:
a) to reproduce the Original Work in copies, either alone or as part of a
collective work;
b) to translate, adapt, alter, transform, modify, or arrange the Original
Work, thereby creating derivative works ("Derivative Works") based upon the
Original Work;
c) to distribute or communicate copies of the Original Work and Derivative
Works to the public, with the proviso that copies of Original Work or
Derivative Works that You distribute or communicate shall be licensed under
this Open Software License;
d) to perform the Original Work publicly; and
e) to display the Original Work publicly.
2) Grant of Patent License. Licensor grants You a worldwide, royalty-free,
non-exclusive, sublicensable license, under patent claims owned or controlled
by the Licensor that are embodied in the Original Work as furnished by the
Licensor, for the duration of the patents, to make, use, sell, offer for sale,
have made, and import the Original Work and Derivative Works.
3) Grant of Source Code License. The term "Source Code" means the preferred
form of the Original Work for making modifications to it and all available
documentation describing how to modify the Original Work. Licensor agrees to
provide a machine-readable copy of the Source Code of the Original Work along
with each copy of the Original Work that Licensor distributes. Licensor
reserves the right to satisfy this obligation by placing a machine-readable
copy of the Source Code in an information repository reasonably calculated to
permit inexpensive and convenient access by You for as long as Licensor
continues to distribute the Original Work.
4) Exclusions From License Grant. Neither the names of Licensor, nor the names
of any contributors to the Original Work, nor any of their trademarks or
service marks, may be used to endorse or promote products derived from this
Original Work without express prior permission of the Licensor. Except as
expressly stated herein, nothing in this License grants any license to
Licensor's trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.
5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone
other than You, whether those works are distributed or communicated to those
persons or made available as an application intended for use over a network.
As an express condition for the grants of license hereunder, You must treat
any External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).
6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and any
descriptive text identified therein as an "Attribution Notice." You must cause
the Source Code for any Derivative Works that You create to carry a prominent
Attribution Notice reasonably calculated to inform recipients that You have
modified the Original Work.
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted herein
by Licensor are owned by the Licensor or are sublicensed to You under the
terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.
8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.
9) Acceptance and Termination. If, at any time, You expressly assented to this
License, that assent indicates your clear and irrevocable acceptance of this
License and all of its terms and conditions. If You distribute or communicate
copies of the Original Work or a Derivative Work, You must make a reasonable
effort under the circumstances to obtain the express assent of recipients to
the terms of this License. This License conditions your rights to undertake
the activities listed in Section 1, including your right to create Derivative
Works based upon the Original Work, and doing so without honoring these terms
and conditions is prohibited by copyright law and international treaty.
Nothing in this License is intended to affect copyright exceptions and
limitations (including "fair use" or "fair dealing"). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).
10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original Work
infringes a patent. This termination provision shall not apply for an action
alleging patent infringement by combinations of the Original Work with other
software or hardware.
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.
12) Attorneys' Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.
13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.
14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.
15) Right to Use. You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.
16) Modification of This License. This License is Copyright © 2005 Lawrence
Rosen. Permission is granted to copy, distribute, or communicate this License
without modification. Nothing in this License permits You to modify this
License as applied to the Original Work or to Derivative Works. However, You
may modify the text of this License and copy, distribute or communicate your
modified version (the "Modified License") and apply it to other original works
of authorship subject to the following conditions: (i) You may not indicate in
any way that your Modified License is the "Open Software License" or "OSL" and
you may not use those names in the name of your Modified License; (ii) You
must replace the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your own
that is not confusingly similar to the notice in this License; and (iii) You
may not claim that your original works are open source software unless your
Modified License has been approved by Open Source Initiative (OSI) and You
comply with its license review and certification process.

216
node_modules/moonlink.js/README.md generated vendored Normal file
View File

@ -0,0 +1,216 @@
# Imagine a Music...
![MoonImage](https://media.discordapp.net/attachments/1211812380850528267/1213558016771625011/48_Sem_Titulo_20231206185046.png?ex=65f5e8fa&is=65e373fa&hm=19170abb2481ba1b9a24bbe136b428bc6cfbfedc95cf53498a10bfbcf735c53d&)
[![NPM](https://nodei.co/npm/moonlink.js.png)](https://nodei.co/npm/moonlink.js)
[![Made with ♥️ in - Brazil](https://img.shields.io/badge/Made_with_♥_in-Brazil-ED186A?style=for-the-badge)](https://github.com/1Lucas1apk)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7dd9288acdc94dacaa11ad80f36a9bd3)](https://www.codacy.com/gh/1Lucas1apk/moonlink.js/dashboard?utm_source=github.com&utm_medium=referral&utm_content=1Lucas1apk/moonlink.js&utm_campaign=Badge_Grade) [![Downloads](https://img.shields.io/npm/dt/moonlink.js.svg?color=3884FF)](https://www.npmjs.com/package/moonlink.js) [![Version](https://img.shields.io/npm/v/moonlink.js.svg?color=3884FF&label=version)](https://www.npmjs.com/package/moonlink.js) [![install size](https://packagephobia.com/badge?p=moonlink.js)](https://packagephobia.com/result?p=moonlink.js) ![node](https://img.shields.io/node/v/moonlink.js) [![Netlify Status](https://api.netlify.com/api/v1/badges/4f4a2a64-a8db-4db3-ad1d-0c4ac7274d0e/deploy-status)](https://app.netlify.com/sites/moonlinkjs/deploys)
Envision a musical journey where creativity knows no bounds, accompanied by the enchantment of the holiday season. 🌌 Moonlink.js invites you to unlock your complete musical potential, designed exclusively for Lavalink clients. Step into a world of seamless communication and fluid interaction, where Moonlink.js elevates your projects to new heights, sprinkled with holiday charm. With full TypeScript support, it empowers your creativity and productivity. 🎵
## Table of Contents
- [Features](#features)
- [Documentation](#documentation)
- [Installation](#installation)
- [How to Use](#how-to-use)
- [Attributions](#attributions)
- [Contributors](#contributors)
- [Final Thanks](#final-thanks)
- [License](#license)
- [Support](#support)
## Features
**Moonlink.js** offers essential features for creating exceptional music bots:
1. **Seamless Communication:** Developed for Lavalink clients, it ensures an uninterrupted musical experience. 🎧
2. **Full TypeScript Support:** Enjoy complete TypeScript support to enhance your productivity and creativity. 💻
3. **Active Community:** Be part of a community of passionate developers and benefit from our active support system. Our project is not just about minimizing package size but maximizing its quality and potential for developers. 🤝
## Documentation
For comprehensive documentation and more examples, visit [moonlink.js.org](https://moonlink.js.org). 📖
## Installation
```bash
npm install moonlink.js
yarn add moonlink.js
pnpm install moonlink.js
bun install moonlink.js
```
## How to Use
```javascript
// Creating an instance of the Discord.js clien
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildVoiceStates
]
});
// Configuring the Moonlink.js package
client.moon = new MoonlinkManager(
[
{
host: "localhost",
port: 2333,
secure: true,
password: "password"
}
],
{
/* Options */
},
(guild, sPayload) => {
// Sending payload information to the server
client.guilds.cache.get(guild).shard.send(JSON.parse(sPayload));
}
);
// Event: Node created
client.moon.on("nodeCreate", node => {
console.log(`${node.host} was connected, and the magic is in the air`);
});
// Event: Track start
client.moon.on("trackStart", async (player, track) => {
// Sending a message when the track starts playing
client.channels.cache
.get(player.textChannel)
.send(`${track.title} is playing now, bringing holiday joy`);
});
// Event: Track end
client.moon.on("trackEnd", async (player, track) => {
// Sending a message when the track finishes playing
client.channels.cache
.get(player.textChannel)
.send(`The track is over, but the magic continues`);
});
// Event: Ready
client.on("ready", () => {
// Initializing the Moonlink.js package with the client's user ID
client.moon.init(client.user.id);
});
// Event: Raw data
client.on("raw", data => {
// Updating the Moonlink.js package with the necessary data
client.moon.packetUpdate(data);
});
// Event: Interaction created
client.on("interactionCreate", async interaction => {
if (!interaction.isChatInputCommand()) return;
let commandName = interaction.commandName;
if (commandName === "play") {
if (!interaction.member.voice.channel) {
// Responding with a message if the user is not in a voice channel
return interaction.reply({
content: `You are not in a voice channel`,
ephemeral: true
});
}
let query = interaction.options.getString("query");
let player = client.moon.players.create({
guildId: interaction.guild.id,
voiceChannel: interaction.member.voice.channel.id,
textChannel: interaction.channel.id,
autoPlay: true
});
if (!player.connected) {
// Connecting to the voice channel if not already connected
player.connect({
setDeaf: true,
setMute: false
});
}
let res = await client.moon.search({
query,
source: "youtube",
requester: interaction.user.id
});
if (res.loadType === "loadfailed") {
// Responding with an error message if loading fails
return interaction.reply({
content: `:x: Load failed - the system is not cooperating.`
});
} else if (res.loadType === "empty") {
// Responding with a message if the search returns no results
return interaction.reply({
content: `:x: No matches found!`
});
}
if (res.loadType === "playlist") {
interaction.reply({
content: `${res.playlistInfo.name} This playlist has been added to the waiting list, spreading joy`
});
for (const track of res.tracks) {
// Adding tracks to the queue if it's a playlist
player.queue.add(track);
}
} else {
player.queue.add(res.tracks[0]);
interaction.reply({
content: `${res.tracks[0].title} was added to the waiting list`
});
}
if (!player.playing) {
// Starting playback if not already playing
player.play();
}
});
// Logging in with the Discord token
client.login(process.env["DISCORD_TOKEN"]);
```
## Contributors
We would like to express our gratitude to the amazing individuals who contributed to this project. Their hard work and dedication have been instrumental in making it a success. 🎉
1. **1Lucas1apk** - Lead Developer, responsible for project architecture and key feature implementation. 🚀
2. **MotoG.js** - Project Ideator and Designer, contributing to the concept and visual design. 🎨
3. **WilsontheWolf** - Contributed to the track position logic in real time, rather than just receiving the payload from lavalink.
4. **PiscesXD** - First sponsor and contributed to making the shuffle method reversible, and autoLeave.
5. **Suryansh** - Second contributor and helped discover bugs 🌷
Other contributors: Nah, ItzGG, SuperPlayerBot, ddemile, Tasty-Kiwi, rrm, WilsontheWolf, Aertic, 'Forster, Fireball, Ghos't, loulou310 - Xotak
We sincerely thank all the contributors mentioned above and everyone who contributed to this project in any way. Your support is truly appreciated. 🙏
## Final Thanks
Thank you to everyone who contributed to the growth of moonlink.js, reporting bugs, installing the package and everyone else's patience, I apologize for any time I wasn't able to help someone
have a great day :)
## License
This project is licensed under the [Open Software License ("OSL") v. 3.0](LICENSE) - see the [LICENSE](LICENSE) file for details.
## Support
Join our Discord server at [Moonlink.js - Imagine a Music Bot](https://discord.com/invite/xQq2A8vku3) to connect with other users, ask questions, and participate in discussions. 🤝
For any inquiries or assistance, we're here to help! 🌟

14
node_modules/moonlink.js/dist/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,14 @@
export declare const version: string;
export * from "./src/@Managers/MoonlinkManager";
export * from "./src/@Managers/PlayerManager";
export * from "./src/@Managers/NodeManager";
export * from "./src/@Entities/MoonlinkNode";
export * from "./src/@Entities/MoonlinkPlayer";
export * from "./src/@Entities/MoonlinkQueue";
export * from "./src/@Services/MoonlinkMakeRequest";
export * from "./src/@Services/MoonlinkRestFul";
export * from "./src/@Typings/";
export * from "./src/@Utils/MoonlinkDatabase";
export * from "./src/@Utils/MoonlinkFilters";
export * from "./src/@Utils/MoonlinkTrack";
export * from "./src/@Utils/Structure";

31
node_modules/moonlink.js/dist/index.js generated vendored Normal file
View File

@ -0,0 +1,31 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = require("../package.json").version;
__exportStar(require("./src/@Managers/MoonlinkManager"), exports);
__exportStar(require("./src/@Managers/PlayerManager"), exports);
__exportStar(require("./src/@Managers/NodeManager"), exports);
__exportStar(require("./src/@Entities/MoonlinkNode"), exports);
__exportStar(require("./src/@Entities/MoonlinkPlayer"), exports);
__exportStar(require("./src/@Entities/MoonlinkQueue"), exports);
__exportStar(require("./src/@Services/MoonlinkMakeRequest"), exports);
__exportStar(require("./src/@Services/MoonlinkRestFul"), exports);
__exportStar(require("./src/@Typings/"), exports);
__exportStar(require("./src/@Utils/MoonlinkDatabase"), exports);
__exportStar(require("./src/@Utils/MoonlinkFilters"), exports);
__exportStar(require("./src/@Utils/MoonlinkTrack"), exports);
__exportStar(require("./src/@Utils/Structure"), exports);

17
node_modules/moonlink.js/dist/index.mjs generated vendored Normal file
View File

@ -0,0 +1,17 @@
import mod from "./index.js";
export default mod;
export const MoonlinkDatabase = mod.MoonlinkDatabase;
export const MoonlinkFilters = mod.MoonlinkFilters;
export const MoonlinkManager = mod.MoonlinkManager;
export const MoonlinkNode = mod.MoonlinkNode;
export const MoonlinkPlayer = mod.MoonlinkPlayer;
export const MoonlinkQueue = mod.MoonlinkQueue;
export const MoonlinkRestFul = mod.MoonlinkRestFul;
export const MoonlinkTrack = mod.MoonlinkTrack;
export const NodeManager = mod.NodeManager;
export const PlayerManager = mod.PlayerManager;
export const Plugin = mod.Plugin;
export const Structure = mod.Structure;
export const makeRequest = mod.makeRequest;
export const version = mod.version;

View File

@ -0,0 +1,5 @@
{
"queue": {
"137291455336022018": []
}
}

View File

@ -0,0 +1,44 @@
/// <reference types="node" />
import { INode, INodeStats } from "../@Typings";
import { MoonlinkWebSocket } from "../@Services/MoonlinkWebSocket";
import { MoonlinkRestFul } from "../../index";
export declare class MoonlinkNode {
private _manager;
private reconnectTimeout?;
private reconnectAttempts;
private retryAmount;
private retryDelay;
private resumeStatus;
host: string;
identifier?: string;
password: string;
port?: number;
secure: boolean;
regions?: string[];
http: string;
rest: MoonlinkRestFul;
info?: Record<string, any>;
version?: string;
resume?: boolean;
resumed?: boolean;
autoResume?: boolean;
resumeTimeout?: number;
sessionId: string;
socket: MoonlinkWebSocket | null;
state: string;
stats: INodeStats | Record<string, any>;
calls: number;
constructor(node: INode);
get address(): string;
check(node: INode): void;
request(endpoint: string, params: any): Promise<object>;
connect(): Promise<any>;
open(): void;
private reconnect;
protected close(code: number, reason: any): void;
protected error(error: Error): void;
protected message(data: Buffer | string): Promise<void>;
protected handleEvent(payload: any): Promise<any>;
private movePlayers;
private get getAllPlayers();
}

View File

@ -0,0 +1,352 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkNode = void 0;
const MoonlinkWebSocket_1 = require("../@Services/MoonlinkWebSocket");
const index_1 = require("../../index");
class MoonlinkNode {
_manager = index_1.Structure.manager;
reconnectTimeout;
reconnectAttempts = 1;
retryAmount = 6;
retryDelay = 120000;
resumeStatus = false;
host;
identifier;
password;
port;
secure;
regions;
http;
rest;
info = {};
version;
resume = index_1.Structure.manager.options?.resume;
resumed;
autoResume = index_1.Structure.manager.options?.autoResume;
resumeTimeout = 30000;
sessionId;
socket;
state = "DISCONNECTED";
stats = {};
calls = 0;
constructor(node) {
this.check(node);
this.host = node.host;
this.identifier = node.identifier || null;
this.password = node.password ? node.password : "youshallnotpass";
this.port = node.port ? node.port : node.secure == true ? 443 : 80;
this.secure = node.secure || false;
this.regions = node.regions;
this.http = `http${node.secure ? "s" : ""}://${this.address}/v4/`;
this.rest = new (index_1.Structure.get("MoonlinkRestFul"))(this);
if (node.sessionId)
this.sessionId = node.sessionId;
this.connect();
}
get address() {
return `${this.host}:${this.port}`;
}
check(node) {
if (typeof node.host !== "string" && typeof node.host !== "undefined")
throw new Error('@Moonlink(Nodes) - "host" option is not configured correctly');
if (typeof node.password !== "string" &&
typeof node.password !== "undefined")
throw new Error('@Moonlink(Nodes) - the option "password" is not set correctly');
if ((node.port && typeof node.port !== "number") ||
node.port > 65535 ||
node.port < 0)
throw new Error('@Moonlink(Nodes) - the "port" option is not set correctly');
if (typeof node.retryAmount !== "undefined" &&
typeof node.retryAmount !== "number")
throw new Error('@Moonlink(Nodes) - the "retryAmount" option is not set correctly');
if (typeof node.retryDelay !== "undefined" &&
typeof node.retryDelay !== "number")
throw new Error('@Moonlink(Nodes) - the "retryDelay" option is not set correctly');
}
request(endpoint, params) {
this.calls++;
return this.rest.get(`${endpoint}?${params}`);
}
async connect() {
if (this.state == "CONNECTED" || this.state == "READY")
return;
this.state = "CONNECTING";
let headers = {
Authorization: this.password,
"User-Id": this._manager.options.clientId,
"Client-Name": this._manager.options.clientName
};
if (this.resume)
headers["Session-Id"] = index_1.Structure.db.get(`sessionId.${this.identifier ?? this.host.replace(/\./g, "-")}`);
this.socket = new MoonlinkWebSocket_1.MoonlinkWebSocket(`ws${this.secure ? "s" : ""}://${this.address}/v4/websocket`, { headers });
this.socket.on("open", this.open.bind(this));
this.socket.on("close", this.close.bind(this));
this.socket.on("message", this.message.bind(this));
this.socket.on("error", this.error.bind(this));
}
open() {
if (this.reconnectTimeout)
clearTimeout(this.reconnectTimeout);
this._manager.emit("debug", `@Moonlink(Node) - The Node ${this.identifier ?? this.host} has been connected successfully`);
this._manager.emit("nodeCreate", this);
this.state = "CONNECTED";
}
reconnect() {
if (this.reconnectAttempts >= this.retryAmount) {
this._manager.emit("debug", `@Moonlink(Node) - Node ${this.identifier ?? this.host} was destroyed due to inactivity, attempts to reconnect were failed`);
this._manager.emit("nodeDestroy", this);
this.socket.close(1000, "destroy");
this.socket.removeAllListeners();
}
else {
this.reconnectTimeout = setTimeout(() => {
this.socket.removeAllListeners();
this.socket = null;
this.state = "RECONNECTING";
this._manager.emit("nodeReconnect", this);
this.connect();
this._manager.emit("debug", `@Moonlink(Node) - We are trying to reconnect node ${this.identifier ?? this.host}, attempted number ${this.reconnectAttempts}
`);
if (this.getAllPlayers.length &&
this._manager.options?.switchPlayersAnotherNode)
this.movePlayers();
this.reconnectAttempts++;
}, this.retryDelay);
}
}
close(code, reason) {
if (code !== 1000 || reason !== "destroy")
this.reconnect();
this._manager.emit("debug", `@Moonlink(Node) - The node connection ${this.identifier ?? this.host} has been closed`);
this._manager.emit("nodeClose", this, code, reason);
this.getAllPlayers.forEach(player => {
player.playing = false;
});
this.state = "DISCONNECTED";
}
error(error) {
if (!error)
return;
this._manager.emit("nodeError", this, error);
this._manager.emit("debug", `@Moonlink(Nodes) - The ${this.identifier ?? this.host} an error has occurred:
${error}`);
}
async message(data) {
if (Array.isArray(data))
data = Buffer.concat(data);
else if (data instanceof ArrayBuffer)
data = Buffer.from(data);
let payload = JSON.parse(data.toString("utf8"));
if (!payload.op)
return;
this._manager.emit("nodeRaw", this, payload);
switch (payload.op) {
case "ready":
this.sessionId = payload.sessionId;
this.resume
? index_1.Structure.db.set(`sessionId.${this.identifier ?? this.host.replace(/\./g, "-")}`, this.sessionId)
: null;
this.resumed = payload.resumed;
this.rest.setSessionId(this.sessionId);
this.state = "READY";
if (!this._manager.initiated && !this.resumed) {
index_1.Structure.db.delete("queue");
index_1.Structure.db.delete("players");
}
this._manager.emit("debug", `@Moonlink(Node) - ${this.resumed ? ` session was resumed,` : ``} session is currently ${this.sessionId}`);
this._manager.emit("nodeReady", this, this.sessionId, this.resumed);
if (this.resume) {
this.rest.patch(`sessions/${this.sessionId}`, {
data: {
resuming: this.resume,
timeout: this.resumeTimeout
}
});
this._manager.emit("debug", `@Moonlink(Nodes) - Resuming configured`);
}
this.version = await this.rest.getVersion();
this.info = await this.rest.getInfo();
if (this.autoResume) {
let resumePlayers = this.getAllPlayers;
for (const resumePlayer of resumePlayers) {
resumePlayer.restart();
}
}
if (this.resumed) {
const resumedPlayers = await this.rest.get(`sessions/${this.sessionId}/players`);
for (const resumedPlayer of resumedPlayers) {
const previousInfosPlayer = index_1.Structure.db.get(`players.${resumedPlayer.guildId}`) || {};
const player = this._manager.players.create({
guildId: resumedPlayer.guildId,
voiceChannel: previousInfosPlayer.voiceChannel,
textChannel: previousInfosPlayer.textChannel,
volume: previousInfosPlayer.volume,
loop: previousInfosPlayer.loop,
autoPlay: previousInfosPlayer.autoPlay,
autoLeave: previousInfosPlayer.autoLeave,
node: this.identifier ?? this.host,
notBackup: true
});
player.previous = previousInfosPlayer.previous;
if (resumedPlayer.track) {
const track = new (index_1.Structure.get("MoonlinkTrack"))(resumedPlayer.track);
player.current = track;
player.current.position =
resumedPlayer.state.position;
await player.restart();
}
}
this._manager.emit("nodeResumed", this, resumedPlayers);
}
break;
case "stats":
delete payload.op;
this.stats = { ...payload };
break;
case "playerUpdate":
let player = this._manager.players.get(payload.guildId);
if (!player)
return;
player.ping = payload.state.ping;
if (!player.current)
return;
player.current.position = payload.state.position;
player.current.time = payload.state.time;
this._manager.emit("playerUpdate", player, payload, this);
break;
case "event":
this.handleEvent(payload);
break;
default:
this._manager.emit("nodeError", this, new Error(`@Moonlink(Nodes) - Unexpected op "${payload.op}" with data: ${payload}`));
}
}
async handleEvent(payload) {
if (!payload)
return;
if (!payload.guildId)
return;
if (!this._manager.players.has(payload.guildId))
return;
let player = this._manager.players.get(payload.guildId);
switch (payload.type) {
case "TrackStartEvent": {
if (!player.current)
player.current = new (index_1.Structure.get("MoonlinkTrack"))(payload.track);
player.playing = true;
player.paused = false;
this._manager.emit("trackStart", player, player.current);
break;
}
case "TrackEndEvent": {
let track = player.current;
let queue = player.queue.all;
player.playing = false;
if (this._manager.options.previousTracksInArray)
player.previous.push(track);
else
player.previous = track;
if (["loadFailed", "cleanup"].includes(payload.reason)) {
if (!queue) {
player.queue.clear();
{
this._manager.emit("trackEnd", player, track, payload);
this._manager.emit("queueEnd", player, track);
return;
}
}
player.play();
return;
}
if (payload.reason === "replaced") {
this._manager.emit("trackEnd", player, track, payload);
return;
}
if (track && player.loop) {
if (player.loop == 1) {
await this.rest.update({
guildId: payload.guildId,
data: { track: { encoded: payload.track.encoded } }
});
if (this.resumed)
player.current = new (index_1.Structure.get("MoonlinkTrack"))(payload.track);
return;
}
if (player.loop == 2) {
player.queue.add(new (index_1.Structure.get("MoonlinkTrack"))(payload.track));
if (!queue || queue.length === 0)
return this._manager.emit("trackEnd", player, track, payload);
player.play();
return;
}
else {
this._manager.emit("trackEnd", player, track);
this._manager.emit("debug", "@Manager(Nodes) - invalid loop value will be ignored!");
}
}
if (player.queue.size) {
this._manager.emit("trackEnd", player, track);
player.play();
return;
}
if (typeof player.autoPlay === "boolean" &&
player.autoPlay === true) {
if (payload.reason == "stopped")
return;
let uri = `https://www.youtube.com/watch?v=${track.identifier}&list=RD${track.identifier}`;
let req = await this._manager.search(uri);
if (!req ||
!req.tracks ||
["loadFailed", "cleanup"].includes(req.loadType))
return player.stop();
let data = req.tracks[Math.floor(Math.random() * Math.floor(req.tracks.length))];
player.queue.add(data);
player.play();
return;
}
if (player.autoLeave) {
player.destroy();
this._manager.emit("autoLeaved", player, track);
}
if (!player.queue.size) {
this._manager.emit("debug", "@Moonlink(Nodes) - The queue is empty");
this._manager.emit("trackEnd", player, track, payload);
this._manager.emit("queueEnd", player);
player.current = null;
player.queue.clear();
}
break;
}
case "TrackStuckEvent": {
this._manager.emit("trackStuck", player, player.current);
player.stop();
break;
}
case "TrackExceptionEvent": {
this._manager.emit("trackError", player, player.current);
player.stop();
break;
}
case "WebSocketClosedEvent": {
this._manager.emit("socketClosed", player, payload);
break;
}
default: {
const error = new Error(`@Moonlink(Nodes) - unknown event '${payload.type}'.`);
this._manager.emit("nodeError", this, error);
}
}
}
movePlayers() {
this.getAllPlayers.forEach(player => {
let anotherNode = this._manager.nodes.sortByUsage(this._manager.options?.sortNode ?? "players")[0];
this._manager.emit("debug", `@Moonlink(Node) - Moving player ${player.guildId} to ${anotherNode.identifier ?? anotherNode.host}`);
player.transferNode(anotherNode);
});
return true;
}
get getAllPlayers() {
return Object.values(this._manager.players.all).filter(player => player.node === this);
}
}
exports.MoonlinkNode = MoonlinkNode;

View File

@ -0,0 +1,52 @@
/// <reference types="node" />
import { EventEmitter } from "node:events";
import { MoonlinkManager, MoonlinkQueue, MoonlinkNode, MoonlinkTrack, MoonlinkFilters } from "../../index";
import { MoonlinkWebSocket } from "../@Services/MoonlinkWebSocket";
import { IPlayerData, connectOptions } from "../@Typings";
export declare class MoonlinkPlayer {
manager: MoonlinkManager;
guildId: string;
textChannel: string;
voiceChannel: string;
voiceRegion: string;
autoPlay: boolean | null;
autoLeave: boolean | null;
connected: boolean | null;
playing: boolean | null;
paused: boolean | null;
loop: number | null;
volume: number;
ping: number;
queue: MoonlinkQueue;
filters: MoonlinkFilters;
current: Record<string, any>;
previous: MoonlinkTrack[] | MoonlinkTrack | Record<string, any>;
data: Record<string, any>;
node: MoonlinkNode | any;
voiceReceiverWs: MoonlinkWebSocket | any;
constructor(data: IPlayerData);
set(key: string, value: unknown): void;
get<T>(key: string): T;
setTextChannel(channelId: string): boolean;
setVoiceChannel(channelId: string): boolean;
setAutoLeave(mode?: boolean | null): boolean | null;
setAutoPlay(mode: boolean): boolean;
connect(options: connectOptions): boolean | null;
disconnect(): boolean;
restart(): Promise<void>;
play(track?: MoonlinkTrack | string): Promise<boolean>;
pause(): Promise<boolean>;
resume(): Promise<boolean>;
private updatePlaybackStatus;
stop(destroy?: boolean): Promise<boolean>;
skip(position?: number): Promise<boolean>;
setVolume(percent: number): Promise<number>;
setLoop(mode: number | string | null): number | string | null;
destroy(): Promise<boolean>;
private validateNumberParam;
seek(position: number): Promise<number | null>;
shuffle(): boolean;
transferNode(node: MoonlinkNode | string): Promise<boolean>;
listenVoice(): EventEmitter | boolean;
stopListeningVoice(): void;
}

View File

@ -0,0 +1,415 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkPlayer = void 0;
const node_events_1 = require("node:events");
const index_1 = require("../../index");
const MoonlinkWebSocket_1 = require("../@Services/MoonlinkWebSocket");
class MoonlinkPlayer {
manager = index_1.Structure.manager;
guildId;
textChannel;
voiceChannel;
voiceRegion;
autoPlay;
autoLeave;
connected;
playing;
paused;
loop;
volume;
ping;
queue;
filters;
current;
previous;
data;
node;
voiceReceiverWs;
constructor(data) {
this.guildId = data.guildId;
this.textChannel = data.textChannel;
this.voiceChannel = data.voiceChannel;
this.voiceRegion = data.voiceRegion;
this.autoPlay = data.autoPlay;
this.autoLeave = data.autoLeave || false;
this.connected = data.connected || false;
this.playing = data.playing || false;
this.paused = data.paused || false;
this.loop = data.loop || 0;
this.volume = data.volume || 100;
this.ping = data.ping || 0;
this.queue = new (index_1.Structure.get("MoonlinkQueue"))(this.manager, this.guildId);
this.current = null;
this.previous = [];
this.data = {};
this.node = this.manager.nodes.get(data.node);
this.filters = new (index_1.Structure.get("MoonlinkFilters"))(this);
this.voiceReceiverWs = null;
if (!data.notBackup && this.manager.options.resume)
this.manager.players.backup(this);
}
set(key, value) {
this.data[key] = value;
}
get(key) {
return this.data[key] || null;
}
setTextChannel(channelId) {
if (!channelId) {
throw new Error('@Moonlink(Player) - "channelId" option is empty');
}
if (typeof channelId !== "string") {
throw new Error('@Moonlink(Player) - option "channelId" is different from a string');
}
this.manager.emit("playerSetTextChannel", this, this.textChannel, channelId);
this.textChannel = channelId;
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
setVoiceChannel(channelId) {
if (!channelId) {
throw new Error('@Moonlink(Player) - "channelId" option is empty');
}
if (typeof channelId !== "string") {
throw new Error('@Moonlink(Player) - option "channelId" is different from a string');
}
this.manager.emit("playerSetVoiceChannel", this, this.voiceChannel, channelId);
this.voiceChannel = channelId;
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
setAutoLeave(mode) {
if (typeof mode !== "boolean") {
throw new Error('@Moonlink(Player) - "mode" option is empty or different from a boolean');
}
mode ? mode : (mode = !this.autoLeave);
this.autoLeave = mode;
this.manager.emit("playerAutoLeaveTriggered", this, mode);
if (this.manager.options.resume)
this.manager.players.backup(this);
return mode;
}
setAutoPlay(mode) {
if (typeof mode !== "boolean") {
throw new Error('@Moonlink(Player) - "mode" option is empty or different from a boolean');
}
this.autoPlay = mode;
this.manager.emit("playerAutoPlayTriggered", this, mode);
if (this.manager.options.resume)
this.manager.players.backup(this);
return mode;
}
connect(options) {
options = options || { setDeaf: false, setMute: false };
const { setDeaf, setMute } = options;
this.manager._SPayload(this.guildId, JSON.stringify({
op: 4,
d: {
guild_id: this.guildId,
channel_id: this.voiceChannel,
self_mute: setMute,
self_deaf: setDeaf
}
}));
this.connected = true;
this.manager.emit("playerConnected", this);
return true;
}
disconnect() {
this.manager._SPayload(this.guildId, JSON.stringify({
op: 4,
d: {
guild_id: this.guildId,
channel_id: null,
self_mute: false,
self_deaf: false
}
}));
this.connected = false;
this.voiceChannel = null;
return true;
}
async restart() {
this.connect({
setDeaf: true,
setMute: false
});
await this.manager.players.attemptConnection(this.guildId);
if (!this.current && this.queue.size) {
this.play();
return;
}
else {
await this.node.rest.update({
guildId: this.guildId,
data: {
track: {
encoded: this.current.encoded
},
position: this.current.position,
volume: this.volume
}
});
}
this.manager.emit("playerRestarted", this);
if (this.manager.options.resume)
this.manager.players.backup(this);
}
async play(track) {
if (!track && !this.queue.size)
return false;
let data = track
? track
: this.queue.shift();
if (!data)
return false;
if (this.loop && Object.keys(this.current).length != 0) {
this.current.time ? (this.current.time = 0) : false;
this.ping = undefined;
this.queue.push(this.current);
}
if (typeof data == "string") {
try {
let resolveTrack = await this.node.rest.decodeTrack(data);
data = new (index_1.Structure.get("MoonlinkTrack"))(resolveTrack, null);
}
catch (err) {
this.manager.emit("debug", "@Moonlink(Player) - Fails when trying to decode a track " +
data +
", error: " +
err);
return;
}
}
this.current = data;
await this.node.rest.update({
guildId: this.guildId,
data: {
track: {
encoded: data.encoded
},
volume: this.volume
}
});
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
async pause() {
if (this.paused)
return true;
await this.updatePlaybackStatus(true);
this.manager.emit("playerPaused", this);
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
async resume() {
if (this.playing)
return true;
await this.updatePlaybackStatus(false);
this.manager.emit("playerResume", this);
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
async updatePlaybackStatus(paused) {
await this.node.rest.update({
guildId: this.guildId,
data: { paused }
});
this.paused = paused;
this.playing = !paused;
}
async stop(destroy) {
if (!this.queue.size) {
await this.node.rest.update({
guildId: this.guildId,
data: {
track: { encoded: null }
}
});
if (this.manager.options.resume)
this.manager.players.backup(this);
}
this.manager.emit("playerStopped", this, this.current);
this.manager.options?.destroyPlayersStopped && destroy
? this.destroy()
: this.queue.clear();
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
async skip(position) {
if (position) {
this.validateNumberParam(position, "position");
let queue = this.queue.all();
if (!queue[position - 1]) {
throw new Error(`@Moonlink(Player) - the indicated position does not exist, make security in your code to avoid errors`);
}
let data = queue.splice(position - 1, 1)[0];
this.manager.emit("playerSkipped", this, this.current, data);
this.current = data;
this.queue.setQueue(queue);
await this.play(data);
return true;
}
if (this.queue.size) {
this.manager.emit("playerSkipped", this, this.current, this.queue.all[0]);
this.play();
return false;
}
else {
this.stop();
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
}
async setVolume(percent) {
if (typeof percent == "undefined" || isNaN(percent)) {
throw new Error('@Moonlink(Player) - option "percent" is empty or different from a number');
}
var beforeChange = this.volume;
this.volume = percent;
await this.node.rest.update({
guildId: this.guildId,
data: { volume: percent }
});
this.manager.emit("playerVolumeChanged", this, beforeChange, percent);
if (this.manager.options.resume)
this.manager.players.backup(this);
return percent;
}
setLoop(mode) {
if (typeof mode == "string" &&
["off", "track", "queue"].includes(mode)) {
mode == "track"
? (mode = 1)
: mode == "queue"
? (mode = 2)
: (mode = 0);
}
if (typeof mode !== "number" ||
(mode !== null && (mode < 0 || mode > 2))) {
throw new Error('@Moonlink(Player) - the option "mode" is different from a number and string or the option does not exist');
}
this.manager.emit("playerLoopSet", this, this.loop, mode);
this.loop = mode;
if (this.manager.options.resume)
this.manager.players.backup(this);
return mode;
}
async destroy() {
if (this.connected)
this.disconnect();
await this.node.rest.destroy(this.guildId);
this.queue.clear();
this.manager.players.delete(this.guildId);
this.manager.emit("debug", "@Moonlink(Player) - Destroyed player " + this.guildId);
this.manager.emit("playerDestroyed", this.guildId);
if (this.manager.options.resume)
this.manager.players.backup(this);
return true;
}
validateNumberParam(param, paramName) {
if (typeof param !== "number") {
throw new Error(`@Moonlink(Player) - option "${paramName}" is empty or different from a number`);
}
}
async seek(position) {
this.validateNumberParam(position, "position");
if (position >= this.current.duration) {
throw new Error(`@Moonlink(Player) - parameter "position" is greater than the duration of the current track`);
}
if (!this.current.isSeekable && this.current.isStream) {
throw new Error(`@Moonlink(Player) - seek function cannot be applied on live video or cannot be applied in "isSeekable"`);
}
this.manager.emit("playerSeeking", this, this.current.position, position);
await this.node.rest.update({
guildId: this.guildId,
data: { position }
});
if (this.manager.options.resume)
this.manager.players.backup(this);
return position;
}
shuffle() {
if (!this.queue.size) {
throw new Error("@Moonlink(Player)the one that is empty so that the shuffle can be performed");
}
let oldQueue = Array.from(this.queue.all);
let shuffleStatus = this.queue.shuffle();
this.manager.emit("playerShuffled", this, oldQueue, this.queue.all, shuffleStatus);
if (this.manager.options.resume)
this.manager.players.backup(this);
return shuffleStatus;
}
async transferNode(node) {
typeof node == "string" ? (node = this.manager.nodes.get(node)) : null;
if (!node)
return false;
this.node = node;
if (this.current || this.queue.size) {
await this.restart();
return true;
}
else {
await this.manager.players.attemptConnection(this.guildId);
return true;
}
}
listenVoice() {
if (!this.node.info.isNodeLink)
return false;
this.voiceReceiverWs = new MoonlinkWebSocket_1.MoonlinkWebSocket(`ws${this.node.secure ? "s" : ""}://${this.node.address}/connection/data`, {
headers: {
Authorization: this.node.password,
"Client-Name": `Moonlink/${this.manager.version}`,
"guild-id": this.guildId,
"user-id": this.manager.clientId
}
});
const listener = new node_events_1.EventEmitter();
this.voiceReceiverWs.on("message", (data) => {
const payload = JSON.parse(data);
switch (payload?.type) {
case "startSpeakingEvent": {
listener.emit("start", {
...payload.data
});
break;
}
case "endSpeakingEvent": {
payload.data.data = Buffer.from(payload.data.data, "base64");
listener.emit("end", {
...payload.data
});
break;
}
default: {
listener.emit("unknown", {
...payload
});
}
}
});
this.voiceReceiverWs.on("close", () => {
listener.emit("close");
});
this.voiceReceiverWs.on("error", error => {
listener.emit("error", error);
});
return listener;
}
stopListeningVoice() {
if (!this.voiceReceiverWs)
return;
this.voiceReceiverWs.close();
this.voiceReceiverWs = null;
}
}
exports.MoonlinkPlayer = MoonlinkPlayer;

View File

@ -0,0 +1,19 @@
import { MoonlinkDatabase, MoonlinkManager, MoonlinkTrack } from "../..";
export declare class MoonlinkQueue {
db: MoonlinkDatabase;
private guildId;
private manager;
constructor(manager: MoonlinkManager, guildId: string);
add(data: MoonlinkTrack, position?: number): void;
has(identifier: string): boolean;
first(): any;
shift(): any;
push(data: any): void;
clear(): boolean;
get size(): number;
shuffle(): boolean;
remove(position: number): boolean;
get all(): any;
getQueue(): MoonlinkTrack[];
setQueue(queue: MoonlinkTrack[]): void;
}

View File

@ -0,0 +1,99 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkQueue = void 0;
const __1 = require("../..");
class MoonlinkQueue {
db = __1.Structure.db;
guildId;
manager;
constructor(manager, guildId) {
if (!manager || !guildId) {
throw new Error("[ @Moonlink/Queue ]: Invalid constructor arguments");
}
this.guildId = guildId;
this.manager = __1.Structure.manager;
}
add(data, position) {
if (!data)
throw new Error('[ @Moonlink/Queue ]: "data" option is empty');
let queue = this.getQueue();
position =
position !== undefined && position >= 1
? position - 1
: queue.length;
if (position < 0 || position > queue.length) {
throw new Error("@Moonlink(Queue) - Invalid position specified");
}
queue.splice(position, 0, data);
this.setQueue(queue);
}
has(identifier) {
if (!identifier || typeof identifier !== "string") {
throw new Error("@Moonlink(Queue) - Invalid identifier specified");
}
const queue = this.getQueue();
return queue.some(track => track.identifier === identifier);
}
first() {
const queue = this.getQueue();
return queue.length > 0 ? queue[0] : null;
}
shift() {
let queue = this.getQueue();
if (!queue.length)
return null;
let track = queue.shift();
this.setQueue(queue);
return track;
}
push(data) {
let queue = this.getQueue();
queue.push(data);
this.setQueue(queue);
}
clear() {
const queue = this.getQueue();
if (queue.length > 0) {
this.setQueue([]);
return true;
}
return false;
}
get size() {
return this.getQueue().length;
}
shuffle() {
const currentQueue = this.all;
for (let i = currentQueue.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[currentQueue[i], currentQueue[j]] = [
currentQueue[j],
currentQueue[i]
];
}
this.setQueue(currentQueue);
return true;
}
remove(position) {
if (!position || typeof position !== "number" || position < 1) {
throw new Error("[ @Moonlink/Queue ]: Invalid position specified");
}
const queue = this.getQueue();
if (position > queue.length) {
throw new Error("[ @Moonlink/Queue ]: Position exceeds queue length");
}
queue.splice(position - 1, 1);
this.setQueue(queue);
return true;
}
get all() {
return this.getQueue();
}
getQueue() {
return this.db.get(`queue.${this.guildId}`) || [];
}
setQueue(queue) {
this.db.set(`queue.${this.guildId}`, queue);
}
}
exports.MoonlinkQueue = MoonlinkQueue;

View File

@ -0,0 +1,61 @@
/// <reference types="node" />
import { EventEmitter } from "node:events";
import { MoonlinkPlayer, MoonlinkTrack, MoonlinkNode, PlayerManager, NodeManager } from "../../index";
import { INode, IOptions, VoicePacket, SearchResult, SearchQuery } from "../@Typings";
export interface MoonlinkEvents {
autoLeaved: (player: MoonlinkPlayer, track?: any) => void;
debug: (...args: any) => void;
nodeCreate: (node: MoonlinkNode) => void;
nodeReady: (node: MoonlinkNode, sessionId: string, resumed: boolean) => void;
nodeDestroy: (node: MoonlinkNode) => void;
nodeResumed: (node: MoonlinkNode, players: MoonlinkEvents[]) => void;
nodeReconnect: (node: MoonlinkNode) => void;
nodeClose: (node: MoonlinkNode, code: number, reason: string) => void;
nodeRaw: (node: MoonlinkNode, payload: object) => void;
nodeError: (node: MoonlinkNode, error: Error) => void;
trackStart: (player: MoonlinkPlayer, current: any) => void;
trackEnd: (player: MoonlinkPlayer, track: any, payload?: Record<string, any>) => void;
trackStuck: (player: MoonlinkPlayer, track: any) => void;
trackError: (player: MoonlinkPlayer, track: any) => void;
queueEnd: (player: MoonlinkPlayer, track?: any) => void;
playerConnected: (player: MoonlinkPlayer) => void;
playerCreated: (guildId: string) => void;
playerPaused: (player: MoonlinkPlayer) => void;
playerRestarted: (player: MoonlinkPlayer) => void;
playerResume: (player: MoonlinkPlayer) => void;
playerStopped: (player: MoonlinkPlayer, current: MoonlinkTrack) => void;
playerSetVoiceChannel: (player: MoonlinkPlayer, oldChannel: string, newChannel: string) => void;
playerAutoPlayTriggered: (player: MoonlinkPlayer, mode: boolean) => void;
playerAutoLeaveTriggered: (player: MoonlinkPlayer, mode: boolean) => void;
playerSetTextChannel: (player: MoonlinkPlayer, oldChannel: string, newChannel: string) => void;
playerVolumeChanged: (player: MoonlinkPlayer, oldVolume: number, newVolume: number) => void;
playerSkipped: (player: MoonlinkPlayer, oldCurrent: MoonlinkTrack, newCurrent: MoonlinkTrack) => void;
playerSeeking: (player: MoonlinkPlayer, oldPosition: number, newPosition: number) => void;
playerLoopSet: (player: MoonlinkPlayer, oldMode: number, mode: number) => void;
playerShuffled: (player: MoonlinkPlayer, oldQueue: unknown[], newQueue: MoonlinkTrack[], status: boolean) => void;
playerMove: (player: MoonlinkPlayer, newVoiceChannel: string, oldVoiceChannel: string) => void;
playerDisconnect: (player: MoonlinkPlayer) => void;
playerDestroyed: (guildId: string) => void;
playerUpdate: (player: MoonlinkPlayer, node: MoonlinkNode, payload: Record<string, any>) => void;
socketClosed: (player: MoonlinkPlayer, track: MoonlinkTrack) => void;
}
export declare interface MoonlinkManager {
on<K extends keyof MoonlinkEvents>(event: K, listener: MoonlinkEvents[K]): this;
once<K extends keyof MoonlinkEvents>(event: K, listener: MoonlinkEvents[K]): this;
emit<K extends keyof MoonlinkEvents>(event: K, ...args: Parameters<MoonlinkEvents[K]>): boolean;
off<K extends keyof MoonlinkEvents>(event: K, listener: MoonlinkEvents[K]): this;
}
export declare class MoonlinkManager extends EventEmitter {
clientId: string;
readonly _nodes: INode[];
readonly _SPayload: Function;
readonly players: PlayerManager;
readonly nodes: NodeManager;
readonly version: number;
options: IOptions;
initiated: boolean;
constructor(nodes: INode[], options: IOptions, SPayload: Function);
init(clientId?: string): Promise<this>;
search(options: string | SearchQuery): Promise<SearchResult>;
packetUpdate(packet: VoicePacket): void;
}

View File

@ -0,0 +1,148 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkManager = void 0;
const node_events_1 = require("node:events");
const index_1 = require("../../index");
class MoonlinkManager extends node_events_1.EventEmitter {
clientId;
_nodes;
_SPayload;
players;
nodes;
version = require("../../index").version;
options;
initiated = false;
constructor(nodes, options, SPayload) {
super();
this._nodes = nodes;
this._SPayload = SPayload;
this.players = new (index_1.Structure.get("PlayerManager"))();
this.nodes = new (index_1.Structure.get("NodeManager"))();
this.options = options;
if (options.plugins) {
options.plugins.forEach(plugin => {
plugin.load(this);
});
}
if (!this.options.clientName)
this.options.clientName = `Moonlink/${this.version} (https://github.com/Ecliptia/moonlink.js)`;
}
async init(clientId) {
if (this.initiated)
return this;
this.emit("debug", "@Moonlink - moonlink.js has started the initialization process, do not attempt to use functions until everything is initialized correctly ");
if (!clientId && !this.options.clientId)
throw new TypeError('@Moonlink(Manager): "clientId" option is required.');
this.options.clientId = clientId;
this.clientId = clientId;
index_1.Structure.init(this);
await index_1.Structure.db.fetch();
this.nodes.init();
this.players.init();
this.initiated = true;
return this;
}
async search(options) {
return new Promise(async (resolve, reject) => {
try {
if (!options) {
throw new Error("@Moonlink(Manager) - the search option has to be in string format or in an array");
}
let query;
let source;
let requester;
let node;
if (typeof options === "object") {
query = options.query;
source = options.source;
requester = options.requester;
node = options.node;
}
else {
query = options;
}
if (requester &&
typeof requester !== "string" &&
typeof requester !== "object") {
throw new Error('@Moonlink(Manager) - The "requester" option in the search function must be in string or array format');
}
if (source && typeof source !== "string") {
throw new Error("@Moonlink(Manager) - the source option has to be in string format");
}
if (typeof query !== "string" && typeof query !== "object") {
throw new Error("@Moonlink(Manager) - (search) the search option has to be in string or array format");
}
(node && (node = this.nodes.get(node))) ??
(node = this.nodes.sortByUsage("memory")[0]);
const sources = {
youtube: "ytsearch",
youtubemusic: "ytmsearch",
soundcloud: "scsearch"
};
let searchIdentifier = query.startsWith("http://") || query.startsWith("https://")
? query
: source
? sources[source]
? `${sources[source]}:${query}`
: `${source}:${query}`
: `ytsearch:${query}`;
const params = new URLSearchParams({
identifier: searchIdentifier
});
const res = await node.request("loadtracks", params);
if (["error", "empty"].includes(res.loadType)) {
this.emit("debug", "@Moonlink(Manager) - not found or there was an error loading the track");
return resolve(res);
}
if (["track"].includes(res.loadType)) {
res.data = [res.data];
}
if (["playlist"].includes(res.loadType)) {
res.playlistInfo = {
duration: res.data.tracks.reduce((acc, cur) => acc + cur.info.length, 0),
name: res.data.info.name,
selectedTrack: res.data.info.selectedTrack
};
res.pluginInfo = res.data.pluginInfo;
res.data = [...res.data.tracks];
}
const tracks = res.data.map(track => new (index_1.Structure.get("MoonlinkTrack"))(track, requester));
resolve({
...res,
tracks
});
}
catch (error) {
this.emit("debug", "@Moonlink(Manager) - An error occurred: " + error);
reject(error);
}
});
}
packetUpdate(packet) {
const { t, d } = packet;
if (!["VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"].includes(t))
return;
const update = d;
const guildId = update.guild_id;
const player = this.players.get(guildId);
if (!update.token && !update.session_id)
return;
if (t === "VOICE_SERVER_UPDATE") {
this.players.handleVoiceServerUpdate(update, guildId);
}
if (t === "VOICE_STATE_UPDATE" && update.user_id === this.clientId) {
if (!player)
return;
if (!update.channel_id) {
this.players.handlePlayerDisconnect(guildId);
}
if (update.channel_id &&
update.channel_id !== player.voiceChannel) {
this.players.handlePlayerMove(update.channel_id, player.voiceChannel, guildId);
}
this.players.updateVoiceStates(guildId, update);
this.players.attemptConnection(guildId);
}
}
}
exports.MoonlinkManager = MoonlinkManager;

View File

@ -0,0 +1,19 @@
import { MoonlinkManager, MoonlinkNode, INode, SortType } from "../../index";
export declare class NodeManager {
initiated: boolean;
_manager: MoonlinkManager;
map: Map<any, any>;
constructor();
init(): void;
check(): void;
add(node: INode): void;
remove(name: string): boolean;
get(name: any): any;
sortByUsage(sortType: SortType): MoonlinkNode[];
private sortNodesByMemoryUsage;
private sortNodesByLavalinkCpuLoad;
private sortNodesBySystemCpuLoad;
private sortNodesByCalls;
private sortNodesByPlayingPlayers;
private sortNodesByPlayers;
}

View File

@ -0,0 +1,89 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeManager = void 0;
const index_1 = require("../../index");
class NodeManager {
initiated = false;
_manager;
map;
constructor() {
this.map = new Map();
}
init() {
this._manager = index_1.Structure.manager;
this.check();
this._manager.emit("debug", "@Moonlink(Nodes) - Structure(Nodes) was successfully initialized and assigned the value of the main class and checked the nodes");
this.initiated = true;
}
check() {
if (!this._manager?._nodes)
throw new Error('@Moonlink(Nodes) - "nodes" option is empty');
if (this._manager?._nodes && !Array.isArray(this._manager?._nodes))
throw new Error('@Moonlink(Nodes) - the "nodes" option has to be in an array');
if (this._manager?._nodes && this._manager?._nodes.length == 0)
throw new Error('@Moonlink(Nodes) - there are no parameters with "node(s)" information in the object');
this._manager?._nodes.forEach(node => this.add(node));
}
add(node) {
this._manager.emit("debug", `@Moonlink(Nodes) - The node ${node.host || node.identifier} has been added, and is starting its initialization process`);
const NodeInstance = new (index_1.Structure.get("MoonlinkNode"))(node);
if (node.identifier)
this.map.set(node.identifier, NodeInstance);
else
this.map.set(node.host, NodeInstance);
return;
}
remove(name) {
if (!name) {
throw new Error('[ @Moonlink/Manager ]: option "name" is empty');
}
const removed = this.map.delete(name);
this._manager.emit("debug", `@Moonlink(Nodes) - The node ${name} has been successfully deleted`);
return removed;
}
get(name) {
return this.map.get(name) ? this.map.get(name) : null;
}
sortByUsage(sortType) {
this._manager.emit("debug", `@Moonlink(Nodes) - A new lavalink server is being drawn, sorting the type ${sortType}`);
const connectedNodes = [...this.map.values()].filter(node => node.state == "READY");
if (connectedNodes.length == 0)
throw new TypeError("[ @Moonlink/Manager ]: No lavalink server connected");
switch (sortType) {
case "memory":
return this.sortNodesByMemoryUsage(connectedNodes);
case "cpuLavalink":
return this.sortNodesByLavalinkCpuLoad(connectedNodes);
case "cpuSystem":
return this.sortNodesBySystemCpuLoad(connectedNodes);
case "calls":
return this.sortNodesByCalls(connectedNodes);
case "playingPlayers":
return this.sortNodesByPlayingPlayers(connectedNodes);
case "players":
default:
return this.sortNodesByPlayers(connectedNodes);
}
}
sortNodesByMemoryUsage(nodes) {
return nodes.sort((a, b) => (a.stats?.memory?.used || 0) - (b.stats?.memory?.used || 0));
}
sortNodesByLavalinkCpuLoad(nodes) {
return nodes.sort((a, b) => (a.stats?.cpu?.lavalinkLoad || 0) -
(b.stats?.cpu?.lavalinkLoad || 0));
}
sortNodesBySystemCpuLoad(nodes) {
return nodes.sort((a, b) => (a.stats?.cpu?.systemLoad || 0) -
(b.stats?.cpu?.systemLoad || 0));
}
sortNodesByCalls(nodes) {
return nodes.sort((a, b) => a.calls - b.calls);
}
sortNodesByPlayingPlayers(nodes) {
return nodes.sort((a, b) => (a.stats?.playingPlayers || 0) - (b.stats?.playingPlayers || 0));
}
sortNodesByPlayers(nodes) {
return nodes.sort((a, b) => (a.stats?.players || 0) - (b.stats?.players || 0));
}
}
exports.NodeManager = NodeManager;

View File

@ -0,0 +1,19 @@
import { MoonlinkPlayer, MoonlinkManager, createOptions } from "../../index";
export declare class PlayerManager {
_manager: MoonlinkManager;
cache: Record<string, MoonlinkPlayer>;
private voices;
constructor();
init(): void;
handleVoiceServerUpdate(update: any, guildId: string): void;
handlePlayerDisconnect(guildId: string): void;
handlePlayerMove(newChannelId: string, oldChannelId: string, guildId: string): void;
updateVoiceStates(guildId: string, update: any): void;
attemptConnection(guildId: string): Promise<boolean>;
has(guildId: string): boolean;
get(guildId: string): MoonlinkPlayer | null;
create(data: createOptions): MoonlinkPlayer;
get all(): Record<string, any> | null;
backup(player: any): boolean;
delete(guildId: any): void;
}

View File

@ -0,0 +1,160 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlayerManager = void 0;
const index_1 = require("../../index");
class PlayerManager {
_manager;
cache = {};
voices = {};
constructor() { }
init() {
this._manager = index_1.Structure.manager;
this._manager.emit("debug", "@Moonlink(Players) - Structure(Players) has been initialized, and assigned the value of the main class ");
}
handleVoiceServerUpdate(update, guildId) {
this.voices[guildId] = {
...this.voices[guildId],
endpoint: update.endpoint,
token: update.token
};
this.attemptConnection(guildId);
}
handlePlayerDisconnect(guildId) {
this._manager.emit("playerDisconnect", this.cache[guildId]);
this._manager.emit("debug", `@Moonlink(PlayerManager) - a player(${guildId}) was disconnected, issuing stop and resolving information`);
Object.assign(this.cache[guildId], {
connected: false,
voiceChannel: null,
playing: false
});
this.cache[guildId].stop();
}
handlePlayerMove(newChannelId, oldChannelId, guildId) {
this._manager.emit("playerMove", this.cache[guildId], newChannelId, oldChannelId);
this._manager.emit("debug", `@Moonlink(PlayerManager) - a player(${guildId}) was moved channel, resolving information`);
this.cache[guildId].voiceChannel = newChannelId;
if (this._manager.options.resume)
this.backup(this.cache[guildId]);
}
updateVoiceStates(guildId, update) {
this.voices[guildId] = {
...this.voices[guildId],
sessionId: update.session_id
};
}
async attemptConnection(guildId) {
if (!this.cache[guildId])
return false;
if (this.voices[guildId] &&
!this.voices[guildId]?.token &&
!this.voices[guildId]?.endpoint &&
!this.voices[guildId]?.sessionId)
return false;
if (this._manager.options?.balancingPlayersByRegion) {
const voiceRegion = this.voices[guildId]?.endpoint?.match(/([a-zA-Z-]+)\d+/)?.[1];
if (!this.cache[guildId].voiceRegion) {
const connectedNodes = [
...this._manager.nodes.map.values()
].filter(node => node.state == "READY");
const matchingNode = connectedNodes.find(node => node.regions.includes(voiceRegion));
this.cache[guildId].voiceRegion = voiceRegion;
if (matchingNode) {
this.cache[guildId].node = matchingNode;
}
}
}
else if (!this.cache[guildId].voiceRegion) {
const voiceRegion = this.voices[guildId]?.endpoint?.match(/([a-zA-Z-]+)\d+/)?.[1];
this.cache[guildId].voiceRegion = voiceRegion;
}
await this.cache[guildId].node.rest.update({
guildId,
data: {
voice: this.voices[guildId]
}
});
return true;
}
has(guildId) {
return !!this.cache[guildId];
}
get(guildId) {
if (!guildId && typeof guildId !== "string")
throw new Error('@Moonlink(PlayerManager) - "guildId" option in parameter to get player is empty or type is different from string');
if (!this.has(guildId))
return null;
return this.cache[guildId];
}
create(data) {
if (typeof data !== "object" ||
!data.guildId ||
typeof data.guildId !== "string" ||
!data.textChannel ||
typeof data.textChannel !== "string" ||
!data.voiceChannel ||
typeof data.voiceChannel !== "string" ||
(data.autoPlay !== undefined &&
typeof data.autoPlay !== "boolean") ||
(data.node && typeof data.node !== "string")) {
const missingParams = [];
if (!data.guildId || typeof data.guildId !== "string")
missingParams.push("guildId");
if (!data.textChannel || typeof data.textChannel !== "string")
missingParams.push("textChannel");
if (!data.voiceChannel || typeof data.voiceChannel !== "string")
missingParams.push("voiceChannel");
if (data.autoPlay !== undefined &&
typeof data.autoPlay !== "boolean")
missingParams.push("autoPlay");
if (data.node && typeof data.node !== "string")
missingParams.push("node");
throw new Error(`@Moonlink(PlayerManager) - Missing parameters for player creation: ${missingParams.join(", ")}`);
}
if (this.has(data.guildId))
return this.get(data.guildId);
let nodeSorted = this._manager.nodes.sortByUsage(`${this._manager.options.sortNode ?? "players"}`)[0];
data.node = nodeSorted.identifier ?? nodeSorted.host;
this._manager.emit("debug", `@Moonlink(Players) - A server player was created (${data.guildId})`);
this._manager.emit("playerCreated", data.guildId);
this.cache[data.guildId] = new (index_1.Structure.get("MoonlinkPlayer"))(data);
return this.cache[data.guildId];
}
get all() {
return this.cache ?? null;
}
backup(player) {
const playerData = {};
const playerKeys = Object.keys(player);
playerKeys.forEach(key => {
if ([
"guildId",
"voiceChannel",
"textChannel",
"volume",
"loop",
"autoPlay",
"autoLeave",
"data",
"previous"
].includes(key)) {
const value = index_1.Structure.db.get(`players.${player.guildId}.${key}`);
if (player[key] !== undefined &&
player[key] !== null &&
player[key] !== value) {
playerData[key] = player[key];
}
else if (value !== undefined && value !== null) {
playerData[key] = value;
}
}
});
index_1.Structure.db.set(`players.${player.guildId}`, playerData);
return true;
}
delete(guildId) {
delete this.cache[guildId];
if (index_1.Structure.db.get(`players.${guildId}`))
index_1.Structure.db.delete(`players.${guildId}`);
}
}
exports.PlayerManager = PlayerManager;

View File

@ -0,0 +1,7 @@
/// <reference types="node" />
/// <reference types="node" />
import * as http from "http";
import * as https from "https";
export declare function makeRequest<T>(uri: string, options: http.RequestOptions | (https.RequestOptions & {
method?: string;
}), data?: Record<string, any>): Promise<T>;

View File

@ -0,0 +1,128 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeRequest = void 0;
const http = __importStar(require("http"));
const https = __importStar(require("https"));
const http2 = __importStar(require("http2"));
const zlib = __importStar(require("zlib"));
const index_1 = require("../../index");
function makeRequest(uri, options, data) {
return new Promise(resolve => {
const url = new URL(uri);
if (index_1.Structure.manager.options.http2 === true) {
let client = http2.connect(url.origin, {
protocol: url.protocol === "https:" ? "https:" : "http:",
rejectUnauthorized: false
});
const reqOptions = {
":method": options.method,
":path": url.pathname + url.search,
"User-Agent": "Moonlink(Bot)",
"Content-Type": "application/json",
...(options.headers || {})
};
let chunks = "";
const req = client.request(reqOptions);
req.on("error", error => {
index_1.Structure.manager.emit("debug", `@Moonlink(MakeRequest[HTTP/2]) - An error occurred when requesting the ${url}: ${error}`);
client.close();
resolve(error);
});
req.on("response", headers => {
req.setEncoding("utf8");
req.on("data", chunk => (chunks += chunk));
req.on("end", () => {
client.close();
try {
const parsedData = JSON.parse(chunks);
resolve(parsedData);
}
catch (parseError) {
resolve(parseError);
}
});
req.on("error", error => {
index_1.Structure.manager.emit("debug", `@Moonlink(MakeRequest[HTTP/2]) - An error occurred when requesting the ${url}: ${error}`);
client.close();
resolve(error);
});
});
data ? req.end(JSON.stringify(data)) : req.end();
}
else {
let requestModule = http;
if (url.protocol === "https:") {
requestModule = https;
}
options.headers = {
"Content-Type": "application/json",
"Accept-Encoding": "br",
...(options.headers || {})
};
const reqOptions = {
host: url.hostname,
port: url.port
? parseInt(url.port)
: url.protocol === "https:"
? 443
: 80,
path: url.pathname + url.search,
method: options.method || "GET",
...options
};
const req = requestModule.request(url, reqOptions, async (res) => {
let newStream = res;
if (res.headers["content-encoding"] === "br") {
newStream = res.pipe(zlib.createBrotliDecompress());
}
const chunks = [];
newStream.on("data", async (chunk) => {
chunks.push(chunk);
});
newStream.on("end", async () => {
try {
const responseData = Buffer.concat(chunks).toString();
if (reqOptions.path == "/version")
resolve(responseData);
const parsedData = JSON.parse(responseData);
resolve(parsedData);
}
catch (err) {
resolve(err);
}
});
res.on("error", (err) => {
resolve(err);
});
});
if (data) {
req.write(JSON.stringify(data));
}
req.end();
}
});
}
exports.makeRequest = makeRequest;

View File

@ -0,0 +1,28 @@
import { MoonlinkManager, MoonlinkNode } from "../../index";
import { RestOptions, Endpoint } from "../@Typings";
export declare class MoonlinkRestFul {
manager: MoonlinkManager;
sessionId: string;
node: MoonlinkNode;
url: string;
constructor(node: MoonlinkNode);
setSessionId(sessionId: string): void;
update(data: RestOptions): Promise<Record<string, unknown>>;
destroy(guildId: string): Promise<Record<string, unknown>>;
get(endpoint: Endpoint): Promise<any>;
post(endpoint: Endpoint, data: RestOptions): Promise<Record<string, unknown>>;
patch(endpoint: Endpoint, data: RestOptions | any): Promise<Record<string, unknown>>;
delete(endpoint: Endpoint): Promise<Record<string, unknown>>;
decodeTrack(encodedTrack: string): Promise<Record<string, unknown>>;
decodeTracks(data: RestOptions): Promise<Record<string, unknown>>;
getInfo(): Promise<Record<string, unknown>>;
getStats(): Promise<Record<string, unknown>>;
getVersion(): Promise<any>;
routePlannerFreeAddress(data: RestOptions): Promise<Record<string, unknown>>;
routePlannerFreeAll(data: RestOptions): Promise<Record<string, unknown>>;
private ensureUrlIsSet;
private makeGetRequest;
private makePostRequest;
private makePatchRequest;
private makeDeleteRequest;
}

View File

@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkRestFul = void 0;
const index_1 = require("../../index");
class MoonlinkRestFul {
manager;
sessionId;
node;
url;
constructor(node) {
this.manager = index_1.Structure.manager;
this.node = node;
}
setSessionId(sessionId) {
this.sessionId = sessionId;
this.ensureUrlIsSet();
}
async update(data) {
this.ensureUrlIsSet();
return this.makePatchRequest(`sessions/${this.sessionId}/players/${data.guildId}`, data.data);
}
async destroy(guildId) {
return this.makeDeleteRequest(`sessions/${this.sessionId}/players/${guildId}`);
}
async get(endpoint) {
this.ensureUrlIsSet();
return this.makeGetRequest(endpoint);
}
async post(endpoint, data) {
this.ensureUrlIsSet();
return this.makePostRequest(endpoint, data);
}
async patch(endpoint, data) {
this.ensureUrlIsSet();
return this.makePatchRequest(endpoint, data.data);
}
async delete(endpoint) {
this.ensureUrlIsSet();
return this.makeDeleteRequest(endpoint);
}
async decodeTrack(encodedTrack) {
return this.get(`decodetrack?encodedTrack=${encodedTrack}`);
}
async decodeTracks(data) {
return this.post("decodetracks", data);
}
async getInfo() {
return this.get("info");
}
async getStats() {
return this.get("stats");
}
async getVersion() {
const headers = {
Authorization: this.node.password
};
return (0, index_1.makeRequest)((this.node.secure
? "https://"
: "http://") + this.node.address + "/version", {
method: "GET",
headers
}).catch(err => err);
}
async routePlannerFreeAddress(data) {
return this.post("routeplanner/free/address", data);
}
async routePlannerFreeAll(data) {
return this.post("routeplanner/free/all", data);
}
ensureUrlIsSet() {
if (!this.url) {
this.url = this.node.http;
}
}
async makeGetRequest(endpoint) {
const headers = {
Authorization: this.node.password
};
return (0, index_1.makeRequest)(this.url + endpoint, {
method: "GET",
headers
}).catch(err => err);
}
async makePostRequest(endpoint, data) {
const headers = {
Authorization: this.node.password
};
return (0, index_1.makeRequest)(this.url + endpoint, {
method: "POST",
headers
}, data).catch(err => err);
}
async makePatchRequest(endpoint, data) {
const headers = {
Authorization: this.node.password
};
return (0, index_1.makeRequest)(this.url + endpoint, {
method: "PATCH",
headers
}, data).catch(err => err);
}
async makeDeleteRequest(endpoint) {
const headers = {
Authorization: this.node.password
};
return (0, index_1.makeRequest)(this.url + endpoint, {
method: "DELETE",
headers
}).catch(err => err);
}
}
exports.MoonlinkRestFul = MoonlinkRestFul;

View File

@ -0,0 +1,19 @@
/// <reference types="node" />
/// <reference types="node" />
import { EventEmitter } from "events";
export declare class MoonlinkWebSocket extends EventEmitter {
private url;
private options;
private socket;
private established;
private closing;
private headers?;
private partialMessage?;
constructor(uri: string, options: any);
private buildRequestOptions;
private buildHandshake;
connect(): void;
private parseFrame;
writeFrame(data: any): Buffer;
close(code?: number, reason?: string): void;
}

View File

@ -0,0 +1,243 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkWebSocket = void 0;
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const crypto_1 = __importDefault(require("crypto"));
const events_1 = require("events");
class MoonlinkWebSocket extends events_1.EventEmitter {
url;
options;
socket;
established;
closing = false;
headers;
partialMessage = null;
constructor(uri, options) {
super();
this.url = new URL(uri);
this.options = {
port: this.url.port
? this.url.port
: this.url.protocol === "wss:"
? 443
: 80,
method: "GET",
protocol: this.url.protocol === "wss:" ? "https:" : "http:",
secure: this.url.protocol === "wss:",
...options
};
if (process.versions &&
process.versions.node &&
process.versions.node.match(/20\.[0-2]\.0/)) {
require("net").setDefaultAutoSelectFamily(false);
}
this.connect();
}
buildRequestOptions() {
const requestOptions = {
port: this.options.port,
headers: this.buildHandshake(this.options),
method: "GET",
keepAlive: true,
noDelay: true,
keepAliveInitialDelay: 0,
timeout: 0
};
return this.options.secure
? requestOptions
: { ...requestOptions, protocol: "http:" };
}
buildHandshake(options) {
const headers = { ...options.headers };
headers["Host"] = this.url.host;
headers["Upgrade"] = "websocket";
headers["Connection"] = "Upgrade";
headers["Sec-WebSocket-Key"] = crypto_1.default
.randomBytes(16)
.toString("base64");
headers["Sec-WebSocket-Version"] = "13";
return headers;
}
connect() {
const { request } = this.options.secure ? https_1.default : http_1.default;
const requestOptions = this.buildRequestOptions();
const req = request(`${this.options.secure ? "https://" : "http://"}${this.url.host}${this.url.pathname}${this.url.search || ""}`, requestOptions);
req.on("upgrade", (res, socket, head) => {
this.established = true;
this.socket = socket;
this.emit("open", this.socket);
if (res.headers.upgrade.toLowerCase() !== "websocket" ||
res.headers["sec-websocket-accept"] !==
crypto_1.default
.createHash("sha1")
.update(requestOptions.headers["Sec-WebSocket-Key"] +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
.digest("base64")) {
socket.destroy();
return;
}
if (head && head.length > 0)
socket.unshift(head);
this.headers = res.headers;
socket.on("data", data => {
const frame = this.parseFrame(data);
switch (frame.opcode) {
case 0: {
if (frame.fin) {
if (!this.partialMessage) {
this.partialMessage = frame.payload;
}
else {
this.partialMessage = Buffer.concat([
this.partialMessage,
frame.payload
]);
;
}
const message = this.partialMessage.toString("utf-8");
this.emit("message", message);
this.partialMessage = null;
}
else {
if (!this.partialMessage) {
this.partialMessage = frame.payload;
}
else {
this.partialMessage = Buffer.concat([
this.partialMessage,
frame.payload
]);
}
}
break;
}
case 1: {
if (frame.fin) {
this.emit("message", frame.payload.toString("utf-8"));
}
else {
this.partialMessage = frame.payload;
}
break;
}
case 8: {
const code = frame.payload.readUInt16BE(0);
const reason = frame.payload.slice(2).toString("utf8");
this.emit("close", code, reason);
break;
}
default: {
console.log("Emitted data that has not been implemented; opcode: " +
frame.opcode);
if (frame.payloadLength + frame.payloadOffset <
data.length) {
this.socket.unshift(data.slice(frame.payloadLength + frame.payloadOffset));
}
}
}
});
socket.on("close", hadError => {
if (hadError)
this.emit("error", hadError);
if (!this.closing)
this.emit("close");
});
socket.on("error", error => this.emit("error", error));
});
req.on("error", error => {
this.emit("error", error);
});
req.on("close", code => {
if (!this.established)
this.emit("close", code);
});
req.end();
}
parseFrame(data) {
const opcode = data[0] & 0x0f;
const fin = (data[0] & 0x80) === 0x80;
const mask = (data[1] & 0x80) === 0x80;
let payloadOffset = 2;
let payloadLength = data[1] & 0x7f;
if (payloadLength === 126) {
payloadLength = data.readUInt16BE(2);
payloadOffset += 2;
}
else if (payloadLength === 127) {
payloadLength = data.readUInt32BE(2);
payloadOffset += 8;
}
const maskingKey = mask
? data.slice(payloadOffset, payloadOffset + 4)
: null;
payloadOffset += mask ? 4 : 0;
const payload = data.slice(payloadOffset, payloadOffset + payloadLength);
if (mask && maskingKey) {
for (let i = 0; i < payload.length; i++) {
payload[i] ^= maskingKey[i % 4];
}
}
return {
opcode,
fin,
mask,
maskingKey,
payloadLength,
payloadOffset,
payload
};
}
writeFrame(data) {
const { fin, opcode, mask, payload } = data;
const header1 = (fin ? 128 : 0) | (opcode & 15);
const header2 = (mask ? 128 : 0) | (payload.length & 127);
let frame = Buffer.allocUnsafe(2);
frame.writeUInt8(header1, 0);
frame.writeUInt8(header2, 1);
if (payload.length > 125 && payload.length < 65535) {
let extendedFrame = Buffer.allocUnsafe(2);
extendedFrame.writeUInt16BE(payload.length, 0);
frame = Buffer.concat([frame, extendedFrame]);
}
else if (payload.length > 65535) {
let extendedFrame = Buffer.allocUnsafe(8);
extendedFrame.writeUInt32BE(0, 0);
extendedFrame.writeUInt32BE(payload.length, 4);
frame = Buffer.concat([frame, extendedFrame]);
}
if (mask) {
let maskingKey = Buffer.from([
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256)
]);
frame = Buffer.concat([frame, maskingKey]);
for (let i = 0; i < payload.length; i++) {
payload[i] ^= maskingKey[i % 4];
}
}
frame = Buffer.concat([frame, payload]);
return frame;
}
close(code = 1000, reason = "normal closing") {
if (this.socket && this.established) {
this.closing = true;
const buffer = Buffer.alloc(2 + Buffer.byteLength(reason));
buffer.writeUInt16BE(code, 0);
buffer.write(reason, 2, Buffer.byteLength(reason), "utf-8");
let frame = this.writeFrame({
fin: true,
opcode: 8,
mask: false,
payload: buffer
});
this.socket.write(frame);
}
}
}
exports.MoonlinkWebSocket = MoonlinkWebSocket;

267
node_modules/moonlink.js/dist/src/@Typings/index.d.ts generated vendored Normal file
View File

@ -0,0 +1,267 @@
import { MoonlinkManager, MoonlinkPlayer, MoonlinkFilters, MoonlinkDatabase, MoonlinkRestFul, MoonlinkQueue, MoonlinkNode, MoonlinkTrack, PlayerManager, NodeManager, Plugin } from "../../index";
export type Constructor<T> = new (...args: any[]) => T;
export interface createOptions {
guildId: string;
textChannel: string;
voiceChannel: string;
autoPlay?: boolean;
autoLeave?: boolean;
notBackup?: boolean;
loop?: number;
volume?: number;
node?: string;
}
export type SortType = "memory" | "cpuLavalink" | "cpuSystem" | "calls" | "playingPlayers" | "players";
export interface VoiceState {
op: "voiceUpdate";
guildId: string;
event: VoiceServer;
sessionId?: string;
}
export interface VoiceServer {
token: string;
guild_id: string;
endpoint: string;
}
export interface VoiceState {
guild_id: string;
user_id: string;
session_id: string;
channel_id: string;
}
export interface VoicePacket {
t?: "VOICE_SERVER_UPDATE" | "VOICE_STATE_UPDATE";
d: VoiceState | VoiceServer;
}
export type LoadType = "track" | "playlist" | "search" | "empty" | "error";
export interface TrackData {
encoded?: string;
info: TrackDataInfo;
pluginInfo: object;
}
export interface TrackDataInfo {
title: string;
identifier: string;
author: string;
length: number;
position: number;
isSeekable: boolean;
isStream: boolean;
uri: string;
}
export type SearchPlatform = "youtube" | "youtubemusic" | "soundcloud" | string;
export interface SearchQuery {
source?: SearchPlatform | string | undefined | null;
query: string | string[];
requester?: string | object | any;
node?: string;
}
export interface SearchResult {
loadType: LoadType;
tracks: MoonlinkTrack[];
playlistInfo?: PlaylistInfo;
exception?: {
message: string;
severity: string;
};
}
export interface INodeStats {
players: number;
playingPlayers: number;
uptime: number;
memory: {
reservable: number;
used: number;
free: number;
allocated: number;
};
frameStats: {
sent: number;
deficit: number;
nulled: number;
};
cpu: {
cores: number;
systemLoad: number;
lavalinkLoad: number;
};
}
export interface INode {
host: string;
identifier?: string;
password: string;
port: number;
secure: boolean;
regions?: string[];
retryAmount?: number;
retryDelay?: number;
sessionId?: string;
}
export interface IOptions {
clientId?: string;
clientName?: string;
sortNode?: SortType;
autoResume?: boolean;
resume?: boolean;
plugins?: Plugin[];
http2?: boolean;
doNotSaveToFiles?: boolean;
switchPlayersAnotherNode?: boolean;
destroyPlayersStopped?: boolean;
balancingPlayersByRegion?: boolean;
previousTracksInArray?: boolean;
}
export interface IHeaders {
Authorization: string;
"User-Id": string;
"Client-Name": string;
}
export interface Extendable {
MoonlinkManager: typeof MoonlinkManager;
MoonlinkPlayer: typeof MoonlinkPlayer;
MoonlinkDatabase: typeof MoonlinkDatabase;
MoonlinkFilters: typeof MoonlinkFilters;
MoonlinkRestFul: typeof MoonlinkRestFul;
MoonlinkQueue: typeof MoonlinkQueue;
MoonlinkNode: typeof MoonlinkNode;
MoonlinkTrack: typeof MoonlinkTrack;
PlayerManager: typeof PlayerManager;
NodeManager: typeof NodeManager;
}
export interface PlaylistInfo {
name: string;
selectedTrack?: MoonlinkTrack;
duration: number;
}
export interface LavalinkResult {
data: TrackData[];
loadType: LoadType;
exception?: {
message: string;
severity: string;
};
playlistInfo: {
name: string;
selectedTrack?: number;
};
}
export interface VoiceOptions {
endpoint: string;
token: string;
sessionId: string;
connected?: boolean;
ping?: number;
}
export type Endpoint = string;
export interface objectTrack {
encoded: string;
}
export interface PreviousInfosPlayer {
voiceChannel?: string;
textChannel?: string;
guildId?: string;
volume?: number;
autoPlay?: boolean;
autoLeave?: boolean;
previous?: object | MoonlinkTrack;
loop?: number;
current?: Record<string, any>;
}
export interface RestOptions {
guildId: string;
data: {
track?: objectTrack;
identifier?: string;
startTime?: number;
endTime?: number;
volume?: number;
position?: number;
paused?: Boolean;
filters?: Object;
voice?: VoiceOptions;
};
}
export interface connectOptions {
setMute?: boolean;
setDeaf?: boolean;
}
export interface IPlayerData {
guildId: string;
textChannel: string | null;
voiceChannel: string | null;
voiceRegion?: string | null;
autoPlay?: boolean | null;
autoLeave?: boolean | null;
connected?: boolean | null;
playing?: boolean | null;
paused?: boolean | null;
shuffled?: boolean | null;
loop?: number | null;
volume?: number | null;
notBackup?: boolean;
ping?: number;
node?: string;
}
export interface TrackInfo {
identifier: string;
isSeekable: boolean;
author: string;
isStream: boolean;
length: number;
position: number;
title: string;
uri?: string;
artworkUrl?: string;
sourceName: string;
isrc?: string;
}
export interface MoonlinkTrackOptions {
info: TrackInfo;
encoded?: string;
pluginInfo?: object;
}
export interface Equalizer {
band: number;
gain: number;
}
export interface Karaoke {
level?: number;
monoLevel?: number;
filterBand?: number;
filterWidth?: number;
}
export interface Timescale {
speed?: number;
pitch?: number;
rate?: number;
}
export interface Tremolo {
frequency?: number;
depth?: number;
}
export interface Vibrato {
frequency?: number;
depth?: number;
}
export interface Rotation {
rotationHz?: number;
}
export interface Distortion {
sinOffset?: number;
sinScale?: number;
cosOffset?: number;
cosScale?: number;
tanOffset?: number;
tanScale?: number;
offset?: number;
scale?: number;
}
export interface ChannelMix {
leftToLeft?: number;
leftToRight?: number;
rightToLeft?: number;
rightToRight?: number;
}
export interface LowPass {
smoothing?: number;
}

2
node_modules/moonlink.js/dist/src/@Typings/index.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,15 @@
type Data = Record<string, any>;
export declare class MoonlinkDatabase {
data: Data;
id: string;
constructor(clientId: string);
set<T>(key: string, value: T): void;
get<T>(key: string): T | undefined;
push<T>(key: string, value: T): void;
delete(key: string): boolean;
private updateData;
private getFilePath;
fetch(): void;
private save;
}
export {};

View File

@ -0,0 +1,113 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkDatabase = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
class MoonlinkDatabase {
data = {};
id;
constructor(clientId) {
this.fetch();
this.id = clientId;
}
set(key, value) {
if (!key)
throw new Error('@Moonlink(Database) - "key" is empty');
const keys = key.split(".");
if (keys.length === 0)
return;
this.updateData(this.data, keys, value);
this.save();
}
get(key) {
if (!key)
throw new Error('[ @Moonlink(Database) - "key" is empty');
if (Object.keys(this.data).length === 0)
this.fetch();
return (key.split(".").reduce((acc, curr) => acc?.[curr], this.data) ?? null);
}
push(key, value) {
if (!key)
throw new Error('@Moonlink(Database) - "key" is empty');
const oldArray = this.get(key) || [];
if (Array.isArray(oldArray)) {
oldArray.push(value);
this.set(key, oldArray);
}
else {
throw new Error("@Moonlink(Database) - Key does not point to an array");
}
}
delete(key) {
if (!key)
throw new Error('@Moonlink(Database) - "key" is empty');
const keys = key.split(".");
if (keys.length === 0)
return false;
const lastKey = keys.pop() || "";
let currentObj = this.data;
keys.forEach(k => {
if (typeof currentObj[k] === "object") {
currentObj = currentObj[k];
}
else {
throw new Error(`@Moonlink(Database) - Key path "${key}" does not exist`);
}
});
if (currentObj && lastKey in currentObj) {
delete currentObj[lastKey];
this.save();
return true;
}
return false;
}
updateData(data, keys, value) {
let currentObj = data;
keys.forEach((key, index) => {
if (index === keys.length - 1) {
currentObj[key] = value;
}
else {
if (typeof currentObj[key] !== "object") {
currentObj[key] = {};
}
currentObj = currentObj[key];
}
});
}
getFilePath() {
return path_1.default.join(__dirname, "../@Datastore", `database-${this.id}.json`);
}
fetch() {
try {
const directory = path_1.default.join(__dirname, "../@Datastore");
if (!fs_1.default.existsSync(directory)) {
fs_1.default.mkdirSync(directory, { recursive: true });
}
const filePath = this.getFilePath();
const rawData = fs_1.default.readFileSync(filePath, "utf-8");
this.data = JSON.parse(rawData) || {};
}
catch (err) {
if (err.code === "ENOENT") {
this.data = {};
}
else {
throw new Error("@Moonlink(Database) - Failed to fetch data (Error):", err);
}
}
}
save() {
try {
const filePath = this.getFilePath();
fs_1.default.writeFileSync(filePath, JSON.stringify(this.data, null, 2));
}
catch (error) {
throw new Error("@Moonlink(Database) - Failed to save data");
}
}
}
exports.MoonlinkDatabase = MoonlinkDatabase;

View File

@ -0,0 +1,29 @@
import { Equalizer, Karaoke, Timescale, Tremolo, Vibrato, Rotation, Distortion, ChannelMix, LowPass } from "../@Typings";
export declare class MoonlinkFilters {
private player;
private manager;
private rest;
volume: number | null;
equalizer: Equalizer[] | null;
karaoke: Karaoke | null;
timescale: Timescale | null;
tremolo: Tremolo | null;
vibrato: Vibrato | null;
rotation: Rotation | null;
distortion: Distortion | null;
channelMix: ChannelMix | null;
lowPass: LowPass | null;
constructor(player: any);
setVolume(volume: number | null): this;
setEqualizer(equalizer: Equalizer[] | null): this;
setKaraoke(karaoke: Karaoke | null): this;
setTimescale(timescale: Timescale | null): this;
setTremolo(tremolo: Tremolo | null): this;
setVibrato(vibrato: Vibrato | null): this;
setRotation(rotation: Rotation | null): this;
setDistortion(distortion: Distortion | null): this;
setChannelMix(channelMix: ChannelMix | null): this;
setLowPass(lowPass: LowPass | null): this;
resetFilters(): this;
private updateFiltersFromRest;
}

View File

@ -0,0 +1,131 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkFilters = void 0;
const index_1 = require("../../index");
class MoonlinkFilters {
player;
manager;
rest;
volume;
equalizer;
karaoke;
timescale;
tremolo;
vibrato;
rotation;
distortion;
channelMix;
lowPass;
constructor(player) {
this.player = player;
this.rest = player.node.rest;
this.manager = index_1.Structure.manager;
this.volume = this.player.get("Fvolume") || null;
this.equalizer = this.player.get("equalizer") || null;
this.karaoke = this.player.get("karaoke") || null;
this.timescale = this.player.get("timescale") || null;
this.tremolo = this.player.get("tremolo") || null;
this.vibrato = this.player.get("vibrato") || null;
this.rotation = this.player.get("rotation") || null;
this.distortion = this.player.get("distortion") || null;
this.channelMix = this.player.get("channelMix") || null;
this.lowPass = this.player.get("lowPass") || null;
}
setVolume(volume) {
this.player.set("Fvolume", volume);
this.volume = volume;
this.updateFiltersFromRest();
return this;
}
setEqualizer(equalizer) {
this.player.set("equalizer", equalizer);
this.equalizer = equalizer;
this.updateFiltersFromRest();
return this;
}
setKaraoke(karaoke) {
this.player.set("karaoke", karaoke);
this.karaoke = karaoke;
this.updateFiltersFromRest();
return this;
}
setTimescale(timescale) {
this.player.set("timescale", timescale);
this.timescale = timescale;
this.updateFiltersFromRest();
return this;
}
setTremolo(tremolo) {
this.player.set("tremolo", tremolo);
this.tremolo = tremolo;
this.updateFiltersFromRest();
return this;
}
setVibrato(vibrato) {
this.player.set("vibrato", vibrato);
this.vibrato = vibrato;
this.updateFiltersFromRest();
return this;
}
setRotation(rotation) {
this.player.set("rotation", rotation);
this.rotation = rotation;
this.updateFiltersFromRest();
return this;
}
setDistortion(distortion) {
this.player.set("distortion", distortion);
this.distortion = distortion;
this.updateFiltersFromRest();
return this;
}
setChannelMix(channelMix) {
this.player.set("channelMix", channelMix);
this.channelMix = channelMix;
this.updateFiltersFromRest();
return this;
}
setLowPass(lowPass) {
this.player.set("lowPass", lowPass);
this.lowPass = lowPass;
this.updateFiltersFromRest();
return this;
}
resetFilters() {
this.setVolume(null);
this.setEqualizer(null);
this.setKaraoke(null);
this.setTimescale(null);
this.setTremolo(null);
this.setVibrato(null);
this.setRotation(null);
this.setDistortion(null);
this.setChannelMix(null);
this.setLowPass(null);
this.updateFiltersFromRest();
return this;
}
async updateFiltersFromRest() {
let { volume, equalizer, karaoke, timescale, tremolo, vibrato, rotation, distortion, channelMix, lowPass } = this;
const dataToUpdate = {
guildId: this.player.guildId,
data: {
filters: {
...(volume !== null && { volume }),
...(equalizer !== null && { equalizer }),
...(karaoke !== null && { karaoke }),
...(timescale !== null && { timescale }),
...(tremolo !== null && { tremolo }),
...(vibrato !== null && { vibrato }),
...(rotation !== null && { rotation }),
...(distortion !== null && { distortion }),
...(channelMix !== null && { channelMix }),
...(lowPass !== null && { lowPass })
}
}
};
await this.rest.update(dataToUpdate);
return true;
}
}
exports.MoonlinkFilters = MoonlinkFilters;

View File

@ -0,0 +1,20 @@
import { MoonlinkTrackOptions } from "../@Typings";
export declare class MoonlinkTrack {
encoded: string | null;
identifier: string;
title: string;
author: string;
url: string;
duration: number;
position: number;
isSeekable: boolean;
isStream: boolean;
sourceName: string;
requester: any;
artworkUrl: string;
isrc: string;
time?: number;
constructor(data?: MoonlinkTrackOptions, requester?: string | any);
get calculateRealTimePosition(): number;
setRequester(data: any): void;
}

View File

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MoonlinkTrack = void 0;
class MoonlinkTrack {
encoded;
identifier;
title;
author;
url;
duration;
position;
isSeekable;
isStream;
sourceName;
requester;
artworkUrl;
isrc;
time = 0;
constructor(data, requester) {
this.encoded = data.encoded;
this.title = data.info.title;
this.author = data.info.author;
this.url = data.info.uri;
this.duration = data.info.length;
this.position = data.info.position;
this.identifier = data.info.identifier;
this.isSeekable = Boolean(data.info.isSeekable);
this.isStream = Boolean(data.info.isStream);
this.sourceName = data.info.sourceName || null;
this.requester = requester;
this.artworkUrl = data.info.artworkUrl;
this.isrc = data.info.isrc;
}
get calculateRealTimePosition() {
if (this.position >= this.duration) {
return this.duration;
}
if (this.time) {
const elapsed = Date.now() - this.time;
const calculatedPosition = this.position + elapsed / 1000;
if (calculatedPosition >= this.duration) {
return this.duration;
}
return calculatedPosition;
}
return this.position;
}
setRequester(data) {
this.requester = data;
}
}
exports.MoonlinkTrack = MoonlinkTrack;

View File

@ -0,0 +1,13 @@
import { Extendable } from "../@Typings";
import { MoonlinkManager, MoonlinkDatabase } from "../../index";
export declare abstract class Structure {
static manager: MoonlinkManager;
static db: MoonlinkDatabase;
static extend<K extends keyof Extendable, T extends Extendable[K]>(name: K, extender: (target: Extendable[K]) => T): T;
static init(manager: MoonlinkManager): void;
static get<K extends keyof Extendable>(name: K): Extendable[K];
}
export declare class Plugin {
load(manager: MoonlinkManager): void;
unload(manager: MoonlinkManager): void;
}

46
node_modules/moonlink.js/dist/src/@Utils/Structure.js generated vendored Normal file
View File

@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Plugin = exports.Structure = void 0;
const index_1 = require("../../index");
const structures = {
MoonlinkManager: index_1.MoonlinkManager,
MoonlinkRestFul: index_1.MoonlinkRestFul,
MoonlinkPlayer: index_1.MoonlinkPlayer,
MoonlinkFilters: index_1.MoonlinkFilters,
MoonlinkDatabase: index_1.MoonlinkDatabase,
MoonlinkQueue: index_1.MoonlinkQueue,
MoonlinkNode: index_1.MoonlinkNode,
MoonlinkTrack: index_1.MoonlinkTrack,
PlayerManager: index_1.PlayerManager,
NodeManager: index_1.NodeManager
};
class Structure {
static manager;
static db;
static extend(name, extender) {
if (!(name in structures)) {
throw new TypeError(`"${name}" is not a valid structure`);
}
const extended = extender(structures[name]);
structures[name] = extended;
return extended;
}
static init(manager) {
this.manager = manager;
this.db = new (Structure.get("MoonlinkDatabase"))(manager.clientId);
this.manager.emit("debug", `@Moonlink(Structure) - The main class and database are assigned to structure :)`);
}
static get(name) {
const structure = structures[name];
if (!structure) {
throw new TypeError(`"${name}" structure must be provided.`);
}
return structure;
}
}
exports.Structure = Structure;
class Plugin {
load(manager) { }
unload(manager) { }
}
exports.Plugin = Plugin;

55
node_modules/moonlink.js/package.json generated vendored Normal file
View File

@ -0,0 +1,55 @@
{
"name": "moonlink.js",
"version": "3.6.4",
"description": "Imagine a Music... 🌙✨ Welcome to Moonlink.js! We invite you to create your own music bot on Discord using Lavalink, in a simple and easy way! 🎶🤖",
"keywords": [
"bot",
"discord.js",
"moonlink",
"discord",
"easy",
"lavalink",
"music"
],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"start": "rm -r dist && tsc && mv ./dist/index.js ./"
},
"homepage": "https://moonlink.js.org",
"bugs": {
"url": "https://github.com/Ecliptia/moonlink.js/issues",
"email": "1Lucas1apk@gmail.com"
},
"license": {
"type": "Apache-2.0",
"url": "https://opensource.org/licenses/apache2.0.php"
},
"contributors": [
"1Lucas1.apk"
],
"repository": {
"type": "git",
"url": "https://github.com/Ecliptia/moonlink.js"
},
"engines": {
"node": ">=16.6.0"
},
"devDependencies": {
"@types/node": "^18.15.11",
"discord.js": "^14.14.1",
"get-image-colors": "^4.0.1",
"mongoose": "^8.1.1",
"typecript": "^0.0.1-security",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1"
}
}

44
package-lock.json generated
View File

@ -1,22 +1,22 @@
{
"name": "subsonics-web",
"version": "2.0.4",
"version": "2.1.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "subsonics-web",
"version": "2.0.4",
"version": "2.1.0",
"dependencies": {
"cookie": "^0.5.0",
"cookie-parser": "^1.4.6",
"discord.js": "^14.9.0",
"ejs": "^3.1.9",
"erela.js": "^2.4.0",
"express": "^4.18.2",
"express-favicon": "^2.0.4",
"loguix": "^1.4.2",
"markdown-it": "^13.0.1",
"moonlink.js": "^3.6.4",
"nodemon": "^2.0.22",
"pm2": "^5.3.0",
"request": "^2.88.2",
@ -1343,20 +1343,6 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/erela.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/erela.js/-/erela.js-2.4.0.tgz",
"integrity": "sha512-wLfPvfzbDZTDV0zwJYXGkjO9Q6mkXi3PNf984apdv58Ktt0cv1Zp8og3hmp7Ose4C4iwAKitHxV/yiP+pt3FRQ==",
"dependencies": {
"@discordjs/collection": "^1.1.0",
"tslib": "^2.4.0",
"undici": "^5.10.0",
"ws": "^8.8.1"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -2284,6 +2270,14 @@
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
},
"node_modules/moonlink.js": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/moonlink.js/-/moonlink.js-3.6.4.tgz",
"integrity": "sha512-qaL/FxrWtwLLJmUHyFIh+kQauM1TUEb1qeK1oeoPxQ175SXPFgGySCV01h/RGc0Ki4Erke5rKAERt/aP1xVA4w==",
"engines": {
"node": ">=16.6.0"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -4855,17 +4849,6 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
"integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="
},
"erela.js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/erela.js/-/erela.js-2.4.0.tgz",
"integrity": "sha512-wLfPvfzbDZTDV0zwJYXGkjO9Q6mkXi3PNf984apdv58Ktt0cv1Zp8og3hmp7Ose4C4iwAKitHxV/yiP+pt3FRQ==",
"requires": {
"@discordjs/collection": "^1.1.0",
"tslib": "^2.4.0",
"undici": "^5.10.0",
"ws": "^8.8.1"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@ -5552,6 +5535,11 @@
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
"integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
},
"moonlink.js": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/moonlink.js/-/moonlink.js-3.6.4.tgz",
"integrity": "sha512-qaL/FxrWtwLLJmUHyFIh+kQauM1TUEb1qeK1oeoPxQ175SXPFgGySCV01h/RGc0Ki4Erke5rKAERt/aP1xVA4w=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",

View File

@ -1,7 +1,7 @@
{
"name": "subsonics-web",
"author": "Raphix",
"version": "2.0.4",
"version": "2.1.0",
"nodemonConfig": {
"ext": "js, html",
"ignore": [
@ -16,11 +16,11 @@
"cookie-parser": "^1.4.6",
"discord.js": "^14.9.0",
"ejs": "^3.1.9",
"erela.js": "^2.4.0",
"express": "^4.18.2",
"express-favicon": "^2.0.4",
"loguix": "^1.4.2",
"markdown-it": "^13.0.1",
"moonlink.js": "^3.6.4",
"nodemon": "^2.0.22",
"pm2": "^5.3.0",
"request": "^2.88.2",

View File

@ -1,12 +1,14 @@
const { Client, GatewayIntentBits, Collection, ActivityType, REST, Routes } = require("discord.js")
const fs = require("node:fs")
const path = require("path")
const { Manager } = require("erela.js")
const { __glob } = require("./global-variables")
const { LogType } = require("loguix")
const { List } = require("./sub-list")
const nodeFinder = require("./nodes-finder")
const metric = require("webmetrik")
const { MoonlinkManager } = require("moonlink.js")
/// <reference path="./types.d.ts" />
const client = new Client({
intents:[GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMembers],
@ -153,16 +155,17 @@ function startErelaManager(dlog, config) {
const elog = new LogType("Lavalink-Manager")
const nodes = nodeFinder.getNodes()
client.manager = new Manager({
client.manager = new MoonlinkManager(
// The nodes to connect to, optional if using default lavalink options
nodes,
// Method to send voice data to Discord
send: (id, payload) => {
const guild = client.guilds.cache.get(id);
// NOTE: FOR ERIS YOU NEED JSON.stringify() THE PAYLOAD
if (guild) guild.shard.send(payload);
{
/* Options */
},
(guild, sPayload) => {
// Sending payload information to the server
client.guilds.cache.get(guild).shard.send(JSON.parse(sPayload));
}
});
);
const plog = new LogType("Lavalink-Player")
@ -191,11 +194,15 @@ function startErelaManager(dlog, config) {
const list = new List()
client.manager.on("playerCreate", async (player) => {
await client.channels.fetch(player.options.voiceChannel).then(channel => {
plog.log("Nouveau Player instancié dans : " + channel.name)
})
player.setVolume(100)
process.emit("MUSIC_UPDATE_STATE")
@ -213,19 +220,20 @@ function startErelaManager(dlog, config) {
})
client.manager.on("trackStart", async (player) => {
console.log("NEW INVOCATION")
// Create a metric to count the number of songs played
const songPlayed = new metric.Metric("songPlayed", "Nombre de musiques jouées")
songPlayed.setValue(songPlayed.getValue() + 1)
if(player) {
plog.log("Lecture de '" + player.queue.current.title + "' de '" + player.queue.current.author + "'")
// Create a metric to count the number of minutes played by the bot using player.queue.current.duration which is in ms (milliseconds) and verify if it's not a livestream
plog.log("Lecture de '" + player.current.title + "' de '" + player.current.author + "'")
// Create a metric to count the number of minutes played by the bot using player.current.duration which is in ms (milliseconds) and verify if it's not a livestream
if(player.queue.current.duration && player.queue.current.duration != 9223372036854776000) {
if(player.current.duration && player.current.duration != 9223372036854776000) {
const songDuration = new metric.Metric("songDuration", "Durée totale des musiques jouées en secondes")
songDuration.setValue(songDuration.getValue() + (player.queue.current.duration / 1000))
songDuration.setValue(songDuration.getValue() + (player.current.duration / 1000))
}
await list.setCurrent(player)
@ -238,9 +246,11 @@ function startErelaManager(dlog, config) {
client.manager.on("queueEnd", async () => {
let player = await client.manager.players.get("137291455336022018")
console.log("END OF QUEUE")
if(player) {
await list.addCurrentToPrevious()
await list.addCurrentToPrevious(player.previous)
if(await list.haveSongs()) {
await player.play(list.next())
@ -252,29 +262,60 @@ function startErelaManager(dlog, config) {
process.emit("MUSIC_UPDATE_STATE")
})
let events = [
"playerConnected",
"playerCreated",
"playerPaused",
"playerRestarted",
"playerResume",
"playerStopped",
"playerSetVoiceChannel",
"playerAutoPlayTriggered",
"playerAutoLeaveTriggered",
"playerSetTextChannel",
"playerVolumeChanged",
"playerSkipped",
"playerSeeking",
"playerLoopSet",
"playerShuffled",
"playerMove",
"playerDisconnect",
"playerDestroyed",
"playerUpdate"
];
events.forEach(event => {
client.manager.on(event, (player) => {
process.emit("MUSIC_UPDATE_STATE");
});
});
// Emitted whenever a node connects
client.manager.on("nodeConnect", node => {
elog.log(`Connecté au serveur Lavalink : "${node.options.identifier}"` )
nodeFinder.setNodeState(node.options.identifier, true)
client.manager.on("nodeReady", node => {
elog.log(`Connecté au serveur Lavalink : "${node.host}"` )
nodeFinder.setNodeState(node.host, true)
})
// Emitted whenever a node encountered an error
client.manager.on("nodeError", (node, error) => {
elog.warn(`Node "${node.options.identifier}" encountered an error: ${error.message}.`)
elog.warn(`Node "${node.host}" encountered an error: ${error.message}.`)
})
client.manager.on("nodeDisconnect", (node) => {
nodeFinder.setNodeState(node.options.identifier, false)
nodeFinder.setNodeState(node.host, false)
})
// THIS IS REQUIRED. Send raw events to Erela.js
client.on("raw", d => client.manager.updateVoiceState(d));
client.on("raw", data => {
client.manager.packetUpdate(data);
});
}

View File

@ -7,6 +7,8 @@ const nlog = new LogType("Node-Finder")
const nodesState = new Map()
/// <reference path="./types.d.ts" />
function checkAndCreate() {
if (!fs.existsSync(__glob.NODES)) {
try {
@ -32,14 +34,16 @@ function saveNodesFile(data) {
module.exports.reloadNode = function (data) {
const client = discord.getClient()
if (client.manager) {
const nodesPresent = client.manager.nodes
if (nodesPresent.get(data.host)) {
client.manager.destroyNode(data.host)
client.manager.nodes.remove(data.host)
}
nlog.log("Redémarrage de la node : " + data.host)
client.manager.createNode(data)
client.manager.nodes.add(data)
client.manager.nodes.get(data.host).connect()
}
}
@ -99,7 +103,7 @@ module.exports.addNodes = function (data) {
if (client.manager) {
const nodesPresent = client.manager.nodes
if (!nodesPresent.get(data.host)) {
client.manager.createNode(data)
client.manager.nodes.add(data)
}
client.manager.nodes.get(data.host).connect()
}
@ -123,7 +127,7 @@ module.exports.deleteNode = function (data) {
if (client.manager) {
const nodesPresent = client.manager.nodes
if (nodesPresent.get(data.host)) {
client.manager.destroyNode(data.host)
client.manager.nodes.remove(data.host)
}
}
saveNodesFile(nodes_data)

View File

@ -8,6 +8,7 @@ const alog = new LogType("Authentification")
var users = new Map()
var sessions = new Array()
/// <reference path="./types.d.ts" />
var packageJson = JSON.parse(fs.readFileSync(__glob.PACKAGE))

View File

@ -6,6 +6,8 @@ const fs = require('fs')
const dlog = new LogType("Queue-List")
/// <reference path="./types.d.ts" />
var next = new Array()
var current = null;
var shuffle = false
@ -116,17 +118,26 @@ module.exports.List = class {
}
setCurrent(song) {
current = song.queue.current
current = song.current
console.log("CURRENT SONG : " + current.title)
}
addCurrentToPrevious() {
if(current) {
addCurrentToPrevious(song) {
if(song) {
var previousList = getPreviousFile()
previousList.unshift(song)
savePreviousFile(previousList)
} else if(current) {
var previousList = getPreviousFile()
//Check if the first song is the same as the current song
previousList.unshift(current)
savePreviousFile(previousList)
}
}
@ -182,7 +193,7 @@ module.exports.List = class {
.setDescription('**Demandé par **' + interaction.member.user.username)
.addFields({name: "Auteur", value: song.author},
{name: "URL", value: song.uri})
.setThumbnail(song.thumbnail)
.setThumbnail(song.artworkUrl)
.setTimestamp();
interaction.reply({embeds: [embed]})
@ -323,8 +334,8 @@ module.exports.List = class {
}
player = client.manager.create({
guild: "137291455336022018",
player = client.manager.players.create({
guildId: "137291455336022018",
voiceChannel: channelId,
textChannel: "664355637685256203",
});

View File

@ -9,6 +9,8 @@ const { Metric } = require("webmetrik");
const list = new List()
/// <reference path="./types.d.ts" />
module.exports.getSong = async function (url) {
var client = discord.getClient()
const songs = await client.manager.search(url)
@ -25,8 +27,8 @@ module.exports.play = async function (client, interaction) {
if(!player) {
player = client.manager.create({
guild: interaction.guild.id,
player = client.manager.players.create({
guildId: interaction.guild.id,
voiceChannel: interaction.member.voice.channel.id,
textChannel: interaction.channel.id,
});
@ -68,8 +70,8 @@ module.exports.play = async function (client, interaction) {
.setTitle('**Lecture de : **' + songs.tracks[0].title)
.setDescription('**Demandé par **' + interaction.member.user.username)
.addFields({name: "Auteur", value: songs.tracks[0].author},
{name: "URL", value: songs.tracks[0].uri})
.setThumbnail(songs.tracks[0].thumbnail)
{name: "URL", value: songs.tracks[0].url})
.setThumbnail(songs.tracks[0].artworkUrl)
.setTimestamp();
interaction.reply({embeds: [embed]})
@ -100,10 +102,10 @@ module.exports.pause = function (client, interaction) {
if(interaction) {
let player = client.manager.players.get(interaction.guild.id)
if(player) {
if(player.playing) {
if(!player.paused) {
const embed = new EmbedBuilder()
.setColor(0x03ff2d)
@ -114,7 +116,8 @@ module.exports.pause = function (client, interaction) {
interaction.reply({embeds: [embed]})
player.pause(true)
player.pause()
process.emit("MUSIC_UPDATE_STATE")
} else {
const embed = new EmbedBuilder()
@ -125,8 +128,9 @@ module.exports.pause = function (client, interaction) {
interaction.reply({embeds: [embed]})
player.pause(false)
player.resume()
process.emit("MUSIC_UPDATE_STATE")
}
} else {
@ -145,19 +149,20 @@ module.exports.pause = function (client, interaction) {
if(player) {
if(player.playing) {
player.pause(true)
if(!player.paused) {
player.pause()
console.log("Paused")
} else {
player.pause(false)
}
player.resume()
console.log("Unpaused")
}
}
}
process.emit("MUSIC_UPDATE_STATE")
}
@ -171,11 +176,11 @@ module.exports.getState = function(client, interaction) {
let embed = new EmbedBuilder()
.setColor(0x32a875)
.setTitle('Information sur la musique')
.addFields({name:"Titre", value: player.queue.current.title},
{name:"Auteur", value: player.queue.current.author},
{name:"URL", value: player.queue.current.uri})
.addFields({name:"Titre", value: player.current.title},
{name:"Auteur", value: player.current.author},
{name:"URL", value: player.current.url})
.setTimestamp()
.setThumbnail(player.queue.current.thumbnail);
.setThumbnail(player.current.artworkUrl);
interaction.reply({embeds: [embed]})
@ -189,7 +194,7 @@ module.exports.getState = function(client, interaction) {
}
process.emit("MUSIC_UPDATE_STATE")
}
@ -200,7 +205,7 @@ module.exports.playPlaylist = function (id, data, quick) {
playlist.videos = data
list.playlistAdd(playlist, null, id, quick)
process.emit("MUSIC_UPDATE_STATE")
}
@ -222,8 +227,8 @@ module.exports.addSong = async function (data, userId, quick, playlist) {
if(!player) {
player = client.manager.create({
guild: "137291455336022018",
player = client.manager.players.create({
guildId: "137291455336022018",
voiceChannel: channelId,
textChannel: "664355637685256203",
});
@ -240,9 +245,7 @@ module.exports.addSong = async function (data, userId, quick, playlist) {
} else {
songs = await client.manager.search(data.uri)
songs = await client.manager.search(data.url)
}
@ -321,7 +324,7 @@ module.exports.skip = function (client, interaction) {
.setColor(0x03ff2d)
.setTitle('On change de morceau !!!')
.setDescription("**Ok, On est reparti avec "+ list.getNextSong().title + " et c'est demandée par " + interaction.member.user.username + "**")
.setThumbnail(list.getNextSong().thumbnail)
.setThumbnail(list.getNextSong().artworkUrl)
.setTimestamp();
player.stop()
@ -355,8 +358,8 @@ module.exports.skip = function (client, interaction) {
}
process.emit("MUSIC_UPDATE_STATE")
}
module.exports.seek = function (data) {
@ -365,20 +368,22 @@ module.exports.seek = function (data) {
client = discord.getClient()
/**
* @type {import("moonlink.js").MoonlinkPlayer}
*/
let player = client.manager.players.get("137291455336022018")
if(player) {
player.seek(data)
player.seek((Number(data) - 1000))
}
process.emit("MUSIC_UPDATE_STATE")
}
module.exports.setVol = function (data) {
@ -396,8 +401,8 @@ module.exports.setVol = function (data) {
}
process.emit("MUSIC_UPDATE_STATE")
@ -411,15 +416,19 @@ module.exports.loop = function (client) {
client = discord.getClient()
}
/**
* @type {import("moonlink.js").MoonlinkPlayer}
*/
let player = client.manager.players.get("137291455336022018")
if(player) {
if(player.queueRepeat == true) {
player.setQueueRepeat(false)
if(player.loop == 1) {
player.setLoop(0)
} else {
player.setQueueRepeat(true)
player.setLoop(1)
}
}
@ -485,10 +494,7 @@ module.exports.search = async function (data, client) {
}
return answer
process.emit("MUSIC_UPDATE_STATE")
}
@ -514,14 +520,14 @@ module.exports.previous = function (client, interaction) {
interaction.reply({embeds: [embed]})
} else {
list.__next_add(player.queue.current)
list.__next_add(player.current)
player.play(list.previous())
embed = new EmbedBuilder()
.setColor(0x03ff2d)
.setTitle('Retour vers le passé !!!')
.setDescription("**Ok, On est reparti avec "+ player.queue.current.title +" et c'est demandée par " + interaction.member.user.username + "**")
.setDescription("**Ok, On est reparti avec "+ player.current.title +" et c'est demandée par " + interaction.member.user.username + "**")
.setTimestamp();
@ -543,7 +549,7 @@ module.exports.previous = function (client, interaction) {
let player = client.manager.players.get("137291455336022018")
if(list.havePreviousSongs()){
list.__next_add(player.queue.current)
list.__next_add(player.current)
player.play(list.previous())
}
@ -624,7 +630,7 @@ module.exports.report = async function (client, interaction, data) {
interaction.reply({embeds: [membed]})
}
process.emit("MUSIC_UPDATE_STATE")
}
@ -661,9 +667,9 @@ module.exports.updateMusicState = function (client, action) {
data["shuffle"] = true
}
data["current"] = player.queue.current
data["current"] = player.current
if(player.queueRepeat == true) {
if(player.loop == 1) {
data["loop"] = true
}
@ -671,18 +677,17 @@ module.exports.updateMusicState = function (client, action) {
data["volume"] = player.volume * 10
if(player.queue.current) {
data["durationNow"] = player.position
data["durationAll"] = player.queue.current.duration
currentTitle = player.queue.current.title
if(player.current) {
data["durationNow"] = player.current.position
data["durationAll"] = player.current.duration
currentTitle = player.current.title
}
if(player.paused) {
if(player.playing == true && player.paused == false) {
data["playing"] = 1
} else {
data["playing"] = 0
} else {
data["playing"] = 1
}
@ -701,8 +706,10 @@ module.exports.updateMusicState = function (client, action) {
}
clog.log("Actualisation Clients - Titre : " + currentTitle + " - Loop : " + data.loop + " - Shuffle : " + data.shuffle + " - Playing : " + data.playing + " - Volume : " + Math.trunc(data.volume / 10) )
return data
}
@ -753,8 +760,8 @@ module.exports.leave = function (client, interaction) {
}
process.emit("MUSIC_UPDATE_STATE")
}
@ -776,7 +783,7 @@ module.exports.moveOut = function(userId) {
}
}
process.emit("MUSIC_UPDATE_STATE")
}
// FINI
@ -801,4 +808,5 @@ function playStats(username) {
var userMusicPlayed = new Metric("userMusicPlayed_" + username, "Nombre de musiques jouées par l'utilisateur : " + username)
userMusicPlayed.setValue(userMusicPlayed.getValue() + 1)
}
}

View File

@ -8,6 +8,8 @@ const { url } = require("inspector");
var playlists = {}
const plog = new LogType("Playlist-Manager")
/// <reference path="./types.d.ts" />
check()

View File

@ -19,7 +19,7 @@ const markdownit = require("markdown-it")({
typographer: true
})
/// <reference path="./types.d.ts" />
module.exports.WebServer = class {

7
src/modules/types.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
import { MoonlinkManager } from 'moonlink.js';
declare module 'discord.js' {
interface Client {
manager: MoonlinkManager;
}
}

View File

@ -229,7 +229,7 @@ class VideoComponent {
this.onPlaylist = false
this.title = data.title
this.author = data.author
this.thumbnail = data.thumbnail
this.thumbnail = data.artworkUrl
this.url = data.uri
this.duration = data.duration
this.identifier = data.identifier

View File

@ -17,7 +17,7 @@ const volume = getID("volume")
const volIcon = getID("volIcon")
const volTxt = getID("volTxt")
const lyrics = getID("lyrics")
// const lyrics = getID("lyrics")
const disconnect = getID("disconnect")
const moveout = getID("moveout")
@ -29,7 +29,7 @@ var durationTotal = 0
var interval = null
var currentTitle = ""
var currentTitle = null
var playerState = "DISCONNECTED"
@ -37,7 +37,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
durationAct = 0
durationTotal = 0
playerState = "DISCONNECTED"
currentTitle = ""
currentTitle = null
stopInterval()
console.log(data)
@ -55,7 +55,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
volume.classList.remove("invisible")
volTxt.classList.remove("invisible")
volIcon.classList.remove("invisible")
lyrics.classList.remove("invisible")
// lyrics.classList.remove("invisible")
} else {
@ -72,7 +72,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
time_act.innerHTML = "&nbsp"
time_total.innerHTML = "&nbsp"
durationBar.classList.add("invisible")
lyrics.classList.add("invisible")
// lyrics.classList.add("invisible")
}
@ -94,7 +94,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
if(data.current) {
var thumbnail = data.current.thumbnail
var thumbnail = data.current.artworkUrl
if(thumbnail) {
video_img.innerHTML = '<img class="showPicture" src="' + thumbnail + '">'
@ -131,7 +131,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
play.classList.add("disabled")
currentTitle = ""
currentTitle = null
}
if(data.queue) {
@ -153,7 +153,7 @@ AlwaysRequest("MUSIC_STATE", async (data) => {
if(data.volume) {
volume.step = 1
volume.max = 200
volume.max = 150
volume.min = 1
volume.value = Math.trunc(data.volume / 10)
volTxt.innerHTML = Math.trunc(data.volume / 10) + "%"
@ -279,93 +279,93 @@ moveout.addEventListener('click', () => {
get("MOVEOUT")
})
lyrics.addEventListener('click', (e) => {
// lyrics.addEventListener('click', (e) => {
currentTitle = currentTitle.replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "").trim()
// currentTitle = currentTitle.replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "").trim()
if(e.ctrlKey) {
const modal = new ModalComponent({"title": "Rechercher des paroles" , "width": "25%", "closable": true})
// if(e.ctrlKey) {
// const modal = new ModalComponent({"title": "Rechercher des paroles" , "width": "25%", "closable": true})
modal.setContent(`
<div class='lyrics-finder'>
<input id="lyrics_search" type="text" placeholder="Rechercher des paroles">
<button id="lyrics_search_btn">Rechercher</button>
</div>
`)
// modal.setContent(`
// <div class='lyrics-finder'>
// <input id="lyrics_search" type="text" placeholder="Rechercher des paroles">
// <button id="lyrics_search_btn">Rechercher</button>
// </div>
// `)
modal.show()
// modal.show()
const lyricsSearch = getID("lyrics_search")
const lyricsSearchBtn = getID("lyrics_search_btn")
// const lyricsSearch = getID("lyrics_search")
// const lyricsSearchBtn = getID("lyrics_search_btn")
lyricsSearchBtn.addEventListener('click', () => {
showLyrics(lyricsSearch.value)
modal.hide()
})
// lyricsSearchBtn.addEventListener('click', () => {
// showLyrics(lyricsSearch.value)
// modal.hide()
// })
} else {
// } else {
if(currentTitle != "") {
showLyrics(currentTitle)
// if(currentTitle) {
// showLyrics(currentTitle)
} else {
const modal = new ModalComponent({"title": "Paroles" , "width": "20%", "closable": true})
modal.setContent(`
// } else {
// const modal = new ModalComponent({"title": "Paroles" , "width": "20%", "closable": true})
// modal.setContent(`
<p class="lyrics">Lancez une musique pour rechercher les paroles ou faites Ctrl + Click !</p>
`)
// <p class="lyrics">Lancez une musique pour rechercher les paroles ou faites Ctrl + Click !</p>
// `)
modal.show()
}
}
// modal.show()
// }
// }
})
// })
function showLyrics(title) {
title = title.replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "").trim()
post("LYRICS", title).then((res) => {
// function showLyrics(title) {
// title = title.replace(/\(.*?\)/g, "").replace(/\[.*?\]/g, "").trim()
// post("LYRICS", title).then((res) => {
if(res.startsWith("<br />")) {
const modal = new ModalComponent({"title": "Paroles" , "width": "50%", "closable": true})
modal.setContent(`
<p class="lyrics">Aucune paroles trouvées pour cette musique !</p>
`)
// if(res.startsWith("<br />")) {
// const modal = new ModalComponent({"title": "Paroles" , "width": "50%", "closable": true})
// modal.setContent(`
// <p class="lyrics">Aucune paroles trouvées pour cette musique !</p>
// `)
modal.show()
} else {
// modal.show()
// } else {
const lyricsArray = new Array()
// const lyricsArray = new Array()
// Split res by line when a new [ appears
// // Split res by line when a new [ appears
res.split("[").forEach((line) => {
if(line.includes("]")) {
const time = line.split("]")[0].trim()
const lyrics = line.split("]")[1].trim()
lyricsArray.push(`<p class="lyrics">${lyrics}</p>`)
}
})
// res.split("[").forEach((line) => {
// if(line.includes("]")) {
// const time = line.split("]")[0].trim()
// const lyrics = line.split("]")[1].trim()
// lyricsArray.push(`<p class="lyrics">${lyrics}</p>`)
// }
// })
setTileActive(null)
// setTileActive(null)
loadView(`
<h1>Paroles de "${title}"</h1>
<div class="lyrics-container">
<div class="lyrics-list">
${lyricsArray.join("")}
</div>
</div>
`)
// loadView(`
// <h1>Paroles de "${title}"</h1>
// <div class="lyrics-container">
// <div class="lyrics-list">
// ${lyricsArray.join("")}
// </div>
// </div>
// `)
}
})
// }
// })
}
// }
volIcon.addEventListener('click', () => {
@ -406,7 +406,7 @@ function startInterval() {
interval = setInterval(() => {
if(playerState == "PLAYING") {
if(playerState == "PLAYING" && currentTitle) {
setTime()
}

View File

@ -153,8 +153,8 @@ class VideoQueue {
console.log(data)
this.title = data.title
this.author = data.author
this.thumbnail = data.thumbnail
this.url = data.uri
this.thumbnail = data.artworkUrl
this.url = data.url
this.duration = data.duration
this.identifier = data.numList + "_" + data.identifier
this.type = data.type

View File

@ -9,7 +9,7 @@
body {
flex-direction: column;
align-items: flex-start;
background: #160120;
background: #19001e;
display: flex;
color: #FFF;
font-family: 'Inter', sans-serif;
@ -351,11 +351,21 @@ button:hover {
padding: 20px 20px 20px 20px;
flex-direction: column;
align-items: center;
gap: 20px;
gap: 7px;
align-self: stretch;
background: #131313;
}
hr {
width: 100%;
display: block;
height: 1px;
border: 0;
border-top: 1px solid #cccccc87;
margin: 1em 0;
padding: 0;
}
/*Title*/
.title {
@ -393,11 +403,11 @@ button:hover {
.homeselector {
display: flex;
width: 344px;
padding: 20px;
justify-content: space-around;
align-items: flex-start;
border-radius: 10px;
background: #2F2F2F;
border-radius: 5px;
}
.homeselector_div:hover {
@ -420,7 +430,7 @@ button:hover {
.homeselector_icon {
border: 2px solid #ffffff;
border-radius: 10px;
border-radius: 5px;
font-size: 23px;
justify-content: center;
align-items: center;
@ -432,7 +442,7 @@ button:hover {
}
.homeselector_icon:hover {
background-color: #ffffff;
/* background-color: #ffffff; */
color: #000000;
transform: scale(0.95);
}
@ -441,14 +451,14 @@ button:hover {
.playlist {
display: flex;
padding: 20px;
flex-direction: column;
align-items: flex-start;
gap: 20px;
flex: 1 0 0;
align-self: stretch;
border-radius: 10px;
background: #2F2F2F;
border-radius: 5px;
}
@ -519,9 +529,9 @@ button:hover {
align-items: center;
flex: 1 0 0;
align-self: stretch;
padding: 20px;
border-radius: 10px;
background: rgba(217, 217, 217, 0.30);
border-radius: 5px;
}
.view-other {
@ -549,7 +559,7 @@ button:hover {
align-items: flex-start;
gap: 10px;
align-self: stretch;
border-radius: 10px;
border-radius: 5px;
height: 30%;
background: rgba(217, 217, 217, 0.30);
@ -599,7 +609,7 @@ button:hover {
flex: 1;
min-height: 0;
align-self: stretch;
border-radius: 10px;
border-radius: 5px;
background: rgba(217, 217, 217, 0.30);
}
@ -1064,7 +1074,7 @@ button:hover {
.playlist-add {
background-color: #545454;
padding: 5px;
border-radius: 10px;
border-radius: 5px;
cursor: pointer;
transition: 0.2s;
font-size: 14px;
@ -1108,7 +1118,7 @@ button:hover {
border: 2px solid #ffffff;
border-radius: 10px;
border-radius: 5px;
font-size: 20px;
width: 56px;
height: 56px;
@ -1191,7 +1201,7 @@ button:hover {
gap: 10px;
align-self: stretch;
border-radius: 10px;
border-radius: 5px;
@ -1447,6 +1457,12 @@ button:hover {
width: 100px !important;
}
.vol_px {
display: flex;
gap: 5px !important;
}
.oobe-list {
display: flex;
flex-direction: column;
@ -1454,7 +1470,7 @@ button:hover {
align-items: flex-start;
align-self: stretch;
padding: 20px;
border-radius: 10px;
border-radius: 5px;
background: rgba(217, 217, 217, 0.30);
width: 30%;
@ -1491,7 +1507,7 @@ button:hover {
align-items: flex-start;
align-self: stretch;
padding: 20px;
border-radius: 10px;
border-radius: 5px;
background: #2F2F2F;
width: 30%;
}
@ -1503,7 +1519,7 @@ button:hover {
align-items: flex-start;
align-self: stretch;
padding: 20px;
border-radius: 10px;
border-radius: 5px;
background: #2F2F2F;
}
@ -1551,7 +1567,7 @@ button:hover {
.oobe-search {
background: #2d2d2d;
padding: 20px;
border-radius: 10px;
border-radius: 5px;
}
@ -1607,15 +1623,20 @@ input[type="range"] {
}
/*Scrollbar */
::-webkit-scrollbar {
width: 10px;
width: 5px;
margin-right: 20px;
}
::-webkit-scrollbar-track {
border-radius: 12px;
}
::-webkit-scrollbar-thumb {
background: #ffffff56;
border-radius: 5px;
transition: 0.2s;
}
::-webkit-scrollbar-thumb:hover {
background: #ffffffa8;
border-radius: 10px;
}
@ -1626,7 +1647,7 @@ input[type="range"] {
display: flex;
flex-direction: column;
width: 150px;
border-radius: 10px;
border-radius: 5px;
z-index: 3;
height: auto;
}
@ -1676,7 +1697,7 @@ input[type="range"] {
padding: 20px;
box-shadow: 4px 5px 12px 5px rgba(0,0,0,0.6);
width: 50%;
border-radius: 10px;
border-radius: 5px;
color: #FFF;
}

View File

@ -28,6 +28,7 @@
<span class="homeselector_text">Soundboard</span>
</div> -->
</div>
<hr>
<div class="playlist">
<div class="wintitle">
<p>Playlists</p>
@ -42,6 +43,7 @@
</div>
<button class="playlist-add-btn" id="playlist-add"><i class="fa fa-plus"></i> Ajouter une playlist</button>
</div>
<hr>
<div class="usercard">
<div class="usercard_div">
<img id="user-img" class="usercard_image" src="/images/default.jpg"></img>
@ -120,16 +122,18 @@
</div>
<div class="actionbar">
<div>
<span id="lyrics" class="wh"><i class="fa fa-microphone-lines icon"></i></span>
<!-- <span id="lyrics" class="wh"><i class="fa fa-microphone-lines icon"></i></span> -->
<span id="disconnect" class="wh"><i class="fa fa-phone-slash icon"></i></span>
<span id="moveout" class="wh"><i class="fa fa-hand icon"></i></span>
</div>
<div>
<span id="volIcon"><i class="fa fa-volume-up"></i></span>
<span id="volTxt"></span>
<div class="vol_px">
<span id="volIcon"><i class="fa fa-volume-up"></i></span>
<span id="volTxt"></span>
</div>
<input id="volume" type="range" value="100" max="100">
</div>