mirror of
https://github.com/InsanusMokrassar/PlaguBotCommandsPlugin.git
synced 2025-10-15 12:20:02 +00:00
initialization
This commit is contained in:
45
src/main/kotlin/BotCommandFullInfo.kt
Normal file
45
src/main/kotlin/BotCommandFullInfo.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package dev.inmo.plagubot.plugins.commands
|
||||
|
||||
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
|
||||
import dev.inmo.tgbotapi.types.BotCommand
|
||||
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Full info about the command its [key] and the [command] itself
|
||||
*
|
||||
* @see full
|
||||
*/
|
||||
@Serializable
|
||||
data class BotCommandFullInfo(
|
||||
val key: CommandsKeeperKey,
|
||||
val command: BotCommand
|
||||
) {
|
||||
constructor(command: BotCommand) : this(CommandsKeeperKey.DEFAULT, command)
|
||||
}
|
||||
|
||||
fun BotCommand.full(
|
||||
key: CommandsKeeperKey = CommandsKeeperKey.DEFAULT
|
||||
) = BotCommandFullInfo(key, this)
|
||||
|
||||
fun BotCommand.full(
|
||||
scope: BotCommandScope
|
||||
) = full(CommandsKeeperKey(scope))
|
||||
|
||||
fun BotCommand.full(
|
||||
languageCode: String
|
||||
) = full(CommandsKeeperKey(languageCode = languageCode))
|
||||
|
||||
fun BotCommand.full(
|
||||
languageCode: IetfLanguageCode
|
||||
) = full(CommandsKeeperKey(BotCommandScope.Default, languageCode = languageCode))
|
||||
|
||||
fun BotCommand.full(
|
||||
scope: BotCommandScope,
|
||||
languageCode: String
|
||||
) = full(CommandsKeeperKey(scope, languageCode))
|
||||
|
||||
fun BotCommand.full(
|
||||
scope: BotCommandScope,
|
||||
languageCode: IetfLanguageCode
|
||||
) = full(CommandsKeeperKey(scope, languageCode))
|
148
src/main/kotlin/CommandsKeeper.kt
Normal file
148
src/main/kotlin/CommandsKeeper.kt
Normal file
@@ -0,0 +1,148 @@
|
||||
package dev.inmo.plagubot.plugins.commands
|
||||
|
||||
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
|
||||
import dev.inmo.tgbotapi.types.BotCommand
|
||||
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
/**
|
||||
* In memory commands keeper. Contains all the registered commands inside and can be useful in case you wish to
|
||||
* [addCommand] or [removeCommand]
|
||||
*/
|
||||
class CommandsKeeper(
|
||||
preset: List<BotCommandFullInfo> = emptyList()
|
||||
) {
|
||||
internal val onScopeChanged = MutableSharedFlow<CommandsKeeperKey>()
|
||||
private val scopesCommands: MutableMap<CommandsKeeperKey, MutableSet<BotCommand>> = preset.groupBy {
|
||||
it.key
|
||||
}.mapValues { (_, v) ->
|
||||
v.map { it.command }.toMutableSet()
|
||||
}.toMutableMap()
|
||||
private val changesMutex = Mutex()
|
||||
|
||||
suspend fun addCommand(scope: CommandsKeeperKey, command: BotCommand) {
|
||||
changesMutex.withLock {
|
||||
val added = scopesCommands.getOrPut(scope) {
|
||||
mutableSetOf()
|
||||
}.add(command)
|
||||
|
||||
if (added) {
|
||||
onScopeChanged.emit(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun addCommand(
|
||||
scope: BotCommandScope,
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey(scope, null),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun addCommand(
|
||||
languageCode: String,
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey(languageCode = languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun addCommand(
|
||||
languageCode: IetfLanguageCode,
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey(BotCommandScopeDefault, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun addCommand(
|
||||
scope: BotCommandScope,
|
||||
languageCode: IetfLanguageCode,
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey(scope, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun addCommand(
|
||||
scope: BotCommandScope,
|
||||
languageCode: String,
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey(scope, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun addCommand(
|
||||
command: BotCommand
|
||||
) = addCommand(
|
||||
CommandsKeeperKey.DEFAULT,
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(scope: CommandsKeeperKey, command: BotCommand) {
|
||||
changesMutex.withLock {
|
||||
val removed = scopesCommands[scope] ?.remove(command) == true
|
||||
|
||||
if (removed) {
|
||||
onScopeChanged.emit(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeCommand(
|
||||
scope: BotCommandScope,
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey(scope),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(
|
||||
languageCode: String,
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey(languageCode = languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(
|
||||
languageCode: IetfLanguageCode,
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey(BotCommandScopeDefault, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(
|
||||
scope: BotCommandScope,
|
||||
languageCode: IetfLanguageCode,
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey(scope, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(
|
||||
scope: BotCommandScope,
|
||||
languageCode: String,
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey(scope, languageCode),
|
||||
command
|
||||
)
|
||||
|
||||
suspend fun removeCommand(
|
||||
command: BotCommand
|
||||
) = removeCommand(
|
||||
CommandsKeeperKey.DEFAULT,
|
||||
command
|
||||
)
|
||||
|
||||
internal fun getCommands(scope: CommandsKeeperKey) = scopesCommands[scope] ?.toList()
|
||||
internal fun getKeys() = scopesCommands.keys.toList()
|
||||
}
|
35
src/main/kotlin/CommandsKeeperKey.kt
Normal file
35
src/main/kotlin/CommandsKeeperKey.kt
Normal file
@@ -0,0 +1,35 @@
|
||||
package dev.inmo.plagubot.plugins.commands
|
||||
|
||||
import dev.inmo.micro_utils.language_codes.IetfLanguageCode
|
||||
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||
import dev.inmo.tgbotapi.types.commands.BotCommandScopeDefault
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Full info about the command scope including [BotCommandScope] and its optional language code (see [languageCode] and
|
||||
* [languageCodeIetf])
|
||||
*
|
||||
* @see CommandsKeeperKey.DEFAULT
|
||||
*/
|
||||
@Serializable
|
||||
@JvmInline
|
||||
value class CommandsKeeperKey(
|
||||
val key: Pair<BotCommandScope, String?>
|
||||
) {
|
||||
val scope: BotCommandScope
|
||||
get() = key.first
|
||||
val languageCode: String?
|
||||
get() = key.second
|
||||
val languageCodeIetf: IetfLanguageCode?
|
||||
get() = languageCode ?.let(::IetfLanguageCode)
|
||||
|
||||
constructor(scope: BotCommandScope = BotCommandScope.Default, languageCode: String? = null) : this(scope to languageCode)
|
||||
constructor(scope: BotCommandScope, languageCode: IetfLanguageCode) : this(scope to languageCode.code)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Default realization of [CommandsKeeperKey] with null [languageCode] and [BotCommandScope.Default] [scope]
|
||||
*/
|
||||
val DEFAULT = CommandsKeeperKey()
|
||||
}
|
||||
}
|
78
src/main/kotlin/CommandsPlugin.kt
Normal file
78
src/main/kotlin/CommandsPlugin.kt
Normal file
@@ -0,0 +1,78 @@
|
||||
package dev.inmo.plagubot.plugins.commands
|
||||
|
||||
import dev.inmo.kslog.common.*
|
||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||
import dev.inmo.plagubot.Plugin
|
||||
import dev.inmo.tgbotapi.extensions.api.bot.*
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||
import dev.inmo.tgbotapi.types.BotCommand
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.module.Module
|
||||
|
||||
/**
|
||||
* This plugin has been created for centralized work with commands in context of [Plugin]s system of plagubot. Pass
|
||||
* [BotCommandFullInfo] in your [Plugin.setupDI] section to declare some command. You may use [CommandsKeeper] for
|
||||
* flexible setup of commands in runtime.
|
||||
*/
|
||||
@Serializable
|
||||
class CommandsPlugin : Plugin {
|
||||
private val log = KSLog(logTag)
|
||||
|
||||
/**
|
||||
* Creating [CommandsKeeper] and pass it to the DI. It uses [org.koin.core.scope.Scope.getAll] to get all the
|
||||
* [BotCommandFullInfo] instances declared in the DI.
|
||||
*/
|
||||
override fun Module.setupDI(database: Database, params: JsonObject) {
|
||||
single { CommandsKeeper(getAll<BotCommandFullInfo>().distinct()) }
|
||||
}
|
||||
|
||||
private suspend fun BehaviourContext.setScopeCommands(key: CommandsKeeperKey, commands: List<BotCommand>?) {
|
||||
runCatchingSafely {
|
||||
commands ?.let {
|
||||
setMyCommands(
|
||||
commands,
|
||||
key.scope,
|
||||
key.languageCode
|
||||
)
|
||||
} ?: deleteMyCommands(
|
||||
key.scope,
|
||||
key.languageCode
|
||||
)
|
||||
}.onFailure {
|
||||
log.e {
|
||||
"Unable to ${if (commands == null) "delete commands" else "set new commands (${commands.joinToString { it.command }})"} for key $key"
|
||||
}
|
||||
}.onSuccess {
|
||||
log.i {
|
||||
"Successfully ${if (commands == null) "deleted commands" else "set new commands (${commands.joinToString { it.command }})"} for key $key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses [CommandsKeeper] from [koin]. Subscribe on [CommandsKeeper.scopesCommands] to follow changed in scopes and
|
||||
* take all the available keys in the [CommandsKeeper] and set commands for each key
|
||||
*/
|
||||
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
|
||||
val commandsKeeper = koin.get<CommandsKeeper>()
|
||||
|
||||
log.d { "Subscribe to scopes changed flow" }
|
||||
commandsKeeper.onScopeChanged.subscribeSafelyWithoutExceptions(scope) {
|
||||
val commands = commandsKeeper.getCommands(it)
|
||||
setScopeCommands(it, commands)
|
||||
}
|
||||
log.d { "Subscribed to scopes changed flow" }
|
||||
|
||||
log.d { "Start setup initially passed commands" }
|
||||
commandsKeeper.getKeys().forEach {
|
||||
val commands = commandsKeeper.getCommands(it)
|
||||
log.d { "Start setup initially passed commands for key $it: ${commands ?.joinToString { it.command }}" }
|
||||
setScopeCommands(it, commandsKeeper.getCommands(it))
|
||||
}
|
||||
log.d { "Complete setup initially passed commands" }
|
||||
}
|
||||
}
|
@@ -1,45 +0,0 @@
|
||||
package plagubot_plugin
|
||||
|
||||
import dev.inmo.kslog.common.KSLog
|
||||
import dev.inmo.kslog.common.logTag
|
||||
import dev.inmo.kslog.common.d
|
||||
import dev.inmo.kslog.common.dS
|
||||
import dev.inmo.plagubot.Plugin
|
||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.koin.core.Koin
|
||||
import org.koin.core.module.Module
|
||||
|
||||
// Left empty constructor for the plugin instantiation in bot
|
||||
@Serializable
|
||||
class PlaguBotPlugin : Plugin {
|
||||
private val logger = KSLog(logTag)
|
||||
|
||||
@Serializable
|
||||
data class Config(
|
||||
val someParam: String = "Default value"
|
||||
)
|
||||
override fun Module.setupDI(database: Database, params: JsonObject) {
|
||||
// Here you may declare any dependencies you need
|
||||
single {
|
||||
// Take from config value by field "plugin". Replace to your plugin name and fill Config with your plugin configuration
|
||||
get<Json>().decodeFromJsonElement(Config.serializer(), params["plugin"] ?: return@single null)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
|
||||
// Here you may configure the behaviour of your plugin
|
||||
// All dependencies available in bot can be accessed via koin
|
||||
logger.d { koin.get<Config>().someParam }
|
||||
logger.dS { getMe().toString() }
|
||||
onCommand("hello_world") {
|
||||
reply(it, "Hello :)")
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user