diff --git a/.env b/.env
index f365365..d81a6c5 100644
--- a/.env
+++ b/.env
@@ -6,6 +6,8 @@ BOT_LANGUAGE=en
BOT_MAX_SONGS_IN_QUEUE=500
BOT_MAX_SONGS_HISTORY_SIZE=60
+BOT_MAX_PLAYLISTS_PER_USER=25
+BOT_MAX_SONGS_IN_USER_PLAYLIST=500
BOT_DISCORD_TOKEN=undefined
BOT_DISCORD_CLIENT_ID=undefined
diff --git a/.prettierrc b/.prettierrc
index 262579f..6aea180 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -2,6 +2,6 @@
"semi": true,
"trailingComma": "none",
"singleQuote": true,
- "printWidth": 100,
+ "printWidth": 120,
"endOfLine": "auto"
}
diff --git a/README.md b/README.md
index 123d67e..58669cf 100644
--- a/README.md
+++ b/README.md
@@ -5,13 +5,17 @@
Cool audiobot for Discord created by @AlexInCube
-## 🌟 Features
-- Command /alcotest which shows your alcohol count in blood
-- Audioplayer based on [Distube](https://github.com/skick1234/DisTube) with buttons
+## 🖥️ Setup
+- Go to [Wiki Setup Section](https://github.com/AlexInCube/AlCoTest/wiki/Setup)
+## 🌟 Features
![play-audioplayer](/wiki/images/commands/play-audioplayer.png)
-
+- Audioplayer based on [Distube](https://github.com/skick1234/DisTube) with buttons
+- Playlists for songs
+- Lyrics for songs
+- Downloading of songs via /download command
- Support YouTube, Spotify, Soundcloud, Apple Music, any HTTP-stream and Discord Attachments (/playfile support MP3/WAV/OGG)
-- Support Slash and Text commands (with customizable prefix per server using /setprefix)
+- Support Slash and Text commands system (with customizable prefix per server using /setprefix)
- Localization (English and Russian are currently supported)
-- Go to [Wiki](https://github.com/AlexInCube/AlCoTest/wiki) to get more information about features and other.
+- Command /alcotest which shows your alcohol count in blood
+- Go to [Wiki](https://github.com/AlexInCube/AlCoTest/wiki) to get more information about features, commands, and others.
diff --git a/icons/audioplayer/player/favorite.png b/icons/audioplayer/player/favorite.png
new file mode 100644
index 0000000..b28a537
Binary files /dev/null and b/icons/audioplayer/player/favorite.png differ
diff --git a/package.json b/package.json
index 3d0676a..9e46319 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "aicbot",
- "version": "3.6.0",
+ "version": "3.7.0",
"description": "Discord Bot for playing music",
"main": "build/main.js",
"scripts": {
@@ -61,7 +61,7 @@
"@typescript-eslint/eslint-plugin": "^8.0.1",
"@typescript-eslint/parser": "^8.0.1",
"@eslint/js": "^9.9.0",
- "typescript-eslint": "7.18.0",
+ "typescript-eslint": "^8.2.0",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0188dae..ab9474b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -142,8 +142,8 @@ importers:
specifier: ^5.5.4
version: 5.5.4
typescript-eslint:
- specifier: 7.18.0
- version: 7.18.0(eslint@9.9.0)(typescript@5.5.4)
+ specifier: ^8.2.0
+ version: 8.2.0(eslint@9.9.0)(typescript@5.5.4)
packages:
@@ -348,19 +348,19 @@ packages:
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
- '@typescript-eslint/eslint-plugin@7.18.0':
- resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/eslint-plugin@8.0.1':
+ resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- '@typescript-eslint/parser': ^7.0.0
- eslint: ^8.56.0
+ '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
+ eslint: ^8.57.0 || ^9.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@typescript-eslint/eslint-plugin@8.0.1':
- resolution: {integrity: sha512-5g3Y7GDFsJAnY4Yhvk8sZtFfV6YNF2caLzjrRPUBzewjPCaj0yokePB4LJSobyCzGMzjZZYFbwuzbfDHlimXbQ==}
+ '@typescript-eslint/eslint-plugin@8.2.0':
+ resolution: {integrity: sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -370,18 +370,18 @@ packages:
typescript:
optional: true
- '@typescript-eslint/parser@7.18.0':
- resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/parser@8.0.1':
+ resolution: {integrity: sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.56.0
+ eslint: ^8.57.0 || ^9.0.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@typescript-eslint/parser@8.0.1':
- resolution: {integrity: sha512-5IgYJ9EO/12pOUwiBKFkpU7rS3IU21mtXzB81TNwq2xEybcmAZrE9qwDtsb5uQd9aVO9o0fdabFyAmKveXyujg==}
+ '@typescript-eslint/parser@8.2.0':
+ resolution: {integrity: sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -390,26 +390,25 @@ packages:
typescript:
optional: true
- '@typescript-eslint/scope-manager@7.18.0':
- resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
- engines: {node: ^18.18.0 || >=20.0.0}
-
'@typescript-eslint/scope-manager@8.0.1':
resolution: {integrity: sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/type-utils@7.18.0':
- resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/scope-manager@8.2.0':
+ resolution: {integrity: sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/type-utils@8.0.1':
+ resolution: {integrity: sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.56.0
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@typescript-eslint/type-utils@8.0.1':
- resolution: {integrity: sha512-+/UT25MWvXeDX9YaHv1IS6KI1fiuTto43WprE7pgSMswHbn1Jm9GEM4Txp+X74ifOWV8emu2AWcbLhpJAvD5Ng==}
+ '@typescript-eslint/type-utils@8.2.0':
+ resolution: {integrity: sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
@@ -417,25 +416,25 @@ packages:
typescript:
optional: true
- '@typescript-eslint/types@7.18.0':
- resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
- engines: {node: ^18.18.0 || >=20.0.0}
-
'@typescript-eslint/types@8.0.1':
resolution: {integrity: sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript-eslint/typescript-estree@7.18.0':
- resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/types@8.2.0':
+ resolution: {integrity: sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.0.1':
+ resolution: {integrity: sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- '@typescript-eslint/typescript-estree@8.0.1':
- resolution: {integrity: sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==}
+ '@typescript-eslint/typescript-estree@8.2.0':
+ resolution: {integrity: sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '*'
@@ -443,26 +442,26 @@ packages:
typescript:
optional: true
- '@typescript-eslint/utils@7.18.0':
- resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==}
- engines: {node: ^18.18.0 || >=20.0.0}
- peerDependencies:
- eslint: ^8.56.0
-
'@typescript-eslint/utils@8.0.1':
resolution: {integrity: sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
- '@typescript-eslint/visitor-keys@7.18.0':
- resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/utils@8.2.0':
+ resolution: {integrity: sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
'@typescript-eslint/visitor-keys@8.0.1':
resolution: {integrity: sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@typescript-eslint/visitor-keys@8.2.0':
+ resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@vladfrangu/async_event_emitter@2.4.5':
resolution: {integrity: sha512-J7T3gUr3Wz0l7Ni1f9upgBZ7+J22/Q1B7dl0X6fG+fTsD+H+31DIosMHj4Um1dWQwqbcQ3oQf+YS2foYkDc9cQ==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
@@ -1728,11 +1727,10 @@ packages:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
- typescript-eslint@7.18.0:
- resolution: {integrity: sha512-PonBkP603E3tt05lDkbOMyaxJjvKqQrXsnow72sVeOFINDE/qNmnnd+f9b4N+U7W6MXnnYyrhtmF2t08QWwUbA==}
- engines: {node: ^18.18.0 || >=20.0.0}
+ typescript-eslint@8.2.0:
+ resolution: {integrity: sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
- eslint: ^8.56.0
typescript: '*'
peerDependenciesMeta:
typescript:
@@ -2127,24 +2125,6 @@ snapshots:
'@types/node': 22.2.0
optional: true
- '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)':
- dependencies:
- '@eslint-community/regexpp': 4.11.0
- '@typescript-eslint/parser': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
- '@typescript-eslint/scope-manager': 7.18.0
- '@typescript-eslint/type-utils': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
- '@typescript-eslint/utils': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
- '@typescript-eslint/visitor-keys': 7.18.0
- eslint: 9.9.0
- graphemer: 1.4.0
- ignore: 5.3.1
- natural-compare: 1.4.0
- ts-api-utils: 1.3.0(typescript@5.5.4)
- optionalDependencies:
- typescript: 5.5.4
- transitivePeerDependencies:
- - supports-color
-
'@typescript-eslint/eslint-plugin@8.0.1(@typescript-eslint/parser@8.0.1(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
'@eslint-community/regexpp': 4.11.0
@@ -2163,14 +2143,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@7.18.0(eslint@9.9.0)(typescript@5.5.4)':
+ '@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/scope-manager': 7.18.0
- '@typescript-eslint/types': 7.18.0
- '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4)
- '@typescript-eslint/visitor-keys': 7.18.0
- debug: 4.3.6
+ '@eslint-community/regexpp': 4.11.0
+ '@typescript-eslint/parser': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/scope-manager': 8.2.0
+ '@typescript-eslint/type-utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/visitor-keys': 8.2.0
eslint: 9.9.0
+ graphemer: 1.4.0
+ ignore: 5.3.1
+ natural-compare: 1.4.0
+ ts-api-utils: 1.3.0(typescript@5.5.4)
optionalDependencies:
typescript: 5.5.4
transitivePeerDependencies:
@@ -2189,32 +2174,45 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@7.18.0':
+ '@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/types': 7.18.0
- '@typescript-eslint/visitor-keys': 7.18.0
+ '@typescript-eslint/scope-manager': 8.2.0
+ '@typescript-eslint/types': 8.2.0
+ '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4)
+ '@typescript-eslint/visitor-keys': 8.2.0
+ debug: 4.3.6
+ eslint: 9.9.0
+ optionalDependencies:
+ typescript: 5.5.4
+ transitivePeerDependencies:
+ - supports-color
'@typescript-eslint/scope-manager@8.0.1':
dependencies:
'@typescript-eslint/types': 8.0.1
'@typescript-eslint/visitor-keys': 8.0.1
- '@typescript-eslint/type-utils@7.18.0(eslint@9.9.0)(typescript@5.5.4)':
+ '@typescript-eslint/scope-manager@8.2.0':
+ dependencies:
+ '@typescript-eslint/types': 8.2.0
+ '@typescript-eslint/visitor-keys': 8.2.0
+
+ '@typescript-eslint/type-utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4)
- '@typescript-eslint/utils': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4)
+ '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4)
debug: 4.3.6
- eslint: 9.9.0
ts-api-utils: 1.3.0(typescript@5.5.4)
optionalDependencies:
typescript: 5.5.4
transitivePeerDependencies:
+ - eslint
- supports-color
- '@typescript-eslint/type-utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)':
+ '@typescript-eslint/type-utils@8.2.0(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4)
- '@typescript-eslint/utils': 8.0.1(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4)
+ '@typescript-eslint/utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
debug: 4.3.6
ts-api-utils: 1.3.0(typescript@5.5.4)
optionalDependencies:
@@ -2223,14 +2221,14 @@ snapshots:
- eslint
- supports-color
- '@typescript-eslint/types@7.18.0': {}
-
'@typescript-eslint/types@8.0.1': {}
- '@typescript-eslint/typescript-estree@7.18.0(typescript@5.5.4)':
+ '@typescript-eslint/types@8.2.0': {}
+
+ '@typescript-eslint/typescript-estree@8.0.1(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/types': 7.18.0
- '@typescript-eslint/visitor-keys': 7.18.0
+ '@typescript-eslint/types': 8.0.1
+ '@typescript-eslint/visitor-keys': 8.0.1
debug: 4.3.6
globby: 11.1.0
is-glob: 4.0.3
@@ -2242,10 +2240,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/typescript-estree@8.0.1(typescript@5.5.4)':
+ '@typescript-eslint/typescript-estree@8.2.0(typescript@5.5.4)':
dependencies:
- '@typescript-eslint/types': 8.0.1
- '@typescript-eslint/visitor-keys': 8.0.1
+ '@typescript-eslint/types': 8.2.0
+ '@typescript-eslint/visitor-keys': 8.2.0
debug: 4.3.6
globby: 11.1.0
is-glob: 4.0.3
@@ -2257,36 +2255,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@7.18.0(eslint@9.9.0)(typescript@5.5.4)':
+ '@typescript-eslint/utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0)
- '@typescript-eslint/scope-manager': 7.18.0
- '@typescript-eslint/types': 7.18.0
- '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4)
+ '@typescript-eslint/scope-manager': 8.0.1
+ '@typescript-eslint/types': 8.0.1
+ '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4)
eslint: 9.9.0
transitivePeerDependencies:
- supports-color
- typescript
- '@typescript-eslint/utils@8.0.1(eslint@9.9.0)(typescript@5.5.4)':
+ '@typescript-eslint/utils@8.2.0(eslint@9.9.0)(typescript@5.5.4)':
dependencies:
'@eslint-community/eslint-utils': 4.4.0(eslint@9.9.0)
- '@typescript-eslint/scope-manager': 8.0.1
- '@typescript-eslint/types': 8.0.1
- '@typescript-eslint/typescript-estree': 8.0.1(typescript@5.5.4)
+ '@typescript-eslint/scope-manager': 8.2.0
+ '@typescript-eslint/types': 8.2.0
+ '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4)
eslint: 9.9.0
transitivePeerDependencies:
- supports-color
- typescript
- '@typescript-eslint/visitor-keys@7.18.0':
+ '@typescript-eslint/visitor-keys@8.0.1':
dependencies:
- '@typescript-eslint/types': 7.18.0
+ '@typescript-eslint/types': 8.0.1
eslint-visitor-keys: 3.4.3
- '@typescript-eslint/visitor-keys@8.0.1':
+ '@typescript-eslint/visitor-keys@8.2.0':
dependencies:
- '@typescript-eslint/types': 8.0.1
+ '@typescript-eslint/types': 8.2.0
eslint-visitor-keys: 3.4.3
'@vladfrangu/async_event_emitter@2.4.5': {}
@@ -3562,15 +3560,15 @@ snapshots:
dependencies:
prelude-ls: 1.2.1
- typescript-eslint@7.18.0(eslint@9.9.0)(typescript@5.5.4):
+ typescript-eslint@8.2.0(eslint@9.9.0)(typescript@5.5.4):
dependencies:
- '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
- '@typescript-eslint/parser': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
- '@typescript-eslint/utils': 7.18.0(eslint@9.9.0)(typescript@5.5.4)
- eslint: 9.9.0
+ '@typescript-eslint/eslint-plugin': 8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.0)(typescript@5.5.4))(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/parser': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
+ '@typescript-eslint/utils': 8.2.0(eslint@9.9.0)(typescript@5.5.4)
optionalDependencies:
typescript: 5.5.4
transitivePeerDependencies:
+ - eslint
- supports-color
typescript@5.5.4: {}
diff --git a/src/CommandTypes.ts b/src/CommandTypes.ts
index 6156092..309f065 100644
--- a/src/CommandTypes.ts
+++ b/src/CommandTypes.ts
@@ -1,6 +1,7 @@
import {
AutocompleteInteraction,
ChatInputCommandInteraction,
+ CommandInteraction,
Message,
PermissionResolvable,
SlashCommandBuilder,
@@ -65,3 +66,6 @@ interface IGuildData {
voice_required?: boolean;
voice_with_bot_only?: boolean; // Property enabled only if voice_required is true
}
+
+export type InteractionReplyContext = ChatInputCommandInteraction | CommandInteraction;
+export type ReplyContext = Message | InteractionReplyContext;
diff --git a/src/EnvironmentVariables.ts b/src/EnvironmentVariables.ts
index 3ee6a8f..4196cbe 100644
--- a/src/EnvironmentVariables.ts
+++ b/src/EnvironmentVariables.ts
@@ -57,6 +57,8 @@ const envVariables = z.object({
BOT_MAX_SONGS_IN_QUEUE: z.coerce.number().positive().min(1).optional().default(500),
BOT_MAX_SONGS_HISTORY_SIZE: z.coerce.number().nonnegative().optional().default(60),
+ BOT_MAX_PLAYLISTS_PER_USER: z.coerce.number().positive().min(1).max(50).optional().default(25),
+ BOT_MAX_SONGS_IN_USER_PLAYLIST: z.coerce.number().positive().min(1).optional().default(500),
MONGO_URI: z.string(),
MONGO_DATABASE_NAME: z.string(),
@@ -85,8 +87,5 @@ export const ENV = envVariables.parse(process.env);
if (fs.existsSync(envPath)) {
loggerSend(`Environment variables is loaded from ${envPath}`, loggerPrefixEnv);
} else {
- loggerSend(
- `Environment variables is loaded from OS / Docker environment variables`,
- loggerPrefixEnv
- );
+ loggerSend(`Environment variables is loaded from OS / Docker environment variables`, loggerPrefixEnv);
}
diff --git a/src/audioplayer/AudioPlayerTypes.ts b/src/audioplayer/AudioPlayerIcons.ts
similarity index 91%
rename from src/audioplayer/AudioPlayerTypes.ts
rename to src/audioplayer/AudioPlayerIcons.ts
index 4126c2e..3642db4 100644
--- a/src/audioplayer/AudioPlayerTypes.ts
+++ b/src/audioplayer/AudioPlayerIcons.ts
@@ -10,7 +10,8 @@ export enum AudioPlayerIcons {
skip = '<:skipbutton:1092107438234275900>',
shuffle = '<:shufflebutton:1092107651384614912>',
list = '<:songlistwhite:1014551771705782405>',
- lyrics = '<:lyrics:1260156581794811974>'
+ lyrics = '<:lyrics:1260156581794811974>',
+ favorite = '<:favorite:1275199463631093860>'
}
export enum AudioSourceIcons {
diff --git a/src/audioplayer/AudioPlayersManager.ts b/src/audioplayer/AudioPlayersManager.ts
index 93d4c44..b5bb938 100644
--- a/src/audioplayer/AudioPlayersManager.ts
+++ b/src/audioplayer/AudioPlayersManager.ts
@@ -1,15 +1,5 @@
-import {
- DisTube,
- PlayOptions,
- Queue,
- RepeatMode,
- Song,
- Events as DistubeEvents,
- Playlist
-} from 'distube';
+import { DisTube, Events as DistubeEvents, Playlist, PlayOptions, Queue, RepeatMode, Song } from 'distube';
import { AudioPlayersStore } from './AudioPlayersStore.js';
-import { pagination } from '../utilities/pagination/pagination.js';
-import { ButtonStyles, ButtonTypes } from '../utilities/pagination/paginationTypes.js';
import { clamp } from '../utilities/clamp.js';
import { generateErrorEmbed } from '../utilities/generateErrorEmbed.js';
import i18next from 'i18next';
@@ -22,7 +12,6 @@ import {
ButtonInteraction,
Client,
CommandInteraction,
- Embed,
EmbedBuilder,
Guild,
Interaction,
@@ -34,6 +23,7 @@ import { generateWarningEmbed } from '../utilities/generateWarningEmbed.js';
import { generateLyricsEmbed } from './Lyrics.js';
import { getGuildOptionLeaveOnEmpty, setGuildOptionLeaveOnEmpty } from '../schemas/SchemaGuild.js';
import { addSongToGuildSongsHistory } from '../schemas/SchemaSongsHistory.js';
+import { PaginationList } from './PaginationList.js';
export const loggerPrefixAudioplayer = `Audioplayer`;
@@ -60,11 +50,11 @@ export class AudioPlayersManager {
async play(
voiceChannel: VoiceBasedChannel,
textChannel: TextChannel,
- song: string | Song,
+ query: string | Song | Playlist,
options?: PlayOptions
) {
try {
- const playableThing: Song | Playlist = await this.distube.handler.resolve(song);
+ const playableThing: Song | Playlist = await this.distube.handler.resolve(query);
// I am need manual connect user to a voice channel, because when I am using only Distube "play"
// method, getVoiceConnection in @discordjs/voice is not working
@@ -78,9 +68,7 @@ export class AudioPlayersManager {
} catch (e) {
if (ENV.BOT_VERBOSE_LOGGING) loggerError(e);
await textChannel.send({
- embeds: [
- generateErrorEmbed(`${song}\n${e.message}`, i18next.t('audioplayer:play_error') as string)
- ]
+ embeds: [generateErrorEmbed(`${query}\n${e.message}`, i18next.t('audioplayer:play_error') as string)]
});
const queue = this.distube.getQueue(voiceChannel.guildId);
@@ -261,22 +249,15 @@ export class AudioPlayersManager {
const startingIndex = pageNumber * entriesPerPage;
- for (
- let i = startingIndex;
- i < Math.min(startingIndex + entriesPerPage, queue.songs.length);
- i++
- ) {
+ for (let i = startingIndex; i < Math.min(startingIndex + entriesPerPage, queue.songs.length); i++) {
const song = queue.songs[i];
- queueList +=
- `${i + 1}. ` + `[${song.name}](${song.url})` + ` - \`${song.formattedDuration}\`\n`;
+ queueList += `${i + 1}. ` + `[${song.name}](${song.url})` + ` - \`${song.formattedDuration}\`\n`;
}
const page = new EmbedBuilder()
.setAuthor({ name: `${i18next.t('audioplayer:show_queue_songs_in_queue')}: ` })
.setTitle(queue.songs[0].name!)
- .setDescription(
- `**${i18next.t('audioplayer:show_queue_title')}: **\n${queueList}`.slice(0, 4096)
- );
+ .setDescription(`**${i18next.t('audioplayer:show_queue_title')}: **\n${queueList}`.slice(0, 4096));
if (queue.songs[0].url) {
page.setURL(queue.songs[0].url);
@@ -293,36 +274,7 @@ export class AudioPlayersManager {
arrayEmbeds.push(buildPage(queue, i, entriesPerPage));
}
- await pagination({
- embeds: arrayEmbeds as unknown as Embed[],
- author: interaction.user,
- interaction: interaction as CommandInteraction,
- ephemeral: true,
- fastSkip: true,
- pageTravel: false,
- buttons: [
- {
- type: ButtonTypes.first,
- emoji: '⬅️',
- style: ButtonStyles.Secondary
- },
- {
- type: ButtonTypes.previous,
- emoji: '◀️',
- style: ButtonStyles.Secondary
- },
- {
- type: ButtonTypes.next,
- emoji: '▶️',
- style: ButtonStyles.Secondary
- },
- {
- type: ButtonTypes.last,
- emoji: '➡️',
- style: ButtonStyles.Secondary
- }
- ]
- });
+ await PaginationList(interaction as CommandInteraction, arrayEmbeds, interaction.user);
}
async setLeaveOnEmpty(guild: Guild, mode: boolean) {
diff --git a/src/audioplayer/AudioPlayersStore.ts b/src/audioplayer/AudioPlayersStore.ts
index 5987dec..ddce13d 100644
--- a/src/audioplayer/AudioPlayersStore.ts
+++ b/src/audioplayer/AudioPlayersStore.ts
@@ -8,11 +8,7 @@ export class AudioPlayersStore {
constructor(_client: Client) {
this.client = _client;
}
- async add(
- guildId: string,
- textChannel: GuildTextBasedChannel,
- queue: Queue
- ): Promise {
+ async add(guildId: string, textChannel: GuildTextBasedChannel, queue: Queue): Promise {
if (this.client.guilds.cache.get(guildId)) {
if (!this.collection.has(guildId)) {
this.collection.set(guildId, new PlayerInstance(this.client, textChannel, queue));
diff --git a/src/audioplayer/LoadPlugins.ts b/src/audioplayer/LoadPlugins.ts
index 0177c6b..311f817 100644
--- a/src/audioplayer/LoadPlugins.ts
+++ b/src/audioplayer/LoadPlugins.ts
@@ -92,18 +92,12 @@ export async function LoadPlugins(): Promise> {
function setupYtCookieSchedule() {
if (ENV.BOT_GOOGLE_EMAIL && ENV.BOT_GOOGLE_PASSWORD) {
- loggerSend(
- 'Google data is provided, setup cron job for cookies fetching',
- loggerPrefixAudioplayerPluginsLoader
- );
+ loggerSend('Google data is provided, setup cron job for cookies fetching', loggerPrefixAudioplayerPluginsLoader);
Cron.schedule('0 0 * * *', async () => {
const cookies = await getYoutubeCookie();
if (!cookies) return;
YtPlugin.cookies = cookies;
- loggerSend(
- 'Cookies is fetched again through Google Auth',
- loggerPrefixAudioplayerPluginsLoader
- );
+ loggerSend('Cookies is fetched by cron job through Google Auth', loggerPrefixAudioplayerPluginsLoader);
});
}
}
@@ -115,9 +109,7 @@ async function loadPluginsPartYoutube(plugins: Array) {
if (fs.existsSync('yt-cookies.json')) {
try {
- YtPlugin.cookies = JSON.parse(
- fs.readFileSync('yt-cookies.json', { encoding: 'utf8', flag: 'r' })
- );
+ YtPlugin.cookies = JSON.parse(fs.readFileSync('yt-cookies.json', { encoding: 'utf8', flag: 'r' }));
loggerSend("'yt-cookies.json' is loaded", loggerPrefixAudioplayerPluginsLoader);
} catch (e) {
loggerError("'yt-cookies.json' error when parsing", loggerPrefixAudioplayerPluginsLoader);
diff --git a/src/audioplayer/PaginationList.ts b/src/audioplayer/PaginationList.ts
new file mode 100644
index 0000000..fb51432
--- /dev/null
+++ b/src/audioplayer/PaginationList.ts
@@ -0,0 +1,38 @@
+import { pagination } from '../utilities/pagination/pagination.js';
+import { Embed, EmbedBuilder, Message, User } from 'discord.js';
+import { ButtonStyles, ButtonTypes } from '../utilities/pagination/paginationTypes.js';
+import { ReplyContext } from '../CommandTypes.js';
+
+export async function PaginationList(ctx: ReplyContext, pages: Array