Add begin of music bot features

This commit is contained in:
Julius de Jeu 2018-10-14 14:16:14 +02:00
parent a3291e02b5
commit 2f9e50fd3e
5 changed files with 366 additions and 4 deletions

View file

@ -20,6 +20,10 @@ dependencies {
compile "ch.qos.logback:logback-classic:1.2.1"
compile 'io.javalin:javalin:2.3.0'
compile 'com.github.salomonbrys.kotson:kotson:2.5.0'
compile 'com.jagrosh:jda-utilities:2.1.4'
compile 'com.sedmelluq:lavaplayer:1.3.7'
compile 'com.sedmelluq:jda-nas:1.0.6'
}

View file

@ -1,22 +1,37 @@
package nl.voidcorp.dbot
import com.jagrosh.jdautilities.command.CommandClientBuilder
import com.jagrosh.jdautilities.examples.command.PingCommand
import com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager
import io.javalin.Javalin
import io.javalin.json.FromJsonMapper
import io.javalin.json.JavalinJson
import io.javalin.json.ToJsonMapper
import net.dv8tion.jda.core.EmbedBuilder
import net.dv8tion.jda.core.JDA
import net.dv8tion.jda.core.JDABuilder
import net.dv8tion.jda.core.OnlineStatus
import net.dv8tion.jda.core.entities.Game
import net.dv8tion.jda.webhook.WebhookClient
import net.dv8tion.jda.webhook.WebhookClientBuilder
import nl.voidcorp.dbot.commands.helloCommand
import nl.voidcorp.dbot.commands.initMusic
import nl.voidcorp.dbot.storage.GitlabWebhook
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileReader
val playerManager = DefaultAudioPlayerManager()
val cb = CommandClientBuilder()
val log = LoggerFactory.getLogger("UnityBot")
fun main(args: Array<String>) {
val log = LoggerFactory.getLogger("UnityBot")
playerManager.registerSourceManager(YoutubeAudioSourceManager(true))
JavalinJson.apply {
toJsonMapper = object : ToJsonMapper {
@ -68,6 +83,19 @@ fun main(args: Array<String>) {
ctx.status(200).result("OK")
}
val bot = JDABuilder(args[0]).setStatus(OnlineStatus.ONLINE).setGame(Game.watching("fraud")).addEventListener(Events).build()
cb.setOwnerId("168743656738521088")
cb.setPrefix("!")
cb.setAlternativePrefix("+")
cb.addCommand(PingCommand())
cb.addCommand(helloCommand)
cb.setGame(Game.watching("fraud (and a broken music bot)"))
initMusic()
val client = cb.build()
bot = JDABuilder(args[0]).addEventListener(client).addEventListener(nl.voidcorp.dbot.Events).setAudioSendFactory(NativeAudioSendFactory()).build()
}
lateinit var bot: JDA

View file

@ -0,0 +1,16 @@
package nl.voidcorp.dbot.commands
import com.jagrosh.jdautilities.command.CommandBuilder
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import net.dv8tion.jda.core.MessageBuilder
import nl.voidcorp.dbot.playerManager
val helloCommand = CommandBuilder().setName("hello").setHelp("Say hello to Andy!")
.setGuildOnly(false)
.build { event ->
event.reply(MessageBuilder("Hello, ").append(event.author).build())
}

View file

@ -0,0 +1,205 @@
package nl.voidcorp.dbot.commands
import com.jagrosh.jdautilities.command.CommandBuilder
import com.jagrosh.jdautilities.command.CommandEvent
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame
import net.dv8tion.jda.core.EmbedBuilder
import net.dv8tion.jda.core.audio.AudioSendHandler
import net.dv8tion.jda.core.entities.Member
import nl.voidcorp.dbot.cb
import nl.voidcorp.dbot.log
import nl.voidcorp.dbot.music.TrackScheduler
import nl.voidcorp.dbot.playerManager
import java.time.LocalDateTime
val guildMusicMap = mutableMapOf<Long, TrackScheduler>()
fun initMusic() {
AudioSourceManagers.registerRemoteSources(playerManager)
val queueCommand = CommandBuilder().setName("queue").addAlias("q").setHelp("Queue's a song").build { event ->
val scheduler = if (guildMusicMap.containsKey(event.guild.idLong)) guildMusicMap[event.guild.idLong]!! else {
val channel = event.guild.voiceChannels.firstOrNull { it.members.contains(event.member) }
if (channel == null) {
event.reply("Join a voice Channel please!")
return@build
}
val s = TrackScheduler(playerManager.createPlayer(), event.guild, channel)
guildMusicMap[event.guild.idLong] = s
s
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
}
override fun noMatches() {
getLinkFromSearch(event, scheduler)
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
}
})
}
val playCommand = CommandBuilder().setName("play").addAlias("p").setHelp("Plays the song instantly").build { event ->
val scheduler = if (guildMusicMap.containsKey(event.guild.idLong)) guildMusicMap[event.guild.idLong]!! else {
val channel = event.guild.voiceChannels.firstOrNull { it.members.contains(event.member) }
if (channel == null) {
event.reply("Join a voice Channel please!")
return@build
}
val s = TrackScheduler(playerManager.createPlayer(), event.guild, channel)
guildMusicMap[event.guild.idLong] = s
s
}
playerManager.loadItem(event.args, object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.insertFront(track, event.member)
}
override fun noMatches() {
getLinkFromSearch(event, scheduler)
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
scheduler.insertFront(t, event.member)
}
}
})
}
val skipCommand = CommandBuilder().setName("skip").addAlias("s").setHelp("Skips the currently playing track").build { event ->
val scheduler = guildMusicMap[event.guild.idLong]
if (scheduler == null) {
event.reply("There is no music playing?")
} else {
scheduler.skip()
}
}
cb.addCommands(playCommand, skipCommand, queueCommand)
}
fun getLinkFromSearch(event: CommandEvent, scheduler: TrackScheduler) {
log.info("Searching for '${event.args}'")
/*val search = yt.search().list("id,snippet")
search.key = "AIzaSyDiEYrQNT-eDRZKy6JvQHBDymttwaLd7Mg"
search.q = event.args
search.type = "video"
search.fields = "items(id/kind,id/videoId,snippet/title,snippet/thumbnails/high/url,snippet/description,snippet/channelTitle)"
search.maxResults = 10
val res = search.execute()
if (res.isEmpty()) {
event.reply("Really nothing was found...")
} else {
var found = false
for (r in res.items) {
if (r.id.kind == "youtube#video" && !found) {
found = true
val id = r.id.videoId
}
}
if (!found) {
event.replyError("I could really not find anything for '${event.args}'")
}
}*/
playerManager.loadItem("ytsearch:${event.args}", object : AudioLoadResultHandler {
override fun loadFailed(exception: FriendlyException) {
event.reply("Shit's fucked!")
}
override fun trackLoaded(track: AudioTrack) {
scheduler.queue(track, event.member)
/*event.reply(
EmbedBuilder().setImage(r.snippet.thumbnails.high.url).setTitle(r.snippet.title, "https://youtu.be/$id")
.setAuthor(r.snippet.channelTitle).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl)
.setDescription(r.snippet.description).build()
)*/
}
override fun noMatches() {
}
override fun playlistLoaded(playlist: AudioPlaylist) {
if (!playlist.isSearchResult)
for (t in playlist.tracks) {
scheduler.queue(t, event.member)
}
else {
val track = playlist.tracks.first()
event.reply(EmbedBuilder()
.setFooter("Requested by ${event.member.effectiveName}", event.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\n")
.setTimestamp(LocalDateTime.now()).build())
scheduler.queue(track, event.member)
}
}
})
}
class AudioPlayerSendHandler(private val audioPlayer: AudioPlayer) : AudioSendHandler {
private var lastFrame: AudioFrame? = null
override fun canProvide(): Boolean {
lastFrame = audioPlayer.provide()
return lastFrame != null
}
override fun provide20MsAudio(): ByteArray {
return lastFrame!!.data
}
override fun isOpus(): Boolean {
return true
}
}

View file

@ -0,0 +1,109 @@
package nl.voidcorp.dbot.music
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason
import net.dv8tion.jda.core.EmbedBuilder
import net.dv8tion.jda.core.MessageBuilder
import net.dv8tion.jda.core.entities.Guild
import net.dv8tion.jda.core.entities.Member
import net.dv8tion.jda.core.entities.TextChannel
import net.dv8tion.jda.core.entities.VoiceChannel
import nl.voidcorp.dbot.commands.AudioPlayerSendHandler
import nl.voidcorp.dbot.commands.guildMusicMap
import nl.voidcorp.dbot.log
import java.time.LocalDateTime
import java.util.*
import kotlin.concurrent.thread
class TrackScheduler(val player: AudioPlayer, val guild: Guild, channel: VoiceChannel) : AudioEventAdapter() {
val musicChannel: TextChannel
init {
player.addListener(this)
val audio = guild.audioManager
audio.sendingHandler = AudioPlayerSendHandler(player)
audio.openAudioConnection(channel)
musicChannel = guild.getTextChannelsByName("music", true).firstOrNull() ?: (guild.getTextChannelsByName("general", true).firstOrNull()
?: guild.defaultChannel!!)
}
private val q = ArrayDeque<AudioTrack>()
/*override fun onEvent(event: AudioEvent) {
if (event is TrackEndEvent) {
}
}*/
override fun onTrackEnd(player: AudioPlayer, track: AudioTrack, endReason: AudioTrackEndReason) {
if (q.isNotEmpty()) {
player.playTrack(q.remove())
} else {
stopPlay(endReason)
player.destroy()
}
}
override fun onTrackStart(player: AudioPlayer, track: AudioTrack) {
if (track.userData is Member)
musicChannel.sendMessage(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\n")
.setTimestamp(LocalDateTime.now()).build()).append("Now playing").queue()
}
fun queue(track: AudioTrack, member: Member) {
track.userData = member
if (q.isEmpty() && player.playingTrack == null) {
log.info("Queue is empty, playing a track")
player.playTrack(track)
} else {
log.info("Track Queue'd")
q.addLast(track)
}
}
fun insertFront(track: AudioTrack, member: Member) {
track.userData = member
if (q.isEmpty() && player.playingTrack == null) {
log.info("Queue is empty, playing a track")
player.playTrack(track)
} else {
log.info("Track inserted")
q.addFirst(track)
}
}
fun skip() {
if (q.isNotEmpty()) {
log.info("skipped Track!")
player.playTrack(q.remove())
} else {
stopPlay(AudioTrackEndReason.REPLACED)
player.destroy()
}
}
fun isQueueEmpty() = q.isEmpty()
fun stopPlay(endReason: AudioTrackEndReason) {
if (endReason != AudioTrackEndReason.REPLACED) {
guildMusicMap.remove(guild.idLong)
musicChannel.sendMessage("Playlist has ended, leaving the voice channel!").queue()
thread { guild.audioManager.closeAudioConnection() }
}
}
}