Compare commits

...

22 Commits

Author SHA1 Message Date
InsanusMokrassar 15551e4dd2
Merge pull request #1 from InsanusMokrassar/part/welcome
WelcomePlugin
2022-09-22 21:36:35 +06:00
InsanusMokrassar 1e6a882896
Merge branch 'master' into part/welcome 2022-09-22 21:36:21 +06:00
InsanusMokrassar eea5fcc360 fixes and improvements 2022-09-22 16:06:21 +06:00
InsanusMokrassar 7483988faa remove handling of welcome message sending errors 2022-09-22 15:24:53 +06:00
InsanusMokrassar 2ba35e7b9b replace unset of welcome message by logging of errors on welcome sending 2022-09-22 15:24:07 +06:00
InsanusMokrassar 8634007e06 small import fixes 2022-09-22 15:13:27 +06:00
InsanusMokrassar 0f77475434 add admins cache api usage 2022-09-22 15:00:40 +06:00
InsanusMokrassar 6cefafbd09 remove redundant imports in WelcomePlugin 2022-09-22 14:30:01 +06:00
InsanusMokrassar 05ddfadb03 update dependencies 2022-09-22 14:29:09 +06:00
InsanusMokrassar c951f074ae updates 2022-09-22 14:28:49 +06:00
InsanusMokrassar 226ef5df24
Update libs.versions.toml 2022-08-31 11:13:40 +06:00
InsanusMokrassar e1ef47e676
Update libs.versions.toml 2022-08-06 01:42:13 +06:00
InsanusMokrassar c0a49ba869
Update libs.versions.toml 2022-08-03 12:31:38 +06:00
InsanusMokrassar d747d8a6be update gradle version 2022-07-29 22:54:23 +06:00
InsanusMokrassar 3e2e4e8cfd
Update libs.versions.toml 2022-07-24 12:08:19 +06:00
InsanusMokrassar 4ee348190b
Update libs.versions.toml 2022-07-23 00:07:11 +06:00
InsanusMokrassar 33b2b4fbcb refactor after dependencies update 2022-07-11 13:07:49 +06:00
InsanusMokrassar cf0b214ee8 update dependencies 2022-07-11 13:04:22 +06:00
InsanusMokrassar c6dad9105e small refactor of welcome plugin 2022-07-11 01:33:53 +06:00
InsanusMokrassar 47a762c8ba fixes 2022-07-11 00:48:45 +06:00
InsanusMokrassar dc8b46dbd6
Update WelcomePlugin.kt 2022-07-10 01:08:55 +06:00
InsanusMokrassar 84f5b5da75 fill welcome plugin 2022-07-09 23:41:37 +06:00
9 changed files with 233 additions and 17 deletions

View File

