From 7b07683a6bab636fd2fd73bb81f159aba3cd167f Mon Sep 17 00:00:00 2001 From: AlexInCube Date: Sun, 28 Jul 2024 03:25:39 +0300 Subject: [PATCH] 3.3.0 Bot now don't join to channel if an error happens during a fetching song query. Added BOT_FFMPEG_LOGGING env var. Changed download folder behavior, now bot removes songs from folder "downloads," instead of deleting it. Update of Yandex Music plugin to fix few bugs. Increased build speed of Docker Image. Add timezone package in Docker image. --- .env | 3 +++ Dockerfile | 25 ++++++++++-------- package.json | 8 +++--- pnpm-lock.yaml | 12 ++++----- runInDocker.sh | 2 +- src/CookiesAutomation.ts | 12 ++++++++- src/EnvironmentVariables.ts | 12 +++++++++ src/Script_getCookie.ts | 10 +++++++ src/audioplayer/AudioPlayerCore.ts | 39 ++++++++++++++-------------- src/audioplayer/util/downloadSong.ts | 16 ++++++++++-- wiki/Setup.md | 7 +++-- 11 files changed, 100 insertions(+), 46 deletions(-) create mode 100644 src/Script_getCookie.ts diff --git a/.env b/.env index d7ef3a2..99d56c0 100644 --- a/.env +++ b/.env @@ -1,4 +1,5 @@ BOT_VERBOSE_LOGGING=false +BOT_FFMPEG_LOGGING=false BOT_COMMAND_PREFIX=// BOT_LANGUAGE=en @@ -23,3 +24,5 @@ BOT_GENIUS_TOKEN=undefined MONGO_URI=undefined MONGO_DATABASE_NAME=undefined + +TZ=Europe/Kiev diff --git a/Dockerfile b/Dockerfile index 81410bd..3ee6f39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,20 @@ # syntax=docker/dockerfile:1 -FROM node:20-alpine as base +FROM node:22-alpine AS base RUN apk update && apk upgrade && apk add ffmpeg && apk add python3 +RUN apk add --no-cache tzdata -FROM base as build +FROM base AS build +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true RUN apk add build-base && apk add make RUN npm i -g pnpm@latest + WORKDIR /botbuild -COPY . . +COPY .. . RUN pnpm install --frozen-lockfile RUN pnpm run build RUN pnpm prune --prod -FROM base as prod +FROM base AS prod RUN apk update && apk add --no-cache nmap && \ echo @edge https://dl-cdn.alpinelinux.org/alpine/edge/community >> /etc/apk/repositories && \ @@ -24,18 +27,18 @@ harfbuzz \ ttf-freefont \ nss -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true -ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser - RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 aicbot +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser + WORKDIR /bot -COPY --from=build /botbuild/build ./build -COPY --from=build /botbuild/node_modules ./node_modules -COPY --from=build /botbuild/package.json . +COPY --chown=aicbot:nodejs --from=build /botbuild/build ./build +COPY --chown=aicbot:nodejs --from=build /botbuild/node_modules ./node_modules +COPY --chown=aicbot:nodejs --from=build /botbuild/package.json . -RUN chown -R aicbot:nodejs /bot +RUN mkdir "downloads" +RUN chmod -R 777 "downloads" USER aicbot diff --git a/package.json b/package.json index fbdcfc3..925a47d 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "aicbot", - "version": "3.2.2", + "version": "3.3.0", "description": "Discord Bot for playing music", "main": "build/main.js", "scripts": { "build": "tsc", "development": "tsc&& cross-env NODE_ENV=development node build/main.js", "production": "cross-env NODE_ENV=production node build/main.js", - "cookies_grabbing": "tsc&& cross-env NODE_ENV=development node build/CookiesAutomation.js" + "cookies_grabbing": "cross-env NODE_ENV=development node build/Script_getCookie.js" }, "type": "module", "keywords": [], @@ -32,7 +32,7 @@ "discord.js": "^14.15.3", "distube": "^5.0.2", "distube-apple-music": "^0.1.0", - "distube-yandex-music-plugin": "^1.0.4", + "distube-yandex-music-plugin": "^1.0.5", "dotenv": "^16.4.5", "genius-lyrics": "^4.4.7", "i18next": "^23.12.2", @@ -42,7 +42,7 @@ "node-os-utils": "^1.3.7", "opusscript": "^0.1.1", "prism-media": "^1.3.5", - "puppeteer": "^22.13.1", + "puppeteer": "22.13.1", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "sodium-native": "^4.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b4229d..e1f7617 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,8 +54,8 @@ importers: specifier: ^0.1.0 version: 0.1.0(distube@5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3)) distube-yandex-music-plugin: - specifier: ^1.0.4 - version: 1.0.4(distube@5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3)) + specifier: ^1.0.5 + version: 1.0.5(distube@5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3)) dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -84,7 +84,7 @@ importers: specifier: ^1.3.5 version: 1.3.5(opusscript@0.1.1) puppeteer: - specifier: ^22.13.1 + specifier: 22.13.1 version: 22.13.1(typescript@5.5.3) puppeteer-extra: specifier: ^3.3.6 @@ -650,8 +650,8 @@ packages: peerDependencies: distube: '5' - distube-yandex-music-plugin@1.0.4: - resolution: {integrity: sha512-A3VnMHqyoKxsp/2S1ZSuYpSOQboY2H2kr4zwIY7OYoVBAy4ZDbzKC2IWB47F49n+bz6kgi4tnps5WI+8wg2DRg==} + distube-yandex-music-plugin@1.0.5: + resolution: {integrity: sha512-sHsnUxdFFVzqTNPhmC9w6nYsseVS2Tsuwgy5IedPCj4bJLTc3iOWUbNBAYNgQnsxPPExH7S/kFWjxXQ/0NAoKg==} peerDependencies: distube: '5' @@ -2404,7 +2404,7 @@ snapshots: isomorphic-unfetch: 4.0.2 node-html-parser: 6.1.13 - distube-yandex-music-plugin@1.0.4(distube@5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3)): + distube-yandex-music-plugin@1.0.5(distube@5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3)): dependencies: distube: 5.0.2(@discordjs/voice@0.17.0(opusscript@0.1.1))(discord.js@14.15.3) ym-api-meowed: 1.3.4 diff --git a/runInDocker.sh b/runInDocker.sh index 6307c64..a5ba33e 100644 --- a/runInDocker.sh +++ b/runInDocker.sh @@ -1,2 +1,2 @@ #!/bin/sh -docker-compose up --detach --force-recreate --no-build +docker-compose up --detach --force-recreate --no-build --remove-orphans diff --git a/src/CookiesAutomation.ts b/src/CookiesAutomation.ts index 3c6ba13..a1a2ded 100644 --- a/src/CookiesAutomation.ts +++ b/src/CookiesAutomation.ts @@ -19,7 +19,11 @@ export async function getYoutubeCookie() { loggerSend('Trying to fetch cookie from Google Auth, this might be take a time'); - const browser = await puppeteer.launch({ headless: true }); + const browser = await puppeteer.launch({ + headless: true, + slowMo: 2000, + args: ['--remote-debugging-port=9222', '--remote-debugging-address=0.0.0.0', '--no-sandbox'] + }); const page = await browser.newPage(); await page.goto('https://www.youtube.com', { waitUntil: 'networkidle2' }); @@ -72,3 +76,9 @@ export async function getYoutubeCookie() { return cookies; } + +/* +function checkCookiesValid(){ + distube.resolve +} +*/ diff --git a/src/EnvironmentVariables.ts b/src/EnvironmentVariables.ts index fa21792..34348ed 100644 --- a/src/EnvironmentVariables.ts +++ b/src/EnvironmentVariables.ts @@ -21,6 +21,18 @@ const envVariables = z.object({ ) .optional() .default(false), + BOT_FFMPEG_LOGGING: z + .preprocess( + (v) => + z + .enum(['true', 'false']) + .transform((v) => JSON.parse(v)) + .catch(v) + .parse(v), + z.boolean() + ) + .optional() + .default(false), BOT_LANGUAGE: z.enum(['en', 'ru']).optional().default('en'), BOT_COMMAND_PREFIX: z.string().min(1), diff --git a/src/Script_getCookie.ts b/src/Script_getCookie.ts new file mode 100644 index 0000000..4a12308 --- /dev/null +++ b/src/Script_getCookie.ts @@ -0,0 +1,10 @@ +import { getYoutubeCookie } from './CookiesAutomation.js'; +import { loggerSend } from './utilities/logger.js'; + +const cookie = await getYoutubeCookie(); + +if (cookie) { + loggerSend('Cookies written in yt-cookies.json'); +} else { + loggerSend('Cookies fetching is failure'); +} diff --git a/src/audioplayer/AudioPlayerCore.ts b/src/audioplayer/AudioPlayerCore.ts index 3c87446..98704d0 100644 --- a/src/audioplayer/AudioPlayerCore.ts +++ b/src/audioplayer/AudioPlayerCore.ts @@ -1,4 +1,4 @@ -import { DisTube, PlayOptions, Queue, RepeatMode, Song, Events as DistubeEvents } from 'distube'; +import { DisTube, PlayOptions, Queue, RepeatMode, Song, Events as DistubeEvents, Playlist } from 'distube'; import { AudioPlayersManager } from './AudioPlayersManager.js'; import { pagination } from '../utilities/pagination/pagination.js'; import { ButtonStyles, ButtonTypes } from '../utilities/pagination/paginationTypes.js'; @@ -58,6 +58,8 @@ export class AudioPlayerCore { options?: PlayOptions ) { try { + const playableThing: Song | Playlist = await this.distube.handler.resolve(song) + // 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 joinVoiceChannel({ @@ -66,12 +68,17 @@ export class AudioPlayerCore { adapterCreator: voiceChannel.guild.voiceAdapterCreator }); - await this.distube.play(voiceChannel, song, options); + await this.distube.play(voiceChannel, playableThing, options); } catch (e) { if (ENV.BOT_VERBOSE_LOGGING) loggerError(e); await textChannel.send({ - embeds: [generateErrorEmbed(e.message, i18next.t('audioplayer:play_error') as string)] + embeds: [generateErrorEmbed(`${song}\n${e.message}`, i18next.t('audioplayer:play_error') as string)] }); + + const queue = this.distube.getQueue(voiceChannel.guildId) + + if (!queue) return + if (queue.songs.length === 0) await this.stop(voiceChannel.guild) } } @@ -231,15 +238,7 @@ export class AudioPlayerCore { const song = queue.songs[0]; - let lyricsQuery: string; - - if (song.source === 'youtube') { - lyricsQuery = song.name!; - } else { - lyricsQuery = `${song.name} - ${song.uploader.name}`; - } - - await interaction.reply({ embeds: [await generateLyricsEmbed(lyricsQuery)] }); + await interaction.reply({ embeds: [await generateLyricsEmbed(song.name!)] }); } async showQueue(interaction: Interaction) { @@ -320,13 +319,15 @@ export class AudioPlayerCore { private setupEvents() { if (ENV.BOT_VERBOSE_LOGGING) { - this.distube - .on(DistubeEvents.DEBUG, (message) => { - loggerSend(message, loggerPrefixAudioplayer); - }) - .on(DistubeEvents.FFMPEG_DEBUG, (message) => { - loggerSend(message, loggerPrefixAudioplayer); - }); + this.distube.on(DistubeEvents.DEBUG, (message) => { + loggerSend(message, loggerPrefixAudioplayer); + }); + } + + if (ENV.BOT_FFMPEG_LOGGING) { + this.distube.on(DistubeEvents.FFMPEG_DEBUG, (message) => { + loggerSend(message, loggerPrefixAudioplayer); + }); } this.distube diff --git a/src/audioplayer/util/downloadSong.ts b/src/audioplayer/util/downloadSong.ts index 3f40636..7291fd3 100644 --- a/src/audioplayer/util/downloadSong.ts +++ b/src/audioplayer/util/downloadSong.ts @@ -6,11 +6,23 @@ import { v4 as uuidv4 } from 'uuid'; import { unlink } from 'fs/promises'; import { isURL, Playlist, Song } from 'distube'; import i18next from 'i18next'; +import path from 'path'; const downloadFolderPath = process.cwd() + '/downloads'; -fs.rmSync(downloadFolderPath, { recursive: true, force: true }); -fs.mkdirSync(downloadFolderPath); +if (fs.existsSync(downloadFolderPath)) { + fs.readdir(downloadFolderPath, (err, files) => { + if (err) throw err; + + for (const file of files) { + fs.unlink(path.join(downloadFolderPath, file), (err) => { + if (err) throw err; + }); + } + }); +} else { + fs.mkdirSync(downloadFolderPath); +} class DownloadSongError extends Error { constructor(message: DownloadSongMessage) { diff --git a/wiki/Setup.md b/wiki/Setup.md index 2cc1160..6c01652 100644 --- a/wiki/Setup.md +++ b/wiki/Setup.md @@ -16,7 +16,8 @@ Also you need retrieve token, client id and enable intents on Discord Developer | Name | Example | Description | Required | |------------------------------|-----------------------|---------------------------------------------------------------------------|----------| -| `BOT_VERBOSE_LOGGING` | false | The bot will give more information to the console, useful for debugging | ❌ | +| `BOT_VERBOSE_LOGGING` | false | The bot will give more info to the console, useful for debugging | ❌ | +| `BOT_FFMPEG_LOGGING=false` | false | The bot will give info about FFMPEGto the console, useful for debugging | | | `BOT_COMMAND_PREFIX` | // | Used only for text commands | ✔️ | | `BOT_LANGUAGE` | en | Supported values: en ru | ❌ | | `MONGO_URI` | mongodb://mongo:27017 | The public key for sending notifications | ✔️ | @@ -25,7 +26,7 @@ Also you need retrieve token, client id and enable intents on Discord Developer | `BOT_DISCORD_CLIENT_ID` | 813750165783... | Application ID from Discord Developer Portal | ✔️ | | `BOT_DISCORD_OVERPOWERED_ID` | 29016845994426.... | Discord bot owner user ID, required for having more bot control for owner | ✔️ | | `BOT_GOOGLE_EMAIL` | | Used to automate cookies fetching for YouTube | ❌ | -| `BOT_GOOGLE_PASSWORD` | | Used to automate cookies fetching for YouTube ❌ | | +| `BOT_GOOGLE_PASSWORD` | | Used to automate cookies fetching for YouTube ❌ | | | `BOT_SPOTIFY_CLIENT_SECRET` | | Used when the Spotify module cannot get the credentials automatically | ❌ | | `BOT_SPOTIFY_CLIENT_ID` | | Used when the Spotify module get the credentials automatically | ❌ | | `BOT_YANDEXMUSIC_TOKEN` | | Provide to enable Yandex Music module | ❌ | @@ -77,7 +78,9 @@ npm run build ``` npm run production ``` + or if you are a developer + ``` npm run development ```