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

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