@ -25,6 +25,7 @@ dependencies {
implementation libs.plagubot
implementation project(":introduction")
implementation project(":welcome")
}
application {

View File

@ -1,12 +1,15 @@
{
"botToken": "1234567890:ABCDEFGHIJKLMNOP_qrstuvwxyz12345678",
"plugins": [
"IntroductionPlugin"
"dev.inmo.plagubot.plugins.commands.CommandsPlugin",
"IntroductionPlugin",
"WelcomePlugin"
],
"introduction": {
"onStartCommandMessage": "Hello World"
},
"database": {
"url": "jdbc:sqlite:file:local.db?mode=memory&cache=shared"
"url": "jdbc:sqlite:file:local.db"
}
}

View File

@ -1,14 +1,18 @@
[versions]
kotlin = "1.6.21"
plagubot = "1.2.2"
kslog = "0.3.2"
kotlin = "1.7.10"
plagubot = "2.3.3"
kslog = "0.5.2"
plagubot-commands = "0.3.4"
tgbotapi-libraries = "0.5.4"
[libraries]
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
plagubot = { module = "dev.inmo:plagubot.bot", version.ref = "plagubot" }
plagubot-plugin = { module = "dev.inmo:plagubot.plugin", version.ref = "plagubot" }
plagubot-commands = { module = "dev.inmo:plagubot.plugins.commands", version.ref = "plagubot-commands" }
tgbotapi-libraries-admins = { module = "dev.inmo:tgbotapi.libraries.cache.admins.plagubot", version.ref = "tgbotapi-libraries" }
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
# Libs for classpath

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,3 +1,4 @@
rootProject.name = "tutorial"
include ":introduction"
include ":welcome"

View File

@ -23,4 +23,6 @@ dependencies {
implementation libs.kotlin
api libs.plagubot.plugin
api libs.kslog
api libs.plagubot.commands
api libs.tgbotapi.libraries.admins
}

View File

@ -1,17 +1,43 @@
import db.WelcomeTable
import dev.inmo.kslog.common.e
import dev.inmo.kslog.common.logger
import dev.inmo.kslog.common.w
import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.plugins.commands.full
import dev.inmo.tgbotapi.bot.exceptions.RequestException
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.delete
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMyChatMemberUpdated
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onNewChatMembers
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.ifCommonGroupContentMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.libraries.cache.admins.AdminsCacheAPI
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.MilliSeconds
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.message.abstracts.CommonGroupContentMessage
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.utils.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import model.ChatSettings
import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.core.qualifier.named
/**
* This is template of plugin with preset [log]ger, [Config] and template configurations of [setupDI] and [setupBotPlugin].
@ -30,31 +56,154 @@ class WelcomePlugin : Plugin {
* See realization of [setupDI] to get know how this class will be deserialized from global config
*
* See realization of [setupBotPlugin] to get know how to get access to this class
*
* @param recheckOfAdmin This parameter will be used before setup of
*/
@Serializable
private class Config(
val recheckOfAdmin: MilliSeconds = 60000L
)
/**
* DI configuration of current plugin. Here we are decoding [Config] and put it into [Module] receiver
*/
override fun Module.setupDI(database: Database, params: JsonObject) {
single { get<Json>().decodeFromJsonElement(Config.serializer(), params[pluginConfigSectionName] ?: return@single null) }
single { get<Json>().decodeFromJsonElement(Config.serializer(), params[pluginConfigSectionName] ?: return@single Config()) }
single { WelcomeTable(database) }
single(named("welcome")) { BotCommand("welcome", "Use to setup welcome message").full(BotCommandScope.AllChatAdministrators) }
}
private suspend fun BehaviourContext.handleWelcomeCommand(
adminsCacheAPI: AdminsCacheAPI,
welcomeTable: WelcomeTable,
config: Config,
groupMessage: CommonGroupContentMessage<MessageContent>
) {
val user = groupMessage.user
if (adminsCacheAPI.isAdmin(groupMessage.chat.id, user.id)) {
val previousMessage = welcomeTable.get(groupMessage.chat.id)
val sentMessage = send(
user,
replyMarkup = flatInlineKeyboard {
if (previousMessage != null) {
dataButton("Unset", unsetData)
}
dataButton("Cancel", cancelData)
}
) {
regular("Ok, send me the message which should be used as welcome message for chat ")
underline(groupMessage.chat.title)
}
oneOf(
parallel {
val query = waitMessageDataCallbackQuery().filter {
it.data == unsetData && it.message.sameMessage(sentMessage)
}.first()
edit(sentMessage) {
if (welcomeTable.unset(groupMessage.chat.id)) {
regular("Welcome message has been removed for chat ")
underline(groupMessage.chat.title)
} else {
regular("Something went wrong on welcome message unsetting for chat ")
underline(groupMessage.chat.title)
}
}
answer(query)
},
parallel {
val query = waitMessageDataCallbackQuery().filter {
it.data == cancelData && it.message.sameMessage(sentMessage)
}.first()
edit(sentMessage) {
regular("You have cancelled change of welcome message for chat ")
underline(groupMessage.chat.title)
}
answer(query)
},
parallel {
val message = waitContentMessage().filter {
it.sameChat(sentMessage)
}.first()
val success = welcomeTable.set(
ChatSettings(
groupMessage.chat.id,
message.chat.id,
message.messageId
)
)
reply(message) {
if (success) {
regular("Welcome message has been changed for chat ")
underline(groupMessage.chat.title)
regular(".\n\n")
bold("Please, do not delete this message if you want it to work and don't stop this bot to keep welcome message works right")
} else {
regular("Something went wrong on welcome message changing for chat ")
underline(groupMessage.chat.title)
}
}
delete(sentMessage)
},
parallel {
while (isActive) {
delay(config.recheckOfAdmin)
if (adminsCacheAPI.isAdmin(groupMessage.chat.id, user.id)) {
edit(sentMessage, "Sorry, but you are not admin in chat ${groupMessage.chat.title} anymore")
break
}
}
}
)
}
}
/**
* Final configuration of bot. Here we are getting [Config] from [koin]
*/
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
val config = koin.getOrNull<Config>()
val config = koin.get<Config>()
if (config == null) {
log.w("Plugin has been disabled due to absence of \"$pluginConfigSectionName\" field in config or some error during configuration loading")
return
val welcomeTable = koin.get<WelcomeTable>()
val adminsCacheAPI = koin.get<AdminsCacheAPI>()
onCommand(
"welcome",
initialFilter = { it.chat is GroupChat }
) {
it.ifCommonGroupContentMessage { groupMessage ->
launch {
handleWelcomeCommand(adminsCacheAPI, welcomeTable, config, groupMessage)
}
}
}
onNewChatMembers {
val chatSettings = welcomeTable.get(it.chat.id)
if (chatSettings == null) {
return@onNewChatMembers
}
reply(
it,
chatSettings.sourceChatId,
chatSettings.sourceMessageId
)
}
}
companion object {
private const val pluginConfigSectionName = "welcome"
private const val cancelData = "cancel"
private const val unsetData = "unset"
}
}

View File

@ -0,0 +1,44 @@
package db
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.tgbotapi.types.ChatId
import model.ChatSettings
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
internal class WelcomeTable(
override val database: Database
) : Table("welcome"), ExposedRepo {
val targetChatIdColumn = long("targetChatId").uniqueIndex()
val sourceChatIdColumn = long("sourceChatId")
val sourceMessageIdColumn = long("sourceMessageId")
override val primaryKey: PrimaryKey = PrimaryKey(targetChatIdColumn)
init {
initTable()
}
fun get(chatId: ChatId): ChatSettings? = transaction(database) {
select { targetChatIdColumn.eq(chatId.chatId) }.limit(1).firstOrNull() ?.let {
ChatSettings(
ChatId(it[targetChatIdColumn]),
ChatId(it[sourceChatIdColumn]),
it[sourceMessageIdColumn]
)
}
}
fun set(chatSettings: ChatSettings): Boolean = transaction(database) {
deleteWhere { targetChatIdColumn.eq(chatSettings.targetChatId.chatId) }
insert {
it[targetChatIdColumn] = chatSettings.targetChatId.chatId
it[sourceChatIdColumn] = chatSettings.sourceChatId.chatId
it[sourceMessageIdColumn] = chatSettings.sourceMessageId
}.insertedCount > 0
}
fun unset(chatId: ChatId): Boolean = transaction(database) {
deleteWhere { targetChatIdColumn.eq(chatId.chatId) } > 0
}
}

View File

@ -0,0 +1,12 @@
package model
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier
import kotlinx.serialization.Serializable
@Serializable
internal data class ChatSettings(
val targetChatId: ChatId,
val sourceChatId: ChatId,
val sourceMessageId: MessageIdentifier
)