diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6fcd41c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,34 @@ +#image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/gradle +image: gradle + +stages: + - build + - publish + +variables: + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + GIT_SUBMODULE_STRATEGY: recursive + +before_script: + - export GRADLE_USER_HOME=`pwd`/.gradle + - rm -f .gradle/caches/modules-2/modules-2.lock + - rm -fr .gradle/caches/*/plugin-resolution/ + +cache: + paths: + - .gradle/wrapper + - .gradle/caches + - build + +build: + stage: build + script: + - gradle shadowJar + except: + - tags + +publish: + stage: publish + script: + - gradle publish + diff --git a/README.md b/README.md index 877eb26..951962a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -# Botendo +# Botendo version 5 + +"5th times the charm" ~ me + A Discord music bot, written in Kotlin using the kord library. +
+ 100% Selfmade + 0% optimized + fuck it, ship it +
+ +## Contributors + +
+ limited_dev: Owner & Developer +
+ +## Commands + +- info -- Show basic infos about the bot +- play -- Play a song +- stop -- Stop playing a song and leave the vc + +## How to self-host + +1. Download the latest release from the Package Registry ("Packages and registries" > "Package Registry") + 1. It should be called something like this: "Botendo-X.X.X-xxxxxxxx-prod.jar" (replace "X.X.X" with the latest + version and xxxxxxxx" with the commit its based on.) + 2. If you want to run an early version, which may be (very) unsable, you can run a development version. Just use an + entry ending in "-dev.jar" +2. Place it anywhere you want. +3. Run the following command: + > java -jar Botendo-X.X.X-xxxxxxxx-prod.jar +4. The bot should start and create a config file named "credentials.nils" +5. Open it and put in your credentials. + 1. token: your Discord bot token + 2. lavaip: the IP of your LavaLink instance e.g.: ("ws://192.168.178.1:2333") + 3. lavapw: your password for the LavaLink instance +6. Rerun the command + > java -jar Botendo-X.X.X-xxxxxxxx-prod.jar +7. The bot should now be up and running. + +## How to set up workspace + +Install IntellJ and import the project from git. +Done. diff --git a/build.gradle.kts b/build.gradle.kts index 29f7dea..b0f98f5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,6 +34,7 @@ group = "de.limited_dev.botendo" version = System.getenv("CI_COMMIT_TAG")?.let { "$it-${System.getenv("CI_COMMIT_SHORT_SHA")}-prod" } ?: System.getenv("CI_COMMIT_SHORT_SHA")?.let { "$it-dev" } ?: "DevelopmentBuild" + val kordver = "0.8.1" val lavaver = "3.8.0" val coroutinesver = "1.1.0" diff --git a/src/main/kotlin/de/limited_dev/botendo/Bot.kt b/src/main/kotlin/de/limited_dev/botendo/Bot.kt index 8e02011..b33179f 100644 --- a/src/main/kotlin/de/limited_dev/botendo/Bot.kt +++ b/src/main/kotlin/de/limited_dev/botendo/Bot.kt @@ -22,9 +22,9 @@ package de.limited_dev.botendo import de.limited_dev.botendo.commands.slash.component.SlashCommandManager import de.limited_dev.botendo.commands.slash.component.options.CommandOption import de.limited_dev.botendo.commands.slash.component.options.OptionType +import de.limited_dev.botendo.util.CredentialManager import de.limited_dev.botendo.util.Logger import de.limited_dev.botendo.util.MessageUtil -import de.limited_dev.botendo.util.TokenManager import dev.kord.core.Kord import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent import dev.kord.core.on @@ -44,10 +44,10 @@ object Bot { Logger.out("Starting Bot...") //Load config - TokenManager.load() + CredentialManager.load() //Don't run the bot when there is no bot token in config - if (TokenManager.token == "empty") { + if (CredentialManager.token == "empty") { Logger.out("The config does not contain a bot token") return } @@ -56,13 +56,13 @@ object Bot { SlashCommandManager.register() //Add Bot token to kord - kord = Kord(TokenManager.token) + kord = Kord(CredentialManager.token) //Register Command Listener kord.on { val response = interaction.deferPublicResponse() val command = interaction.command - Logger.out("Command /${command.rootName}") + Logger.out("Command /${command.rootName} with ${command.options.size} Option${if (command.options.size == 1) "" else "s"}") for (c in SlashCommandManager.commands) { if (c.name != command.rootName) continue @@ -128,10 +128,8 @@ object Bot { //Add LavaLink lava = kord.lavakord() - //Add the LavaLink node - lava.addNode("ws://192.168.178.2:2333", "youshallnotpass") - - + //Add the LavaLink node from config + lava.addNode(CredentialManager.linkip, CredentialManager.linkpw) Logger.out("Logging in") diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt index dcc3a67..a69f7e1 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/SlashCommandManager.kt @@ -19,8 +19,7 @@ package de.limited_dev.botendo.commands.slash.component -import de.limited_dev.botendo.commands.slash.music.PlayCommand -import de.limited_dev.botendo.commands.slash.music.StopCommand +import de.limited_dev.botendo.commands.slash.music.* import de.limited_dev.botendo.commands.slash.util.InfoCommand object SlashCommandManager { @@ -31,5 +30,8 @@ object SlashCommandManager { commands.add(InfoCommand()) commands.add(PlayCommand()) commands.add(StopCommand()) + commands.add(NowPlayingCommand()) + commands.add(QueueCommand()) + commands.add(SkipCommand()) } } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt index 9d816ba..abeef23 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/component/options/CommandOption.kt @@ -19,5 +19,4 @@ package de.limited_dev.botendo.commands.slash.component.options -data class CommandOption(val name: String, val description: String, val required: Boolean, val type: OptionType) { -} +data class CommandOption(val name: String, val description: String, val required: Boolean, val type: OptionType) diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/NowPlayingCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/NowPlayingCommand.kt new file mode 100644 index 0000000..81928e1 --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/NowPlayingCommand.kt @@ -0,0 +1,73 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.music + +import de.limited_dev.botendo.Bot +import de.limited_dev.botendo.commands.slash.component.SlashCommand +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.util.MessageUtil +import de.limited_dev.botendo.util.TimeUtil +import de.limited_dev.botendo.util.UrlUtil +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction +import dev.schlaubi.lavakord.audio.Link + +class NowPlayingCommand : SlashCommand("nowplaying", "Show what's currently playing", null) { + override suspend fun onSlashCommand( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + args: Map + ) { + val guildId = interaction.guildId + val link = Bot.lava.getLink(guildId.toString()) + val player = link.player + if (link.state != Link.State.CONNECTED) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not connected", + "I'm not in a VC and therefore, I am not playing anything." + ) + return + } + val track = player.playingTrack + if (track == null) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not Playing", + "I'm not playing anything currently" + ) + return + } + MessageUtil.sendEmbedForInteractionWithImage( + interaction, + response, + "Currently playing", + "**${track.title}**\n*Now Playing*\nby ${track.author} :: ${ + TimeUtil.getTimeFormatedRaw( + track.length.inWholeMilliseconds + ) + }\n" + + ">>>${track.uri}", + "https://img.youtube.com/vi/" + UrlUtil.getYtThumbnailUrl(track.uri!!) + "/maxresdefault.jpg" + ) + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/PlayCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/PlayCommand.kt index bb8a009..7338ae0 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/PlayCommand.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/PlayCommand.kt @@ -19,10 +19,11 @@ package de.limited_dev.botendo.commands.slash.music -import de.limited_dev.botendo.Bot +import de.limited_dev.botendo.Bot.lava import de.limited_dev.botendo.commands.slash.component.SlashCommand import de.limited_dev.botendo.commands.slash.component.options.CommandOption import de.limited_dev.botendo.commands.slash.component.options.OptionType +import de.limited_dev.botendo.commands.slash.music.component.MusicManager import de.limited_dev.botendo.util.MessageUtil import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction @@ -39,7 +40,7 @@ class PlayCommand : SlashCommand( args: Map ) { val guildId = interaction.guildId - val link = Bot.lava.getLink(guildId.toString()) + val link = lava.getLink(guildId.toString()) val voiceState = interaction.user.getVoiceStateOrNull() if (voiceState == null) { @@ -56,6 +57,14 @@ class PlayCommand : SlashCommand( if (link.state != Link.State.CONNECTED) { link.connectAudio(channelId!!.value) + } else if (link.state == Link.State.CONNECTED && link.lastChannelId != channelId!!.value) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "You are not in my VC", + "We are not in the same VC and therefore, you cannot play any music" + ) + return } val query = args[this.options!![0]] @@ -64,5 +73,7 @@ class PlayCommand : SlashCommand( } else { "ytsearch:$query" } + + MusicManager.addToQueue(interaction, response, link, search) } } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/QueueCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/QueueCommand.kt new file mode 100644 index 0000000..56cb05f --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/QueueCommand.kt @@ -0,0 +1,71 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.music + +import de.limited_dev.botendo.Bot +import de.limited_dev.botendo.commands.slash.component.SlashCommand +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.commands.slash.music.component.MusicManager +import de.limited_dev.botendo.util.MessageUtil +import de.limited_dev.botendo.util.TimeUtil +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction +import dev.schlaubi.lavakord.audio.Link + +class QueueCommand : SlashCommand("queue", "Show whats up next", null) { + override suspend fun onSlashCommand( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + args: Map + ) { + val guildId = interaction.guildId + val link = Bot.lava.getLink(guildId.toString()) + val player = link.player + if (link.state != Link.State.CONNECTED) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not connected", + "I'm not in a VC and therefore, I am not playing anything." + ) + return + } + val track = player.playingTrack + if (track == null) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Queue empty", + "I'm not playing anything currently" + ) + return + } + val gts = MusicManager.getGuildTrackScheduler(interaction.guild.asGuild(), player) + val q = gts.getQueue() + var desc = + """${"**" + track.title + " - " + TimeUtil.getTimeFormatedShortend(track.length.inWholeMilliseconds)} (${track.author})**""" + "\n" + for ((i, tr) in q.withIndex()) { + if (i >= 14) + continue + desc += tr.info.title + " - " + TimeUtil.getTimeFormatedShortend(tr.info.length) + " (" + tr.info.author + ")\n" + } + MessageUtil.sendEmbedForInteraction(interaction, response, "Queue", desc) + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/SkipCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/SkipCommand.kt new file mode 100644 index 0000000..48ef34f --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/SkipCommand.kt @@ -0,0 +1,97 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.music + +import de.limited_dev.botendo.Bot +import de.limited_dev.botendo.commands.slash.component.SlashCommand +import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.commands.slash.music.component.MusicManager +import de.limited_dev.botendo.util.MessageUtil +import de.limited_dev.botendo.util.TimeUtil +import de.limited_dev.botendo.util.UrlUtil +import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction +import dev.schlaubi.lavakord.audio.Link + +class SkipCommand : SlashCommand("skip", "Skip to the next song in queue", null) { + override suspend fun onSlashCommand( + interaction: GuildChatInputCommandInteraction, + response: DeferredPublicMessageInteractionResponseBehavior, + args: Map + ) { + val guildId = interaction.guildId + val link = Bot.lava.getLink(guildId.toString()) + val player = link.player + val voiceState = interaction.user.getVoiceStateOrNull() + if (voiceState == null) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "You are not connected to a VC", + "Please connect to a VC" + ) + return + } + + val channelId = voiceState.channelId + if (link.state != Link.State.CONNECTED) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not connected", + "I'm not in a VC and therefore, I am not playing anything." + ) + return + } else if (link.state == Link.State.CONNECTED && link.lastChannelId != channelId!!.value) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "You are not in my VC", + "We are not in the same VC and therefore, you cannot control the music" + ) + return + } + var track = player.playingTrack + if (track == null) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not playing", + "I'm not playing anything currently" + ) + return + } + val gts = MusicManager.getGuildTrackScheduler(interaction.guild.asGuild(), player) + track = gts.getHead().toTrack() + gts.playNext() + MessageUtil.sendEmbedForInteractionWithImage( + interaction, + response, + "Skipped song; now playing", + "**${track.title}**\n*Now Playing*\nby ${track.author} :: ${ + TimeUtil.getTimeFormatedRaw( + track.length.inWholeMilliseconds + ) + }\n" + + ">>>${track.uri}", + "https://img.youtube.com/vi/" + UrlUtil.getYtThumbnailUrl(track.uri!!) + "/maxresdefault.jpg" + ) + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/StopCommand.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/StopCommand.kt index 8bc0933..785a12f 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/StopCommand.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/StopCommand.kt @@ -22,9 +22,11 @@ package de.limited_dev.botendo.commands.slash.music import de.limited_dev.botendo.Bot import de.limited_dev.botendo.commands.slash.component.SlashCommand import de.limited_dev.botendo.commands.slash.component.options.CommandOption +import de.limited_dev.botendo.commands.slash.music.component.MusicManager import de.limited_dev.botendo.util.MessageUtil import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction +import dev.schlaubi.lavakord.audio.Link class StopCommand : SlashCommand("stop", "Stop playing and start leavin'", null) { override suspend fun onSlashCommand( @@ -35,8 +37,39 @@ class StopCommand : SlashCommand("stop", "Stop playing and start leavin'", null) val guildId = interaction.guildId val link = Bot.lava.getLink(guildId.toString()) val player = link.player + + val voiceState = interaction.user.getVoiceStateOrNull() + if (voiceState == null) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "You are not connected to a VC", + "Please connect to my VC" + ) + return + } + + val channelId = voiceState.channelId + if (link.state != Link.State.CONNECTED) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "Not connected", + "I'm not in a VC and therefore, I am not playing anything." + ) + return + } else if (link.state == Link.State.CONNECTED && link.lastChannelId != channelId!!.value) { + MessageUtil.sendEmbedForInteraction( + interaction, + response, + "You are not in my VC", + "We are not in the same VC" + ) + return + } player.stopTrack() link.destroy() + MusicManager.getGuildTrackScheduler(interaction.getGuild(), player).clear() MessageUtil.sendEmbedForInteraction(interaction, response, "I stopped and left", "just like your girlfriend") } } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildTrackScheduler.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildTrackScheduler.kt new file mode 100644 index 0000000..c9ca8e9 --- /dev/null +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildTrackScheduler.kt @@ -0,0 +1,112 @@ +/* + * Botendo + * Copyright (C) 2023 limited_dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package de.limited_dev.botendo.commands.slash.music.component + +import de.limited_dev.botendo.util.Logger +import dev.schlaubi.lavakord.audio.TrackEndEvent +import dev.schlaubi.lavakord.audio.TrackExceptionEvent +import dev.schlaubi.lavakord.audio.TrackStuckEvent +import dev.schlaubi.lavakord.audio.on +import dev.schlaubi.lavakord.audio.player.Player +import dev.schlaubi.lavakord.rest.TrackResponse +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue + +class GuildTrackScheduler(val pl: Player) { + + private var queue: BlockingQueue = LinkedBlockingQueue() + var repeating = false + private var hasRegisteredEvents = false + + ///Add a track to queue and start playing, if there is no song currently playing + suspend fun queue(track: TrackResponse.PartialTrack) { + if (this.pl.playingTrack == null) { + play(track) + } else { + queue.offer(track) + } + } + + suspend fun playNext() { + if (!queue.isEmpty()) + this.pl.playTrack(queue.poll()) + else + this.pl.stopTrack() + } + + private suspend fun play(tr: TrackResponse.PartialTrack) { + this.pl.playTrack(tr) + } + + fun addEvents() { + if (hasRegisteredEvents) + return + hasRegisteredEvents = true + + Logger.out("Adding track events to GuildTrackScheduler...") + + pl.on { + onTrackEnd(this) + } + + pl.on { + onTrackStuck(this) + } + + pl.on { + onTrackExc(this) + } + } + + private suspend fun onTrackEnd(e: TrackEndEvent) { + if (e.reason.mayStartNext) { + if (repeating) { + this.pl.playTrack(e.track.copy()) + return + } + Logger.out("Track has ended; Playing next...") + playNext() + } + } + + private suspend fun onTrackStuck(e: TrackStuckEvent) { + Logger.out("Track is stuck, retrying...") + this.pl.playTrack(e.track.copy()) + } + + private suspend fun onTrackExc(e: TrackExceptionEvent) { + Logger.out("Track had an exception, retrying...") + this.pl.playTrack(e.track.copy()) + } + + fun clear() { + Logger.out("Clearing queue...") + repeating = false + queue.clear() + } + + fun getQueue(): List { + return queue.toList() + } + + fun getHead(): TrackResponse.PartialTrack { + return queue.first() + } +} diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/MusicManager.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/MusicManager.kt index 25d0efa..8fc4867 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/MusicManager.kt +++ b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/MusicManager.kt @@ -21,21 +21,26 @@ package de.limited_dev.botendo.commands.slash.music.component import de.limited_dev.botendo.util.MessageUtil import de.limited_dev.botendo.util.TimeUtil +import de.limited_dev.botendo.util.UrlUtil +import dev.kord.common.entity.Snowflake import dev.kord.core.behavior.interaction.response.DeferredPublicMessageInteractionResponseBehavior +import dev.kord.core.entity.Guild import dev.kord.core.entity.interaction.GuildChatInputCommandInteraction import dev.schlaubi.lavakord.audio.Link -import dev.schlaubi.lavakord.audio.TrackEndEvent -import dev.schlaubi.lavakord.audio.on +import dev.schlaubi.lavakord.audio.player.Player import dev.schlaubi.lavakord.rest.TrackResponse import dev.schlaubi.lavakord.rest.loadItem object MusicManager { - private var musicManagerMap: MutableMap? = null + private var musicManagerMap: MutableMap = mutableMapOf() - init { - musicManagerMap = HashMap() + fun getGuildTrackScheduler(guild: Guild, player: Player): GuildTrackScheduler { + return musicManagerMap.computeIfAbsent(guild.id) { + GuildTrackScheduler(player) + } } + suspend fun addToQueue( interaction: GuildChatInputCommandInteraction, response: DeferredPublicMessageInteractionResponseBehavior, @@ -54,60 +59,68 @@ object MusicManager { ) { val player = link.player val item = link.loadItem(search) + val gts = getGuildTrackScheduler(interaction.getGuild(), player) - player.on { - - } + gts.addEvents() when (item.loadType) { TrackResponse.LoadType.TRACK_LOADED -> { - player.playTrack(item.track) + gts.queue(item.track) if (!silent) - MessageUtil.sendEmbedForInteraction( - interaction, response, - "Playing track from link", + MessageUtil.sendEmbedForInteractionWithImage( + interaction, + response, + "Queuing track from link", "**${item.track.info.title}**\n*Queue*\nby ${item.track.info.author} :: ${ TimeUtil.getTimeFormatedRaw( item.track.info.length ) - }" + }\n" + + ">>>${item.track.info.uri}", + "https://img.youtube.com/vi/" + UrlUtil.getYtThumbnailUrl(item.track.info.uri) + "/maxresdefault.jpg" ) } TrackResponse.LoadType.PLAYLIST_LOADED -> { - val l = item.tracks.asReversed() - for (p in l) { - player.playTrack(p) + val l = item.tracks.reversed() + for (partialTrack in l) { + gts.queue(partialTrack) } if (!silent) - MessageUtil.sendEmbedForInteraction( - interaction, response, - "Playing playlist from link", + MessageUtil.sendEmbedForInteractionWithImage( + interaction, + response, + "Queuing playlist from link", "**${item.tracks.first().info.title}**\n*${item.playlistInfo.name}*\nby ${item.tracks.first().info.author} :: ${ TimeUtil.getTimeFormatedRaw( item.tracks.first().info.length ) - }" + }\n" + + ">>>${item.tracks.first().info.uri}", + "https://img.youtube.com/vi/" + UrlUtil.getYtThumbnailUrl(item.tracks.first().info.uri) + "/maxresdefault.jpg" ) } TrackResponse.LoadType.SEARCH_RESULT -> { - player.playTrack(item.tracks.first()) + gts.queue(item.tracks.first()) if (!silent) - MessageUtil.sendEmbedForInteraction( - interaction, response, - "Playing from query", + MessageUtil.sendEmbedForInteractionWithImage( + interaction, + response, + "Queuing track from query", "**${item.tracks.first().info.title}**\n*Queue*\nby ${item.tracks.first().info.author} :: ${ TimeUtil.getTimeFormatedRaw( item.tracks.first().info.length ) - }" + }\n" + + ">>>${item.tracks.first().info.uri}", + "https://img.youtube.com/vi/" + UrlUtil.getYtThumbnailUrl(item.tracks.first().info.uri) + "/maxresdefault.jpg" ) } TrackResponse.LoadType.NO_MATCHES -> { if (!silent) - MessageUtil.sendEmbedForInteraction(interaction, response, "Not found", "There were not matches.") + MessageUtil.sendEmbedForInteraction(interaction, response, "Not found", "There were no matches.") } TrackResponse.LoadType.LOAD_FAILED -> { diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/TrackScheduler.kt b/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/TrackScheduler.kt deleted file mode 100644 index 1e07d70..0000000 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/TrackScheduler.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Botendo - * Copyright (C) 2023 limited_dev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -package de.limited_dev.botendo.commands.slash.music.component - -class TrackScheduler { -} diff --git a/src/main/kotlin/de/limited_dev/botendo/util/TokenManager.kt b/src/main/kotlin/de/limited_dev/botendo/util/CredentialManager.kt similarity index 78% rename from src/main/kotlin/de/limited_dev/botendo/util/TokenManager.kt rename to src/main/kotlin/de/limited_dev/botendo/util/CredentialManager.kt index 0763a4a..3ee45c8 100644 --- a/src/main/kotlin/de/limited_dev/botendo/util/TokenManager.kt +++ b/src/main/kotlin/de/limited_dev/botendo/util/CredentialManager.kt @@ -22,11 +22,13 @@ package de.limited_dev.botendo.util import java.io.* import java.util.* -object TokenManager { - private const val filename = "token.nils" +object CredentialManager { + private const val filename = "credentials.nils" var token: String = "empty" + var linkip: String = "empty" + var linkpw: String = "empty" - ///Load the bot token, generate a config if there is none + ///Load the needed credentials, generate a config if there is none fun load() { val configFile = File(filename) if (!configFile.exists()) { @@ -38,6 +40,8 @@ object TokenManager { val prop = Properties() prop.load(input) token = if (prop.getProperty("token").equals("empty")) "empty" else prop.getProperty("token") + linkip = if (prop.getProperty("lavaip").equals("empty")) "empty" else prop.getProperty("lavaip") + linkpw = if (prop.getProperty("lavapw").equals("empty")) "empty" else prop.getProperty("lavapw") input.close() } catch (e: IOException) { e.printStackTrace() @@ -58,6 +62,8 @@ object TokenManager { val output: OutputStream = FileOutputStream(filename) val prop = Properties() prop.setProperty("token", "empty") + prop.setProperty("lavaip", "empty") + prop.setProperty("lavapw", "empty") prop.store(output, null) output.close() } catch (e: IOException) { diff --git a/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt b/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt index 92313f6..d2a00e5 100644 --- a/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt +++ b/src/main/kotlin/de/limited_dev/botendo/util/MessageUtil.kt @@ -65,7 +65,7 @@ object MessageUtil { } } - fun getEmbed(title: String, description: String, source: String): EmbedBuilder { + private fun getEmbed(title: String, description: String, source: String): EmbedBuilder { val now: LocalDateTime = LocalDateTime.now() val ebb = EmbedBuilder() ebb.title = title @@ -75,7 +75,12 @@ object MessageUtil { return ebb } - fun getEmbedWithImage(title: String, description: String, source: String, thumbnailUrl: String): EmbedBuilder { + private fun getEmbedWithImage( + title: String, + description: String, + source: String, + thumbnailUrl: String + ): EmbedBuilder { val ebb = getEmbed(title, description, source) ebb.thumbnail = EmbedBuilder.Thumbnail() ebb.thumbnail!!.url = thumbnailUrl diff --git a/src/main/kotlin/de/limited_dev/botendo/util/Status.kt b/src/main/kotlin/de/limited_dev/botendo/util/Status.kt index 23203bf..98972b7 100644 --- a/src/main/kotlin/de/limited_dev/botendo/util/Status.kt +++ b/src/main/kotlin/de/limited_dev/botendo/util/Status.kt @@ -22,8 +22,9 @@ package de.limited_dev.botendo.util import de.limited_dev.botendo.Bot object Status { + //TODO: impl. suspend fun updateStatus() { - Bot.kord!!.editPresence { + Bot.kord.editPresence { this.playing("") } } diff --git a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildMusicManager.kt b/src/main/kotlin/de/limited_dev/botendo/util/UrlUtil.kt similarity index 79% rename from src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildMusicManager.kt rename to src/main/kotlin/de/limited_dev/botendo/util/UrlUtil.kt index 24695e5..14e95a6 100644 --- a/src/main/kotlin/de/limited_dev/botendo/commands/slash/music/component/GuildMusicManager.kt +++ b/src/main/kotlin/de/limited_dev/botendo/util/UrlUtil.kt @@ -17,7 +17,10 @@ * */ -package de.limited_dev.botendo.commands.slash.music.component +package de.limited_dev.botendo.util -class GuildMusicManager { +object UrlUtil { + fun getYtThumbnailUrl(uri: String): String { + return uri.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1] + } }