aboutsummaryrefslogtreecommitdiff
path: root/src/utils
diff options
context:
space:
mode:
authorMax Isom <[email protected]>2022-01-26 12:58:33 -0500
committerMax Isom <[email protected]>2022-01-26 12:58:33 -0500
commitaacb107f43db6b8c53d160b5913701959f81cf09 (patch)
treea63d0717d03c84987b69d5af1c1f5b45ef019a4a /src/utils
parent09665af53ee1b1903fc9ea719722aa5dfdc26325 (diff)
parentaf05210be4a8857ea707866192efa79b3945b314 (diff)
downloadmuse-aacb107f43db6b8c53d160b5913701959f81cf09.tar.xz
muse-aacb107f43db6b8c53d160b5913701959f81cf09.zip
Merge branch 'master' into feature/slash-commands
Diffstat (limited to 'src/utils')
-rw-r--r--src/utils/build-embed.ts127
-rw-r--r--src/utils/error-msg.ts2
-rw-r--r--src/utils/loading-message.ts81
-rw-r--r--src/utils/string.ts2
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;