feat: added basic music controls, added music, added gitlab-ci.yml updated README.md

This commit is contained in:
limited_dev 2023-03-28 23:34:10 +02:00
parent a9c4fe5da3
commit 8280620091
18 changed files with 553 additions and 73 deletions

34
.gitlab-ci.yml Normal file
View file

@ -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

View file

@ -1,2 +1,46 @@
# Botendo
# Botendo version 5
"5th times the charm" ~ me
A Discord music bot, written in Kotlin using the kord library.
<div class="aside">
<img src="https://img.shields.io/badge/100%25-Selfmade-success" alt="100% Selfmade"/>
<img src="https://img.shields.io/badge/0%25-optimized-orange" alt="0% optimized"/>
<img src="https://img.shields.io/badge/fuck%20it-ship%20it-orange" alt="fuck it, ship it"/>
</div>
## Contributors
<div class="aside">
<img src="https://img.shields.io/badge/limited__dev-Owner%20%26%20Developer-blue" alt="limited_dev: Owner & Developer"/>
</div>
## 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.

View file

@ -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"

View file

@ -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<GuildChatInputCommandInteractionCreateEvent> {
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")

View file

@ -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())
}
}

View file

@ -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)

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<CommandOption, String>
) {
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"
)
}
}

View file

@ -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<CommandOption, String>
) {
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)
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<CommandOption, String>
) {
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)
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<CommandOption, String>
) {
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"
)
}
}

View file

@ -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")
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
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<TrackResponse.PartialTrack> = 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<TrackEndEvent> {
onTrackEnd(this)
}
pl.on<TrackStuckEvent> {
onTrackStuck(this)
}
pl.on<TrackExceptionEvent> {
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<TrackResponse.PartialTrack> {
return queue.toList()
}
fun getHead(): TrackResponse.PartialTrack {
return queue.first()
}
}

View file

@ -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<Long, GuildMusicManager>? = null
private var musicManagerMap: MutableMap<Snowflake, GuildTrackScheduler> = 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<TrackEndEvent> {
}
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 -> {

View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
package de.limited_dev.botendo.commands.slash.music.component
class TrackScheduler {
}

View file

@ -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) {

View file

@ -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

View file

@ -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("")
}
}

View file

@ -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]
}
}