Skip to content

Commit

Permalink
3.1.0
Browse files Browse the repository at this point in the history
Added /lyrics command and button in audioplayer which fetch lyrics for the current playing song.
Commands now can be disabled during loading.
  • Loading branch information
AlexInCube committed Jul 9, 2024
1 parent 18ec83c commit 08b40f0
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 49 deletions.
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ BOT_DISCORD_TOKEN=undefined
BOT_DISCORD_CLIENT_ID=undefined
BOT_DISCORD_OVERPOWERED_ID=undefined

BOT_YOUTUBE_COOKIE=undefined

BOT_SPOTIFY_CLIENT_SECRET=undefined
BOT_SPOTIFY_CLIENT_ID=undefined

Expand All @@ -18,5 +16,7 @@ BOT_SOUNDCLOUD_TOKEN=undefined
BOT_YANDEXMUSIC_TOKEN=undefined
BOT_YANDEXMUSIC_UID=undefined

BOT_GENIUS_TOKEN=undefined

MONGO_URI=undefined
MONGO_DATABASE_NAME=undefined
Binary file added icons/audioplayer/player/lyrics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aicbot",
"version": "3.0.1",
"version": "3.1.0",
"description": "Discord Bot for playing music",
"main": "build/main.js",
"scripts": {
Expand Down Expand Up @@ -29,6 +29,7 @@
"distube-apple-music": "^0.1.0",
"distube-yandex-music-plugin": "^1.0.4",
"dotenv": "^16.4.5",
"genius-lyrics": "^4.4.7",
"i18next": "^22.5.1",
"i18next-fs-backend": "^2.3.1",
"mongoose": "^7.7.0",
Expand Down
27 changes: 19 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 16 additions & 7 deletions src/CommandTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@ export type SlashBuilder =
| SlashCommandSubcommandsOnlyBuilder
| Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
export interface ICommand {
text_data: ITextCommandData; // Related to text commands
slash_data?: ISlashCommandData; // Related to slash commands
group: ICommandGroup; // Group for better orientation in /help
user_permissions?: Array<PermissionResolvable>; // Permissions for user, to allow executing commands
bot_permissions: Array<PermissionResolvable>; // Permissions for bot, to try to execute commands
hidden?: boolean; // Hidden from everything (disable slash_data property if true)
guild_data?: IGuildData; // Guild related data such as voice settings
// Disable command registering
disable?: boolean | false;
// Related to text commands
text_data: ITextCommandData;
// Related to slash commands
slash_data?: ISlashCommandData;
// Group for better orientation in /help
group: ICommandGroup;
// Permissions for user, to allow executing commands
user_permissions?: Array<PermissionResolvable>;
// Permissions for bot, to try to execute commands
bot_permissions: Array<PermissionResolvable>;
// Hidden from everything (disable slash_data property if true)
hidden?: boolean;
// Guild related data such as voice settings
guild_data?: IGuildData;
}

interface ITextCommandData {
Expand Down
4 changes: 3 additions & 1 deletion src/EnvironmentVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ const envVariables = z.object({
BOT_SPOTIFY_CLIENT_ID: z.string().optional(),

BOT_YANDEXMUSIC_TOKEN: z.string().optional(),
BOT_YANDEXMUSIC_UID: z.coerce.number().optional()
BOT_YANDEXMUSIC_UID: z.coerce.number().optional(),

BOT_GENIUS_TOKEN: z.string().optional()
});

export const ENV = envVariables.parse(process.env);
Expand Down
22 changes: 22 additions & 0 deletions src/audioplayer/AudioPlayerCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { LoadPlugins } from './LoadPlugins.js';
import { generateAddedPlaylistMessage } from './util/generateAddedPlaylistMessage.js';
import { generateAddedSongMessage } from './util/generateAddedSongMessage.js';
import {
ButtonInteraction,
Client,
CommandInteraction,
Embed,
Expand All @@ -22,6 +23,7 @@ import {
} from 'discord.js';
import { joinVoiceChannel } from '@discordjs/voice';
import { generateWarningEmbed } from '../utilities/generateWarningEmbed.js';
import { generateLyricsEmbed } from './Lyrics.js';

export const queueSongsLimit = 500;

Expand Down Expand Up @@ -218,6 +220,26 @@ export class AudioPlayerCore {
}
}

async showLyrics(interaction: ButtonInteraction) {
if (!interaction.guild) return;
const queue = this.distube.getQueue(interaction.guild);
if (!queue) {
return;
}

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)] });
}

