mirror of
https://github.com/InsanusMokrassar/TelegramBotTutorial.git
synced 2024-12-22 06:07:11 +00:00
fill welcome plugin
This commit is contained in:
parent
5861b909cd
commit
84f5b5da75
@ -25,6 +25,7 @@ dependencies {
|
|||||||
implementation libs.plagubot
|
implementation libs.plagubot
|
||||||
|
|
||||||
implementation project(":introduction")
|
implementation project(":introduction")
|
||||||
|
implementation project(":welcome")
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"botToken": "1234567890:ABCDEFGHIJKLMNOP_qrstuvwxyz12345678",
|
"botToken": "1234567890:ABCDEFGHIJKLMNOP_qrstuvwxyz12345678",
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"IntroductionPlugin"
|
"dev.inmo.plagubot.plugins.commands.CommandsPlugin",
|
||||||
|
|
||||||
|
"IntroductionPlugin",
|
||||||
|
"WelcomePlugin"
|
||||||
],
|
],
|
||||||
"introduction": {
|
"introduction": {
|
||||||
"onStartCommandMessage": "Hello World"
|
"onStartCommandMessage": "Hello World"
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"url": "jdbc:sqlite:file:local.db?mode=memory&cache=shared"
|
"url": "jdbc:sqlite:file:local.db"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
[versions]
|
[versions]
|
||||||
|
|
||||||
kotlin = "1.6.21"
|
kotlin = "1.6.21"
|
||||||
plagubot = "1.2.2"
|
plagubot = "1.2.3"
|
||||||
kslog = "0.3.2"
|
kslog = "0.3.2"
|
||||||
|
plagubot-commands = "0.1.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|
||||||
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
kotlin = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||||
plagubot = { module = "dev.inmo:plagubot.bot", version.ref = "plagubot" }
|
plagubot = { module = "dev.inmo:plagubot.bot", version.ref = "plagubot" }
|
||||||
plagubot-plugin = { module = "dev.inmo:plagubot.plugin", 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" }
|
||||||
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
|
kslog = { module = "dev.inmo:kslog", version.ref = "kslog" }
|
||||||
|
|
||||||
# Libs for classpath
|
# Libs for classpath
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
rootProject.name = "tutorial"
|
rootProject.name = "tutorial"
|
||||||
|
|
||||||
include ":introduction"
|
include ":introduction"
|
||||||
|
include ":welcome"
|
||||||
|
@ -23,4 +23,5 @@ dependencies {
|
|||||||
implementation libs.kotlin
|
implementation libs.kotlin
|
||||||
api libs.plagubot.plugin
|
api libs.plagubot.plugin
|
||||||
api libs.kslog
|
api libs.kslog
|
||||||
|
api libs.plagubot.commands
|
||||||
}
|
}
|
||||||
|
11
welcome/src/main/kotlin/UserAdminChecker.kt
Normal file
11
welcome/src/main/kotlin/UserAdminChecker.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChatAdministrators
|
||||||
|
import dev.inmo.tgbotapi.types.chat.GroupChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.User
|
||||||
|
|
||||||
|
suspend fun TelegramBot.userIsAdmin(user: User, chat: GroupChat): Boolean {
|
||||||
|
val chatAdmins = getChatAdministrators(chat)
|
||||||
|
val chatAdminsIds = chatAdmins.map { adminMember -> adminMember.user.id }
|
||||||
|
|
||||||
|
return user.id in chatAdminsIds
|
||||||
|
}
|
@ -1,17 +1,40 @@
|
|||||||
|
import db.WelcomeTable
|
||||||
import dev.inmo.kslog.common.logger
|
import dev.inmo.kslog.common.logger
|
||||||
import dev.inmo.kslog.common.w
|
import dev.inmo.kslog.common.w
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.plagubot.Plugin
|
import dev.inmo.plagubot.Plugin
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.plagubot.plugins.commands.full
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
|
import dev.inmo.tgbotapi.bot.exceptions.RequestException
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChatAdministrators
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.edit.edit
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.*
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitContentMessage
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMyChatMemberUpdated
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
|
||||||
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.oneOf
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.formatting.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
||||||
|
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.types.message.content.TextContent
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
import model.ChatSettings
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.koin.core.Koin
|
import org.koin.core.Koin
|
||||||
import org.koin.core.module.Module
|
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].
|
* This is template of plugin with preset [log]ger, [Config] and template configurations of [setupDI] and [setupBotPlugin].
|
||||||
@ -30,27 +53,182 @@ class WelcomePlugin : Plugin {
|
|||||||
* See realization of [setupDI] to get know how this class will be deserialized from global config
|
* 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
|
* 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
|
@Serializable
|
||||||
private class Config(
|
private class Config(
|
||||||
|
val recheckOfAdmin: MilliSeconds = 60L
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DI configuration of current plugin. Here we are decoding [Config] and put it into [Module] receiver
|
* DI configuration of current plugin. Here we are decoding [Config] and put it into [Module] receiver
|
||||||
*/
|
*/
|
||||||
override fun Module.setupDI(database: Database, params: JsonObject) {
|
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(
|
||||||
|
welcomeTable: WelcomeTable,
|
||||||
|
config: Config,
|
||||||
|
groupMessage: CommonGroupContentMessage<MessageContent>
|
||||||
|
) {
|
||||||
|
val user = groupMessage.user
|
||||||
|
|
||||||
|
if (userIsAdmin(user, groupMessage.chat)) {
|
||||||
|
val cancelData = "cancel_${groupMessage.chat.id}"
|
||||||
|
val unsetData = "unset_${groupMessage.chat.id}"
|
||||||
|
|
||||||
|
val sentMessage = sendMessage(
|
||||||
|
user,
|
||||||
|
buildEntities {
|
||||||
|
regular("Ok, send me the message which should be used as welcome message for chat ")
|
||||||
|
underline(groupMessage.chat.title)
|
||||||
|
},
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
row {
|
||||||
|
dataButton("Unset", unsetData)
|
||||||
|
dataButton("Cancel", cancelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
oneOf(
|
||||||
|
async {
|
||||||
|
val query = waitMessageDataCallbackQuery().filter {
|
||||||
|
it.data == unsetData
|
||||||
|
&& it.message.chat.id == sentMessage.chat.id
|
||||||
|
&& it.message.messageId == sentMessage.messageId
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
if (welcomeTable.unset(groupMessage.chat.id)) {
|
||||||
|
edit(
|
||||||
|
sentMessage,
|
||||||
|
buildEntities {
|
||||||
|
regular("Welcome message has been removed for chat ")
|
||||||
|
underline(groupMessage.chat.title)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
edit(
|
||||||
|
sentMessage,
|
||||||
|
buildEntities {
|
||||||
|
regular("Something went wrong on welcome message unsetting for chat ")
|
||||||
|
underline(groupMessage.chat.title)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
answer(query)
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
val query = waitMessageDataCallbackQuery().filter {
|
||||||
|
it.data == cancelData
|
||||||
|
&& it.message.chat.id == sentMessage.chat.id
|
||||||
|
&& it.message.messageId == sentMessage.messageId
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
edit(
|
||||||
|
sentMessage,
|
||||||
|
buildEntities {
|
||||||
|
regular("You have cancelled change of welcome message for chat ")
|
||||||
|
underline(groupMessage.chat.title)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
answer(query)
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
val message = waitContentMessage().filter {
|
||||||
|
it.chat.id == sentMessage.chat.id
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
val success = welcomeTable.set(
|
||||||
|
ChatSettings(
|
||||||
|
groupMessage.chat.id,
|
||||||
|
message.chat.id,
|
||||||
|
message.messageId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
reply(
|
||||||
|
message,
|
||||||
|
buildEntities {
|
||||||
|
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 {
|
||||||
|
reply(
|
||||||
|
message,
|
||||||
|
buildEntities {
|
||||||
|
regular("Something went wrong on welcome message changing for chat ")
|
||||||
|
underline(groupMessage.chat.title)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
while (isActive) {
|
||||||
|
delay(config.recheckOfAdmin)
|
||||||
|
|
||||||
|
if (!userIsAdmin(user, groupMessage.chat)) {
|
||||||
|
edit(sentMessage, "Sorry, but you are not admin in chat ${groupMessage.chat.title} more")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Final configuration of bot. Here we are getting [Config] from [koin]
|
* Final configuration of bot. Here we are getting [Config] from [koin]
|
||||||
*/
|
*/
|
||||||
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
|
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
|
||||||
val config = koin.getOrNull<Config>()
|
val config = koin.get<Config>()
|
||||||
|
|
||||||
if (config == null) {
|
val welcomeTable = koin.get<WelcomeTable>()
|
||||||
log.w("Plugin has been disabled due to absence of \"$pluginConfigSectionName\" field in config or some error during configuration loading")
|
|
||||||
return
|
onCommand(
|
||||||
|
"welcome",
|
||||||
|
initialFilter = {
|
||||||
|
it.chat is GroupChat
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
it.whenCommonGroupContentMessage { groupMessage ->
|
||||||
|
launch {
|
||||||
|
handleWelcomeCommand(welcomeTable, config, groupMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewChatMembers {
|
||||||
|
val chatSettings = welcomeTable.get(it.chat.id)
|
||||||
|
|
||||||
|
if (chatSettings == null) {
|
||||||
|
return@onNewChatMembers
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
copyMessage(
|
||||||
|
it.chat.id,
|
||||||
|
chatSettings.sourceChatId,
|
||||||
|
chatSettings.sourceMessageId
|
||||||
|
)
|
||||||
|
} catch (e: RequestException) {
|
||||||
|
welcomeTable.unset(it.chat.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(scope) {
|
||||||
|
println(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
welcome/src/main/kotlin/db/WelcomeTable.kt
Normal file
44
welcome/src/main/kotlin/db/WelcomeTable.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
12
welcome/src/main/kotlin/model/ChatSettings.kt
Normal file
12
welcome/src/main/kotlin/model/ChatSettings.kt
Normal 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
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user