Woo, audio! Added a basic play command, which for now just loads a totally random video. Also added a kill command to forcefully stop a connection, might move that to debug
This commit is contained in:
parent
e8ae29d1ce
commit
faab0c818c
|
@ -35,6 +35,7 @@ dependencies {
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter:5.4.2"
|
testImplementation "org.junit.jupiter:junit-jupiter:5.4.2"
|
||||||
|
|
||||||
implementation 'net.dv8tion:JDA:4.ALPHA.0_88'
|
implementation 'net.dv8tion:JDA:4.ALPHA.0_88'
|
||||||
|
implementation 'com.sedmelluq:lavaplayer:1.3.17'
|
||||||
|
|
||||||
implementation "com.h2database:h2"
|
implementation "com.h2database:h2"
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package nl.voidcorp.discord
|
package nl.voidcorp.discord
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager
|
||||||
|
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers
|
||||||
import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder
|
import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder
|
||||||
import nl.voidcorp.discord.events.CommandListener
|
import nl.voidcorp.discord.events.CommandListener
|
||||||
import nl.voidcorp.discord.events.OttoListener
|
import nl.voidcorp.discord.events.OttoListener
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
@Component
|
|
||||||
class Loader(listener: CommandListener) {
|
@Service
|
||||||
|
class Loader(listener: CommandListener, playerManager: DefaultAudioPlayerManager) {
|
||||||
init {
|
init {
|
||||||
val token = System.getenv("DISCORD_TOKEN") ?: throw RuntimeException("'DISCORD_TOKEN' not set!")
|
val token = System.getenv("DISCORD_TOKEN") ?: throw RuntimeException("'DISCORD_TOKEN' not set!")
|
||||||
val builder = DefaultShardManagerBuilder(token)
|
val builder = DefaultShardManagerBuilder(token)
|
||||||
|
@ -16,5 +19,6 @@ class Loader(listener: CommandListener) {
|
||||||
builder.addEventListeners(OttoListener, listener)
|
builder.addEventListeners(OttoListener, listener)
|
||||||
|
|
||||||
jda = builder.build()
|
jda = builder.build()
|
||||||
|
AudioSourceManagers.registerRemoteSources(playerManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package nl.voidcorp.discord.command
|
||||||
enum class CommandGroup {
|
enum class CommandGroup {
|
||||||
GENERAL,
|
GENERAL,
|
||||||
FUN,
|
FUN,
|
||||||
|
MUSIC,
|
||||||
ROLES,
|
ROLES,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
VeRY_hIdden_CaTegoRY_LoL,
|
VeRY_hIdden_CaTegoRY_LoL,
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package nl.voidcorp.discord.commands.music
|
||||||
|
|
||||||
|
import nl.voidcorp.discord.command.*
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class ForceLeave : Command(
|
||||||
|
"forceleave",
|
||||||
|
group = CommandGroup.MUSIC,
|
||||||
|
location = CommandSource.GUILD,
|
||||||
|
commandLevel = CommandLevel.MODERATOR
|
||||||
|
) {
|
||||||
|
override fun handle(event: CommandMessage): CommandResult {
|
||||||
|
event.guild!!.audioManager.closeAudioConnection()
|
||||||
|
return CommandResult.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
28
src/main/kotlin/nl/voidcorp/discord/commands/music/Play.kt
Normal file
28
src/main/kotlin/nl/voidcorp/discord/commands/music/Play.kt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package nl.voidcorp.discord.commands.music
|
||||||
|
|
||||||
|
import nl.voidcorp.discord.command.*
|
||||||
|
import nl.voidcorp.discord.music.AudioLoadHandler
|
||||||
|
import nl.voidcorp.discord.music.PlayerManager
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class Play(val playerManager: PlayerManager) :
|
||||||
|
Command("play", location = CommandSource.GUILD, group = CommandGroup.MUSIC) {
|
||||||
|
override fun handle(event: CommandMessage): CommandResult {
|
||||||
|
val chan = event.member!!.voiceState!!.channel
|
||||||
|
if (chan == null) {
|
||||||
|
event.reply("Please join a voice channel to play music!")
|
||||||
|
return CommandResult.SUCCESS
|
||||||
|
}
|
||||||
|
val am = event.guild!!.audioManager
|
||||||
|
|
||||||
|
am.openAudioConnection(chan)
|
||||||
|
|
||||||
|
val ts = playerManager.getGuildPlayer(event.guild)
|
||||||
|
|
||||||
|
playerManager.loadItem("https://www.youtube.com/watch?v=kPgiJUeSP6Q", AudioLoadHandler(ts))
|
||||||
|
|
||||||
|
|
||||||
|
return CommandResult.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package nl.voidcorp.discord.music
|
||||||
|
|
||||||
|
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 nl.voidcorp.discord.logger
|
||||||
|
|
||||||
|
|
||||||
|
class AudioLoadHandler(private val trackScheduler: TrackScheduler) : AudioLoadResultHandler {
|
||||||
|
|
||||||
|
override fun loadFailed(exception: FriendlyException) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun trackLoaded(track: AudioTrack) {
|
||||||
|
logger.info("loaded track ${track.identifier}")
|
||||||
|
trackScheduler.queue(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noMatches() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun playlistLoaded(playlist: AudioPlaylist) {
|
||||||
|
for (t in playlist.tracks) {
|
||||||
|
trackScheduler.queue(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package nl.voidcorp.discord.music
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer
|
||||||
|
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame
|
||||||
|
import net.dv8tion.jda.api.audio.AudioSendHandler
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
|
||||||
|
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() = ByteBuffer.wrap(lastFrame!!.data)
|
||||||
|
|
||||||
|
override fun isOpus(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
22
src/main/kotlin/nl/voidcorp/discord/music/PlayerManager.kt
Normal file
22
src/main/kotlin/nl/voidcorp/discord/music/PlayerManager.kt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package nl.voidcorp.discord.music
|
||||||
|
|
||||||
|
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager
|
||||||
|
import net.dv8tion.jda.api.entities.Guild
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class PlayerManager : DefaultAudioPlayerManager() {
|
||||||
|
val guildPlayMap = mutableMapOf<Long, TrackScheduler>()
|
||||||
|
fun getGuildPlayer(guild: Guild): TrackScheduler {
|
||||||
|
return if (guildPlayMap.containsKey(guild.idLong)) {
|
||||||
|
guildPlayMap[guild.idLong] ?: error("oof?")
|
||||||
|
} else {
|
||||||
|
val player = createPlayer()
|
||||||
|
val ts = TrackScheduler(player)
|
||||||
|
player.addListener(ts)
|
||||||
|
guild.audioManager.sendingHandler = AudioPlayerSendHandler(player)
|
||||||
|
guildPlayMap[guild.idLong] = ts
|
||||||
|
ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/main/kotlin/nl/voidcorp/discord/music/TrackScheduler.kt
Normal file
25
src/main/kotlin/nl/voidcorp/discord/music/TrackScheduler.kt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.voidcorp.discord.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 java.util.*
|
||||||
|
|
||||||
|
class TrackScheduler(private val player: AudioPlayer) : AudioEventAdapter() {
|
||||||
|
private val queue = ArrayDeque<AudioTrack>()
|
||||||
|
fun queue(track: AudioTrack) {
|
||||||
|
if (!player.startTrack(track, true)) {
|
||||||
|
queue.addLast(track)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun pop() = queue.pop()
|
||||||
|
|
||||||
|
override fun onTrackEnd(player: AudioPlayer, track: AudioTrack, endReason: AudioTrackEndReason) {
|
||||||
|
if (endReason.mayStartNext && queue.isNotEmpty()) {
|
||||||
|
player.startTrack(pop(), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue