diff --git a/.gitignore b/.gitignore index efa7f3b..bb01f9c 100644 --- a/.gitignore +++ b/.gitignore @@ -124,4 +124,7 @@ gradle-app.setting # gradle/wrapper/gradle-wrapper.properties -# End of https://www.gitignore.io/api/kotlin,gradle,intellij+all \ No newline at end of file +# End of https://www.gitignore.io/api/kotlin,gradle,intellij+all + +settings.json +/libs/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 4bf0947..4b89108 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,6 @@ plugins { id 'java' id 'org.jetbrains.kotlin.jvm' version '1.3.0' - id 'com.github.johnrengelman.shadow' version '4.0.1' } group 'nl.voidcorp.dbot' @@ -36,14 +35,13 @@ compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } -shadowJar { - baseName = 'UnityBot' - classifier = null - version = null +task copyDependencies(type: Copy) { + delete 'libs' + from configurations.compile + into 'libs' } jar { - manifest { - attributes 'Main-Class': 'nl.voidcorp.dbot.UnityBotKt' - } -} \ No newline at end of file + + archiveName = "UnityBot.jar" +} diff --git a/src/main/kotlin/nl/voidcorp/dbot/Events.kt b/src/main/kotlin/nl/voidcorp/dbot/Events.kt index b008251..ce35e2a 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/Events.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/Events.kt @@ -1,9 +1,11 @@ package nl.voidcorp.dbot +import net.dv8tion.jda.core.events.guild.voice.GuildVoiceLeaveEvent import net.dv8tion.jda.core.events.message.MessageReceivedEvent import net.dv8tion.jda.core.events.message.react.MessageReactionAddEvent import net.dv8tion.jda.core.events.message.react.MessageReactionRemoveEvent import net.dv8tion.jda.core.hooks.ListenerAdapter +import nl.voidcorp.dbot.music.guildMusicMap object Events : ListenerAdapter() { override fun onMessageReceived(event: MessageReceivedEvent) { @@ -30,7 +32,12 @@ object Events : ListenerAdapter() { } } }*/ - if (event.author.idLong == 168743656738521088 && event.message.mentionedMembers.contains(event.guild.getMember(event.jda.selfUser))) { + if (event.author.idLong == 168743656738521088 && event.message.mentionedMembers.contains( + event.guild.getMember( + event.jda.selfUser + ) + ) + ) { /*if (event.message.contentStripped.contains("hide")) { if (event.message.mentionedChannels.isNotEmpty()) { val override = event.message.mentionedChannels[0].getPermissionOverride(event.guild.getRoleById(499512961103167498)) @@ -69,7 +76,8 @@ object Events : ListenerAdapter() { override fun onMessageReactionAdd(event: MessageReactionAddEvent) { if (event.channel.idLong == 499523462449201162 && event.messageIdLong == 499929881883181064) { - event.guild.controller.addSingleRoleToMember(event.member, event.guild.getRoleById( + event.guild.controller.addSingleRoleToMember( + event.member, event.guild.getRoleById( when (event.reactionEmote.name) { "\uD83C\uDFB5" -> 499925623188488192 "\uD83D\uDCBE" -> 499525260912361472 @@ -77,13 +85,16 @@ object Events : ListenerAdapter() { "\uD83C\uDFA8" -> 499525305405276181 "\uD83D\uDD0D" -> 499525364708540416 else -> 499938201444810782 - })).queue() + } + ) + ).queue() } } override fun onMessageReactionRemove(event: MessageReactionRemoveEvent) { if (event.channel.idLong == 499523462449201162 && event.messageIdLong == 499929881883181064) { - event.guild.controller.removeSingleRoleFromMember(event.member, event.guild.getRoleById( + event.guild.controller.removeSingleRoleFromMember( + event.member, event.guild.getRoleById( when (event.reactionEmote.name) { "\uD83C\uDFB5" -> 499925623188488192 "\uD83D\uDCBE" -> 499525260912361472 @@ -91,7 +102,15 @@ object Events : ListenerAdapter() { "\uD83C\uDFA8" -> 499525305405276181 "\uD83D\uDD0D" -> 499525364708540416 else -> 499938201444810782 - })).queue() + } + ) + ).queue() + } + } + + override fun onGuildVoiceLeave(event: GuildVoiceLeaveEvent) { + if (event.channelLeft.members.size == 1) { + guildMusicMap[event.guild.idLong]?.clearQueue() } } diff --git a/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt b/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt index 69af13f..d309ae6 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt @@ -34,7 +34,7 @@ fun main(args: Array) { val custom = UnityCommandClient("!") custom.addCommands(commands) - custom.version = "1.4" + custom.version = "1.5" bot = JDABuilder(args[0]).addEventListener(custom).addEventListener(nl.voidcorp.dbot.Events) diff --git a/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt b/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt index 22d5e67..cf06067 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt @@ -10,10 +10,7 @@ import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame import net.dv8tion.jda.core.audio.AudioSendHandler import nl.voidcorp.dbot.addAll import nl.voidcorp.dbot.commands -import nl.voidcorp.dbot.commands.MusicCategoryPrivate -import nl.voidcorp.dbot.commands.UnityCommand -import nl.voidcorp.dbot.commands.UnityCommandEvent -import nl.voidcorp.dbot.commands.UnityMusicCommand +import nl.voidcorp.dbot.commands.* import nl.voidcorp.dbot.log import nl.voidcorp.dbot.playerManager @@ -252,6 +249,25 @@ fun initMusic() { } } + val loopCommand = + UnityMusicCommand("loop", "Loops the queue!\nOr disables it!", MusicCategory) { event, scheduler -> + if (scheduler.loop) { + event.reply("The queue will no longer loop!") + } else { + event.reply("The queue wil now loop") + } + scheduler.loop = !scheduler.loop + } + + val stopCommand = UnityMusicCommand( + "stop", + "Stops this shit!", + MusicCategoryPrivate, + aliases = mutableListOf() + ) { event, scheduler -> + scheduler.clearQueue() + event.reply("Stopped all playback!") + } commands.addAll( playCommand, @@ -261,7 +277,9 @@ fun initMusic() { soundcloudCommand, npCommand, attachmentPlay, - voteSkipCommand + voteSkipCommand, + loopCommand, + stopCommand ) } @@ -283,8 +301,7 @@ fun getLinkFromSearch( scheduler.insertFront(track, event.member) if (!scheduler.isQueueEmpty()) scheduler.skip() - } - else + } else scheduler.queue(track, event.member) /*event.reply( EmbedBuilder().setImage(r.snippet.thumbnails.high.url).setTitle(r.snippet.title, "https://youtu.be/$id") diff --git a/src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt b/src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt index 408a010..be2e4b1 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt @@ -14,6 +14,7 @@ import nl.voidcorp.dbot.log import java.awt.Color import java.time.LocalDateTime import java.util.* +import java.util.concurrent.TimeUnit import kotlin.concurrent.timerTask import kotlin.math.roundToInt @@ -21,6 +22,8 @@ import kotlin.math.roundToInt class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel: VoiceChannel) : AudioEventAdapter() { val musicChannel: TextChannel + var loop = false + init { player.addListener(this) @@ -45,10 +48,16 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel }*/ override fun onTrackEnd(player: AudioPlayer, track: AudioTrack, endReason: AudioTrackEndReason) { - pressedSkip++ + pressedSkip = 0 if (q.isNotEmpty()) { - player.playTrack(q.poll()) + val t = q.poll()!! + if (loop) q.addLast(t.makeClone().apply { userData = t.userData }) + player.playTrack(t) } else { + if (loop) { + player.playTrack(track.makeClone().apply { userData = track.userData }) + return + } stopPlay(endReason) player.destroy() } @@ -135,52 +144,66 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel fun getCurrentTrackInfo(): MessageEmbed = getTrackInfo(player.playingTrack) fun getTrackInfo(track: AudioTrack): MessageEmbed { - if (track is YoutubeAudioTrack) { - if (track.userData is Member) - return EmbedBuilder() - .setFooter( - "Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) - .user.effectiveAvatarUrl - ).setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) - .setThumbnail("https://img.youtube.com/vi/${track.info.identifier}/hqdefault.jpg") - .setTimestamp(LocalDateTime.now()).setColor(Color.decode("#ff0000")).build() - } else if (track is SoundCloudAudioTrack) { - val scsm = track.sourceManager as SoundCloudAudioSourceManager - scsm.updateClientId() - val art = khttp.get( - "http://api.soundcloud.com/tracks/${track.info.identifier}?client_id=${scsm.clientId}", - headers = mapOf("Content-Type" to "application/json") - ).jsonObject["artwork_url"].toString().replace("large", "t300x300") - - if (track.userData is Member) - return EmbedBuilder() - .setFooter( - "Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) - .user.effectiveAvatarUrl - ).setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) - .setThumbnail(if (art == "null") null else art) - .setTimestamp(LocalDateTime.now()).setColor(Color.decode("#ff5500")).build() - - } else if (track is HttpAudioTrack) { - if (track.userData is Member) - return EmbedBuilder() - .setFooter( - "Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) - .user.effectiveAvatarUrl - ).setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) - .setTimestamp(LocalDateTime.now()).setColor(Color.decode("#005A9C")).build() - } - - return EmbedBuilder() - .setFooter( + val eb = EmbedBuilder().setTimestamp(LocalDateTime.now()) + if (track.userData is Member) { + eb.setFooter( "Requested by ${(track.userData as Member).effectiveName}", (track.userData as Member) .user.effectiveAvatarUrl - ).setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) - .setTimestamp(LocalDateTime.now()).setColor(musicChannel.guild.selfMember.color).build() + ) + } + when (track) { + is YoutubeAudioTrack -> eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) + .setThumbnail("https://img.youtube.com/vi/${track.info.identifier}/hqdefault.jpg") + .setColor(Color.decode("#ff0000")) + + is SoundCloudAudioTrack -> { + val scsm = track.sourceManager as SoundCloudAudioSourceManager + scsm.updateClientId() + val art = khttp.get( + "http://api.soundcloud.com/tracks/${track.info.identifier}?client_id=${scsm.clientId}", + headers = mapOf("Content-Type" to "application/json") + ).jsonObject["artwork_url"].toString().replace("large", "t300x300") + + eb + .setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) + .setThumbnail(if (art == "null") null else art) + .setColor(Color.decode("#ff5500")) + + } + + is HttpAudioTrack -> eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) + .setColor(Color.decode("#005A9C")) + + else -> + eb.setAuthor(track.info.author).setTitle(track.info.title, track.info.uri) + .setColor(musicChannel.guild.selfMember.color) + } + + + eb.setDescription("${millisToTime(track.position)}/${millisToTime(track.duration)}") + + return eb.build() + } + + + fun millisToTime(millis: Long): String { + var ms = millis + val days = TimeUnit.MILLISECONDS.toDays(ms) + ms -= TimeUnit.DAYS.toMillis(days) + val hours = TimeUnit.MILLISECONDS.toHours(ms) + ms -= TimeUnit.HOURS.toMillis(hours) + val minutes = TimeUnit.MILLISECONDS.toMinutes(ms) + ms -= TimeUnit.MINUTES.toMillis(minutes) + val seconds = TimeUnit.MILLISECONDS.toSeconds(ms) + val current = String.format( + "%02d:%02d:%02d:%02d", + days, hours, minutes, seconds + ).removePrefix("00:").removePrefix("00:") + return current } fun getTrackList(member: Member): MessageEmbed { - return EmbedBuilder().setTitle("Hey ${member.effectiveName}, here is the playlist") + return EmbedBuilder().setTitle("Hey ${member.effectiveName}, here is the playlist${if (loop) ", it loops!" else ""}") .setTimestamp(LocalDateTime.now()).setColor(musicChannel.guild.selfMember.color) .setFooter("Requested by ${member.effectiveName}", member.user.effectiveAvatarUrl).setDescription( "**${player.playingTrack.info.title}**, requested by ${if (player.playingTrack.userData is Member) (player.playingTrack.userData as Member).effectiveName else "someone unknown..."} (*now playing*)\n" + @@ -196,5 +219,11 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel return press >= half } + fun clearQueue() { + q.clear() + loop = false + player.stopTrack() + } + }