async showQueue(interaction: Interaction) {
if (!interaction.guild) return;
const queue = this.distube.getQueue(interaction.guild);
Expand Down
3 changes: 2 additions & 1 deletion src/audioplayer/AudioPlayerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export enum AudioPlayerIcons {
previous = '<:previousbutton:1092107334542696512>',
skip = '<:skipbutton:1092107438234275900>',
shuffle = '<:shufflebutton:1092107651384614912>',
list = '<:songlistwhite:1014551771705782405>'
list = '<:songlistwhite:1014551771705782405>',
lyrics = '<:lyrics:1260156581794811974>'
}

export enum AudioSourceIcons {
Expand Down
2 changes: 1 addition & 1 deletion src/audioplayer/LoadPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { ExtractorPlugin, InfoExtractorPlugin, PlayableExtractorPlugin } from 'd
import { BOT_YOUTUBE_COOKIE, ENV } from '../EnvironmentVariables.js';
import { loggerSend, loggerWarn } from '../utilities/logger.js';
import { SpotifyPlugin } from '@distube/spotify';
import { SoundCloudPlugin } from '@distube/soundcloud';
import { YtDlpPlugin } from '@distube/yt-dlp';
import { loggerPrefixAudioplayer } from './AudioPlayerCore.js';
import { YouTubePlugin } from '@distube/youtube';
import { DirectLinkPlugin } from '@distube/direct-link';
import { FilePlugin } from '@distube/file';
import { AppleMusicPlugin } from 'distube-apple-music';
import { YandexMusicPlugin } from 'distube-yandex-music-plugin';
import { SoundCloudPlugin } from '@distube/soundcloud';

export type DistubePlugin = ExtractorPlugin | InfoExtractorPlugin | PlayableExtractorPlugin;

Expand Down
40 changes: 40 additions & 0 deletions src/audioplayer/Lyrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Genius from 'genius-lyrics';
import { ENV } from '../EnvironmentVariables.js';
import { Colors, EmbedBuilder } from 'discord.js';
import i18next from 'i18next';
import { loggerWarn } from '../utilities/logger.js';
import { generateErrorEmbed } from '../utilities/generateErrorEmbed.js';
const Lyrics = new Genius.Client(ENV.BOT_GENIUS_TOKEN);

if (!ENV.BOT_GENIUS_TOKEN) {
loggerWarn('BOT_GENIUS_TOKEN is not provided, lyrics module disabled', 'Lyrics');
}

export async function getLyricsSong(searchQuery: string) {
const geniusSearch = await Lyrics.songs.search(searchQuery);

if (geniusSearch.length === 0) {
return undefined;
}

return geniusSearch[0];
}

export async function generateLyricsEmbed(songQuery: string) {
const geniusSong = await getLyricsSong(songQuery);

if (!geniusSong) {
return generateErrorEmbed(i18next.t('commands:lyrics_embed_lyrics_not_found'));
}

const lyrics = await geniusSong.lyrics();

const lyricsText = lyrics.slice(0, 4096);

return new EmbedBuilder()
.setTitle(geniusSong.title)
.setURL(geniusSong.url)
.setDescription(lyricsText)
.setColor(Colors.Yellow)
.setFooter({ text: i18next.t('commands:lyrics_embed_text_not_correct') });
}
17 changes: 16 additions & 1 deletion src/audioplayer/MessagePlayerButtonsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
generateEmbedAudioPlayerPrevious,
generateEmbedAudioPlayerPreviousFailure
} from '../commands/audio/previous.command.js';
import { ENV } from '../EnvironmentVariables.js';

enum ButtonIDs {
stopMusic = 'stopMusic',
Expand All @@ -33,7 +34,8 @@ enum ButtonIDs {
skipSong = 'skipSong',
//downloadSong = 'downloadSong',
shuffle = 'shuffle',
showQueue = 'showQueue'
showQueue = 'showQueue',
lyrics = 'lyrics'
}

const rowPrimary = new ActionRowBuilder<ButtonBuilder>().addComponents(
Expand Down Expand Up @@ -94,6 +96,15 @@ const rowSecondary = new ActionRowBuilder<ButtonBuilder>().addComponents(
.setEmoji(AudioPlayerIcons.list)
);

if (ENV.BOT_GENIUS_TOKEN) {
rowSecondary.addComponents(
new ButtonBuilder()
.setCustomId(ButtonIDs.lyrics)
.setStyle(ButtonStyle.Secondary)
.setEmoji(AudioPlayerIcons.lyrics)
);
}

const rowWithOnlyStop = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder()
.setCustomId(ButtonIDs.stopMusic)
Expand Down Expand Up @@ -210,6 +221,10 @@ export class MessagePlayerButtonsHandler {
}
break;
}

case ButtonIDs.lyrics: {
await this.client.audioPlayer.showLyrics(ButtonInteraction);
}
}
} catch (e) {
loggerError(e);
Expand Down
43 changes: 43 additions & 0 deletions src/commands/audio/lyrics.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { CommandArgument, ICommand } from '../../CommandTypes.js';
import { Message, PermissionsBitField, SlashCommandBuilder } from 'discord.js';
import i18next from 'i18next';
import { services } from './play.command.js';
import { GroupAudio } from './AudioTypes.js';
import { generateLyricsEmbed } from '../../audioplayer/Lyrics.js';
import { ENV } from '../../EnvironmentVariables.js';

export default function (): ICommand {
return {
disable: !ENV.BOT_GENIUS_TOKEN,
text_data: {
name: 'lyrics',
description: i18next.t('commands:lyrics_desc'),
arguments: [
new CommandArgument(i18next.t('commands:lyrics_arg_query', { services: services }), true)
],
execute: async (message: Message, args: string[]) => {
const songQuery = args.join(' ');

await message.reply({ embeds: [await generateLyricsEmbed(songQuery)] });
}
},
slash_data: {
slash_builder: new SlashCommandBuilder()
.setName('lyrics')
.setDescription(i18next.t('commands:lyrics_desc'))
.addStringOption((option) =>
option
.setName('request')
.setDescription(i18next.t('commands:lyrics_arg_query', { services: services }))
.setRequired(true)
),
execute: async (interaction) => {
const songQuery = interaction.options.getString('request')!;

await interaction.reply({ embeds: [await generateLyricsEmbed(songQuery)] });
}
},
group: GroupAudio,
bot_permissions: [PermissionsBitField.Flags.SendMessages, PermissionsBitField.Flags.ViewChannel]
};
}
4 changes: 3 additions & 1 deletion src/commands/audio/play.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import ytsr from '@distube/ytsr';
import { queueSongsLimit } from '../../audioplayer/AudioPlayerCore.js';
import { generateWarningEmbed } from '../../utilities/generateWarningEmbed.js';

export const services = 'Youtube, Spotify, Soundcloud, Yandex Music, HTTP-stream';
export const services = 'Youtube, Spotify, Soundcloud, Yandex Music, Apple Music, HTTP-stream';
export default function (): ICommand {
return {
text_data: {
Expand All @@ -30,6 +30,8 @@ export default function (): ICommand {
new CommandArgument(i18next.t('commands:play_arg_link', { services: services }), true)
],
execute: async (message: Message, args: string[]) => {
// Play command accept only one arg is a query string.
// In text command system we need to merge all words for request in one string
const songQuery = args.join(' ');

const member = message.member as GuildMember;
Expand Down
Loading

0 comments on commit 08b40f0

Please sign in to comment.