diff --git a/build.gradle b/build.gradle index 179a317..9b92b92 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '1.2.71' + id 'org.jetbrains.kotlin.jvm' version '1.3.0' id 'com.github.johnrengelman.shadow' version '4.0.1' } @@ -18,9 +18,10 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' compile 'net.dv8tion:JDA:3.7.1_421' compile "ch.qos.logback:logback-classic:1.2.1" +/* compile 'io.javalin:javalin:2.3.0' +*/ compile 'com.github.salomonbrys.kotson:kotson:2.5.0' - compile 'com.jagrosh:jda-utilities:2.1.4' compile 'com.sedmelluq:lavaplayer:1.3.7' compile 'com.sedmelluq:jda-nas:1.0.6' compile 'khttp:khttp:0.1.0' diff --git a/src/main/kotlin/nl/voidcorp/dbot/Events.kt b/src/main/kotlin/nl/voidcorp/dbot/Events.kt index 044f074..9fa112f 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/Events.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/Events.kt @@ -7,16 +7,18 @@ import net.dv8tion.jda.core.hooks.ListenerAdapter object Events : ListenerAdapter() { override fun onMessageReceived(event: MessageReceivedEvent) { - if (event.message.contentStripped.toLowerCase().contains("fraud") or event.message.contentRaw.contains(Regex("`(\\w+|\\s+|\\W)+`"))) { + if ((event.message.contentStripped.toLowerCase().contains("fraud") or event.message.contentRaw.contains(Regex("`(\\w+|\\s+|\\W)+`"))) + and (event.author != event.jda.selfUser) + ) { val e = event.message.guild.getEmotesByName("fr00d", true).firstOrNull() if (e != null) event.message.addReaction(e).queue() } - if (event.message.channel.idLong == 499628388659625995) { + /*if (event.message.channel.idLong == 499628388659625995) { if (event.message.mentionedMembers.contains(event.guild.getMember(event.jda.selfUser)) and (event.message.author != event.jda.selfUser)) { if (event.message.contentStripped.toLowerCase().contains("hello")) { - val i = random.nextInt(10) + val i = Random.nextInt(10) if (i > 8) { event.channel.sendMessage("Can you speak up or I'll throw you a microphone").queue() } else { @@ -27,7 +29,7 @@ object Events : ListenerAdapter() { } } - } + }*/ if (event.author.idLong == 168743656738521088 && event.message.mentionedMembers.contains(event.guild.getMember(event.jda.selfUser))) { /*if (event.message.contentStripped.contains("hide")) { if (event.message.mentionedChannels.isNotEmpty()) { diff --git a/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt b/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt index bed1d34..35fc883 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/UnityBot.kt @@ -4,21 +4,12 @@ import com.sedmelluq.discord.lavaplayer.jdaudp.NativeAudioSendFactory import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager -import io.javalin.Javalin -import io.javalin.json.FromJsonMapper -import io.javalin.json.JavalinJson -import io.javalin.json.ToJsonMapper -import net.dv8tion.jda.core.EmbedBuilder import net.dv8tion.jda.core.JDA import net.dv8tion.jda.core.JDABuilder -import net.dv8tion.jda.webhook.WebhookClient -import net.dv8tion.jda.webhook.WebhookClientBuilder import nl.voidcorp.dbot.commands.* import nl.voidcorp.dbot.music.initMusic -import nl.voidcorp.dbot.storage.GitlabWebhook import org.slf4j.LoggerFactory -import java.io.File -import java.io.FileReader +import kotlin.concurrent.thread val playerManager = DefaultAudioPlayerManager() @@ -28,12 +19,35 @@ val log = LoggerFactory.getLogger("UnityBot") val commands = mutableListOf() -@Suppress("JoinDeclarationAndAssignment") fun main(args: Array) { playerManager.registerSourceManager(YoutubeAudioSourceManager(true)) playerManager.registerSourceManager(SoundCloudAudioSourceManager(true)) + + commands.addAll(helloCommand, pingCommand, helpCommand) + + + initMusic() + initFun() + initAdmin() + + + val custom = UnityCommandClient("!") + custom.addCommands(commands) + custom.version = "1.4" + + + bot = JDABuilder(args[0]).addEventListener(custom).addEventListener(nl.voidcorp.dbot.Events) + .setAudioSendFactory(NativeAudioSendFactory()).build() + + Runtime.getRuntime().addShutdownHook(thread(start = false) { + custom.shutdown() + }) +} + + +/*fun tokenStuff(){ JavalinJson.apply { toJsonMapper = object : ToJsonMapper { override fun map(obj: Any): String = gson.toJson(obj) @@ -85,27 +99,7 @@ fun main(args: Array) { } - - commands.addAll(helloCommand, pingCommand, helpCommand) - - - - - - - - initMusic() - initFun() - - - val custom = UnityCommandClient("!") - custom.addCommands(commands) - custom.version = "1.4" - - - bot = JDABuilder(args[0]).addEventListener(custom).addEventListener(nl.voidcorp.dbot.Events).setAudioSendFactory(NativeAudioSendFactory()).build() - -} +}*/ lateinit var bot: JDA diff --git a/src/main/kotlin/nl/voidcorp/dbot/Util.kt b/src/main/kotlin/nl/voidcorp/dbot/Util.kt index 7b62419..d49228c 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/Util.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/Util.kt @@ -2,21 +2,23 @@ package nl.voidcorp.dbot import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.jagrosh.jdautilities.command.Command +import nl.voidcorp.dbot.commands.UnityCommand +import java.io.File import java.util.* +import kotlin.random.Random + val gson: Gson = GsonBuilder().setPrettyPrinting().create() -val random = Random() fun MutableList.addAll(vararg e: E) = this.addAll(listOf(*e)) -fun List.random(): E = this[random.nextInt(this.size)] +fun List.random(): E = this[Random.nextInt(this.size)] -fun List.catMap(): Map> { - val m = mutableMapOf>() +fun List.catMap(): Map> { + val m = mutableMapOf>() for (c in this) { - val cname = if (c.category == null) "Unknown" else c.category.name + val cname = c.category.name if (!m.containsKey(cname)) { m[cname] = mutableListOf() } @@ -26,4 +28,23 @@ fun List.catMap(): Map> { return m } -fun String.emptyOr(other: String): String = if (this.isEmpty()) other else this \ No newline at end of file +fun String.emptyOr(other: String): String = if (this.isEmpty()) other else this + +fun restartApplication() { + val javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java" + val currentJar = File(Events::class.java.protectionDomain.codeSource.location.toURI()) + + /* is it a jar file? */ + if (!currentJar.name.endsWith(".jar")) + return + + /* Build command: java -jar application.jar */ + val command = ArrayList() + command.add(javaBin) + command.add("-jar") + command.add(currentJar.path) + + val builder = ProcessBuilder(command) + builder.start() + System.exit(0) +} \ No newline at end of file diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/AdminCommands.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/AdminCommands.kt new file mode 100644 index 0000000..f159637 --- /dev/null +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/AdminCommands.kt @@ -0,0 +1,36 @@ +package nl.voidcorp.dbot.commands + +import nl.voidcorp.dbot.commands +import nl.voidcorp.dbot.storage.settings + +fun initAdmin() { + commands += SetPrefix + commands += ListPrefixes + commands += RemovePrefix +} + +object SetPrefix : UnityCommand( + "setprefix", + "Sets the prefix for this command", + AdminCategory, + howTo = "!setprefix prefix", + exec = { ce -> + ce.guild.settings().prefixes += ce.args + ce.reply("This servers prefix is now set to ${ce.args}, but you can still use ${ce.selfMember.asMention} to reset it!") + }) + +object ListPrefixes : UnityCommand("listprefixes", "Lists all server prefixes", AdminCategory, exec = { ce -> + ce.reply("This servers prefixes are: ${ce.guild.settings().prefixes.ifEmpty { listOf("!") }.joinToString { "`$it`" }}") +}) + +object RemovePrefix : UnityCommand("removeprefix", "Removes a prefix", AdminCategory, exec = { ce -> + val res = ce.guild.settings().prefixes.removeIf { it == ce.args } + if (res) { + ce.reply("Remove the prefix `${ce.args}`") + } else { + ce.reply("`${ce.args}` is not a known prefix?") + } +}) + + +object UnknownCommandError : Exception("yeet!") \ No newline at end of file diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/Categories.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/Categories.kt index 459dedd..e3a4138 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/commands/Categories.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/Categories.kt @@ -1,15 +1,16 @@ package nl.voidcorp.dbot.commands -import com.jagrosh.jdautilities.command.Command -import com.jagrosh.jdautilities.command.CommandEvent +import net.dv8tion.jda.core.Permission import net.dv8tion.jda.core.entities.ChannelType import net.dv8tion.jda.core.entities.TextChannel -open class UnityCategory(name: String, - val channels: List = listOf("bot"), val roles: List = listOf(), - val errorMessage: (TextChannel) -> String = { "This command can only be used in ${it.asMention}." }, - val check: (CommandEvent) -> Boolean = { true }) : Command.Category(name) { - override fun test(ce: CommandEvent): Boolean { +open class UnityCategory( + val name: String, + val channels: List = listOf("bot"), val roles: List = listOf(), + val errorMessage: (TextChannel) -> String = { "This command can only be used in ${it.asMention}." }, + val check: (UnityCommandEvent) -> Boolean = { true } +) { + open fun test(ce: UnityCommandEvent): Boolean { /*if (ce.member.hasPermission(Permission.ADMINISTRATOR)) return true*/ if (ce.member.roles.firstOrNull { it.name.equals("admin", true) } != null) return true if (channels.all { ce.guild.getTextChannelsByName(it, true).firstOrNull() == null }) return true @@ -81,6 +82,30 @@ object GeneralCategory : UnityCategory("general") object MusicCategory : UnityCategory("Music Commands", listOf("bot", "music", "music-bot"), errorMessage = { "Music commands can only be used in ${it.asMention}!" }) object MusicCategoryPrivate : UnityCategory("Music Commands", listOf("bot", "music", "music-bot"), listOf("admin"), { "Music commands can only be used in ${it.asMention}!" }) +object AdminCategory : UnityCategory("Admin Stuff") { + override fun test(ce: UnityCommandEvent): Boolean { + val res = when { + ce.member.hasPermission(Permission.ADMINISTRATOR) -> true + ce.member.roles.firstOrNull { it.name.equals("admin", true) } != null -> true + else -> false + } + if (!res) { + ce.reply("You are not an admin!") + } + return res + } +} + +object HiddenCategory : UnityCategory("hidden") { + override fun test(ce: UnityCommandEvent): Boolean { + if (ce.member.user.id != "168743656738521088") { + throw UnknownCommandError + } else { + return true + } + } +} + /* val GeneralCategory = Command.Category("General commands") { ce -> if (ce.member.roles.firstOrNull { it.name.equals("admin", true) } != null) return@Category true diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt index 01f5caf..492afe7 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/Commands.kt @@ -9,10 +9,16 @@ import nl.voidcorp.dbot.emptyOr import nl.voidcorp.dbot.random import java.time.LocalDateTime import java.time.temporal.ChronoUnit +import kotlin.random.Random val helloCommand = UnityCommand("hello", "Say hello to Andy!", aliases = *arrayOf()) { - it.reply(MessageBuilder("Hello, ").append(it.author).append("!").build()) + val i = Random.nextInt(10) + if (i > 8) { + it.reply("Can you speak up or I'll throw you a microphone") + } else { + it.reply(MessageBuilder("Hello, ").append(it.author).append("!").build()) + } } @@ -22,51 +28,68 @@ val pingCommand = UnityCommand("ping", help = "Check the bot's ping", aliases = m.editMessage("Ping: " + ping + "ms | Websocket: " + event.jda.ping + "ms").queue() } } -val replies = listOf("Hello %%", "Why hello there %%!", "Hello there %%", "General %%. You are a bold one", "A wild %% appeared!") +val replies = + listOf("Hello %%", "Why hello there %%!", "Hello there %%", "General %%. You are a bold one", "A wild %% appeared!") -val helpCommand = UnityCommand("help", "Guess what?"/*, category = UnityCategory("Yeet", channels = listOf())*/) { event -> - val greeting = replies.random().replace("%%", event.member.effectiveName) - val eb = EmbedBuilder() - when { - event.args.isEmpty() -> { - eb.setTitle(greeting) - .appendDescription("My name is ${event.selfMember.effectiveName}, and I am definitely the best Discord bot around!\n\nUse `!help command` for a chance that I have more info about a command!") - .setColor(event.selfMember.color).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl).setTimestamp(LocalDateTime.now()) - val m = commands.catMap() - for (e in m.entries) { - val f = MessageEmbed.Field(e.key, e.value.asSequence().filter { it.name != "help" }.joinToString(separator = "\n", postfix = "\n") { "`!${it.name}`" }, true) - eb.addField(f) +val helpCommand = + UnityCommand("help", "Guess what?"/*, category = UnityCategory("Yeet", channels = listOf())*/) { event -> + val greeting = replies.random().replace("%%", event.member.effectiveName) + val eb = EmbedBuilder() + when { + event.args.isEmpty() -> { + val prefix = GSM.getSettings(event.guild).primaryPrefix + + eb.setTitle(greeting) + .appendDescription("My name is ${event.selfMember.effectiveName}, and I am definitely the best Discord bot around!\n\nUse `${prefix}help command` for a chance that I have more info about a command!") + .setColor(event.selfMember.color) + .setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl) + .setTimestamp(LocalDateTime.now()) + val m = commands.catMap().filter { it.key != "hidden" } + for (e in m.entries) { + val f = MessageEmbed.Field( + e.key, + e.value.asSequence().filter { it.name != "help" }.joinToString( + separator = "\n", + postfix = "\n" + ) { "`$prefix${it.name}`" }, + true + ) + eb.addField(f) + } + eb.setThumbnail(event.selfUser.effectiveAvatarUrl) } - eb.setThumbnail(event.selfUser.effectiveAvatarUrl) - } - event.args == "help" -> { - eb.setTitle("Ha Ha") + event.args == "help" -> { + eb.setTitle("Ha Ha") .appendDescription("Ha ha, very funny ${event.member.effectiveName}") - .setColor(event.selfMember.color).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl).setTimestamp(LocalDateTime.now()) - } - commands.any { it.name == event.args } -> { - val cmd = commands.first { it.name == event.args } - eb.setTitle("**!${cmd.name}**") + .setColor(event.selfMember.color) + .setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl) + .setTimestamp(LocalDateTime.now()) + } + commands.any { it.name == event.args } -> { + val cmd = commands.first { it.name == event.args } + eb.setTitle("**!${cmd.name}**") .appendDescription(cmd.help) - .setColor(event.selfMember.color).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl).setTimestamp(LocalDateTime.now()) - if (cmd.aliases.isNotEmpty()) eb.addField("Aliases", cmd.aliases.joinToString { "`$it`" }, true) - eb.addField("Usage", "`!${cmd.howTo.emptyOr(cmd.name)}`", true) - val cat = cmd.category - if (cat is UnityCategory) { + .setColor(event.selfMember.color) + .setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl) + .setTimestamp(LocalDateTime.now()) + if (cmd.aliases.isNotEmpty()) eb.addField("Aliases", cmd.aliases.joinToString { "`$it`" }, true) + eb.addField("Usage", "`!${cmd.howTo.emptyOr(cmd.name)}`", true) + val cat = cmd.category if (cat.roles.isNotEmpty()) eb.addField("Roles", cat.roles.joinToString { "`$it`" }, true) + + } + + else -> { + eb.setTitle(greeting) + .appendDescription("I honestly have no idea what the command `${event.args}` does...") + .setColor(event.selfMember.color) + .setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl) + .setTimestamp(LocalDateTime.now()) } } - - else -> { - eb.setTitle(greeting) - .appendDescription("I honestly have no idea what the command `${event.args}` does...") - .setColor(event.selfMember.color).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl).setTimestamp(LocalDateTime.now()) - } + event.reply(eb.build()) } - event.reply(eb.build()) - -} diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/FunCommands.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/FunCommands.kt index 31d0b5b..94777ab 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/commands/FunCommands.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/FunCommands.kt @@ -5,9 +5,9 @@ import net.dv8tion.jda.core.EmbedBuilder import nl.voidcorp.dbot.addAll import nl.voidcorp.dbot.commands import nl.voidcorp.dbot.gson -import nl.voidcorp.dbot.random import nl.voidcorp.dbot.storage.XKCD import java.time.LocalDateTime +import kotlin.random.Random val xkcd = UnityCommand("xkcd", help = "Fetch an xkcd comic, either a random one, or a specific one if specified", category = FunCategory, arguments = "the comic number (optional)") { event -> @@ -17,7 +17,7 @@ val xkcd = UnityCommand("xkcd", help = "Fetch an xkcd comic, either a random one .setColor(event.selfMember.color) if (event.args.isEmpty()) { val latest = gson.fromJson(khttp.get(burl + eurl.substring(1)).text) - val num = random.nextInt(latest.num - 1) - 1 + val num = Random.nextInt(latest.num - 1) - 1 val rnd = gson.fromJson(khttp.get(burl + num + eurl).text) eb.setTitle(rnd.title, "$burl${rnd.num}") eb.setImage(rnd.img) diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommand.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommand.kt index f021d64..dacd625 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommand.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommand.kt @@ -1,34 +1,73 @@ package nl.voidcorp.dbot.commands -import com.jagrosh.jdautilities.command.Command -import com.jagrosh.jdautilities.command.CommandEvent import nl.voidcorp.dbot.music.TrackScheduler import nl.voidcorp.dbot.music.guildMusicMap import nl.voidcorp.dbot.playerManager - -open class UnityCommand(name: String, help: String = "", - category: UnityCategory = GeneralCategory, - arguments: String = "", vararg aliases: String = arrayOf(), val howTo: String = "", - val exec: (event: CommandEvent) -> Unit) : Command() { - init { - super.name = name - super.help = help - super.category = category - super.arguments = arguments - super.aliases = aliases - } - - - override fun execute(event: CommandEvent) = exec(event) +enum class CooldownType { + USER, + SERVER, + GLOBAL } -class UnityMusicCommand(name: String, help: String = "", - category: UnityCategory = MusicCategory, - arguments: String = "", vararg aliases: String = arrayOf(name.first().toString()), - howTo: String = "", - val mExec: (event: CommandEvent, scheduler: TrackScheduler) -> Unit) : UnityCommand(name, help, category, arguments, *aliases, howTo = howTo, exec = {}) { - override fun execute(event: CommandEvent) { +open class UnityCommand( + val name: String, val help: String = "", + val category: UnityCategory = GeneralCategory, + val arguments: String = "", vararg val aliases: String = arrayOf(), val howTo: String = "", + val cooldown: Int = 0, val cooldownType: CooldownType = CooldownType.USER, + val exec: (event: UnityCommandEvent) -> Unit +) /*: Command()*/ { + + + open fun execute(event: UnityCommandEvent) = exec(event) + + fun run(event: UnityCommandEvent) { + + + // category check + if (!category.test(event)) { + return + } + + + /*//cooldown check + if (cooldown > 0) { + val key = getCooldownKey(event) + val remaining = event.client.getRemainingCooldown(key) + if (remaining > 0) { + val error = getCooldownError(event, remaining) + if (error != null) { +*//* + event.reply("There still is a cooldown (`$error`) on this command!") +*//* + return + } + } else + event.client.applyCooldown(key, cooldown) + }*/ + + // run + try { + execute(event) + } catch (t: Throwable) { + + event.client.onException(t) + + } + + /*if (event.client.listener != null) + event.client.listener.onCompletedCommand(event, this)*/ + } +} + +class UnityMusicCommand( + name: String, help: String = "", + category: UnityCategory = MusicCategory, + arguments: String = "", vararg aliases: String = arrayOf(name.first().toString()), + howTo: String = "", + val mExec: (event: UnityCommandEvent, scheduler: TrackScheduler) -> Unit +) : UnityCommand(name, help, category, arguments, *aliases, howTo = howTo, exec = {}) { + override fun execute(event: UnityCommandEvent) { val scheduler = if (guildMusicMap.containsKey(event.guild.idLong)) guildMusicMap[event.guild.idLong]!! else { val channel = event.guild.voiceChannels.firstOrNull { it.members.contains(event.member) } if (channel == null) { diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandClient.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandClient.kt index 68473b4..304a449 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandClient.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandClient.kt @@ -1,35 +1,33 @@ package nl.voidcorp.dbot.commands import com.github.salomonbrys.kotson.fromJson -import com.jagrosh.jdautilities.command.* import net.dv8tion.jda.core.EmbedBuilder import net.dv8tion.jda.core.OnlineStatus import net.dv8tion.jda.core.entities.ChannelType import net.dv8tion.jda.core.entities.Game import net.dv8tion.jda.core.entities.Guild -import net.dv8tion.jda.core.entities.Message import net.dv8tion.jda.core.events.ReadyEvent import net.dv8tion.jda.core.events.message.MessageReceivedEvent import net.dv8tion.jda.core.hooks.ListenerAdapter import nl.voidcorp.dbot.bot import nl.voidcorp.dbot.gson import nl.voidcorp.dbot.log +import nl.voidcorp.dbot.storage.CommandString import nl.voidcorp.dbot.storage.GuildSettings import java.io.File import java.time.LocalDateTime import java.time.OffsetDateTime -import java.util.* import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledThreadPoolExecutor -import java.util.function.Function val gmap = mutableMapOf() -object GSM : GuildSettingsManager { +object GSM { private val f = File("settings.json") - override fun getSettings(it: Guild): GuildSettings { + + fun getSettings(it: Guild): GuildSettings { val res = gmap[it.idLong] return if (res != null) { res @@ -40,48 +38,42 @@ object GSM : GuildSettingsManager { } } - override fun init() { + fun init() { if (f.exists()) { - gmap.putAll(gson.fromJson(f.readText())) + try { + val m = gson.fromJson>(f.readText()) + gmap.putAll(m) + } catch (ex: IllegalStateException) { + } } } - override fun shutdown() { + fun shutdown() { gson.toJson(gmap, f.bufferedWriter()) } } -data class UnityCommandClient(private val prefix: String, - private val commands: MutableList = mutableListOf() -) : CommandClient, ListenerAdapter() { +data class UnityCommandClient( + private val prefix: String, + private val commands: MutableList = mutableListOf() +) : ListenerAdapter() { private val cooldownMap = mutableMapOf() - override fun applyCooldown(name: String, seconds: Int) { + fun applyCooldown(name: String, seconds: Int) { cooldownMap[name] = OffsetDateTime.now().plusSeconds(seconds.toLong()) } - override fun > getSettingsManager(): M? { - return null - } + fun getError(): String = "❌" - override fun getError(): String = "❌" - override fun addAnnotatedModule(module: Any?) { - //NOP - } - - override fun addAnnotatedModule(module: Any?, mapFunction: Function?) { - //NOP - } - - override fun getServerInvite(): String? { - return null + fun getServerInvite(): String { + return "" } private val ses = ScheduledThreadPoolExecutor(8) - override fun getScheduleExecutor(): ScheduledExecutorService = ses + fun getScheduleExecutor(): ScheduledExecutorService = ses - override fun cleanCooldowns() { + fun cleanCooldowns() { for ((k, v) in cooldownMap) { if (v.isBefore(OffsetDateTime.now())) { cooldownMap.remove(k) @@ -89,42 +81,35 @@ data class UnityCommandClient(private val prefix: String, } } - override fun getWarning(): String = "⚠️" + fun getWarning(): String = "⚠️" - override fun addCommand(command: Command) { + fun addCommand(command: UnityCommand) { commands.add(command) } - override fun addCommand(command: Command, index: Int) { + fun addCommand(command: UnityCommand, index: Int) { commands.add(index, command) } - fun addCommands(commands: List) { + fun addCommands(commands: List) { for (c in commands) { addCommand(c) } } - override fun getSettingsFor(guild: Guild): S? = null private fun getSettings(guild: Guild): GuildSettings = GSM.getSettings(guild) - private var cmdListener: CommandListener? = null - override fun setListener(listener: CommandListener) { - cmdListener = listener - } + private val start = OffsetDateTime.now() - override fun getStartTime(): OffsetDateTime = start + fun getStartTime(): OffsetDateTime = start - override fun getListener(): CommandListener? = cmdListener - - override fun getSuccess(): String = "✔️" + fun getSuccess(): String = "✔️" private val usageMap = mutableMapOf() - override fun getCommandUses(command: Command): Int = usageMap[command.name] ?: 0 private operator fun MutableMap.plus(t: T) { val res = this[t] @@ -135,30 +120,25 @@ data class UnityCommandClient(private val prefix: String, } } - override fun getCommandUses(name: String): Int = usageMap[name] ?: 0 + fun getCommandUses(name: String): Int = usageMap[name] ?: 0 - override fun getCooldown(name: String): OffsetDateTime? = cooldownMap[name] + fun getCooldown(name: String): OffsetDateTime? = cooldownMap[name] - override fun getHelpWord(): String = "help" - - override fun shutdown() { + fun shutdown() { ses.shutdown() GSM.shutdown() } - override fun removeCommand(name: String) { - commands.removeIf { it.name == name } - } - override fun usesLinkedDeletion(): Boolean = false + fun usesLinkedDeletion(): Boolean = false - override fun getTextualPrefix(): String = prefix + fun getTextualPrefix(): String = prefix - override fun getTotalGuilds(): Int = bot.guilds.size + fun getTotalGuilds(): Int = bot.guilds.size - override fun getRemainingCooldown(name: String): Int { + fun getRemainingCooldown(name: String): Int { val cd = cooldownMap[name] return if (cd == null) { 0 @@ -168,31 +148,27 @@ data class UnityCommandClient(private val prefix: String, } - override fun getCoOwnerIds(): Array { - return arrayOf() - } - override fun getAltPrefix(): String? = null + val ownerId = "168743656738521088" + fun getOwnerIdLong(): Long = ownerId.toLong() - - override fun getPrefix(): String = prefix - - override fun getCommands(): MutableList = commands - - override fun getCoOwnerIdsLong(): LongArray = coOwnerIds.toList().map { it.toLong() }.toLongArray() - - override fun getOwnerId(): String = "168743656738521088" - override fun getOwnerIdLong(): Long = ownerId.toLong() - - private fun splitOnPrefixLength(rawContent: String, length: Int): Array { - return Arrays.copyOf(rawContent.substring(length).trim { it <= ' ' }.split("\\s+".toRegex(), 2).toTypedArray(), 2) + private fun splitOnPrefixLength(rawContent: String, length: Int): CommandString { + val m = rawContent.substring(length).trim { it <= ' ' }.split("\\s+".toRegex(), 2) + val cmds = if (m.size == 2) { + CommandString(m[0], m[1]) + } else { + CommandString(m[0]) + } + return cmds } var unknownCommandHandler: (MessageReceivedEvent, String?) -> Unit = { event, command -> val eb = EmbedBuilder().setTitle("Something went wrong...") - .addField("Unknown command: `$command`", "I don't know this command...", false) - .setColor(event.guild.selfMember.color).setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl).setTimestamp(LocalDateTime.now()) + .addField("Unknown command: `$command`", "I don't know this command...", false) + .setColor(event.guild.selfMember.color) + .setFooter("Requested by ${event.member.effectiveName}", event.author.effectiveAvatarUrl) + .setTimestamp(LocalDateTime.now()) event.textChannel.sendMessage(eb.build()).queue() } @@ -200,67 +176,79 @@ data class UnityCommandClient(private val prefix: String, if (event.author.isBot) return - var parts: Array? = null + var commandString: CommandString? = null val rawContent = event.message.contentRaw val settings = getSettings(event.guild) //Check for mention - if (settings.prefixes.isEmpty()) { - if (rawContent.startsWith("<@" + event.jda.selfUser.id + ">") || rawContent.startsWith("<@!" + event.jda.selfUser.id + ">")) { - parts = splitOnPrefixLength(rawContent, rawContent.indexOf(">") + 1) - } + + if (rawContent.startsWith("<@" + event.jda.selfUser.id + ">") || rawContent.startsWith("<@!" + event.jda.selfUser.id + ">")) { + commandString = splitOnPrefixLength(rawContent, rawContent.indexOf(">") + 1) } + //check for default prefix if not overridden if (rawContent.startsWith(prefix) and settings.prefixes.isEmpty()) - parts = splitOnPrefixLength(rawContent, rawContent.indexOf(prefix)) + commandString = splitOnPrefixLength(rawContent, prefix.length) // Check for guild specific prefixes - if (parts == null) { + if (commandString == null) { val prefixes = settings.getPrefixes() for (prefix in prefixes) { - if (parts == null && rawContent.toLowerCase().startsWith(prefix.toLowerCase())) - parts = splitOnPrefixLength(rawContent, prefix.length) + if (commandString == null && rawContent.toLowerCase().startsWith(prefix.toLowerCase())) + commandString = splitOnPrefixLength(rawContent, prefix.length) } } - if (parts != null) + if (commandString != null) //starts with valid prefix { - if ((event.channel.type == ChannelType.PRIVATE) or event.textChannel.canTalk()) { - val name = parts[0] - val args = if (parts[1] == null) "" else parts[1] - var command: Command? // this will be null if it's not a command - command = commands.firstOrNull { it.name == name } + var command: UnityCommand?// this will be null if it's not a command + command = commands.firstOrNull { it.name == commandString.command } if (command == null) { - command = commands.firstOrNull { name in it.aliases } + command = commands.firstOrNull { commandString.command in it.aliases } } if (command != null) { - val cevent = CommandEventOverride(event, args, this) + val cevent = UnityCommandEvent(event, commandString.args, this) - if (cmdListener != null) - cmdListener!!.onCommand(cevent, command) + /*if (cmdListener != null) + cmdListener!!.onCommand(cevent, command)*/ usageMap + command.name - command.run(cevent) + try { + command.run(cevent) + } catch (ex: UnknownCommandError) { + unknownCommandHandler(event, commandString.command) + } + return // Command is done } else { - unknownCommandHandler(event, name) + unknownCommandHandler(event, commandString.command) } } } - } + fun onException(t: Throwable) { + val channel = bot.getPrivateChannelById("501009066479452170") + channel.sendMessage("There was an error: ${t.message}").queue() + } + + private val status = OnlineStatus.ONLINE var version = "?.?" + set(value) { + game = Game.watching("fraud and \uD83C\uDFB5 (v$value)") + + } + + private var game: Game = Game.watching("fraud and \uD83C\uDFB5 (v$version)") - private val game = Game.watching("fraud and \uD83C\uDFB5 (v$version)") override fun onReady(event: ReadyEvent) { @@ -268,12 +256,10 @@ data class UnityCommandClient(private val prefix: String, log.error("This bot can't run as a client!") return } - event.jda.presence.setPresence(status, - when { - game == null -> null - "default" == game.name -> Game.playing("Type ${prefix}help") - else -> game - }) + event.jda.presence.setPresence( + status, + game + ) // Start SettingsManager if necessary GSM.init() @@ -281,9 +267,3 @@ data class UnityCommandClient(private val prefix: String, } - -class CommandEventOverride(event: MessageReceivedEvent, args: String?, client: UnityCommandClient) : CommandEvent(event, args, client) { - override fun linkId(message: Message?) { - - } -} \ No newline at end of file diff --git a/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandEvent.kt b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandEvent.kt new file mode 100644 index 0000000..4a535d1 --- /dev/null +++ b/src/main/kotlin/nl/voidcorp/dbot/commands/UnityCommandEvent.kt @@ -0,0 +1,77 @@ +package nl.voidcorp.dbot.commands + +import net.dv8tion.jda.core.JDA +import net.dv8tion.jda.core.entities.* +import net.dv8tion.jda.core.events.message.MessageReceivedEvent + +class UnityCommandEvent( + val event: MessageReceivedEvent, + val args: String, + val client: UnityCommandClient +) { + fun splitMessage(stringtoSend: String): MutableList { + var msg = stringtoSend + val msgs = mutableListOf() + msg = msg.trim { it <= ' ' } + while (msg.length > 2000) { + val leeway = 2000 - msg.length % 2000 + var index = msg.lastIndexOf("\n", 2000) + if (index < leeway) + index = msg.lastIndexOf(" ", 2000) + if (index < leeway) + index = 2000 + val temp = msg.substring(0, index).trim { it <= ' ' } + if (temp != "") + msgs.add(temp) + msg = msg.substring(index).trim { it <= ' ' } + } + if (msg != "") + msgs.add(msg) + return msgs + } + + + fun reply(embed: MessageEmbed) { + event.channel.sendMessage(embed).queue() + } + + fun reply(message: Message) { + event.channel.sendMessage(message).queue() + } + + fun reply(message: String) { + event.channel.sendMessage(message).queue() + } + + fun reply(message: String, success: (message: Message) -> Unit) { + event.channel.sendMessage(message).queue(success) + } + + val member: Member + get() = event.member + + val guild: Guild + get() = event.guild + + val channelType: ChannelType + get() = event.channelType + + val textChannel: TextChannel? + get() = event.textChannel + + val message: Message + get() = event.message + + val selfUser: SelfUser + get() = jda.selfUser + + val jda: JDA + get() = event.jda + + val selfMember: Member + get() = guild.selfMember + + val author: User + get() = event.author + +} \ No newline at end of file diff --git a/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt b/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt index cf1a294..63490fb 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/music/Music.kt @@ -1,6 +1,5 @@ package nl.voidcorp.dbot.music -import com.jagrosh.jdautilities.command.CommandEvent import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler import com.sedmelluq.discord.lavaplayer.player.AudioPlayer import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers @@ -11,10 +10,7 @@ import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame import net.dv8tion.jda.core.audio.AudioSendHandler import nl.voidcorp.dbot.addAll import nl.voidcorp.dbot.commands -import nl.voidcorp.dbot.commands.UnityCommand -import nl.voidcorp.dbot.commands.UnityMusicCommand -import nl.voidcorp.dbot.commands.MusicCategory -import nl.voidcorp.dbot.commands.MusicCategoryPrivate +import nl.voidcorp.dbot.commands.* import nl.voidcorp.dbot.log import nl.voidcorp.dbot.playerManager @@ -25,7 +21,12 @@ fun initMusic() { AudioSourceManagers.registerRemoteSources(playerManager) - val queueCommand = UnityMusicCommand("queue", aliases = *arrayOf("q"), help = "Use this command to queue a song, if you don't add a link it will search on youtube with the specified arguments\n\nExecute with no arguments to view the current queue!", howTo = "q [link]") { event, scheduler -> + val queueCommand = UnityMusicCommand( + "queue", + aliases = *arrayOf("q"), + help = "Use this command to queue a song, if you don't add a link it will search on youtube with the specified arguments\n\nExecute with no arguments to view the current queue!", + howTo = "q [link]" + ) { event, scheduler -> if (event.args.isEmpty()) { if (!guildMusicMap.containsKey(event.guild.idLong) || (guildMusicMap[event.guild.idLong]!!.player.playingTrack == null)) @@ -61,7 +62,12 @@ fun initMusic() { }) } - val ytCommand = UnityMusicCommand("youtube", aliases = *arrayOf("yt"), help = "Search YouTube for a song!", howTo = "youtube ") { event, scheduler -> + val ytCommand = UnityMusicCommand( + "youtube", + aliases = *arrayOf("yt"), + help = "Search YouTube for a song!", + howTo = "youtube " + ) { event, scheduler -> if (event.args.isEmpty()) { event.reply("Please supply a song name or URL!") return@UnityMusicCommand @@ -90,7 +96,12 @@ fun initMusic() { }) } - val soundcloudCommand = UnityMusicCommand("soundcloud", help = "Play a song via SoundCloud!", aliases = *arrayOf("sc"), howTo = "sc ") { event, scheduler -> + val soundcloudCommand = UnityMusicCommand( + "soundcloud", + help = "Play a song via SoundCloud!", + aliases = *arrayOf("sc"), + howTo = "sc " + ) { event, scheduler -> if (event.args.isEmpty()) { event.reply("Please supply a song name or URL!") return@UnityMusicCommand @@ -120,7 +131,13 @@ fun initMusic() { }) } - val playCommand = UnityMusicCommand("play", "Force this song to be the next!", aliases = *arrayOf("p"), howTo = "play ", category = MusicCategoryPrivate) { event, scheduler -> + val playCommand = UnityMusicCommand( + "play", + "Force this song to be the next!", + aliases = *arrayOf("p"), + howTo = "play ", + category = MusicCategoryPrivate + ) { event, scheduler -> if (event.args.isEmpty()) { event.reply("Please supply a song name or URL!") return@UnityMusicCommand @@ -150,7 +167,12 @@ fun initMusic() { } - val skipCommand = UnityCommand("skip", help = "Skip the current song", aliases = *arrayOf("s"), category = MusicCategory) { event -> + val skipCommand = UnityCommand( + "skip", + help = "Skip the current song", + aliases = *arrayOf("s"), + category = MusicCategory + ) { event -> val scheduler = guildMusicMap[event.guild.idLong] if (scheduler == null) { event.reply("There is no music playing?") @@ -159,7 +181,11 @@ fun initMusic() { } } - val npCommand = UnityMusicCommand("nowplaying", aliases = *arrayOf("np"), help = "Show the currently playing song") { event, scheduler -> + val npCommand = UnityMusicCommand( + "nowplaying", + aliases = *arrayOf("np"), + help = "Show the currently playing song" + ) { event, scheduler -> if (!scheduler.isSongPlaying()) { event.reply("There is no song playing?") } else { @@ -168,7 +194,11 @@ fun initMusic() { } - val attachmentPlay = UnityMusicCommand("attachment", help = "Play any attached song, if it is in a normal format that is...\n\n`just drag and drop the song on your Discord window and in the optional comment add !attach(ment)`", aliases = *arrayOf("attach")) { event, scheduler -> + val attachmentPlay = UnityMusicCommand( + "attachment", + help = "Play any attached song, if it is in a normal format that is...\n\n`just drag and drop the song on your Discord window and in the optional comment add !attach(ment)`", + aliases = *arrayOf("attach") + ) { event, scheduler -> val attach = event.message.attachments.firstOrNull() if (attach == null) { event.reply("I can't play an attachment without an attachment...") @@ -206,7 +236,12 @@ fun initMusic() { commands.addAll(playCommand, skipCommand, queueCommand, ytCommand, soundcloudCommand, npCommand, attachmentPlay) } -fun getLinkFromSearch(event: CommandEvent, scheduler: TrackScheduler, shouldInsertFront: Boolean, searchPrefix: String = "ytsearch") { +fun getLinkFromSearch( + event: UnityCommandEvent, + scheduler: TrackScheduler, + shouldInsertFront: Boolean, + searchPrefix: String = "ytsearch" +) { log.info("Searching youtube for '${event.args}'") playerManager.loadItem("$searchPrefix:${event.args}", object : AudioLoadResultHandler { diff --git a/src/main/kotlin/nl/voidcorp/dbot/storage/GuildSettings.kt b/src/main/kotlin/nl/voidcorp/dbot/storage/GuildSettings.kt index 6154f2f..a9f6b6e 100644 --- a/src/main/kotlin/nl/voidcorp/dbot/storage/GuildSettings.kt +++ b/src/main/kotlin/nl/voidcorp/dbot/storage/GuildSettings.kt @@ -1,9 +1,17 @@ package nl.voidcorp.dbot.storage -import com.jagrosh.jdautilities.command.GuildSettingsProvider +import net.dv8tion.jda.core.entities.Guild +import nl.voidcorp.dbot.commands.GSM -data class GuildSettings(val prefixes: MutableList = mutableListOf("!")) : GuildSettingsProvider { - override fun getPrefixes(): MutableCollection { +data class GuildSettings(val prefixes: MutableList = mutableListOf()) { + fun getPrefixes(): MutableCollection { return prefixes } + + val primaryPrefix: String + get() = prefixes.firstOrNull() ?: "!" +} + +fun Guild.settings(): GuildSettings { + return GSM.getSettings(this) } \ No newline at end of file diff --git a/src/main/kotlin/nl/voidcorp/dbot/storage/Other.kt b/src/main/kotlin/nl/voidcorp/dbot/storage/Other.kt new file mode 100644 index 0000000..359b5ed --- /dev/null +++ b/src/main/kotlin/nl/voidcorp/dbot/storage/Other.kt @@ -0,0 +1,3 @@ +package nl.voidcorp.dbot.storage + +data class CommandString(val command: String, val args: String = "") \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index e3626ca..ad82005 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -1,7 +1,7 @@ - %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger{36} - %msg%n + %d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %logger{0} - %msg%n @@ -32,4 +32,5 @@ + \ No newline at end of file