initial
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { DisplayComponentV2Builder } from "../lib/displayComponents/builders/v2Builder";
|
||||
import { LikeService } from "../services/LikeService";
|
||||
import { PlaylistService } from "../services/PlaylistService";
|
||||
import { MusicHistoryService } from "../services/MusicHistoryService";
|
||||
import { isAutoplayEnabledForGuild } from "../../commands/messages/music/autoplay";
|
||||
import { isShuffleEnabled, getRepeatMode, getRepeatModeLabel, getRepeatModeEmoji } from "../services/MusicStateService";
|
||||
|
||||
interface TrackInfo {
|
||||
title: string;
|
||||
@@ -18,7 +21,8 @@ export async function createNowPlayingMessage(
|
||||
trackInfo: TrackInfo,
|
||||
queueLength: number,
|
||||
userId: string,
|
||||
guildId: string
|
||||
guildId: string,
|
||||
client: any // Add client parameter
|
||||
) {
|
||||
// Create consistent trackId from title and author (same format as music_like.ts)
|
||||
const consistentTrackId = Buffer.from(
|
||||
@@ -45,14 +49,14 @@ export async function createNowPlayingMessage(
|
||||
[
|
||||
{
|
||||
type: 10,
|
||||
content: `**Añadido a la cola**\\n${trackInfo.title}\\n${trackInfo.author} • Duración: ${durationText}`,
|
||||
content: `**Añadido a la cola**\n${trackInfo.title}\n${trackInfo.author} • Duración: ${durationText}`,
|
||||
},
|
||||
],
|
||||
{ type: 11, media: { url: trackInfo.thumbnail } }
|
||||
);
|
||||
} else {
|
||||
infoContainer.addText(
|
||||
`**Añadido a la cola**\\n${trackInfo.title}\\n${trackInfo.author} • Duración: ${durationText}`
|
||||
`**Añadido a la cola**\n${trackInfo.title}\n${trackInfo.author} • Duración: ${durationText}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,39 +68,64 @@ export async function createNowPlayingMessage(
|
||||
|
||||
// Create buttons
|
||||
const likeButton = DisplayComponentV2Builder.createButton(
|
||||
isLiked ? "Te gusta" : "Like",
|
||||
isLiked ? 2 : 1,
|
||||
isLiked ? "music_unlike" : "music_like",
|
||||
isLiked ? "Te gusta" : "Like",
|
||||
isLiked ? 1 : 2, // 2 = secondary (gray) when liked, 1 = primary (blue) when not liked
|
||||
false,
|
||||
{ name: "❤️" }
|
||||
);
|
||||
|
||||
const repeatButton = DisplayComponentV2Builder.createButton(
|
||||
"Repeat",
|
||||
1,
|
||||
"music_repeat",
|
||||
{ name: "🔁" }
|
||||
// Check shuffle state
|
||||
const shuffleActive = isShuffleEnabled(guildId);
|
||||
const shuffleButton = DisplayComponentV2Builder.createButton(
|
||||
"music_shuffle",
|
||||
"Shuffle",
|
||||
shuffleActive ? 3 : 1, // 3 = success (green) when active, 1 = primary (blue) when inactive
|
||||
false,
|
||||
{ name: "🔀" }
|
||||
);
|
||||
|
||||
const shuffleButton = DisplayComponentV2Builder.createButton(
|
||||
"Shuffle",
|
||||
1,
|
||||
"music_shuffle",
|
||||
{ name: "🔂" }
|
||||
// Check repeat mode
|
||||
const repeatModeValue = getRepeatMode(guildId);
|
||||
let repeatStyle = 1; // Default: blue
|
||||
if (repeatModeValue === 'one') repeatStyle = 3; // Green for repeat one
|
||||
if (repeatModeValue === 'all') repeatStyle = 4; // Red for repeat all
|
||||
|
||||
const repeatButton = DisplayComponentV2Builder.createButton(
|
||||
"music_repeat",
|
||||
getRepeatModeLabel(repeatModeValue),
|
||||
repeatStyle,
|
||||
false,
|
||||
{ name: getRepeatModeEmoji(repeatModeValue) }
|
||||
);
|
||||
|
||||
// Check if autoplay is enabled for this guild
|
||||
const isAutoplayEnabled = isAutoplayEnabledForGuild(guildId);
|
||||
|
||||
const autoplayButton = DisplayComponentV2Builder.createButton(
|
||||
"Autoplay",
|
||||
1,
|
||||
"music_autoplay_toggle",
|
||||
"Autoplay",
|
||||
isAutoplayEnabled ? 3 : 1, // 3 = success (green) when enabled, 1 = primary (blue) when disabled
|
||||
false,
|
||||
{ name: "⏩" }
|
||||
);
|
||||
|
||||
// Add ActionRow with buttons
|
||||
// Add Skip button
|
||||
const skipButton = DisplayComponentV2Builder.createButton(
|
||||
"music_skip",
|
||||
"Skip",
|
||||
2, // 2 = secondary (gray)
|
||||
false,
|
||||
{ name: "⏭️" }
|
||||
);
|
||||
|
||||
// Add ActionRow with buttons (max 5 buttons per row)
|
||||
buttonsContainer.addActionRow([
|
||||
likeButton,
|
||||
repeatButton,
|
||||
shuffleButton,
|
||||
repeatButton,
|
||||
autoplayButton,
|
||||
skipButton,
|
||||
]);
|
||||
|
||||
// Get user's playlists for SelectMenu
|
||||
@@ -132,6 +161,92 @@ export async function createNowPlayingMessage(
|
||||
// Add SelectMenu in its own ActionRow
|
||||
buttonsContainer.addActionRow([selectMenu]);
|
||||
|
||||
// Get user's listening history for second SelectMenu
|
||||
try {
|
||||
const history = await MusicHistoryService.getRecentHistory(userId, 50);
|
||||
|
||||
// Remove duplicates by trackId (keep most recent)
|
||||
const uniqueTracksMap = new Map();
|
||||
for (const track of history) {
|
||||
// Skip tracks without title or author
|
||||
if (!track.title || !track.author) continue;
|
||||
|
||||
const trackId = track.trackId || `${track.title}:${track.author}`;
|
||||
if (!uniqueTracksMap.has(trackId)) {
|
||||
uniqueTracksMap.set(trackId, track);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to array and take first 25 (Discord limit)
|
||||
const uniqueTracks = Array.from(uniqueTracksMap.values()).slice(0, 25);
|
||||
|
||||
// Only create history menu if there are valid tracks
|
||||
if (uniqueTracks.length > 0) {
|
||||
// Create history select menu options with validation
|
||||
const historyOptions = uniqueTracks
|
||||
.filter(track => track.title && track.author) // Ensure valid data
|
||||
.map(track => ({
|
||||
label: track.title.substring(0, 100), // Discord limit
|
||||
value: (track.trackId || `${track.title}:${track.author}`).substring(0, 100), // Discord limit
|
||||
description: track.author.substring(0, 100),
|
||||
emoji: { name: '🎵' }
|
||||
}));
|
||||
|
||||
// Only add if we have valid options
|
||||
if (historyOptions.length > 0) {
|
||||
const historyMenu = DisplayComponentV2Builder.createSelectMenu(
|
||||
'music_history_select',
|
||||
'Reproducir del historial',
|
||||
historyOptions
|
||||
);
|
||||
|
||||
// Add history SelectMenu in its own ActionRow
|
||||
buttonsContainer.addActionRow([historyMenu]);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading history for select menu:", error);
|
||||
// Continue without history menu if there's an error
|
||||
}
|
||||
|
||||
// Add "Similar Songs" select menu (like YouTube's related videos)
|
||||
try {
|
||||
const { getSimilarTracks } = await import("../../commands/messages/music/autoplay.js");
|
||||
const similarTracks = await getSimilarTracks(trackInfo, guildId, client);
|
||||
|
||||
if (similarTracks && similarTracks.length > 0) {
|
||||
// Store similar tracks in Redis for the select menu handler
|
||||
const { redis } = await import("../../core/database/redis.js");
|
||||
const redisKey = `music:similar:${guildId}`;
|
||||
await redis.set(redisKey, JSON.stringify(similarTracks), { EX: 3600 }); // Expire after 1 hour
|
||||
console.log(`[musicMessages] Stored ${similarTracks.length} similar tracks in Redis for guild ${guildId}`);
|
||||
|
||||
// Create similar songs select menu options
|
||||
const similarOptions = similarTracks
|
||||
.slice(0, 15) // Limit to 15 similar tracks
|
||||
.map(track => ({
|
||||
label: track.info.title.substring(0, 100), // Discord limit
|
||||
value: track.encoded.substring(0, 100), // Discord limit
|
||||
description: track.info.author.substring(0, 100),
|
||||
emoji: { name: '🔗' } // Link emoji for "related"
|
||||
}));
|
||||
|
||||
if (similarOptions.length > 0) {
|
||||
const similarMenu = DisplayComponentV2Builder.createSelectMenu(
|
||||
'music_similar',
|
||||
'🎵 Canciones similares',
|
||||
similarOptions
|
||||
);
|
||||
|
||||
// Add similar SelectMenu in its own ActionRow
|
||||
buttonsContainer.addActionRow([similarMenu]);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading similar tracks for select menu:", error);
|
||||
// Continue without similar menu if there's an error
|
||||
}
|
||||
|
||||
// Return both containers
|
||||
return [infoContainer.toJSON(), buttonsContainer.toJSON()];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user