Add more music commands and clean them up a bit. Add version string to discord presence.

merge-requests/1/head
Julius de Jeu 2019-05-28 22:35:24 +02:00
parent faab0c818c
commit 1a0b3a1cd0
14 changed files with 132 additions and 22 deletions

View File

@ -1,3 +1,5 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.3.31'
@ -5,6 +7,8 @@ plugins {
id "io.spring.dependency-management" version "1.0.7.RELEASE"
id 'org.jetbrains.kotlin.plugin.spring' version '1.3.31'
id 'org.jetbrains.kotlin.plugin.jpa' version '1.3.31'
id "org.jetbrains.kotlin.kapt" version "1.3.31"
}
apply plugin: 'kotlin-jpa'
@ -40,6 +44,7 @@ dependencies {
implementation "com.h2database:h2"
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
kapt 'org.springframework.boot:spring-boot-configuration-processor'
implementation "org.springframework:spring-web"
implementation "com.fasterxml.jackson.core:jackson-databind"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin"

View File

@ -1,24 +1,28 @@
package nl.voidcorp.discord
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers
import net.dv8tion.jda.api.entities.Activity
import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder
import nl.voidcorp.discord.events.CommandListener
import nl.voidcorp.discord.events.OttoListener
import nl.voidcorp.discord.music.PlayerManager
import nl.voidcorp.discord.storage.ConfigStore
import org.springframework.stereotype.Service
@Service
class Loader(listener: CommandListener, playerManager: DefaultAudioPlayerManager) {
class Loader(listener: CommandListener, playerManager: PlayerManager, store: ConfigStore, otto: OttoListener) {
init {
val token = System.getenv("DISCORD_TOKEN") ?: throw RuntimeException("'DISCORD_TOKEN' not set!")
val builder = DefaultShardManagerBuilder(token)
builder.addEventListeners(OttoListener, listener)
builder.addEventListeners(otto, listener)
jda = builder.build()
jda.setActivityProvider {
Activity.playing("v${store.version} ($it)")
}
AudioSourceManagers.registerRemoteSources(playerManager)
}
}

View File

@ -1,17 +1,21 @@
package nl.voidcorp.discord
import net.dv8tion.jda.api.sharding.ShardManager
import nl.voidcorp.discord.storage.ConfigStore
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.runApplication
import java.util.jar.Manifest
val logger: Logger = LoggerFactory.getLogger("OttoBot")
lateinit var jda: ShardManager
val creator = 168743656738521088
const val creator = 168743656738521088
@SpringBootApplication
@EnableConfigurationProperties(ConfigStore::class)
class SpringApp

View File

@ -1,10 +1,11 @@
package nl.voidcorp.discord.commands.music
import nl.voidcorp.discord.command.*
import nl.voidcorp.discord.music.PlayerManager
import org.springframework.stereotype.Service
@Service
class ForceLeave : Command(
class ForceLeave(val playerManager: PlayerManager) : Command(
"forceleave",
group = CommandGroup.MUSIC,
location = CommandSource.GUILD,
@ -12,6 +13,7 @@ class ForceLeave : Command(
) {
override fun handle(event: CommandMessage): CommandResult {
event.guild!!.audioManager.closeAudioConnection()
playerManager.delGuildPlayer(event.guild)
return CommandResult.SUCCESS
}
}

View File

@ -7,20 +7,27 @@ import org.springframework.stereotype.Service
@Service
class Play(val playerManager: PlayerManager) :
Command("play", location = CommandSource.GUILD, group = CommandGroup.MUSIC) {
Command("play", location = CommandSource.GUILD, group = CommandGroup.MUSIC, usage = "play song url (or song name prepended with ytsearch:)") {
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
} else if (event.params.drop(1).isEmpty()) {
event.reply("I'm going to need a url or a search term to actually find a song...")
return CommandResult.PARAMETERS
}
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))
if (!ts.playing) {
am.openAudioConnection(chan)
}
playerManager.loadItem(event.params.drop(1).joinToString(" "), AudioLoadHandler(ts))
return CommandResult.SUCCESS

View File

@ -0,0 +1,14 @@
package nl.voidcorp.discord.commands.music
import nl.voidcorp.discord.command.*
import nl.voidcorp.discord.music.PlayerManager
import org.springframework.stereotype.Service
@Service
class Skip(val playerManager: PlayerManager) :
Command("skip", location = CommandSource.GUILD, group = CommandGroup.MUSIC) {
override fun handle(event: CommandMessage): CommandResult {
playerManager.getGuildPlayer(event.guild!!).skip()
return CommandResult.SUCCESS
}
}

View File

@ -1,11 +1,24 @@
package nl.voidcorp.discord.events
import net.dv8tion.jda.api.entities.Activity
import net.dv8tion.jda.api.events.ReadyEvent
import net.dv8tion.jda.api.events.ReconnectedEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import nl.voidcorp.discord.logger
import nl.voidcorp.discord.storage.ConfigStore
import org.springframework.stereotype.Service
object OttoListener : ListenerAdapter() {
@Service
class OttoListener(val configStore: ConfigStore) : ListenerAdapter() {
override fun onReady(event: ReadyEvent) {
logger.info("Found ${event.guildTotalCount} different guilds!")
}
override fun onReconnect(event: ReconnectedEvent) {
val id = event.jda.shardInfo!!.shardId
val reconn = event.responseNumber
val version = configStore.version
event.jda.presence.activity = Activity.playing("v$version ($id~$reconn)")
}
}

View File

@ -4,7 +4,6 @@ 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 {
@ -14,7 +13,6 @@ class AudioLoadHandler(private val trackScheduler: TrackScheduler) : AudioLoadRe
}
override fun trackLoaded(track: AudioTrack) {
logger.info("loaded track ${track.identifier}")
trackScheduler.queue(track)
}
@ -23,8 +21,11 @@ class AudioLoadHandler(private val trackScheduler: TrackScheduler) : AudioLoadRe
}
override fun playlistLoaded(playlist: AudioPlaylist) {
for (t in playlist.tracks) {
trackScheduler.queue(t)
}
if (playlist.isSearchResult) {
trackScheduler.queue(playlist.selectedTrack ?: playlist.tracks.first())
} else
for (t in playlist.tracks) {
trackScheduler.queue(t)
}
}
}

View File

@ -0,0 +1,12 @@
package nl.voidcorp.discord.music
import com.sedmelluq.discord.lavaplayer.track.AudioTrack
import net.dv8tion.jda.api.entities.TextChannel
import nl.voidcorp.discord.logger
class MusicAnnouncer(val channel: TextChannel) {
fun sendPlayTrack(track: AudioTrack) {
logger.info(track.info.uri)
}
}

View File

@ -2,21 +2,42 @@ package nl.voidcorp.discord.music
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager
import net.dv8tion.jda.api.entities.Guild
import nl.voidcorp.discord.storage.GuildRepo
import nl.voidcorp.discord.storage.GuildStore
import org.springframework.stereotype.Service
@Service
class PlayerManager : DefaultAudioPlayerManager() {
class PlayerManager(val repo: GuildRepo) : 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)
val store = repo.findByGuildId(guild.idLong) ?: GuildStore(guild.idLong)
val channel = store.musicChannels.firstOrNull()
?: store.botChannels.firstOrNull()
?: guild.textChannels.firstOrNull {
it.name.contains("music", true) && it.name.contains(
"bot",
true
)
}?.idLong
?: guild.textChannels.firstOrNull { it.name.contains("music", true) }?.idLong
?: guild.textChannels.firstOrNull { it.name.contains("bot", true) }?.idLong
?: guild.defaultChannel!!.idLong
player.volume = 50
val ts = TrackScheduler(player, MusicAnnouncer(guild.getTextChannelById(channel)!!)) {
delGuildPlayer(guild)
}
player.addListener(ts)
guild.audioManager.sendingHandler = AudioPlayerSendHandler(player)
guildPlayMap[guild.idLong] = ts
ts
}
}
fun delGuildPlayer(guild: Guild) = guildPlayMap.remove(guild.idLong)
}

