diff --git a/build.gradle.kts b/build.gradle.kts index d1f88fd..13cfa3d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,6 +12,9 @@ version = "0.1.0" repositories { maven("https://jitpack.io") mavenCentral() + flatDir { + dirs = mutableSetOf(file("dependencies")) + } } val jupyterVersion = "5.6.0" @@ -20,15 +23,17 @@ val kotlinVersion = "1.5.30" dependencies { implementation("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", kotlinVersion) implementation("org.jetbrains.kotlin", "kotlin-reflect", kotlinVersion) - implementation("org.jetbrains.kotlinx", "kotlinx-serialization-protobuf", "1.2.2") - implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.5.1") + implementation("org.jetbrains.kotlinx", "kotlinx-serialization-protobuf", "1.3.0") + runtimeOnly("org.jetbrains.kotlinx", "kotlinx-serialization-runtime", "1.0-M1-1.4.0-rc") + implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.5.2") implementation("com.github.elbekD", "kt-telegram-bot", "1.3.8") + implementation("org.pool-party", "flume", "0.0.1") implementation("com.natpryce", "konfig", "1.6.10.0") implementation("org.slf4j", "slf4j-simple", "2.0.0-alpha2") - implementation("io.github.microutils", "kotlin-logging", "2.0.10") + implementation("io.github.microutils", "kotlin-logging", "2.0.11") testImplementation("org.jetbrains.kotlin", "kotlin-test-junit5", kotlinVersion) testImplementation("org.junit.jupiter", "junit-jupiter-api", jupyterVersion) diff --git a/dependencies/flume-test.jar b/dependencies/flume-test.jar new file mode 100644 index 0000000..cef00fa Binary files /dev/null and b/dependencies/flume-test.jar differ diff --git a/dependencies/flume.jar b/dependencies/flume.jar new file mode 100644 index 0000000..2b36d4e Binary files /dev/null and b/dependencies/flume.jar differ diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/Configuration.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/Configuration.kt index 8036af9..33e24fb 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/Configuration.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/Configuration.kt @@ -1,23 +1,9 @@ package com.github.pool_party.resistance_bot +import com.github.pool_party.flume.configuration.AbstractConfiguration import com.github.pool_party.resistance_bot.state.Board -import com.github.pool_party.resistance_bot.utils.ConfigurationUtils.boolean -import com.github.pool_party.resistance_bot.utils.ConfigurationUtils.int -import com.github.pool_party.resistance_bot.utils.ConfigurationUtils.seconds -import com.github.pool_party.resistance_bot.utils.ConfigurationUtils.string -object Configuration { - - val APP_URL by string() - val USERNAME by string() - val PORT by int() - val HOST by string() - - val DEBUG by boolean() - - val LONGPOLL by boolean() - - val TELEGRAM_TOKEN by string() +object Configuration : AbstractConfiguration() { val REGISTRATION_TIME by seconds() @@ -52,8 +38,5 @@ object Configuration { init { check((PLAYERS_GAME_MINIMUM..PLAYERS_GAME_MAXIMUM).all { it in BOARDS }) - if (DEBUG) { - System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG") - } } } diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/Interaction.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/Interaction.kt deleted file mode 100644 index ab80b9c..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/Interaction.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.pool_party.resistance_bot - -import com.elbekD.bot.Bot - -interface Interaction { - - fun apply(bot: Bot) -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/Main.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/Main.kt index 4788cb0..5facd63 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/Main.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/Main.kt @@ -1,26 +1,54 @@ package com.github.pool_party.resistance_bot -import com.elbekD.bot.Bot -import com.elbekD.bot.server -import com.github.pool_party.resistance_bot.utils.initHandlers +import com.github.pool_party.resistance_bot.callback.CallbackDispatcher +import com.github.pool_party.resistance_bot.callback.MissionVoteCallback +import com.github.pool_party.resistance_bot.callback.SquadChoiceCallback +import com.github.pool_party.resistance_bot.callback.SquadVoteCallback +import com.github.pool_party.resistance_bot.callback.StopVoteCallback +import com.github.pool_party.resistance_bot.command.ExtendCommand +import com.github.pool_party.resistance_bot.command.GameCommand +import com.github.pool_party.resistance_bot.command.HistoryCommand +import com.github.pool_party.resistance_bot.command.RulesCommand +import com.github.pool_party.resistance_bot.command.StartCommand +import com.github.pool_party.resistance_bot.command.StopCommand +import com.github.pool_party.resistance_bot.command.TagCommand +import com.github.pool_party.resistance_bot.state.Coder +import com.github.pool_party.resistance_bot.state.InMemoryStateStorage +import com.github.pool_party.resistance_bot.state.LongCoder +import com.github.pool_party.resistance_bot.state.StateStorage +import com.github.pool_party.telegram_bot_utils.bot.BotBuilder fun main() { - val token = Configuration.TELEGRAM_TOKEN - val userName = Configuration.USERNAME + val stateStorage: StateStorage = InMemoryStateStorage() + val longCoder: Coder = LongCoder() - val bot = if (Configuration.LONGPOLL) { - Bot.createPolling(userName, token) - } else { - Bot.createWebhook(userName, token) { - url = "${Configuration.APP_URL}/$token" + val callbacks = listOf( + SquadChoiceCallback(stateStorage), + MissionVoteCallback(stateStorage), + SquadVoteCallback(stateStorage), + StopVoteCallback(stateStorage), + ) - server { - host = Configuration.HOST - port = Configuration.PORT - } - } - } - - bot.initHandlers() - bot.start() + BotBuilder(Configuration).apply { + interactions = listOf( + // registration + listOf( + GameCommand(stateStorage, longCoder), + StartCommand(stateStorage, longCoder), + StopCommand(stateStorage), + ), + // game + listOf( + HistoryCommand(stateStorage), + TagCommand(stateStorage), + ), + // utils + listOf( + ExtendCommand(stateStorage), + RulesCommand(), + ), + // other + listOf(CallbackDispatcher(callbacks)), + ) + }.start() } diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/action/RoleDistribution.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/action/RoleDistribution.kt index a67c327..56c2734 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/action/RoleDistribution.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/action/RoleDistribution.kt @@ -1,10 +1,10 @@ package com.github.pool_party.resistance_bot.action import com.elbekD.bot.Bot +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.message.RECEIVE_RESISTANCE_ROLE import com.github.pool_party.resistance_bot.message.receiveSpyRole import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.sendMessageLogging suspend fun Bot.distributeRoles(chatId: Long, stateStorage: StateStorage) { val state = stateStorage.getGameState(chatId) ?: return diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/action/SquadChoice.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/action/SquadChoice.kt index 7f231d0..cd61b7d 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/action/SquadChoice.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/action/SquadChoice.kt @@ -2,13 +2,13 @@ package com.github.pool_party.resistance_bot.action import com.elbekD.bot.Bot import com.elbekD.bot.types.InlineKeyboardButton +import com.github.pool_party.flume.utils.sendMessageLogging +import com.github.pool_party.flume.utils.toMarkUp import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.callback.SquadChoiceCallbackData import com.github.pool_party.resistance_bot.message.leaderChooseMessage import com.github.pool_party.resistance_bot.message.roundSummary import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.sendMessageLogging -import com.github.pool_party.resistance_bot.utils.toMarkUp suspend fun Bot.chooseSquad(chatId: Long, stateStorage: StateStorage) { diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/action/StartGame.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/action/StartGame.kt index 5c46b91..cf90c40 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/action/StartGame.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/action/StartGame.kt @@ -1,13 +1,13 @@ package com.github.pool_party.resistance_bot.action import com.elbekD.bot.Bot +import com.github.pool_party.flume.utils.deleteMessageLogging +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.message.ON_GAME_START import com.github.pool_party.resistance_bot.message.ON_LESS_PLAYERS import com.github.pool_party.resistance_bot.message.ON_MORE_PLAYERS import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.deleteMessageLogging -import com.github.pool_party.resistance_bot.utils.sendMessageLogging suspend fun Bot.startGame(chatId: Long, stateStorage: StateStorage) { val state = stateStorage.getRegistrationState(chatId) ?: return diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/action/Vote.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/action/Vote.kt index ef5532e..be1fc30 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/action/Vote.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/action/Vote.kt @@ -2,14 +2,14 @@ package com.github.pool_party.resistance_bot.action import com.elbekD.bot.Bot import com.elbekD.bot.types.InlineKeyboardButton +import com.github.pool_party.flume.utils.sendMessageLogging +import com.github.pool_party.flume.utils.toMarkUp import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.callback.CallbackData import com.github.pool_party.resistance_bot.callback.MissionVoteCallbackData import com.github.pool_party.resistance_bot.callback.SquadVoteCallbackData import com.github.pool_party.resistance_bot.message.MISSION_VOTE import com.github.pool_party.resistance_bot.message.TEAM_VOTE -import com.github.pool_party.resistance_bot.utils.sendMessageLogging -import com.github.pool_party.resistance_bot.utils.toMarkUp private suspend fun Bot.vote(text: String, memberIds: List, verdictVoteCallBackData: (Boolean) -> CallbackData) { diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/AbstractVoteCallback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/AbstractVoteCallback.kt index 4b92ccc..dc8d5df 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/AbstractVoteCallback.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/AbstractVoteCallback.kt @@ -2,21 +2,22 @@ package com.github.pool_party.resistance_bot.callback import com.elbekD.bot.Bot import com.elbekD.bot.types.CallbackQuery +import com.github.pool_party.flume.interaction.callback.Callback +import com.github.pool_party.flume.utils.editMessageTextLogging +import com.github.pool_party.flume.utils.name import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.action.chooseSquad import com.github.pool_party.resistance_bot.state.GameState import com.github.pool_party.resistance_bot.state.Member import com.github.pool_party.resistance_bot.state.StateStorage import com.github.pool_party.resistance_bot.state.Vote -import com.github.pool_party.resistance_bot.utils.editMessageTextLogging -import com.github.pool_party.resistance_bot.utils.name interface VoteCallbackData { val gameChatId: Long val verdict: Boolean } -abstract class AbstractVoteCallback(protected val stateStorage: StateStorage) : Callback { +abstract class AbstractVoteCallback(protected val stateStorage: StateStorage) : Callback { abstract suspend fun getMemberNumber(voteCallbackData: VoteCallbackData): Int? diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/Callback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/Callback.kt deleted file mode 100644 index cf551cc..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/Callback.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.github.pool_party.resistance_bot.callback - -import com.elbekD.bot.Bot -import com.elbekD.bot.types.CallbackQuery -import com.github.pool_party.resistance_bot.Interaction -import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromByteArray -import kotlinx.serialization.encodeToByteArray -import kotlinx.serialization.protobuf.ProtoBuf -import mu.KotlinLogging -import kotlin.reflect.KClass - -/** - * There is a hack with short property serial names to fit into 64 Telegram API callback data bytes. - */ -@Serializable -sealed class CallbackData { - abstract val gameChatId: Long - - val encoded: String - get() = ProtoBuf.encodeToByteArray(this).joinToString("") { it.toInt().toChar().toString() } - - companion object { - fun of(string: String) = - ProtoBuf.decodeFromByteArray(string.map { it.code.toByte() }.toByteArray()) - } -} - -interface Callback { - - val callbackDataKClass: KClass - - suspend fun Bot.process(callbackQuery: CallbackQuery, callbackData: CallbackData) -} - -class CallbackDispatcher(callbacks: List) : Interaction { - - private val logger = KotlinLogging.logger {} - - private val callbackMap: Map, Callback> = callbacks.associateBy { it.callbackDataKClass } - - override fun apply(bot: Bot) = bot.onCallbackQuery { - val callbackData = it.data?.let { data -> CallbackData.of(data) } - - logger.info { - val bytes = it.data?.asSequence()?.joinToString("") { char -> "%02x".format(char.code.toByte()) } - "callback ${it.from.username}@${it.message?.chat?.title}: $bytes >=> $callbackData" - } - - if (callbackData == null) return@onCallbackQuery - - val callback = callbackMap[callbackData::class] ?: return@onCallbackQuery - - with(callback) { bot.process(it, callbackData) } - } -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/CallbackDispatcher.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/CallbackDispatcher.kt new file mode 100644 index 0000000..76c59ad --- /dev/null +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/CallbackDispatcher.kt @@ -0,0 +1,30 @@ +package com.github.pool_party.resistance_bot.callback + +import com.github.pool_party.flume.interaction.callback.AbstractCallbackDispatcher +import com.github.pool_party.flume.interaction.callback.Callback +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlinx.serialization.protobuf.ProtoBuf + +/** + * There is a hack with short property serial names to fit into 64 Telegram API callback data bytes. + */ +@Serializable +sealed class CallbackData { + abstract val gameChatId: Long + + val encoded: String + get() = ProtoBuf.encodeToByteArray(this).joinToString("") { it.toInt().toChar().toString() } + + companion object { + fun of(string: String) = + ProtoBuf.decodeFromByteArray(string.map { it.code.toByte() }.toByteArray()) + } +} + +class CallbackDispatcher(callbacks: List>) : + AbstractCallbackDispatcher(callbacks) { + + override fun getCallbackData(data: String): CallbackData = CallbackData.of(data) +} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/MissionVoteCallback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/MissionVoteCallback.kt index 02519c7..497946e 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/MissionVoteCallback.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/MissionVoteCallback.kt @@ -1,12 +1,12 @@ package com.github.pool_party.resistance_bot.callback import com.elbekD.bot.Bot +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.state.GameState import com.github.pool_party.resistance_bot.state.RoundResult import com.github.pool_party.resistance_bot.state.StateStorage import com.github.pool_party.resistance_bot.state.Vote -import com.github.pool_party.resistance_bot.utils.sendMessageLogging import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadChoiceCallback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadChoiceCallback.kt index f2be9ad..ad2f938 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadChoiceCallback.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadChoiceCallback.kt @@ -2,16 +2,17 @@ package com.github.pool_party.resistance_bot.callback import com.elbekD.bot.Bot import com.elbekD.bot.types.CallbackQuery +import com.github.pool_party.flume.interaction.callback.Callback +import com.github.pool_party.flume.utils.editMessageReplyMarkupLogging +import com.github.pool_party.flume.utils.editMessageTextLogging +import com.github.pool_party.flume.utils.name +import com.github.pool_party.flume.utils.sendMessageLogging +import com.github.pool_party.flume.utils.toMarkUp import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.action.squadVote import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.editMessageReplyMarkupLogging -import com.github.pool_party.resistance_bot.utils.editMessageTextLogging import com.github.pool_party.resistance_bot.utils.goToBotMarkup import com.github.pool_party.resistance_bot.utils.makeUserLink -import com.github.pool_party.resistance_bot.utils.name -import com.github.pool_party.resistance_bot.utils.sendMessageLogging -import com.github.pool_party.resistance_bot.utils.toMarkUp import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -23,7 +24,7 @@ data class SquadChoiceCallbackData(override val gameChatId: Long, val personId: } } -class SquadChoiceCallback(private val stateStorage: StateStorage) : Callback { +class SquadChoiceCallback(private val stateStorage: StateStorage) : Callback { override val callbackDataKClass = SquadChoiceCallbackData::class diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadVoteCallback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadVoteCallback.kt index 4d53e17..6ce864b 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadVoteCallback.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/SquadVoteCallback.kt @@ -1,6 +1,7 @@ package com.github.pool_party.resistance_bot.callback import com.elbekD.bot.Bot +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.action.missionVote import com.github.pool_party.resistance_bot.message.TEAM_APPROVED @@ -10,7 +11,6 @@ import com.github.pool_party.resistance_bot.state.GameState import com.github.pool_party.resistance_bot.state.StateStorage import com.github.pool_party.resistance_bot.state.Vote import com.github.pool_party.resistance_bot.utils.goToBotMarkup -import com.github.pool_party.resistance_bot.utils.sendMessageLogging import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/StopVoteCallback.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/StopVoteCallback.kt index 21f446f..662b00a 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/callback/StopVoteCallback.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/callback/StopVoteCallback.kt @@ -2,9 +2,10 @@ package com.github.pool_party.resistance_bot.callback import com.elbekD.bot.Bot import com.elbekD.bot.types.CallbackQuery +import com.github.pool_party.flume.interaction.callback.Callback +import com.github.pool_party.flume.utils.editMessageReplyMarkupLogging import com.github.pool_party.resistance_bot.command.StopCommand.Companion.processResults import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.editMessageReplyMarkupLogging import com.github.pool_party.resistance_bot.utils.makeStopVoteMarkup import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -14,7 +15,7 @@ import mu.KotlinLogging @SerialName("stop") data class StopCallbackData(override val gameChatId: Long, val verdict: Boolean) : CallbackData() -class StopVoteCallback(private val stateStorage: StateStorage) : Callback { +class StopVoteCallback(private val stateStorage: StateStorage) : Callback { private val logger = KotlinLogging.logger {} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/Command.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/Command.kt deleted file mode 100644 index cb51486..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/Command.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.github.pool_party.resistance_bot.command - -import com.elbekD.bot.Bot -import com.elbekD.bot.types.BotCommand -import com.elbekD.bot.types.Message -import com.github.pool_party.resistance_bot.Interaction -import mu.KotlinLogging -import java.time.LocalDateTime -import kotlin.system.measureNanoTime - -enum class CommandType { - REGISTRATION, - GAME, - UTILS, -} - -interface Command : Interaction { - - /** - * Command name starting with "/". - */ - val command: String - - /** - * Command description that will be displayed in a small popup in Telegram as you type commands. - */ - val description: String - - /** - * Command help message that will be displayed on command /help - */ - val helpMessage: String - - /** - * Affects only indentation in /help command - */ - val commandType: CommandType - - fun toBotCommand() = BotCommand(command, description) -} - -abstract class AbstractCommand( - commandName: String, - override val description: String, - override val helpMessage: String, - override val commandType: CommandType, -) : Command { - - private val logger = KotlinLogging.logger {} - - override val command = "/$commandName" - - abstract suspend fun Bot.action(message: Message, args: List) - - override fun apply(bot: Bot) = bot.onCommand(command) { message, args -> - logger.info { - "${LocalDateTime.now()} $command <- ${message.from?.username}@${message.chat.title}: \"${message.text}\"" - } - - val nanoseconds = measureNanoTime { bot.action(message, parseArgs(args)) } - - logger.info { - "$command -> finished in ${nanoseconds / 1000000000}.${nanoseconds % 1000000000}s" - } - } - - private fun parseArgs(args: String?): List = - args?.split(' ')?.map { it.trim().lowercase() }?.filter { it.isNotBlank() }.orEmpty() -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/ExtendCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/ExtendCommand.kt index 4f553aa..f47faf8 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/ExtendCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/ExtendCommand.kt @@ -2,6 +2,9 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.message.HELP_EXTEND import com.github.pool_party.resistance_bot.message.ON_IN_GAME_EXTEND @@ -11,16 +14,13 @@ import com.github.pool_party.resistance_bot.message.onExtend import com.github.pool_party.resistance_bot.state.GameState import com.github.pool_party.resistance_bot.state.RegistrationState import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.chatId import com.github.pool_party.resistance_bot.utils.inc -import com.github.pool_party.resistance_bot.utils.sendMessageLogging class ExtendCommand(private val stateStorage: StateStorage) : AbstractCommand( "extend", "add extra ${Configuration.REGISTRATION_EXTEND.inWholeSeconds}s for registration", HELP_EXTEND, - CommandType.UTILS, ) { override suspend fun Bot.action(message: Message, args: List) { diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/GameCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/GameCommand.kt index 25a9a9a..113eb3e 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/GameCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/GameCommand.kt @@ -2,6 +2,10 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.logging +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.action.startGame import com.github.pool_party.resistance_bot.message.GET_PIN_RIGHTS_SUGGEST @@ -13,17 +17,14 @@ import com.github.pool_party.resistance_bot.message.onRegistrationTimestamp import com.github.pool_party.resistance_bot.state.Coder import com.github.pool_party.resistance_bot.state.StateStorage import com.github.pool_party.resistance_bot.utils.addBotMarkup -import com.github.pool_party.resistance_bot.utils.chatId import com.github.pool_party.resistance_bot.utils.dec -import com.github.pool_party.resistance_bot.utils.logging import com.github.pool_party.resistance_bot.utils.makeRegistrationMarkup -import com.github.pool_party.resistance_bot.utils.sendMessageLogging import kotlinx.coroutines.delay import java.util.concurrent.CompletableFuture import kotlin.time.Duration.Companion.minutes class GameCommand(private val stateStorage: StateStorage, private val longCoder: Coder) : - AbstractCommand("game", "start the registration", HELP_GAME, CommandType.REGISTRATION) { + AbstractCommand("game", "start the registration", HELP_GAME) { override suspend fun Bot.action(message: Message, args: List) { val chatId = message.chatId diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/HelpCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/HelpCommand.kt deleted file mode 100644 index e7b99b5..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/HelpCommand.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.pool_party.resistance_bot.command - -import com.elbekD.bot.Bot -import com.elbekD.bot.types.Message -import com.github.pool_party.resistance_bot.message.ON_HELP_ERROR -import com.github.pool_party.resistance_bot.message.helpMessage -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.sendMessageLogging - -class HelpCommand(commands: List) : - AbstractCommand( - "help", - "show this usage guide", - helpMessage( - commands.groupBy { it.commandType }.values.map { list -> list.associate { it.command to it.description } } - ), - CommandType.UTILS, - ) { - - private val helpMessages = commands.associate { it.command.removePrefix("/") to it.helpMessage } - - override suspend fun Bot.action(message: Message, args: List) { - sendMessageLogging( - message.chatId, - when { - args.isEmpty() -> helpMessage - args.size > 1 -> ON_HELP_ERROR - else -> helpMessages[args[0].removePrefix("/")] ?: ON_HELP_ERROR - }, - ) - } -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/HistoryCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/HistoryCommand.kt index 4b96dfc..a0220aa 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/HistoryCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/HistoryCommand.kt @@ -2,15 +2,16 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.message.HELP_HISTORY import com.github.pool_party.resistance_bot.message.ON_NO_REGISTRATION import com.github.pool_party.resistance_bot.message.history import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.sendMessageLogging class HistoryCommand(private val stateStorage: StateStorage) : - AbstractCommand("history", "show round history", HELP_HISTORY, CommandType.GAME) { + AbstractCommand("history", "show round history", HELP_HISTORY) { override suspend fun Bot.action(message: Message, args: List) { val chatId = message.chatId diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/RulesCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/RulesCommand.kt index 24a8b37..4ea708c 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/RulesCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/RulesCommand.kt @@ -2,13 +2,14 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.message.GAME_RULES import com.github.pool_party.resistance_bot.message.HELP_RULES -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.sendMessageLogging class RulesCommand : - AbstractCommand("rules", "show the list of rules", HELP_RULES, CommandType.UTILS) { + AbstractCommand("rules", "show the list of rules", HELP_RULES) { override suspend fun Bot.action(message: Message, args: List) { sendMessageLogging(message.chatId, GAME_RULES) diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/StartCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/StartCommand.kt index 0bac8f9..3b9b2bd 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/StartCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/StartCommand.kt @@ -2,6 +2,11 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.editMessageTextLogging +import com.github.pool_party.flume.utils.name +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.action.startGame import com.github.pool_party.resistance_bot.message.HELP_START @@ -15,18 +20,13 @@ import com.github.pool_party.resistance_bot.state.Coder import com.github.pool_party.resistance_bot.state.Member import com.github.pool_party.resistance_bot.state.StateStorage import com.github.pool_party.resistance_bot.utils.addBotMarkup -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.editMessageTextLogging import com.github.pool_party.resistance_bot.utils.makeRegistrationMarkup -import com.github.pool_party.resistance_bot.utils.name -import com.github.pool_party.resistance_bot.utils.sendMessageLogging class StartCommand(private val stateStorage: StateStorage, private val longCoder: Coder) : AbstractCommand( "start", "finish the registration and begin a game", HELP_START, - CommandType.REGISTRATION, ) { override suspend fun Bot.action(message: Message, args: List) = when { diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/StopCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/StopCommand.kt index 4fc12b0..2f271cc 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/StopCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/StopCommand.kt @@ -2,6 +2,11 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.deleteMessageLogging +import com.github.pool_party.flume.utils.name +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.message.HELP_STOP import com.github.pool_party.resistance_bot.message.ON_GAME_STOP @@ -14,11 +19,7 @@ import com.github.pool_party.resistance_bot.message.stopTheGameVote import com.github.pool_party.resistance_bot.state.GameState import com.github.pool_party.resistance_bot.state.RegistrationState import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.deleteMessageLogging import com.github.pool_party.resistance_bot.utils.makeStopVoteMarkup -import com.github.pool_party.resistance_bot.utils.name -import com.github.pool_party.resistance_bot.utils.sendMessageLogging import kotlinx.coroutines.delay import java.util.concurrent.ConcurrentHashMap @@ -27,7 +28,6 @@ class StopCommand(private val stateStorage: StateStorage) : "stop", "stop a game or registration", HELP_STOP, - CommandType.REGISTRATION, ) { override suspend fun Bot.action(message: Message, args: List) { diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/command/TagCommand.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/command/TagCommand.kt index e5583a3..210555a 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/command/TagCommand.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/command/TagCommand.kt @@ -2,15 +2,16 @@ package com.github.pool_party.resistance_bot.command import com.elbekD.bot.Bot import com.elbekD.bot.types.Message +import com.github.pool_party.flume.interaction.command.AbstractCommand +import com.github.pool_party.flume.utils.chatId +import com.github.pool_party.flume.utils.sendMessageLogging import com.github.pool_party.resistance_bot.message.HELP_TAG import com.github.pool_party.resistance_bot.message.ON_NO_REGISTRATION import com.github.pool_party.resistance_bot.message.tagAfkPlayers import com.github.pool_party.resistance_bot.state.StateStorage -import com.github.pool_party.resistance_bot.utils.chatId -import com.github.pool_party.resistance_bot.utils.sendMessageLogging class TagCommand(private val stateStorage: StateStorage) : - AbstractCommand("tag", "notify players delaying a game", HELP_TAG, CommandType.GAME) { + AbstractCommand("tag", "notify players delaying a game", HELP_TAG) { override suspend fun Bot.action(message: Message, args: List) { val chatId = message.chatId diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/BotUtils.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/utils/BotUtils.kt index fec7fdf..a836eee 100644 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/BotUtils.kt +++ b/src/main/kotlin/com/github/pool_party/resistance_bot/utils/BotUtils.kt @@ -2,22 +2,13 @@ package com.github.pool_party.resistance_bot.utils import com.elbekD.bot.types.InlineKeyboardButton import com.elbekD.bot.types.InlineKeyboardMarkup -import com.elbekD.bot.types.Message -import com.elbekD.bot.types.User +import com.github.pool_party.flume.utils.toMarkUp import com.github.pool_party.resistance_bot.Configuration import com.github.pool_party.resistance_bot.callback.StopCallbackData import com.github.pool_party.resistance_bot.message.INIT_MARKUP import com.github.pool_party.resistance_bot.message.REGISTRATION_BUTTON import com.github.pool_party.resistance_bot.message.VOTING_SUGGEST -val Message.chatId - get() = chat.id - -val User.name - get() = sequenceOf(first_name, last_name).filterNotNull().joinToString(" ") - -fun List.toMarkUp() = InlineKeyboardMarkup(listOf(this)) - fun makeRegistrationMarkup(hash: String) = listOf(InlineKeyboardButton(REGISTRATION_BUTTON, url = botLink(hash))).toMarkUp() diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/ConfigurationUtils.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/utils/ConfigurationUtils.kt deleted file mode 100644 index cab0329..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/ConfigurationUtils.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.pool_party.resistance_bot.utils - -import com.natpryce.konfig.ConfigurationProperties -import com.natpryce.konfig.EnvironmentVariables -import com.natpryce.konfig.Key -import com.natpryce.konfig.PropertyLocation -import com.natpryce.konfig.booleanType -import com.natpryce.konfig.intType -import com.natpryce.konfig.overriding -import com.natpryce.konfig.stringType -import kotlin.reflect.KProperty -import kotlin.time.Duration - -object ConfigurationUtils { - - private val configuration = EnvironmentVariables() - .overriding(ConfigurationProperties.fromResource("default.properties")) - .let { - val testProperties = "test.properties" - if (ClassLoader.getSystemClassLoader().getResource(testProperties) != null) - ConfigurationProperties.fromResource(testProperties) overriding it - else it - } - - fun boolean() = Configured(booleanType) - - fun int() = Configured(intType) - - fun string() = Configured(stringType) - - fun seconds() = ConfiguredDuration("seconds", Duration::seconds) - - private val KProperty.configName - get() = name.lowercase().replace('_', '.') - - class Configured(private val parse: (PropertyLocation, String) -> T) { - - private var value: T? = null - - operator fun getValue(thisRef: K, property: KProperty<*>): T { - if (value == null) { - value = configuration[Key(property.configName, parse)] - } - return value!! - } - - operator fun setValue(thisRef: K, property: KProperty<*>, value: T) { - this.value = value - } - } - - class ConfiguredDuration(private val name: String, private val duration: (Int) -> Duration) { - - private var value: Duration? = null - - operator fun getValue(thisRef: K, property: KProperty<*>): Duration { - if (value == null) { - value = duration(configuration[Key("${property.configName}.$name", intType)]) - } - return value!! - } - } -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/InitHandlers.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/utils/InitHandlers.kt deleted file mode 100644 index 512db2a..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/InitHandlers.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.pool_party.resistance_bot.utils - -import com.elbekD.bot.Bot -import com.github.pool_party.resistance_bot.Interaction -import com.github.pool_party.resistance_bot.callback.CallbackDispatcher -import com.github.pool_party.resistance_bot.callback.MissionVoteCallback -import com.github.pool_party.resistance_bot.callback.SquadChoiceCallback -import com.github.pool_party.resistance_bot.callback.SquadVoteCallback -import com.github.pool_party.resistance_bot.callback.StopVoteCallback -import com.github.pool_party.resistance_bot.command.Command -import com.github.pool_party.resistance_bot.command.ExtendCommand -import com.github.pool_party.resistance_bot.command.GameCommand -import com.github.pool_party.resistance_bot.command.HelpCommand -import com.github.pool_party.resistance_bot.command.HistoryCommand -import com.github.pool_party.resistance_bot.command.RulesCommand -import com.github.pool_party.resistance_bot.command.StartCommand -import com.github.pool_party.resistance_bot.command.StopCommand -import com.github.pool_party.resistance_bot.command.TagCommand -import com.github.pool_party.resistance_bot.state.Coder -import com.github.pool_party.resistance_bot.state.InMemoryStateStorage -import com.github.pool_party.resistance_bot.state.LongCoder -import com.github.pool_party.resistance_bot.state.StateStorage - -fun Bot.initHandlers() { - val stateStorage: StateStorage = InMemoryStateStorage() - val longCoder: Coder = LongCoder() - - val callbacks = listOf( - SquadChoiceCallback(stateStorage), - MissionVoteCallback(stateStorage), - SquadVoteCallback(stateStorage), - StopVoteCallback(stateStorage), - ) - - val interactions: MutableList = mutableListOf( - CallbackDispatcher(callbacks), - GameCommand(stateStorage, longCoder), - StartCommand(stateStorage, longCoder), - StopCommand(stateStorage), - TagCommand(stateStorage), - HistoryCommand(stateStorage), - ExtendCommand(stateStorage), - RulesCommand(), - ) - - val commands = interactions.mapNotNull { it as? Command }.toMutableList() - - val helpCommand = HelpCommand(commands) - commands += helpCommand - interactions.add(helpCommand) - - interactions.forEach { it.apply(this) } - setMyCommands(commands.map { it.toBotCommand() }) -} diff --git a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/MessageUtils.kt b/src/main/kotlin/com/github/pool_party/resistance_bot/utils/MessageUtils.kt deleted file mode 100644 index d215d57..0000000 --- a/src/main/kotlin/com/github/pool_party/resistance_bot/utils/MessageUtils.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.pool_party.resistance_bot.utils - -import com.elbekD.bot.Bot -import com.elbekD.bot.types.InlineKeyboardMarkup -import com.elbekD.bot.types.Message -import com.elbekD.bot.types.ReplyKeyboard -import mu.KotlinLogging -import java.util.concurrent.CompletableFuture - -private val logger = KotlinLogging.logger { } - -fun CompletableFuture.logging(prefix: String = ""): CompletableFuture = handleAsync { value, throwable -> - if (throwable != null) { - logger.error { "$prefix: ${throwable.message}:\n${throwable.stackTraceToString()}" } - throw throwable - } - value -} - -private fun String.escapeMarkdown() = replace("[-!.]".toRegex()) { "\\${it.groupValues[0]}" } - -fun Bot.sendMessageLogging( - chatId: Long, - text: String, - markup: ReplyKeyboard? = null, - replyTo: Int? = null -): CompletableFuture { - logger.debug { "Sending '$text'" } - return sendMessage(chatId, text.escapeMarkdown(), "MarkdownV2", replyTo = replyTo, markup = markup) - .logging("Failed to send message \"$text\"") -} - -fun Bot.deleteMessageLogging(chatId: Long, messageId: Int): CompletableFuture { - logger.debug { "Deleting message $chatId/$messageId" } - return deleteMessage(chatId, messageId).logging("Failed to delete message $chatId/$messageId") -} - -fun Bot.editMessageTextLogging( - chatId: Long, - messageId: Int, - text: String, - markup: InlineKeyboardMarkup? = null -): CompletableFuture { - logger.debug { "Editing '$text'" } - return editMessageText(chatId, messageId, text = text.escapeMarkdown(), parseMode = "MarkdownV2", markup = markup) - .logging("Failed to send message \"$text\"") -} - -fun Bot.editMessageReplyMarkupLogging( - chatId: Long, - messageId: Int, - markup: InlineKeyboardMarkup? = null -): CompletableFuture { - logger.debug { "Editing reply markup: '$markup'" } - return editMessageReplyMarkup(chatId, messageId, markup = markup) - .logging("Failed to edit reply markup \"$markup\"") -}