diff --git a/src/Discord/Private/Embed.ts b/src/Discord/Private/Embed.ts new file mode 100644 index 0000000..d9a5afd --- /dev/null +++ b/src/Discord/Private/Embed.ts @@ -0,0 +1,21 @@ +import { Colors, EmbedBuilder } from 'discord.js'; + +type EmbedColor = keyof typeof Colors | 'Random'; + +class Embed { + title: string; + description: string; + color: EmbedColor = 'Random'; + author: string | null; + constructor(data: { title: string; description: string; color?: EmbedColor; author?: string }) { + this.title = data.title; + this.description = data.description; + this.color = data.color || 'Random'; + this.author = data.author || null; + } + build(): EmbedBuilder { + return new EmbedBuilder().setColor(this.color).setTimestamp().setTitle(this.title).setDescription(this.description); + } +} + +export default Embed; diff --git a/src/Discord/commands/About.ts b/src/Discord/commands/About.ts index abafd27..0f5490c 100644 --- a/src/Discord/commands/About.ts +++ b/src/Discord/commands/About.ts @@ -1,9 +1,9 @@ import Command from '../Private/Command'; import DiscordManager from '../DiscordManager'; +import Embed from '../Private/Embed'; import { ApplicationIntegrationType, ChatInputCommandInteraction, - EmbedBuilder, InteractionContextType, SlashCommandBuilder } from 'discord.js'; @@ -24,12 +24,10 @@ class AboutCommand extends Command { const app = await interaction.client.application.fetch(); await interaction.followUp({ embeds: [ - new EmbedBuilder() - .setTitle('Kath Bot') - .setColor('Random') - .setDescription( - `Kath Bot made with :purple_heart: by <@1276524855445164098>\nFollow my Github https://github.com/Kathund\n\n**Stats:**\nServers: ${app.approximateGuildCount || 0}\nUser Installs: ${app.approximateUserInstallCount || 0}` - ) + new Embed({ + title: 'Kath Bot', + description: `Kath Bot made with :purple_heart: by <@1276524855445164098>\nFollow my Github https://github.com/Kathund\n\n**Stats:**\nServers: ${app.approximateGuildCount || 0}\nUser Installs: ${app.approximateUserInstallCount || 0}` + }).build() ] }); } catch (error) { diff --git a/src/Discord/commands/Search.ts b/src/Discord/commands/Search.ts index 923a86d..bd4dc01 100644 --- a/src/Discord/commands/Search.ts +++ b/src/Discord/commands/Search.ts @@ -2,13 +2,11 @@ import Command from '../Private/Command'; import DiscordManager from '../DiscordManager'; import Search from '../../Spotify/Private/API/Search/Search'; import { - ActionRowBuilder, ApplicationIntegrationType, ChatInputCommandInteraction, InteractionContextType, SlashCommandBuilder, - SlashCommandOptionsOnlyBuilder, - StringSelectMenuBuilder + SlashCommandOptionsOnlyBuilder } from 'discord.js'; class SearchCommand extends Command { @@ -42,7 +40,7 @@ class SearchCommand extends Command { const data = new Search((await res.json()).data); await interaction.followUp({ embeds: [data.toEmbed()], - components: [new ActionRowBuilder().addComponents(data.toSelectMenu())], + components: [data.toButtons(), data.toSelectMenu()], ephemeral: true }); } catch (error) { diff --git a/src/Spotify/Private/API/Album.ts b/src/Spotify/Private/API/Album.ts index 6e71d99..e16acce 100644 --- a/src/Spotify/Private/API/Album.ts +++ b/src/Spotify/Private/API/Album.ts @@ -36,23 +36,6 @@ class Album { toString(): string { return this.name; } - - toJSON(): Record { - return { - albumType: this.albumType, - artists: this.artists.map((artist) => artist.toJSON()), - availableMarkets: this.availableMarkets, - url: this.url, - id: this.id, - images: this.images.map((img) => img.toJSON()), - name: this.name, - releaseDate: this.releaseDate, - releaseDatePrecision: this.releaseDatePrecision, - totalTracks: this.totalTracks, - type: this.type, - uri: this.uri - }; - } } export default Album; diff --git a/src/Spotify/Private/API/Artist.ts b/src/Spotify/Private/API/Artist.ts index 9475a73..1bc4438 100644 --- a/src/Spotify/Private/API/Artist.ts +++ b/src/Spotify/Private/API/Artist.ts @@ -17,10 +17,6 @@ class Artist { toString(): string { return this.name; } - - toJSON(): Record { - return { id: this.id, name: this.name, type: this.type, uri: this.uri, url: this.url }; - } } export default Artist; diff --git a/src/Spotify/Private/API/Devices.ts b/src/Spotify/Private/API/Devices.ts index f8b8d45..6f5addf 100644 --- a/src/Spotify/Private/API/Devices.ts +++ b/src/Spotify/Private/API/Devices.ts @@ -1,39 +1,30 @@ +export type DeviceType = 'computer' | 'smartphone' | 'speaker'; + class Device { id: string; active: boolean; privateSession: boolean; restricted: boolean; name: string; + volume: number | null; + volumePercent: number | null; supportsVolume: boolean; - type: string; - volume: number; + type: DeviceType; constructor(data: Record) { this.id = data.id; this.active = data.is_active; this.privateSession = data.is_private_session; this.restricted = data.is_restricted; this.name = data.name; + this.volume = data.volume_percent; + this.volumePercent = this.volume ? this.volume / 100 : null; this.supportsVolume = data.supports_volume; this.type = data.type; - this.volume = data.volume_percent; } toString(): string { return this.name; } - - toJSON(): Record { - return { - id: this.id, - active: this.active, - privateSession: this.privateSession, - restricted: this.restricted, - name: this.name, - supportsVolume: this.supportsVolume, - type: this.type, - volume: this.volume - }; - } } export default Device; diff --git a/src/Spotify/Private/API/Image.ts b/src/Spotify/Private/API/Image.ts index 224e17e..853156d 100644 --- a/src/Spotify/Private/API/Image.ts +++ b/src/Spotify/Private/API/Image.ts @@ -13,10 +13,6 @@ class Image { toString(): string | null { return this.url; } - - toJSON(): Record { - return { url: this.url, width: this.width, height: this.height, pixels: this.pixels }; - } } export default Image; diff --git a/src/Spotify/Private/API/Playback.ts b/src/Spotify/Private/API/Playback.ts index 7c202e1..39061ab 100644 --- a/src/Spotify/Private/API/Playback.ts +++ b/src/Spotify/Private/API/Playback.ts @@ -32,35 +32,31 @@ class Playback { return this.item; } - toJSON(): Record { - return { - device: this.device.toJSON(), - shuffleState: this.shuffleState, - smartShuffle: this.smartShuffle, - repeatState: this.repeatState, - timestamp: this.timestamp, - progress: this.progressMS, - item: this.item.toJSON(), - playingType: this.playingType, - playing: this.playing - }; - } - getPrograssBar(): string { - return '█'.repeat(Math.floor(this.progress * 20)) + '-'.repeat(20 - Math.floor(this.progress * 20)); - } - - toEmbed(): EmbedBuilder { - const duration = `${Math.floor(this.item.duration / 60000)}:${Math.floor((this.item.duration % 60000) / 1000) + const progress = `${Math.floor(this.progressMS / 60000)}:${Math.floor((this.progressMS % 60000) / 1000) .toString() .padStart(2, '0')}`; - const progress = `${Math.floor(this.progressMS / 60000)}:${Math.floor((this.progressMS % 60000) / 1000) + const duration = `${Math.floor(this.item.duration / 60000)}:${Math.floor((this.item.duration % 60000) / 1000) .toString() .padStart(2, '0')}`; + + const prograssBar = '█'.repeat(Math.floor(this.progress * 20)) + '-'.repeat(20 - Math.floor(this.progress * 20)); + return `${progress} ${prograssBar} ${duration}`; + } + + getVolumeBar(): string { + if (null === this.device.volumePercent) return `0% ${'-'.repeat(20)} 100%`; + return `0% ${'█'.repeat(Math.floor(this.device.volumePercent * 20))}${'-'.repeat( + 20 - Math.floor(this.device.volumePercent * 20) + )} 100%`; + } + + toEmbed(): EmbedBuilder { return this.item .toEmbed() .setTitle(`Currently ${this.playing ? 'Playing' : 'Paused'}`) - .addFields({ name: 'Progress', value: `${progress} ${this.getPrograssBar()} ${duration}`, inline: true }); + .addFields({ name: 'Progress', value: this.getPrograssBar() }) + .addFields({ name: 'Volume', value: this.getVolumeBar() }); } toButtons(): ActionRowBuilder[] { diff --git a/src/Spotify/Private/API/Queue.ts b/src/Spotify/Private/API/Queue.ts index 01ce4f9..8b6c113 100644 --- a/src/Spotify/Private/API/Queue.ts +++ b/src/Spotify/Private/API/Queue.ts @@ -1,3 +1,4 @@ +import Embed from '../../../Discord/Private/Embed'; import Track from './Track'; import { EmbedBuilder } from 'discord.js'; @@ -13,12 +14,8 @@ class Queue { return this.queue[0]; } - toJSON(): Record { - return { currentPlayback: this.currentPlayback.toJSON(), queue: this.queue.map((track) => track.toJSON()) }; - } - toEmbed(): EmbedBuilder { - const embed = new EmbedBuilder().setColor('Random').setTitle('Queue'); + const embed = new Embed({ title: 'Queue', description: 'Upcomming Queue' }).build(); this.queue.map((track) => { embed.addFields({ name: track.name, diff --git a/src/Spotify/Private/API/Search/Search.ts b/src/Spotify/Private/API/Search/Search.ts index 10eaa2f..3655206 100644 --- a/src/Spotify/Private/API/Search/Search.ts +++ b/src/Spotify/Private/API/Search/Search.ts @@ -1,5 +1,7 @@ +import Embed from '../../../../Discord/Private/Embed'; import TrackSearch from './Track'; -import { EmbedBuilder, StringSelectMenuBuilder } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, StringSelectMenuBuilder } from 'discord.js'; +import { emojis } from '../../../../../config.json'; class Search { query: string; @@ -9,20 +11,60 @@ class Search { this.track = data.tracks ? new TrackSearch({ query: this.query, ...data.tracks }) : null; } - toJSON(): Record { - return { query: this.query, track: this.track ? this.track.toJSON() : null }; + getPages(): number { + return this.track ? Math.ceil(this.track.total / this.track.limit) : 0; + } + + getPage(): number { + return this.track ? Math.floor(this.track.offset / this.track.limit) + 1 : 0; } toEmbed(): EmbedBuilder { - if (!this.track) return new EmbedBuilder().setColor('Red').setTitle(`No results found for ${this.query}.`); + if (!this.track) { + return new Embed({ + title: `No results found for ${this.query}!`, + description: 'Please try again', + color: 'Red' + }).build(); + } return this.track.toEmbed(); } - toSelectMenu(): StringSelectMenuBuilder { + toButtons(): ActionRowBuilder { + const buttons: ButtonBuilder[] = [ + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setCustomId('searchStart').setEmoji(emojis.back), + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setCustomId('searchBack').setEmoji(emojis.back), + new ButtonBuilder() + .setStyle(ButtonStyle.Secondary) + .setLabel(`Page ${this.getPage()} / ${this.getPages()}`) + .setDisabled(true), + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setCustomId('searchForward').setEmoji(emojis.skip), + new ButtonBuilder().setStyle(ButtonStyle.Secondary).setCustomId('searchEnd').setEmoji(emojis.skip) + ]; + if (1 === this.getPages() || 0 === this.getPages()) { + buttons[0].setDisabled(true); + buttons[1].setDisabled(true); + buttons[3].setDisabled(true); + buttons[4].setDisabled(true); + } + if (1 === this.getPage()) { + buttons[0].setDisabled(true); + buttons[1].setDisabled(true); + } + if (this.getPage() === this.getPages()) { + buttons[3].setDisabled(true); + buttons[4].setDisabled(true); + } + return new ActionRowBuilder().addComponents(buttons); + } + + toSelectMenu(): ActionRowBuilder { if (!this.track) { - return new StringSelectMenuBuilder().setCustomId('searchSelectMenu').setPlaceholder('No results found.'); + return new ActionRowBuilder().addComponents( + new StringSelectMenuBuilder().setCustomId('searchSelectMenu').setPlaceholder('No results found.') + ); } - return this.track.toSelectMenu(); + return new ActionRowBuilder().addComponents(this.track.toSelectMenu()); } } diff --git a/src/Spotify/Private/API/Search/Track.ts b/src/Spotify/Private/API/Search/Track.ts index beeb02d..d12add9 100644 --- a/src/Spotify/Private/API/Search/Track.ts +++ b/src/Spotify/Private/API/Search/Track.ts @@ -1,3 +1,4 @@ +import Embed from '../../../../Discord/Private/Embed'; import Track from '../Track'; import { EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from 'discord.js'; import { emojis } from '../../../../../config.json'; @@ -20,20 +21,12 @@ class TrackSearch { this.total = data.total; } - toJSON(): Record { - return { - query: this.query, - items: this.items.map((item: Track) => item.toJSON()), - limit: this.limit, - next: this.next, - offset: this.offset, - previous: this.previous, - total: this.total - }; - } - toEmbed(): EmbedBuilder { - const embed = new EmbedBuilder().setColor('Random').setTitle(`Search Results for ${this.query}`); + const embed = new Embed({ + title: `Search Results for ${this.query}`, + description: `Found ${this.total} results`, + author: `Found ${this.total} results` + }).build(); this.items.map((track) => { embed.addFields({ name: `${track.name} ${track.explicit ? emojis.explicit : ''}`, @@ -47,7 +40,10 @@ class TrackSearch { const select = new StringSelectMenuBuilder().setCustomId('searchSelectMenu').setPlaceholder('look at an item'); this.items.map((track) => { select.addOptions( - new StringSelectMenuOptionBuilder().setLabel(`${track.name} ${track.explicit ? '[E]' : ''}`).setValue(track.id) + new StringSelectMenuOptionBuilder() + .setLabel(`${track.name} ${track.explicit ? '[E]' : ''}`) + .setValue(track.id) + .setDescription(`${track.album.name} | ${track.artists[0].name}`) ); }); return select; diff --git a/src/Spotify/Private/API/Track.ts b/src/Spotify/Private/API/Track.ts index cab3605..9a45439 100644 --- a/src/Spotify/Private/API/Track.ts +++ b/src/Spotify/Private/API/Track.ts @@ -1,5 +1,6 @@ import Album from './Album'; import Artist from './Artist'; +import Embed from '../../../Discord/Private/Embed'; import { ButtonBuilder, ButtonStyle, EmbedBuilder } from 'discord.js'; import { emojis } from '../../../../config.json'; @@ -41,32 +42,11 @@ class Track { return this.name; } - toJSON(): Record { - return { - album: this.album.toJSON(), - artists: this.artists.map((artist) => artist.toJSON()), - availableMarkets: this.availableMarkets, - disc: this.disc, - duration: this.duration, - explicit: this.explicit, - url: this.url, - id: this.id, - local: this.local, - name: this.name, - popularity: this.popularity, - track: this.track, - type: this.type, - uri: this.uri - }; - } - toEmbed(): EmbedBuilder { - const embed = new EmbedBuilder() - .setTitle('Track Infomation') - .setColor('Random') - .setDescription( - `[${this.name}](${this.spotifyUrl || 'https://open.spotify.com'}) ${this.explicit ? emojis.explicit : ''}\n\n[${this.album.name}](<${this.album.spotifyUrl || 'https://open.spotify.com/'}>) | [${this.artists[0].name}](<${this.artists[0].spotifyUrl || 'https://open.spotify.com/'}>)` - ); + const embed = new Embed({ + title: 'Track Infomation', + description: `[${this.name}](${this.spotifyUrl || 'https://open.spotify.com'}) ${this.explicit ? emojis.explicit : ''}\n\n[${this.album.name}](<${this.album.spotifyUrl || 'https://open.spotify.com/'}>) | [${this.artists[0].name}](<${this.artists[0].spotifyUrl || 'https://open.spotify.com/'}>)` + }).build(); if (this.album.images[0]) embed.setThumbnail(this.album.images[0].url); return embed; }