View File

@ -4,22 +4,40 @@ 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 nl.voidcorp.discord.logger
import java.util.*
class TrackScheduler(private val player: AudioPlayer) : AudioEventAdapter() {
class TrackScheduler(private val player: AudioPlayer, private val announcer: MusicAnnouncer, val delet: () -> Unit) :
AudioEventAdapter() {
private val queue = ArrayDeque<AudioTrack>()
fun queue(track: AudioTrack) {
if (!player.startTrack(track, true)) {
queue.addLast(track)
}
}
fun pop() = queue.pop()
override fun onTrackStart(player: AudioPlayer, track: AudioTrack) {
announcer.sendPlayTrack(track)
}
override fun onTrackEnd(player: AudioPlayer, track: AudioTrack, endReason: AudioTrackEndReason) {
if (endReason.mayStartNext && queue.isNotEmpty()) {
player.startTrack(pop(), true)
player.startTrack(queue.pop(), true)
} else if (queue.isEmpty()) {
announcer.channel.guild.audioManager.closeAudioConnection()
delet()
}
}
val playing
get() = player.playingTrack != null
fun skip() {
if (queue.isEmpty()) {
player.stopTrack()
} else {
player.startTrack(queue.pop(), false)
}
}
}

View File

@ -0,0 +1,8 @@
package nl.voidcorp.discord.storage
import org.springframework.boot.context.properties.ConfigurationProperties
@ConfigurationProperties("ottobot")
class ConfigStore {
lateinit var version: String
}

View File

@ -5,5 +5,4 @@ import org.springframework.data.jpa.repository.JpaRepository
interface GuildRepo : JpaRepository<GuildStore, Long> {
fun findByGuildId(id: Long): GuildStore?
fun existsByGuildId(id: Long): Boolean
}

View File

@ -7,3 +7,5 @@ spring.datasource.url=jdbc:h2:mem:
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
ottobot.version=1.0