diff options
Diffstat (limited to 'src/utils')
| -rw-r--r-- | src/utils/build-embed.ts | 127 | ||||
| -rw-r--r-- | src/utils/error-msg.ts | 2 | ||||
| -rw-r--r-- | src/utils/loading-message.ts | 81 | ||||
| -rw-r--r-- | src/utils/string.ts | 2 |
4 files changed, 130 insertions, 82 deletions
diff --git a/src/utils/build-embed.ts b/src/utils/build-embed.ts new file mode 100644 index 0000000..5a2f758 --- /dev/null +++ b/src/utils/build-embed.ts @@ -0,0 +1,127 @@ +import getYouTubeID from 'get-youtube-id'; +import {MessageEmbed} from 'discord.js'; +import Player, {QueuedSong, STATUS} from '../services/player.js'; +import getProgressBar from './get-progress-bar.js'; +import {prettyTime} from './time.js'; +import {truncate} from './string.js'; + +const PAGE_SIZE = 10; + +const getMaxSongTitleLength = (title: string) => { + // eslint-disable-next-line no-control-regex + const nonASCII = /[^\x00-\x7F]+/; + return nonASCII.test(title) ? 28 : 48; +}; + +const getSongTitle = ({title, url}: QueuedSong, shouldTruncate = false) => { + const cleanSongTitle = title.replace(/\[.*\]/, '').trim(); + + const songTitle = shouldTruncate ? truncate(cleanSongTitle, getMaxSongTitleLength(cleanSongTitle)) : cleanSongTitle; + const youtubeId = url.length === 11 ? url : getYouTubeID(url) ?? ''; + + return `[${songTitle}](https://www.youtube.com/watch?v=${youtubeId})`; +}; + +const getQueueInfo = (player: Player) => { + const queueSize = player.queueSize(); + if (queueSize === 0) { + return '-'; + } + + return queueSize === 1 ? '1 song' : `${queueSize} songs`; +}; + +const getPlayerUI = (player: Player) => { + const song = player.getCurrent(); + + if (!song) { + return ''; + } + + const position = player.getPosition(); + const button = player.status === STATUS.PLAYING ? 'âšī¸' : 'âļī¸'; + const progressBar = getProgressBar(15, position / song.length); + const elapsedTime = `${prettyTime(position)}/${song.isLive ? 'live' : prettyTime(song.length)}`; + + return `${button} ${progressBar} \`[${elapsedTime}]\` đ`; +}; + +export const buildPlayingMessageEmbed = (player: Player): MessageEmbed => { + const currentlyPlaying = player.getCurrent(); + + if (!currentlyPlaying) { + throw new Error('No playing song found'); + } + + const {artist, thumbnailUrl, requestedBy} = currentlyPlaying; + const message = new MessageEmbed(); + + message + .setColor('DARK_GREEN') + .setTitle('Now Playing') + .setDescription(` + **${getSongTitle(currentlyPlaying)}** + Requested by: <@${requestedBy}>\n + ${getPlayerUI(player)} + `) + .setFooter({text: `Source: ${artist}`}); + + if (thumbnailUrl) { + message.setThumbnail(thumbnailUrl); + } + + return message; +}; + +export const buildQueueEmbed = (player: Player, page: number): MessageEmbed => { + const currentlyPlaying = player.getCurrent(); + + if (!currentlyPlaying) { + throw new Error('queue is empty'); + } + + const queueSize = player.queueSize(); + const maxQueuePage = Math.ceil((queueSize + 1) / PAGE_SIZE); + + if (page > maxQueuePage) { + throw new Error('the queue isn\'t that big'); + } + + const queuePageBegin = (page - 1) * PAGE_SIZE; + const queuePageEnd = queuePageBegin + PAGE_SIZE; + const queuedSongs = player + .getQueue() + .slice(queuePageBegin, queuePageEnd) + .map((song, index) => `\`${index + 1 + queuePageBegin}.\` ${getSongTitle(song, true)} \`[${prettyTime(song.length)}]\``) + .join('\n'); + + const {artist, thumbnailUrl, playlist, requestedBy} = currentlyPlaying; + const playlistTitle = playlist ? `(${playlist.title})` : ''; + const totalLength = player.getQueue().reduce((accumulator, current) => accumulator + current.length, 0); + + const message = new MessageEmbed(); + + let description = `**${getSongTitle(currentlyPlaying)}**\n`; + description += `Requested by: <@${requestedBy}>\n\n`; + description += `${getPlayerUI(player)}\n\n`; + + if (player.getQueue().length > 0) { + description += '**Up next:**\n'; + description += queuedSongs; + } + + message + .setTitle(player.status === STATUS.PLAYING ? 'Now Playing' : 'Queued songs') + .setColor(player.status === STATUS.PLAYING ? 'DARK_GREEN' : 'NOT_QUITE_BLACK') + .setDescription(description) + .addField('In queue', getQueueInfo(player), true) + .addField('Total length', `${totalLength > 0 ? prettyTime(totalLength) : '-'}`, true) + .addField('Page', `${page} out of ${maxQueuePage}`, true) + .setFooter({text: `Source: ${artist} ${playlistTitle}`}); + + if (thumbnailUrl) { + message.setThumbnail(thumbnailUrl); + } + + return message; +}; diff --git a/src/utils/error-msg.ts b/src/utils/error-msg.ts index 832de3c..0f4e0b2 100644 --- a/src/utils/error-msg.ts +++ b/src/utils/error-msg.ts @@ -5,7 +5,7 @@ export default (error?: string | Error): string => { if (typeof error === 'string') { str = `đĢ ${error}`; } else if (error instanceof Error) { - str = `đĢ ope: ${error.name}`; + str = `đĢ ope: ${error.message}`; } } diff --git a/src/utils/loading-message.ts b/src/utils/loading-message.ts deleted file mode 100644 index 53a2aed..0000000 --- a/src/utils/loading-message.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {TextChannel, Message, MessageReaction} from 'discord.js'; -import delay from 'delay'; - -const INITAL_DELAY = 500; -const PERIOD = 500; - -export default class { - public isStopped = true; - private readonly channel: TextChannel; - private readonly text: string; - private msg!: Message; - - constructor(channel: TextChannel, text = 'cows! count \'em') { - this.channel = channel; - this.text = text; - } - - async start(): Promise<void> { - this.msg = await this.channel.send(this.text); - - const icons = ['đŽ', 'đ´', 'đ']; - - const reactions: MessageReaction[] = []; - - let i = 0; - let isRemoving = false; - - this.isStopped = false; - - (async () => { - await delay(INITAL_DELAY); - - while (!this.isStopped) { - if (reactions.length === icons.length) { - isRemoving = true; - } - - // eslint-disable-next-line no-await-in-loop - await delay(PERIOD); - - if (isRemoving) { - const reactionToRemove = reactions.shift(); - - if (reactionToRemove) { - // eslint-disable-next-line no-await-in-loop - await reactionToRemove.users.remove(this.msg.client.user!.id); - } else { - isRemoving = false; - } - } else { - if (!this.isStopped) { - // eslint-disable-next-line no-await-in-loop - reactions.push(await this.msg.react(icons[i % icons.length])); - } - - i++; - } - } - })(); - } - - async stop(str = 'u betcha'): Promise<Message> { - const wasAlreadyStopped = this.isStopped; - - this.isStopped = true; - - const editPromise = str ? this.msg.edit(str) : null; - const reactPromise = str && !wasAlreadyStopped ? (async () => { - await this.msg.fetch(); - await Promise.all(this.msg.reactions.cache.map(async react => { - if (react.me) { - await react.users.remove(this.msg.client.user!.id); - } - })); - })() : null; - - await Promise.all([editPromise, reactPromise]); - - return this.msg; - } -} diff --git a/src/utils/string.ts b/src/utils/string.ts new file mode 100644 index 0000000..0b49114 --- /dev/null +++ b/src/utils/string.ts @@ -0,0 +1,2 @@ +export const truncate = (text: string, maxLength = 50) => + text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text; |
