init: Initial commit
This commit is contained in:
commit
0fa9a44640
25 changed files with 767 additions and 0 deletions
12
core/build.gradle.kts
Normal file
12
core/build.gradle.kts
Normal file
|
@ -0,0 +1,12 @@
|
|||
plugins {
|
||||
kotlin("jvm") version "2.0.0"
|
||||
}
|
||||
|
||||
group = "io.github.null2264.tsukumogami.core"
|
||||
|
||||
dependencies {
|
||||
api("dev.kord:kord-core:0.15.0")
|
||||
api("io.insert-koin:koin-core:4.0.3")
|
||||
api("co.touchlab:kermit:2.0.4")
|
||||
api(kotlin("reflect"))
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package io.github.null2264.tsukumogami.core
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.kord.core.Kord
|
||||
import dev.kord.core.entity.Message
|
||||
import dev.kord.core.event.gateway.ReadyEvent
|
||||
import dev.kord.core.event.message.MessageCreateEvent
|
||||
import dev.kord.core.on
|
||||
import dev.kord.gateway.Intent
|
||||
import dev.kord.gateway.PrivilegedIntent
|
||||
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
||||
import io.github.null2264.tsukumogami.core.exceptions.CommandNotFound
|
||||
import io.github.null2264.tsukumogami.core.module.BotModule
|
||||
import io.github.null2264.tsukumogami.core.module.Command
|
||||
import kotlin.reflect.full.callSuspend
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
abstract class AbstractBot(configurator: BotConfigurator.() -> Unit) {
|
||||
|
||||
private val commands: Map<String, Command>
|
||||
private val extensions: Map<String, BotModule>
|
||||
private val prefixes: List<String>
|
||||
private val client: Kord
|
||||
|
||||
init {
|
||||
val currentConfig = BotConfigurator()
|
||||
currentConfig.apply(configurator)
|
||||
|
||||
extensions = mutableMapOf()
|
||||
currentConfig.extensions.forEach {
|
||||
val module = it.call()
|
||||
module.setup(this, currentConfig)
|
||||
extensions[module.name] = module
|
||||
}
|
||||
commands = currentConfig.commands
|
||||
prefixes = currentConfig.prefixes
|
||||
|
||||
client = runBlocking {
|
||||
Kord(currentConfig.token).apply {
|
||||
on<ReadyEvent> {
|
||||
Logger.i { "Online! ${self.username}" }
|
||||
}
|
||||
|
||||
on<MessageCreateEvent> { onMessage(this.message, this) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open suspend fun start() {
|
||||
client.login {
|
||||
@OptIn(PrivilegedIntent::class)
|
||||
intents += Intent.MessageContent
|
||||
}
|
||||
}
|
||||
|
||||
open suspend fun stop() {
|
||||
client.shutdown()
|
||||
}
|
||||
|
||||
fun getCommand(name: String) = commands[name]
|
||||
|
||||
private fun getContext(message: Message): Context {
|
||||
val candidate = message.content.hasPrefix()
|
||||
return Context(this, message, candidate?.first, candidate?.second)
|
||||
}
|
||||
|
||||
suspend fun onCommandError(context: Context, error: CommandException) {
|
||||
context.send(error.message!!)
|
||||
}
|
||||
|
||||
suspend fun processCommand(message: Message) {
|
||||
val ctx = getContext(message)
|
||||
|
||||
try {
|
||||
ctx.command?.let {
|
||||
it.callback.callSuspend(extensions[it.extension], ctx)
|
||||
} ?: throw CommandNotFound()
|
||||
} catch (e: CommandException) {
|
||||
onCommandError(ctx, e)
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Something went wrong while trying to process command '${ctx.command?.name}'" }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun onMessage(message: Message, event: MessageCreateEvent) {
|
||||
if (message.author?.isBot != false) return
|
||||
|
||||
processCommand(message)
|
||||
}
|
||||
|
||||
fun String.hasPrefix(): Pair<String, String>? {
|
||||
if (this.isBlank()) return null
|
||||
|
||||
var ret: Pair<String, String>? = null
|
||||
|
||||
prefixes.forEach {
|
||||
if (this.substring(0, it.length) == it) {
|
||||
ret = Pair(this.substring(0, it.length), this.substring(it.length))
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package io.github.null2264.tsukumogami.core
|
||||
|
||||
import io.github.null2264.tsukumogami.core.module.BotModule
|
||||
import io.github.null2264.tsukumogami.core.module.Command
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
class BotConfigurator internal constructor() {
|
||||
|
||||
internal val commands = mutableMapOf<String, Command>()
|
||||
internal val extensions = mutableListOf<KFunction<BotModule>>()
|
||||
internal val prefixes = mutableListOf<String>()
|
||||
var token: String = ""
|
||||
|
||||
internal fun isExists(name: String?) = this.commands.containsKey(name)
|
||||
|
||||
fun commands(command: Command, name: String? = null) {
|
||||
this.commands[if (name.isNullOrEmpty()) command.name else name] = command
|
||||
}
|
||||
|
||||
fun extensions(vararg extensions: KFunction<BotModule>) {
|
||||
extensions.forEach {
|
||||
val kClass = it.returnType.classifier as KClass<*>
|
||||
if (!kClass.isSubclassOf(BotModule::class))
|
||||
return
|
||||
|
||||
this.extensions.add(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun prefixes(vararg prefixes: String) {
|
||||
prefixes(prefixes.toList())
|
||||
}
|
||||
|
||||
fun prefixes(prefixes: List<String>) {
|
||||
this.prefixes.addAll(prefixes)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.github.null2264.tsukumogami.core
|
||||
|
||||
import dev.kord.core.behavior.channel.createMessage
|
||||
import dev.kord.core.entity.Message
|
||||
import dev.kord.rest.builder.message.AllowedMentionsBuilder
|
||||
import dev.kord.rest.builder.message.allowedMentions
|
||||
|
||||
class Context(private val bot: AbstractBot, private val message: Message, val prefix: String?, private val commandName: String?) {
|
||||
|
||||
val author get() = message.author
|
||||
val command get() = commandName?.let { bot.getCommand(it) }
|
||||
|
||||
suspend fun send(content: String) = message.channel.createMessage(content)
|
||||
|
||||
suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage {
|
||||
this.content = content
|
||||
messageReference = message.id
|
||||
if (!mentionsAuthor)
|
||||
allowedMentions {
|
||||
AllowedMentionsBuilder().repliedUser
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun typing() = message.channel.type()
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package io.github.null2264.tsukumogami.core.exceptions
|
||||
|
||||
open class CommandException(message: String = "Something went wrong while executing the command") : Exception(message)
|
|
@ -0,0 +1,3 @@
|
|||
package io.github.null2264.tsukumogami.core.exceptions
|
||||
|
||||
class CommandNotFound : CommandException("Command not found")
|
|
@ -0,0 +1,44 @@
|
|||
package io.github.null2264.tsukumogami.core.module
|
||||
|
||||
import io.github.null2264.tsukumogami.core.AbstractBot
|
||||
import io.github.null2264.tsukumogami.core.module.annotation.Command as CommandAnnotation
|
||||
import io.github.null2264.tsukumogami.core.BotConfigurator
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.kotlinFunction
|
||||
|
||||
abstract class BotModule(val name: String, val description: String? = null) {
|
||||
|
||||
var bot: AbstractBot? = null
|
||||
internal set
|
||||
|
||||
internal fun setup(bot: AbstractBot, configurator: BotConfigurator) {
|
||||
this.bot = bot
|
||||
|
||||
val methods = this::class.java.declaredMethods
|
||||
for (method in methods) {
|
||||
for (annotation in method.annotations) {
|
||||
if (annotation !is CommandAnnotation)
|
||||
continue
|
||||
|
||||
configurator.apply {
|
||||
val kMethod = method.kotlinFunction
|
||||
if (isExists(kMethod?.name)) {
|
||||
println("Command already exists")
|
||||
return
|
||||
}
|
||||
kMethod?.let {
|
||||
it.isAccessible = true
|
||||
commands(
|
||||
Command(
|
||||
annotation.name.ifEmpty { it.name },
|
||||
name,
|
||||
it,
|
||||
annotation.description.ifEmpty { description },
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.github.null2264.tsukumogami.core.module
|
||||
|
||||
import kotlin.reflect.KFunction
|
||||
|
||||
/**
|
||||
* Class holding information about a command
|
||||
*/
|
||||
data class Command(
|
||||
val name: String,
|
||||
val extension: String,
|
||||
val callback: KFunction<*>,
|
||||
val description: String? = null,
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package io.github.null2264.tsukumogami.core.module.annotation
|
||||
|
||||
|
||||
/**
|
||||
* Annotation to tag a function as command
|
||||
*
|
||||
* @param name the command's name
|
||||
* @param description the command's description
|
||||
* @param help the command's extended description (a more detailed description)
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class Command(val name: String = "", val description: String = "", val help: String = "")
|
Loading…
Add table
Add a link
Reference in a new issue