2022-06-06 21:40:35 +07:00
import DiscordModule , { HybridInteractionMessage } from '../../utils/DiscordModule' ;
2022-03-03 20:56:49 +07:00
2022-06-06 21:40:35 +07:00
import {
Message ,
CommandInteraction ,
Interaction ,
VoiceChannel ,
Permissions ,
GuildMember ,
DMChannel ,
StageChannel ,
MessageActionRow ,
MessageButton ,
TextChannel ,
VoiceBasedChannel
} from 'discord.js' ;
import {
makeSuccessEmbed ,
makeErrorEmbed ,
sendMessage ,
sendHybridInteractionMessageResponse ,
makeInfoEmbed
} from '../../utils/DiscordMessage' ;
import DiscordProvider from '../../providers/Discord' ;
import DiscordMusicPlayer , {
PlayerPlayingEvent ,
PlayerErrorEvent ,
VoiceDisconnectedEvent ,
ValidTracks ,
DiscordMusicPlayerInstance ,
DiscordMusicPlayerLoopMode
} from '../../providers/DiscordMusicPlayer' ;
2022-01-30 20:10:52 +07:00
const EMBEDS = {
VOICECHANNEL_JOINED : ( data : Message | Interaction ) => {
return makeSuccessEmbed ({
title : `Success` ,
description : `Joined voice channel` ,
2022-06-06 21:40:35 +07:00
user : data instanceof Interaction ? data.user : data.author
2022-01-30 20:10:52 +07:00
});
},
2022-02-27 17:15:20 +07:00
VOICECHANNEL_DISCONNECTED : ( data : Message | Interaction ) => {
return makeSuccessEmbed ({
title : `Disconnected` ,
description : `I got disconnected from the voice channel` ,
2022-06-06 21:40:35 +07:00
user : data instanceof Interaction ? data.user : data.author
2022-02-27 17:15:20 +07:00
});
},
2022-01-30 20:10:52 +07:00
VOICECHANNEL_INUSE : ( data : Message | Interaction ) => {
2022-06-06 21:40:35 +07:00
let haveForceMove =
data instanceof Message
? ( data as Message ). member ! . permissions . has ([ Permissions . FLAGS . MOVE_MEMBERS ])
: (( data as Interaction ). member ! as GuildMember ). permissions . has ([ Permissions . FLAGS . MOVE_MEMBERS ]);
2022-01-30 20:10:52 +07:00
return makeErrorEmbed ({
title : `I can't open portal to parallel universe` ,
description : `I can't join mutiple voice channel on the same guild.
I wish I had a superpower, maybe... one day I will.
2022-06-06 21:40:35 +07:00
There are also somebody listening to the music in the voice channel I'm currently in. ${
! haveForceMove
? ` Wait until I finish playing there or I'm alone there. Better yet, join them!`
: " Wait until I finish playing there or I'm alone there. Better yet, join them! \n\n**Or... just (ab)use your admin permissions and move me to where you want!**"
} ` ,
user : data instanceof Interaction ? data.user : data.author
2022-01-30 20:10:52 +07:00
});
},
VOICECHANNEL_ALREADY_JOINED : ( data : Message | Interaction ) => {
return makeInfoEmbed ({
title : `I'm already here` ,
description : `Already in the voice channel you are currently connected to` ,
2022-06-06 21:40:35 +07:00
user : data instanceof Interaction ? data.user : data.author
2022-01-30 20:10:52 +07:00
});
},
2022-01-30 22:42:00 +07:00
USER_NOT_IN_VOICECHANNEL : ( data : Message | Interaction ) => {
return makeErrorEmbed ({
title : `You need to be in the voice channel first!` ,
2022-06-06 21:40:35 +07:00
user : data instanceof Interaction ? data.user : data.author
2022-01-30 22:42:00 +07:00
});
},
2022-02-27 17:15:20 +07:00
MUSIC_ERROR : ( data : Message | Interaction , err : Error ) => {
return makeErrorEmbed ({
2022-03-03 22:18:09 +07:00
title : `Error while playing music, skipping this track` ,
2022-02-27 17:15:20 +07:00
description : ` ${ err . message } ` ,
2022-06-06 21:40:35 +07:00
user : data instanceof Interaction ? data.user : data.author
2022-02-27 17:15:20 +07:00
});
},
2022-01-30 20:10:52 +07:00
NOW_PLAYING : ( data : Message | Interaction , track : ValidTracks ) => {
const embed = makeInfoEmbed ({
title : ' Now playing' ,
icon : '🎵' ,
2022-02-05 00:23:53 +07:00
description : ` ${ track . title } ` ,
2022-01-30 20:10:52 +07:00
user : DiscordProvider.client.user
});
2022-06-06 21:40:35 +07:00
const highestResolutionThumbnail = track . thumbnails . reduce (( prev , current ) =>
prev . height * prev . width > current . height * current . width ? prev : current
);
2022-03-04 00:29:50 +07:00
2022-06-06 21:40:35 +07:00
if ( highestResolutionThumbnail ) embed . setImage ( highestResolutionThumbnail . url );
2022-05-01 16:56:37 +07:00
return embed ;
},
NOW_REPEATING : ( data : Message | Interaction , track : ValidTracks ) => {
const embed = makeInfoEmbed ({
title : ' Now playing (Repeating)' ,
icon : '🎵' ,
description : ` ${ track . title } ` ,
user : DiscordProvider.client.user
});
2022-06-06 21:40:35 +07:00
const highestResolutionThumbnail = track . thumbnails . reduce (( prev , current ) =>
prev . height * prev . width > current . height * current . width ? prev : current
);
2022-05-01 16:56:37 +07:00
2022-06-06 21:40:35 +07:00
if ( highestResolutionThumbnail ) embed . setImage ( highestResolutionThumbnail . url );
2022-03-04 00:29:50 +07:00
2022-01-30 20:10:52 +07:00
return embed ;
}
2022-06-06 21:40:35 +07:00
};
2022-01-30 20:10:52 +07:00
2022-06-06 21:40:35 +07:00
export async function joinVoiceChannelProcedure (
data : HybridInteractionMessage ,
instance : DiscordMusicPlayerInstance | null ,
voiceChannel : VoiceChannel | StageChannel
) {
2022-06-06 21:33:02 +07:00
const isSlashCommand = data . isSlashCommand ();
const isAcceptableInteraction = data . isSelectMenu () || data . isButton ();
const isMessage = data . isMessage ();
2022-06-06 21:40:35 +07:00
if ( ! isSlashCommand && ! isAcceptableInteraction && ! isMessage ) return ;
2022-01-30 20:10:52 +07:00
2022-06-06 21:33:02 +07:00
const member = data . getMember ();
const channel = data . getChannel ();
const guild = data . getGuild ();
2022-01-30 20:10:52 +07:00
2022-06-06 21:40:35 +07:00
if ( ! member || ! channel || ! guild ) return ;
2022-01-30 20:10:52 +07:00
2022-06-06 21:33:02 +07:00
if ( channel instanceof DMChannel ) return ;
if ( ! ( channel instanceof TextChannel )) return ;
2022-01-30 20:10:52 +07:00
2022-06-06 21:40:35 +07:00
const memberVoiceChannel = member . voice . channel ; //isMessage ? member.voice.channel : DiscordProvider.client.guilds.cache.get(guild.id)!.members.cache.get((data as Interaction).user.id)?.voice.channel;
2022-06-06 21:33:02 +07:00
if ( ! memberVoiceChannel ) return ;
const bot = guild . me ;
2022-06-06 21:40:35 +07:00
if ( ! bot ) return ;
2022-01-30 20:10:52 +07:00
// If already in VoiceChannel
2022-06-06 21:33:02 +07:00
if ( bot . voice . channelId ) {
2022-01-30 20:10:52 +07:00
// But, no music instance yet (The bot might just restarted)
if ( ! instance ) {
// User is in different VoiceChannel
2022-06-06 21:33:02 +07:00
if ( memberVoiceChannel . id !== bot . voice . channelId ) {
2022-01-30 20:10:52 +07:00
//Disconnect it
2022-06-06 21:33:02 +07:00
await bot . voice . disconnect ();
2022-01-30 20:10:52 +07:00
}
//Create instance for a new one
2022-06-06 21:33:02 +07:00
DiscordMusicPlayer . createGuildInstance ( guild . id , voiceChannel );
instance = DiscordMusicPlayer . getGuildInstance ( guild . id );
2022-01-30 20:10:52 +07:00
2022-06-06 21:33:02 +07:00
instance ! . joinVoiceChannel ( voiceChannel , channel );
2022-06-06 21:40:35 +07:00
if ( ! isAcceptableInteraction )
await sendHybridInteractionMessageResponse ( data , {
embeds : [ EMBEDS . VOICECHANNEL_JOINED ( data . getRaw ())]
});
2022-01-30 20:10:52 +07:00
}
// And, already have instance on the guild
else {
// And, User VoiceChannel is same as the instance
if ( channel . id === instance . voiceChannel . id ) {
2022-06-06 21:40:35 +07:00
if ( ! isAcceptableInteraction )
return await sendHybridInteractionMessageResponse ( data , {
embeds : [ EMBEDS . VOICECHANNEL_ALREADY_JOINED ( data . getRaw ())]
});
2022-01-30 20:10:52 +07:00
else return ;
}
// But, not the same VoiceChannel
else {
2022-06-06 21:40:35 +07:00
let activeMembers = instance . voiceChannel . members . filter (( member ) => member . user . bot === false );
2022-01-30 20:10:52 +07:00
// And someone else is using the bot
2022-06-06 21:40:35 +07:00
if ( activeMembers . size > 0 ) {
if ( ! isAcceptableInteraction )
return await sendHybridInteractionMessageResponse ( data , {
embeds : [ EMBEDS . VOICECHANNEL_INUSE ( data . getRaw ())]
});
2022-01-30 20:10:52 +07:00
else return ;
}
// And alone in the VoiceChannel
else {
// Move to the new voice channel
2022-06-06 21:33:02 +07:00
await bot . voice . setChannel ( voiceChannel );
2022-06-06 21:40:35 +07:00
if ( ! isAcceptableInteraction )
return await sendHybridInteractionMessageResponse ( data , {
embeds : [ EMBEDS . VOICECHANNEL_JOINED ( data . getRaw ())]
});
2022-01-30 20:10:52 +07:00
else return ;
}
}
}
}
// Not in any VoiceChannel
else {
2022-06-06 21:40:35 +07:00
if ( instance ) await DiscordMusicPlayer . destoryGuildInstance ( guild . id );
2022-01-30 20:10:52 +07:00
2022-06-06 21:33:02 +07:00
DiscordMusicPlayer . createGuildInstance ( guild . id , voiceChannel );
instance = DiscordMusicPlayer . getGuildInstance ( guild . id );
2022-01-30 20:10:52 +07:00
2022-06-06 21:33:02 +07:00
instance ! . joinVoiceChannel ( voiceChannel , channel );
2022-06-06 21:40:35 +07:00
if ( ! isAcceptableInteraction )
2022-06-06 21:33:02 +07:00
await sendHybridInteractionMessageResponse ( data , { embeds : [ EMBEDS . VOICECHANNEL_JOINED ( data . getRaw ())] });
2022-01-30 20:10:52 +07:00
}
if ( ! instance ) return ;
2022-05-01 16:56:37 +07:00
let previousTrack : ValidTracks | undefined ;
let isLoopMessageSent = false ;
2022-01-30 20:10:52 +07:00
// Register Event Listeners
instance . events . on ( 'playing' , async ( event : PlayerPlayingEvent ) => {
2022-06-06 21:40:35 +07:00
if ( ! event . instance . queue || ! event . instance . queue . track || event . instance . queue . track . length === 0 ) return ;
2022-05-01 16:56:37 +07:00
previousTrack = event . instance . getPreviousTrack ();
2022-06-06 21:40:35 +07:00
if ( event . instance . queue . track [ 0 ] !== previousTrack ) isLoopMessageSent = false ;
else if ( event . instance . queue . track [ 0 ] === previousTrack && isLoopMessageSent ) return ;
2022-03-03 22:18:09 +07:00
2022-06-06 21:40:35 +07:00
const row = new MessageActionRow (). addComponents (
new MessageButton ()
. setEmoji ( '▶️' )
. setLabel ( ' Open on YouTube' )
. setURL ( encodeURI ( `https://www.youtube.com/watch?v= ${ event . instance . queue . track [ 0 ]. id } ` ))
. setStyle ( 'LINK' )
);
2022-01-30 20:10:52 +07:00
if ( event . instance . textChannel ) {
2022-06-06 21:40:35 +07:00
if ( event . instance . getLoopMode () === DiscordMusicPlayerLoopMode . Current ) {
2022-05-01 16:56:37 +07:00
isLoopMessageSent = true ;
2022-06-06 21:40:35 +07:00
await sendMessage ( event . instance . textChannel , undefined , {
embeds : [ EMBEDS . NOW_REPEATING ( data . getRaw (), event . instance . queue . track [ 0 ])],
components : [ row ]
});
} else {
await sendMessage ( event . instance . textChannel , undefined , {
embeds : [ EMBEDS . NOW_PLAYING ( data . getRaw (), event . instance . queue . track [ 0 ])],
components : [ row ]
});
2022-05-01 16:56:37 +07:00
}
2022-01-30 20:10:52 +07:00
}
2022-02-27 17:15:20 +07:00
});
instance . events . on ( 'error' , async ( event : PlayerErrorEvent ) => {
if ( event . instance . textChannel ) {
2022-06-06 21:40:35 +07:00
await sendMessage ( event . instance . textChannel , undefined , {
embeds : [ EMBEDS . MUSIC_ERROR ( data . getRaw (), event . error )]
});
2022-02-27 17:15:20 +07:00
}
});
instance . events . on ( 'disconnect' , async ( event : VoiceDisconnectedEvent ) => {
if ( event . instance . textChannel ) {
2022-06-06 21:40:35 +07:00
await sendMessage ( event . instance . textChannel , undefined , {
embeds : [ EMBEDS . VOICECHANNEL_DISCONNECTED ( data . getRaw ())]
});
2022-02-27 17:15:20 +07:00
}
2022-03-03 22:48:23 +07:00
DiscordMusicPlayer . destoryGuildInstance ( event . instance . voiceChannel . guildId );
2022-02-27 17:15:20 +07:00
});
2022-01-30 20:10:52 +07:00
}
2022-03-03 20:56:49 +07:00
export default class Join extends DiscordModule {
2022-06-06 21:40:35 +07:00
public id = 'Discord_MusicPlayer_Join' ;
public commands = [ 'join' ];
public commandInteractionName = 'join' ;
2022-01-30 20:10:52 +07:00
2022-03-03 20:56:49 +07:00
async GuildOnModuleCommand ( args : any , message : Message ) {
await this . run ( new HybridInteractionMessage ( message ), args );
2022-01-30 20:10:52 +07:00
}
2022-06-06 21:40:35 +07:00
async GuildModuleCommandInteractionCreate ( interaction : CommandInteraction ) {
2022-03-03 20:56:49 +07:00
await this . run ( new HybridInteractionMessage ( interaction ), interaction . options );
2022-01-30 20:10:52 +07:00
}
2022-03-03 20:56:49 +07:00
async run ( data : HybridInteractionMessage , args : any ) {
const guild = data . getGuild ();
const member = data . getMember ();
2022-01-30 20:10:52 +07:00
2022-06-06 21:40:35 +07:00
if ( ! guild || ! member ) return ;
2022-01-30 20:10:52 +07:00
2022-03-03 20:56:49 +07:00
const voiceChannel = member . voice . channel ;
2022-01-30 20:10:52 +07:00
2022-03-03 20:56:49 +07:00
if ( ! voiceChannel )
2022-06-06 21:40:35 +07:00
return await sendHybridInteractionMessageResponse ( data , {
embeds : [ EMBEDS . USER_NOT_IN_VOICECHANNEL ( data . getRaw ())]
});
2022-03-03 20:56:49 +07:00
const instance = DiscordMusicPlayer . getGuildInstance ( guild . id );
2022-06-06 21:40:35 +07:00
console . log ( data . isButton );
2022-06-06 21:33:02 +07:00
await joinVoiceChannelProcedure ( data , instance , voiceChannel );
2022-01-30 20:10:52 +07:00
}
2022-06-06 21:40:35 +07:00
}