diff --git a/package.json b/package.json index 82aecfb..63da2bc 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,19 @@ { "name": "hoot", - "version": "0.2.1", + "version": "0.2.2", "main": "dist/index.js", "author": "@Owlvernyte", "license": "Apache-2.0", "type": "commonjs", "dependencies": { + "@discordjs/formatters": "^0.4.0", "@discordjs/opus": "^0.9.0", "@discordjs/voice": "^0.17.0", + "@distube/direct-link": "^1.0.1", + "@distube/soundcloud": "^2.0.3", + "@distube/spotify": "^2.0.2", + "@distube/youtube": "^1.0.2", + "@distube/yt-dlp": "^2.0.1", "@sapphire/decorators": "^6.1.0", "@sapphire/discord-utilities": "^3.2.3", "@sapphire/discord.js-utilities": "7.1.6", @@ -23,7 +29,7 @@ "@skyra/env-utilities": "^1.3.0", "colorette": "^2.0.20", "discord.js": "^14.15.2", - "distube": "^4.2.2", + "distube": "^5.0.2", "lodash": "^4.17.21", "millify": "^6.1.0", "node-cron": "^3.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67d5907..fa0c843 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,30 @@ settings: excludeLinksFromLockfile: false dependencies: + '@discordjs/formatters': + specifier: ^0.4.0 + version: 0.4.0 '@discordjs/opus': specifier: ^0.9.0 version: 0.9.0 '@discordjs/voice': specifier: ^0.17.0 version: 0.17.0(@discordjs/opus@0.9.0) + '@distube/direct-link': + specifier: ^1.0.1 + version: 1.0.1(distube@5.0.2) + '@distube/soundcloud': + specifier: ^2.0.3 + version: 2.0.3(distube@5.0.2) + '@distube/spotify': + specifier: ^2.0.2 + version: 2.0.2(distube@5.0.2) + '@distube/youtube': + specifier: ^1.0.2 + version: 1.0.2(distube@5.0.2) + '@distube/yt-dlp': + specifier: ^2.0.1 + version: 2.0.1(distube@5.0.2) '@sapphire/decorators': specifier: ^6.1.0 version: 6.1.0 @@ -57,8 +75,8 @@ dependencies: specifier: ^14.15.2 version: 14.15.2 distube: - specifier: ^4.2.2 - version: 4.2.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + specifier: ^5.0.2 + version: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) lodash: specifier: ^4.17.21 version: 4.17.21 @@ -226,18 +244,73 @@ packages: - utf-8-validate dev: false - /@distube/ytdl-core@4.13.3: - resolution: {integrity: sha512-WHVzp0NyUkmdxRkfU8tN7eRquL7bnia2U/EDNWVupCptRo7EToTdBKHwJrDFqvavbXsdqLG/kR1r+1LaPglrFQ==} - engines: {node: '>=12'} + /@distube/direct-link@1.0.1(distube@5.0.2): + resolution: {integrity: sha512-NbUzRj1mVOkpmcUrjQ1KjfYDQEeS8uRWGPcgunNmRGdI+BQ5BG+pcHK1r6wMVwzaIdQrpg3X6Sh/T7uG3bdImg==} + peerDependencies: + distube: '5' + dependencies: + distube: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + undici: 6.19.2 + dev: false + + /@distube/soundcloud@2.0.3(distube@5.0.2): + resolution: {integrity: sha512-Hm0xtSVJge67W1QdXcOBjJmVaQWR45S1BrPP8oXie4dgYPPasNksXAqEAo8KwsqFNCVwHkCQcgQlTcgU7LzYBg==} + peerDependencies: + distube: '5' + dependencies: + distube: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + soundcloud.ts: 0.5.3 + dev: false + + /@distube/spotify@2.0.2(distube@5.0.2): + resolution: {integrity: sha512-rIncX05PhedMZuZyhgz7dt9y/nxir9KPmxUo6sBxfITQcztkXlG3r0EhKivp9+BRCQlEBO/GitRTHVwhRKtmLg==} + peerDependencies: + distube: '5' + dependencies: + distube: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + spotify-uri: 4.1.0 + spotify-url-info: 3.2.16 + spotify-web-api-node: 5.0.2 + undici: 6.19.2 + transitivePeerDependencies: + - supports-color + dev: false + + /@distube/youtube@1.0.2(distube@5.0.2): + resolution: {integrity: sha512-vNOE7kGhg3pC87iqT3Q4eu+yTae8Nr3J1XDR67YMH9HAg1RsPjwM7TvfabZoGxIUiPl4ua/PG6s8Nfxf2U9cxQ==} + peerDependencies: + distube: '5' + dependencies: + '@distube/ytdl-core': 4.13.5 + '@distube/ytpl': 1.2.1 + '@distube/ytsr': 2.0.4 + distube: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + transitivePeerDependencies: + - supports-color + dev: false + + /@distube/yt-dlp@2.0.1(distube@5.0.2): + resolution: {integrity: sha512-9c16lRU6jbyal38UUr5E36+2lp36s0DaJySOtFjuAPgaJkp2xvKvyd+s4rFZSqVQGJO5GOhBiH+HD115SKfKAw==} + requiresBuild: true + peerDependencies: + distube: '5' + dependencies: + dargs: 7.0.0 + distube: 5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2) + undici: 6.19.2 + dev: false + + /@distube/ytdl-core@4.13.5: + resolution: {integrity: sha512-g+4UJIR/auAJbia7iB0aWvaJDbs22P53NySWa47b1NT4xMTDJYguxHFArPrvRkcJrb/AgKjv/XoSZGghpL0CJA==} + engines: {node: '>=16'} dependencies: - http-cookie-agent: 5.0.4(tough-cookie@4.1.4)(undici@5.28.4) + http-cookie-agent: 6.0.5(tough-cookie@4.1.4)(undici@6.19.2) m3u8stream: 0.8.6 miniget: 4.2.3 - sax: 1.3.0 + sax: 1.4.1 tough-cookie: 4.1.4 - undici: 5.28.4 + undici: 6.19.2 transitivePeerDependencies: - - deasync - supports-color dev: false @@ -248,11 +321,11 @@ packages: undici: 5.28.4 dev: false - /@distube/ytsr@2.0.0: - resolution: {integrity: sha512-N9z8IMbBCQ/gNnJmBgc0TBOU7tdl2nYDOnT6adN1utzIlrKWa2Ux+3UdAPV38f/qRrWohcmyMHPbSbex80ap3A==} - engines: {node: '>=18.0'} + /@distube/ytsr@2.0.4: + resolution: {integrity: sha512-OiSWgARQ9LTj+dXt3jmMFzUH4l86VVCD4dVC4hEHNXdqp+DyU4QEzc+W6YY6//kWkvzTaUxOo7JUY7lBzwIF0A==} + engines: {node: '>=14.0'} dependencies: - undici: 6.0.1 + undici: 6.19.2 dev: false /@fastify/busboy@2.1.1: @@ -582,6 +655,10 @@ packages: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true + /asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + dev: false + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -598,6 +675,17 @@ packages: balanced-match: 1.0.2 dev: true + /call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + dev: false + /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -631,11 +719,22 @@ packages: /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + /combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + dependencies: + delayed-stream: 1.0.0 + dev: false + /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} dev: true + /component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + dev: false + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: false @@ -644,6 +743,10 @@ packages: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} dev: false + /cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -653,6 +756,11 @@ packages: which: 2.0.2 dev: true + /dargs@7.0.0: + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -665,6 +773,20 @@ packages: ms: 2.1.2 dev: false + /define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + dev: false + + /delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dev: false + /delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} dev: false @@ -699,25 +821,17 @@ packages: - utf-8-validate dev: false - /distube@4.2.2(@discordjs/voice@0.17.0)(discord.js@14.15.2): - resolution: {integrity: sha512-DBfyScFM66RFGWzPlf+mXy4uU4wfF33qyCr+ojDjHsVYy7m7CJo3e9tkpz/PhIo8OdU/gb4TDiUHc8Pj0r5B/Q==} + /distube@5.0.2(@discordjs/voice@0.17.0)(discord.js@14.15.2): + resolution: {integrity: sha512-VR+js/RS+sbxQIGaewh1jZtQDeSE1x05V6CFDb7/KATXSln+nUemIh/RTqKPuPeFJhcPcYGdZWPvEliDU/RS/g==} engines: {node: '>=18.17'} peerDependencies: '@discordjs/voice': '*' discord.js: '14' dependencies: '@discordjs/voice': 0.17.0(@discordjs/opus@0.9.0) - '@distube/ytdl-core': 4.13.3 - '@distube/ytpl': 1.2.1 - '@distube/ytsr': 2.0.0 discord.js: 14.15.2 tiny-typed-emitter: 2.1.0 - tough-cookie: 4.1.4 - tslib: 2.6.2 - undici: 6.16.1 - transitivePeerDependencies: - - deasync - - supports-color + undici: 6.19.2 dev: false /dotenv-expand@10.0.0: @@ -738,6 +852,18 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false + /es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.4 + dev: false + + /es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + dev: false + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -774,6 +900,10 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + dev: false + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -782,6 +912,20 @@ packages: path-exists: 4.0.0 dev: true + /form-data@3.0.1: + resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==} + engines: {node: '>= 6'} + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + dev: false + + /formidable@1.2.6: + resolution: {integrity: sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==} + deprecated: 'Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau' + dev: false + /from@0.1.7: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: true @@ -797,6 +941,10 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: false + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + dev: false + /gauge@3.0.2: resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} engines: {node: '>=10'} @@ -817,6 +965,17 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: false + /get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + dev: false + /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -833,26 +992,56 @@ packages: path-is-absolute: 1.0.1 dev: false + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.4 + dev: false + + /has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + dependencies: + es-define-property: 1.0.0 + dev: false + + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + dev: false + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: false + /has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} dev: false - /http-cookie-agent@5.0.4(tough-cookie@4.1.4)(undici@5.28.4): - resolution: {integrity: sha512-OtvikW69RvfyP6Lsequ0fN5R49S+8QcS9zwd58k6VSr6r57T8G29BkPdyrBcSwLq6ExLs9V+rBlfxu7gDstJag==} - engines: {node: '>=14.18.0 <15.0.0 || >=16.0.0'} + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + dev: false + + /himalaya@1.1.0: + resolution: {integrity: sha512-LLase1dHCRMel68/HZTFft0N0wti0epHr3nNY7ynpLbyZpmrKMQ8YIpiOV77TM97cNpC8Wb2n6f66IRggwdWPw==} + dev: false + + /http-cookie-agent@6.0.5(tough-cookie@4.1.4)(undici@6.19.2): + resolution: {integrity: sha512-sfZ8fDgDP3B1YB+teqSnAK1aPgBu8reUUGxSsndP2XnYN6cM29EURXWXZqQQiaRdor3B4QjpkUNfv21syaO4DA==} + engines: {node: '>=18.0.0'} peerDependencies: - deasync: ^0.1.26 tough-cookie: ^4.0.0 - undici: ^5.11.0 + undici: ^5.11.0 || ^6.0.0 peerDependenciesMeta: - deasync: - optional: true undici: optional: true dependencies: agent-base: 7.1.1 tough-cookie: 4.1.4 - undici: 5.28.4 + undici: 6.19.2 transitivePeerDependencies: - supports-color dev: false @@ -934,7 +1123,7 @@ packages: engines: {node: '>=12'} dependencies: miniget: 4.2.3 - sax: 1.3.0 + sax: 1.4.1 dev: false /magic-bytes.js@1.10.0: @@ -961,6 +1150,11 @@ packages: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + /millify@6.1.0: resolution: {integrity: sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA==} hasBin: true @@ -968,6 +1162,24 @@ packages: yargs: 17.7.2 dev: false + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: false + /mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -1105,6 +1317,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -1219,6 +1436,13 @@ packages: engines: {node: '>=6'} dev: false + /qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.6 + dev: false + /querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} dev: false @@ -1260,8 +1484,8 @@ packages: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false - /sax@1.3.0: - resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + /sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} dev: false /semver@6.3.1: @@ -1279,6 +1503,18 @@ packages: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: false + /set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1295,6 +1531,16 @@ packages: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} dev: true + /side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + dev: false + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: false @@ -1315,12 +1561,39 @@ packages: node-gyp-build: 4.8.1 dev: false + /soundcloud.ts@0.5.3: + resolution: {integrity: sha512-ZMH6gG5e7WqJrIYXTv14MNArPhx3WzfrL1Ij/2qBDW8mVbNJc8lxOQOc4kLvrfvDl5TkCdZa7zXOiwD6ESXq+g==} + dependencies: + undici: 6.19.2 + dev: false + /split@0.3.3: resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} dependencies: through: 2.3.8 dev: true + /spotify-uri@4.1.0: + resolution: {integrity: sha512-SFpBt8pQqO7DOFBsdUjv3GxGZAKYP7UqcTflfE7h3YL1lynl/6Motq7NERoJJR8eF9kXQRSpcdMmV5ou84rbng==} + engines: {node: '>= 16'} + dev: false + + /spotify-url-info@3.2.16: + resolution: {integrity: sha512-szXt1PLt8lqhaXsTNYH7zPd+EBj6Ha0xtqitqicCijGX6x/jYvn6wgGaK2F1OQfJzx8lxDNfZbtarn4DxzaZ2Q==} + engines: {node: '>= 12'} + dependencies: + himalaya: 1.1.0 + spotify-uri: 4.1.0 + dev: false + + /spotify-web-api-node@5.0.2: + resolution: {integrity: sha512-r82dRWU9PMimHvHEzL0DwEJrzFk+SMCVfq249SLt3I7EFez7R+jeoKQd+M1//QcnjqlXPs2am4DFsGk8/GCsrA==} + dependencies: + superagent: 6.1.0 + transitivePeerDependencies: + - supports-color + dev: false + /stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} dependencies: @@ -1359,6 +1632,26 @@ packages: engines: {node: '>=12'} dev: true + /superagent@6.1.0: + resolution: {integrity: sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==} + engines: {node: '>= 7.0.0'} + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.3.4 + fast-safe-stringify: 2.1.1 + form-data: 3.0.1 + formidable: 1.2.6 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.12.3 + readable-stream: 3.6.2 + semver: 7.6.2 + transitivePeerDependencies: + - supports-color + dev: false + /tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -1441,13 +1734,6 @@ packages: '@fastify/busboy': 2.1.1 dev: false - /undici@6.0.1: - resolution: {integrity: sha512-eZFYQLeS9BiXpsU0cuFhCwfeda2MnC48EVmmOz/eCjsTgmyTdaHdVsPSC/kwC2GtW2e0uH0HIPbadf3/bRWSxw==} - engines: {node: '>=18.0'} - dependencies: - '@fastify/busboy': 2.1.1 - dev: false - /undici@6.13.0: resolution: {integrity: sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==} engines: {node: '>=18.0'} @@ -1458,6 +1744,11 @@ packages: engines: {node: '>=18.17'} dev: false + /undici@6.19.2: + resolution: {integrity: sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==} + engines: {node: '>=18.17'} + dev: false + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} diff --git a/src/commands/General/eval.ts b/src/commands/General/eval.ts index ed25f5b..906a746 100644 --- a/src/commands/General/eval.ts +++ b/src/commands/General/eval.ts @@ -13,7 +13,7 @@ import { inspect } from 'util'; preconditions: ['OwnerOnly'], flags: ['async', 'hidden', 'showHidden', 'silent', 's'], options: ['depth'], - enabled: false, + enabled: false }) export class UserCommand extends Command { public override async messageRun(message: Message, args: Args) { diff --git a/src/commands/General/help.ts b/src/commands/General/help.ts index 16e154a..cf8ace9 100644 --- a/src/commands/General/help.ts +++ b/src/commands/General/help.ts @@ -30,8 +30,8 @@ export class UserCommand extends Command { }); paginatedMessage.addPageEmbed(this.getWelcomeEmbed()); - const commandEmbeds = await this.getCommandsEmbeds(interaction); - commandEmbeds.forEach((embed) => paginatedMessage.addPageEmbed(embed)); + const commandEmbeds = await this.getCommandsEmbeds(interaction); + commandEmbeds.forEach((embed) => paginatedMessage.addPageEmbed(embed)); await paginatedMessage.run(response, interaction.user); diff --git a/src/commands/General/uptime.ts b/src/commands/General/uptime.ts index 58b4881..6d68044 100644 --- a/src/commands/General/uptime.ts +++ b/src/commands/General/uptime.ts @@ -16,39 +16,37 @@ export class UserCommand extends Command { } public override async chatInputRun(interaction: Command.ChatInputCommandInteraction) { - const { client } = this.container + const { client } = this.container; - let totalSeconds = (client.uptime || -1) / 1000 - let days = Math.floor(totalSeconds / 86400) - totalSeconds %= 86400 - let hours = Math.floor(totalSeconds / 3600) - totalSeconds %= 3600 - let minutes = Math.floor(totalSeconds / 60) - let seconds = Math.floor(totalSeconds % 60) - let uptime = `${days}d${hours}h${minutes}m${seconds}s` + let totalSeconds = (client.uptime || -1) / 1000; + let days = Math.floor(totalSeconds / 86400); + totalSeconds %= 86400; + let hours = Math.floor(totalSeconds / 3600); + totalSeconds %= 3600; + let minutes = Math.floor(totalSeconds / 60); + let seconds = Math.floor(totalSeconds % 60); + let uptime = `${days}d${hours}h${minutes}m${seconds}s`; - const embed = new EmbedBuilder().setColor('Random').addFields([ - { - name: `Online`, - value: codeBlock(uptime), - inline: false, - }, - { - name: `API Latency`, - value: codeBlock(Math.round(client.ws.ping) + 'ms'), - inline: true, - }, - { - name: `Client Latency`, - value: codeBlock( - Math.round(Date.now() - interaction.createdTimestamp) + 'ms' - ), - inline: true, - }, - ]) + const embed = new EmbedBuilder().setColor('Random').addFields([ + { + name: `Online`, + value: codeBlock(uptime), + inline: false + }, + { + name: `API Latency`, + value: codeBlock(Math.round(client.ws.ping) + 'ms'), + inline: true + }, + { + name: `Client Latency`, + value: codeBlock(Math.round(Date.now() - interaction.createdTimestamp) + 'ms'), + inline: true + } + ]); - return interaction.reply({ - embeds: [embed], - }) + return interaction.reply({ + embeds: [embed] + }); } } diff --git a/src/commands/Music/nowplaying.ts b/src/commands/Music/nowplaying.ts index 3b06199..1e2f0fd 100644 --- a/src/commands/Music/nowplaying.ts +++ b/src/commands/Music/nowplaying.ts @@ -24,41 +24,35 @@ export class UserCommand extends Command { const song = queue.songs[0]; - const descriptionArray = []; + const descriptionArray: string[][] = [[], [], []]; - if (song.isLive) descriptionArray.push(`🔴 \`Live\``); - else { - descriptionArray.push(`⌛ \`${queue.formattedCurrentTime}\``); - } - - if (song.views) descriptionArray.push(`👁 \`${millify(song.views)}\``); + if (song.views) descriptionArray[0].push(`👁 \`${millify(song.views)}\``); if (song.likes || song.dislikes) - descriptionArray.push(`👍 \`${song.likes ? millify(song.likes) : '-'}\`/👎 \`${song.dislikes ? millify(song.dislikes) : '-'}\``); - - if (song.uploader) { - descriptionArray.push( - `🎙 ${ - song.uploader.url - ? `[${song.uploader.name ? song.uploader.name : 'Unknown'}](${song.uploader.url})` - : `${song.uploader.name ? song.uploader.name : 'Unknown'}` - }` - ); - } + descriptionArray[0].push(`👍 \`${song.likes ? millify(song.likes) : '-'}\`/👎 \`${song.dislikes ? millify(song.dislikes) : '-'}\``); + + descriptionArray[1].push( + `🎙 ${ + song.uploader.url + ? `[${song.uploader.name ? song.uploader.name : 'Unknown'}](${song.uploader.url})` + : `${song.uploader.name ? song.uploader.name : 'Unknown'}` + }` + ); + if (song.member) descriptionArray[2].push(`🎧 ${song.member}`); - if (song.member) descriptionArray.push(`🎧 ${song.member}`); + const queueStatus = getQueueStatus(queue, false); const embed = new EmbedBuilder() .setColor('Random') - .setTitle(`${song.name}`) - .setURL(song.url) + .setTitle(`${song.name || 'Unknown'}`) + .setURL(song.url || null) .setThumbnail(song.thumbnail || null) .setFooter({ - text: `${song.formattedDuration} | ${getQueueStatus(queue)}` + text: `${song.isLive ? `🔴 Live` : `⌛ ${queue.formattedCurrentTime}/${song.formattedDuration}`} | ${Object.values(queueStatus).filter((x) => !!x.length).join(' | ')}` }); if (descriptionArray.length) { - const stat = descriptionArray.join(' | '); + const stat = descriptionArray.map((x) => x.join(' | ')).join('\n'); embed.setDescription(`${stat}`); } diff --git a/src/commands/Music/play.ts b/src/commands/Music/play.ts index f94add2..68e0fb6 100644 --- a/src/commands/Music/play.ts +++ b/src/commands/Music/play.ts @@ -1,7 +1,8 @@ import { ApplyOptions } from '@sapphire/decorators'; import { Command } from '@sapphire/framework'; -import { QueueMetadata } from '../../lib/HootClient'; import { HootBaseError } from '../../lib/errors/HootBaseError'; +import { QueueMetadata } from '../../lib/@types'; +import { VoiceBasedChannel } from 'discord.js'; @ApplyOptions({ description: 'Play a song', @@ -30,11 +31,15 @@ export class UserCommand extends Command { } await interaction.deferReply({ ephemeral: true }); + + const queue = this.container.distube.getQueue(interaction.guild!); + const songName = interaction.options.getString('song')!; const skip = interaction.options.getBoolean('skip') || false; const position = interaction.options.getInteger('position') ?? 0; const metadata: QueueMetadata = { - i: interaction + i: interaction, + queueStarter: queue ? queue.owner : member }; const voiceChannel = await interaction.guild?.channels.fetch(member.voice.channelId!); @@ -44,13 +49,14 @@ export class UserCommand extends Command { throw new HootBaseError('Voice channel or text channel should not be null', interaction); } - // @ts-ignore - this.container.distube.play(voiceChannel, songName, { + await this.container.distube.play(voiceChannel as VoiceBasedChannel, songName, { member, textChannel, skip, position: position, metadata }); + + await this.container.distube.updatePanel(interaction); } } diff --git a/src/interaction-handlers/Music/autocompleteHandler.ts b/src/interaction-handlers/Music/autocompleteHandler.ts index e15f399..b21f6a4 100644 --- a/src/interaction-handlers/Music/autocompleteHandler.ts +++ b/src/interaction-handlers/Music/autocompleteHandler.ts @@ -1,7 +1,7 @@ +import { SearchResultType } from '@distube/youtube'; import { ApplyOptions } from '@sapphire/decorators'; import { InteractionHandler, InteractionHandlerTypes } from '@sapphire/framework'; import { AutocompleteInteraction, type ApplicationCommandOptionChoiceData } from 'discord.js'; -import { SearchResultType } from 'distube'; @ApplyOptions({ interactionHandlerType: InteractionHandlerTypes.Autocomplete @@ -13,19 +13,19 @@ export class AutocompleteHandler extends InteractionHandler { public override async parse(interaction: AutocompleteInteraction) { if (interaction.commandName !== 'play') return this.none(); - // Get the focussed (current) option + const focusedOption = interaction.options.getFocused(true); - // Ensure that the option name is one that can be autocompleted, or return none if not. switch (focusedOption.name) { case 'song': { if (!focusedOption.value) return this.some([]); - // Search for song! - const searchResult = await this.container.distube.search(focusedOption.value, { + + const searchResult = await this.container.youtubePlugin.search(focusedOption.value, { limit: 15, - type: SearchResultType.VIDEO + type: SearchResultType.VIDEO, + safeSearch: true }); - // Map the search results to the structure required for Autocomplete + return this.some(searchResult.map((match) => ({ name: match.name, value: match.url }))); } default: diff --git a/src/interaction-handlers/Music/nextTrackButton.ts b/src/interaction-handlers/Music/nextTrackButton.ts index 870e255..4d782aa 100644 --- a/src/interaction-handlers/Music/nextTrackButton.ts +++ b/src/interaction-handlers/Music/nextTrackButton.ts @@ -14,7 +14,7 @@ export class ButtonHandler extends InteractionHandler { const queue = this.container.distube.getQueue(guild!.id)!; if (!queue) throw new HootBaseError('There is nothing playing!', interaction); - + voteAction(interaction, 'skip'); } diff --git a/src/lib/@types/index.ts b/src/lib/@types/index.ts new file mode 100644 index 0000000..6c299b5 --- /dev/null +++ b/src/lib/@types/index.ts @@ -0,0 +1,35 @@ +import { Collection, GuildMember, Interaction, Snowflake, User } from 'discord.js'; +import { GuildIdResolvable } from 'distube'; +import { HootQueue } from '../distube/HootQueue'; + +declare module '@sapphire/pieces' { + interface Container { + distube: import('distube').DisTube; + youtubePlugin: import('@distube/youtube').YouTubePlugin; + } +} + +declare module '@sapphire/framework' { + interface Container { + distube: import('distube').DisTube; + } +} + +declare module 'distube' { + interface DisTube { + getQueue(guild: GuildIdResolvable): HootQueue | undefined; + updatePanel(interaction: Interaction): Promise; + } + + interface Queue { + owner?: GuildMember; + skipVotes: Collection; + backVotes: Collection; + panelId?: Snowflake; + } +} + +export type QueueMetadata = { + queueStarter?: GuildMember; + i?: import('discord.js').Interaction; +}; diff --git a/src/lib/HootClient.ts b/src/lib/HootClient.ts index e739cba..895f751 100644 --- a/src/lib/HootClient.ts +++ b/src/lib/HootClient.ts @@ -1,47 +1,37 @@ import { ApplicationCommandRegistries, SapphireClient, container } from '@sapphire/framework'; import { ClientOptions } from 'discord.js'; -import { DisTube, DisTubeOptions } from 'distube'; +import { DisTube } from 'distube'; +import { YtDlpPlugin } from '@distube/yt-dlp'; +import { DirectLinkPlugin } from '@distube/direct-link'; +import { YouTubePlugin } from '@distube/youtube'; +import { SoundCloudPlugin } from '@distube/soundcloud'; +import { SpotifyPlugin } from '@distube/spotify'; +import { CustomEvents } from './constants'; const dev = process.env.NODE_ENV !== 'production'; export class HootClient extends SapphireClient { distube: DisTube; + youtubePlugin = new YouTubePlugin(); - constructor( - options: ClientOptions, - distubeOptions: DisTubeOptions = { - leaveOnStop: false, - plugins: [] - } - ) { + constructor(options: ClientOptions) { super(options); - const distube = new DisTube(this, distubeOptions); + const distube = new DisTube(this, { + plugins: [new YtDlpPlugin({ update: true }), new DirectLinkPlugin(), this.youtubePlugin, new SoundCloudPlugin(), new SpotifyPlugin()] + }); this.distube = distube; + distube.updatePanel = async (interaction) => { + this.emit(CustomEvents.UpdatePanel, interaction); + }; container.distube = distube; + container.youtubePlugin = this.youtubePlugin; dev && ApplicationCommandRegistries.setDefaultGuildIds([process.env.DEV_GUILD_ID]); - for (const command of container.stores.get('commands').values()) { - command.applicationCommandRegistry.registerChatInputCommand((b) => b.setDMPermission(false)); - } + for (const command of container.stores.get('commands').values()) { + command.applicationCommandRegistry.registerChatInputCommand((b) => b.setDMPermission(false)); + } ApplicationCommandRegistries.registries.forEach((r) => r.registerChatInputCommand((b) => b.setDMPermission(false))); } } - -declare module '@sapphire/pieces' { - interface Container { - distube: import('distube').DisTube; - } -} - -declare module 'distube' { - interface DisTube { - getQueue(guildId: import('discord.js').Snowflake): import('./distube/HootQueue').HootQueue | undefined; - } -} - -export type QueueMetadata = { - queueStarter?: import('discord.js').GuildMember | import('discord.js').User; - i?: import('discord.js').Interaction; -}; diff --git a/src/lib/distube/HootQueue.ts b/src/lib/distube/HootQueue.ts index c07b32b..c6fac56 100644 --- a/src/lib/distube/HootQueue.ts +++ b/src/lib/distube/HootQueue.ts @@ -1,15 +1,12 @@ +import { Collection, GuildTextBasedChannel } from 'discord.js'; import DisTube, { DisTubeVoice, Queue, Song } from 'distube'; -import { GuildMember, User, Collection, Snowflake } from 'discord.js'; -import { GuildTextBasedChannel } from 'discord.js'; +import { QueueMetadata } from '../@types'; export class HootQueue extends Queue { - owner?: GuildMember; - skipVotes: Collection; - backVotes: Collection; - panelId?: Snowflake; + override songs: Song[] = []; - constructor(distube: DisTube, voice: DisTubeVoice, song: Song | Song[], textChannel?: GuildTextBasedChannel | undefined) { - super(distube, voice, song, textChannel); + constructor(distube: DisTube, voice: DisTubeVoice, textChannel?: GuildTextBasedChannel | undefined) { + super(distube, voice, textChannel); this.skipVotes = new Collection(); this.backVotes = new Collection(); } diff --git a/src/lib/distube/voteAction.ts b/src/lib/distube/voteAction.ts index 67abbfe..bdfa671 100644 --- a/src/lib/distube/voteAction.ts +++ b/src/lib/distube/voteAction.ts @@ -62,15 +62,3 @@ export async function voteAction(interaction: AcceptableInteraction, action: Vot return actionFn(); } - -declare module '@sapphire/framework' { - interface Container { - distube: import('distube').DisTube; - } -} - -declare module 'distube' { - interface DisTube { - getQueue(guildId: import('discord.js').Snowflake): import('../distube/HootQueue').HootQueue | undefined; - } -} diff --git a/src/lib/setup.ts b/src/lib/setup.ts index a8de793..34cf192 100644 --- a/src/lib/setup.ts +++ b/src/lib/setup.ts @@ -29,7 +29,7 @@ declare module '@skyra/env-utilities' { OWNERS: ArrayString; DEV_GUILD_ID: string; SUPPORT_SERVER_ID: string; - SUPPORT_SERVER_INVITE_LINK: string; + SUPPORT_SERVER_INVITE_LINK: string; SUPPORT_URL: string; } } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 7bb8e85..b6a2efe 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -12,10 +12,11 @@ import { type Message, type User } from 'discord.js'; -import { Queue } from 'distube'; import { RandomLoadingMessage } from './constants'; import { schedule as cronSchedule } from 'node-cron'; import { Cron, predefined } from '@sapphire/time-utilities'; +import { HootQueue } from './distube/HootQueue'; +import { bold } from 'discord.js'; /** * Picks a random item from an array @@ -82,21 +83,48 @@ function getGuildInfo(guild: Guild | null) { return `${guild.name}[${cyan(guild.id)}]`; } -export function getQueueStatus(queue: Queue) { +export function getQueueStatus( + queue: HootQueue, + isBold: boolean = true +): { + volume: string; + loop: string; + filter: string; + upNext: string; +} { + const result = { + volume: '', + loop: '', + filter: '', + upNext: '' + }; + const volumeIcon = queue.volume > 75 ? '🔊' : queue.volume > 25 ? '🔉' : queue.volume > 0 ? '🔈' : '🔇'; - const loopMode = queue.repeatMode ? (queue.repeatMode === 2 ? '🔁 **Queue**' : '🔂 **This song**') : ''; + result.volume = `${volumeIcon} ${queue.volume}%`; - const filter = !queue.filters.names.length ? '' : `🎛 **${queue.filters.names.join(', ').toLocaleUpperCase()}**`; + result.loop = queue.repeatMode ? (queue.repeatMode === 2 ? '🔁 Queue' : '🔂 This song') : ''; - const autoplay = !!queue.autoplay ? `\`🅰\` **Up Next**: [\`${queue.songs[0].related[0].name}\`](${queue.songs[0].related[0].url})` : ''; + result.filter = !queue.filters.names.length ? '' : `🎛 ${queue.filters.names.join(', ').toLocaleUpperCase()}`; - return { - volume: `${volumeIcon} **${queue.volume}%**`, - loop: `${loopMode}`, - filter: `${filter}`, - autoplay: `${autoplay}` - }; + if (isBold) { + result.volume = bold(result.volume); + if (!!result.filter) result.filter = bold(result.filter); + if (!!result.loop) result.loop = bold(result.loop); + } + + const nextSong = queue.songs[1]; + if (!nextSong || !nextSong.url || !nextSong.name) return result; + + // const upNextString = (name: string, url: string) => `\`🎵\` **Up Next**: [${name}](${url})`; + + result.upNext = `\`🎵\` **Up Next**: [\`${nextSong.name}\`](${nextSong.url})`; + + if (isBold) { + result.upNext = bold(result.upNext); + } + + return result; } export const getRandomActivity = ( diff --git a/src/listeners/distube/addListListener.ts b/src/listeners/distube/addListListener.ts index d628797..80cfe1a 100644 --- a/src/listeners/distube/addListListener.ts +++ b/src/listeners/distube/addListListener.ts @@ -4,8 +4,8 @@ import { AutocompleteInteraction } from 'discord.js'; import { Events, Playlist } from 'distube'; import { maxSongs } from '../../lib/constants'; import { HootQueue } from '../../lib/distube/HootQueue'; -import { QueueMetadata } from '../../lib/HootClient'; import { ErrorEmbed, SuccessEmbed } from '../../messages'; +import { QueueMetadata } from '../../lib/@types'; @ApplyOptions(({ container }) => ({ emitter: container.distube, diff --git a/src/listeners/distube/addSongListener.ts b/src/listeners/distube/addSongListener.ts index 656c5da..35de8b8 100644 --- a/src/listeners/distube/addSongListener.ts +++ b/src/listeners/distube/addSongListener.ts @@ -1,8 +1,7 @@ import { ApplyOptions } from '@sapphire/decorators'; import { Listener } from '@sapphire/framework'; import { AutocompleteInteraction } from 'discord.js'; -import { Events, Song } from 'distube'; -import { QueueMetadata } from '../../lib/HootClient'; +import { Events } from 'distube'; import { maxSongs } from '../../lib/constants'; import { HootQueue } from '../../lib/distube/HootQueue'; import { ErrorEmbed, SuccessEmbed } from '../../messages'; @@ -12,7 +11,7 @@ import { ErrorEmbed, SuccessEmbed } from '../../messages'; event: Events.ADD_SONG })) export class AddSongListener extends Listener { - public override async run(queue: HootQueue, song: Song) { + public override async run(queue: HootQueue, song: HootQueue['songs'][number]) { if (queue.songs.length - 1 > maxSongs) { queue.songs.splice(maxSongs + 1); diff --git a/src/listeners/distube/errorListener.ts b/src/listeners/distube/errorListener.ts index 720d45c..ac2ac2c 100644 --- a/src/listeners/distube/errorListener.ts +++ b/src/listeners/distube/errorListener.ts @@ -1,21 +1,22 @@ import { ApplyOptions } from '@sapphire/decorators'; import { Listener } from '@sapphire/framework'; -import { Channel } from 'discord.js'; import { Events } from 'distube'; import { ErrorEmbed } from '../../messages'; +import { HootQueue } from '../../lib/distube/HootQueue'; @ApplyOptions(({ container }) => ({ emitter: container.distube, event: Events.ERROR })) export class UserEvent extends Listener { - public override run(channel: Channel, e: unknown) { - const embed = new ErrorEmbed(`An error encountered: \`\`\`${(e as Error).toString().slice(0, 1974)}\`\`\``); - - if (channel && channel.isTextBased()) - channel.send({ + public override run(e: unknown, queue: HootQueue) { + try { + const embed = new ErrorEmbed(`An error encountered: \`\`\`${(e as Error).toString().slice(0, 1974)}\`\`\``); + queue.textChannel?.send({ embeds: [embed] }); - else this.container.logger.error(e); + } catch (error) { + this.container.logger.error(error); + } } } diff --git a/src/listeners/distube/initQueueListener.ts b/src/listeners/distube/initQueueListener.ts index 2ccf061..03495f5 100644 --- a/src/listeners/distube/initQueueListener.ts +++ b/src/listeners/distube/initQueueListener.ts @@ -10,10 +10,8 @@ import { Collection } from 'discord.js'; })) export class InitQueueListener extends Listener { run(queue: HootQueue) { - queue.owner = queue.songs[0].member; queue.skipVotes = new Collection(); queue.backVotes = new Collection(); - // this.container.logger.debug('InitQueueListener', queue); } } diff --git a/src/listeners/distube/playSongListener.ts b/src/listeners/distube/playSongListener.ts index a5e3961..384f927 100644 --- a/src/listeners/distube/playSongListener.ts +++ b/src/listeners/distube/playSongListener.ts @@ -1,6 +1,6 @@ import { ApplyOptions } from '@sapphire/decorators'; import { Listener } from '@sapphire/framework'; -import { Events, Song } from 'distube'; +import { Events } from 'distube'; import { HootQueue } from '../../lib/distube/HootQueue'; import { PlayPanelComponents } from '../../messages/components/PlayPanelComponents'; import { PlayPanelEmbed } from '../../messages/embeds/PlayPanelEmbed'; @@ -10,8 +10,10 @@ import { PlayPanelEmbed } from '../../messages/embeds/PlayPanelEmbed'; event: Events.PLAY_SONG })) export class PlaySongListener extends Listener { - async run(queue: HootQueue, song: Song) { - // this.container.logger.debug('PlaySongListener', queue); + async run(queue: HootQueue, song: HootQueue['songs'][number]) { + if (!queue.owner) { + queue.owner = song.metadata.queueStarter; + } const embeds = [PlayPanelEmbed.create(song, queue)]; diff --git a/src/listeners/distube/searchNoResultListener.ts b/src/listeners/distube/searchNoResultListener.ts deleted file mode 100644 index 07fc171..0000000 --- a/src/listeners/distube/searchNoResultListener.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApplyOptions } from '@sapphire/decorators'; -import { Listener } from '@sapphire/framework'; -import { Message } from 'discord.js'; -import { Events } from 'distube'; -import { ErrorEmbed } from '../../messages'; - -@ApplyOptions(({ container }) => ({ - emitter: container.distube, - event: Events.SEARCH_NO_RESULT -})) -export class UserEvent extends Listener { - public override run(message: Message, query: string) { - const embed = new ErrorEmbed(`No result found for \`${query}\`!`); - - message.channel.send({ - embeds: [embed] - }); - } -} diff --git a/src/messages/embeds/PlayPanelEmbed.ts b/src/messages/embeds/PlayPanelEmbed.ts index 85cf766..cfa00dd 100644 --- a/src/messages/embeds/PlayPanelEmbed.ts +++ b/src/messages/embeds/PlayPanelEmbed.ts @@ -5,7 +5,7 @@ import { getQueueStatus } from '../../lib/utils'; export class PlayPanelEmbed { static create(song: Song, queue: HootQueue) { - const { autoplay, filter, loop, volume } = getQueueStatus(queue); + const { upNext, filter, loop, volume } = getQueueStatus(queue); const firstLine = [`⌛ **${song.formattedDuration}**`, volume, loop, filter].filter((x) => !!x.length).join(' | '); return new EmbedBuilder() .setColor(queue.paused ? 'Red' : 'Random') @@ -14,9 +14,9 @@ export class PlayPanelEmbed { iconURL: `${song.user?.displayAvatarURL()}` }) .setTitle(song.name || null) - .setURL(song.url) + .setURL(song.url || null) .setThumbnail(song.thumbnail || null) - .setDescription(`${firstLine}\n\n${autoplay}`) + .setDescription(`${firstLine}\n\n${upNext}`) .setFooter({ text: `${queue.owner?.user.tag} 💂‍♂️`, iconURL: `${queue.owner?.user.displayAvatarURL()}`