Add begin of music bot features
This commit is contained in:
parent
a3291e02b5
commit
2f9e50fd3e
|
@ -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'
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
16
src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt
Normal file
16
src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt
Normal 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())
|
||||
}
|
||||
|
205
src/main/kotlin/nl/voidcorp/dbot/commands/Music.kt
Normal file
205
src/main/kotlin/nl/voidcorp/dbot/commands/Music.kt
Normal 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
|
||||
}
|
||||
}
|
109
src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt
Normal file
109
src/main/kotlin/nl/voidcorp/dbot/music/TrackScheduler.kt
Normal 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() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in a new issue