Merge pull request #235 from InsanusMokrassar/9.2.0

Migration onto 9.2.0
This commit is contained in:
InsanusMokrassar 2023-09-28 19:37:41 +06:00 committed by GitHub
commit d3d6cd16c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 431 additions and 51 deletions

View File

@ -18,4 +18,5 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version" implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation 'io.ktor:ktor-client-logging-jvm:2.3.3'
} }

View File

@ -1,34 +1,50 @@
import dev.inmo.micro_utils.coroutines.runCatchingSafely import dev.inmo.micro_utils.coroutines.firstOf
import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChannelAdministrator
import dev.inmo.tgbotapi.extensions.api.chat.members.restrictChatMember import dev.inmo.tgbotapi.extensions.api.chat.members.restrictChatMember
import dev.inmo.tgbotapi.extensions.api.edit.edit import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithFSMAndStartLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.utils.asContentMessage import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.asPossiblyReplyMessage import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.commonMessageOrNull import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.contentMessageOrNull import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.extensions.utils.extendedGroupChatOrNull
import dev.inmo.tgbotapi.extensions.utils.fromUserMessageOrNull
import dev.inmo.tgbotapi.extensions.utils.restrictedChatMemberOrNull
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
import dev.inmo.tgbotapi.extensions.utils.whenMemberChatMember
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
import dev.inmo.tgbotapi.types.chat.ChannelChat
import dev.inmo.tgbotapi.types.chat.ChatPermissions import dev.inmo.tgbotapi.types.chat.ChatPermissions
import dev.inmo.tgbotapi.types.chat.PublicChat import dev.inmo.tgbotapi.types.chat.PublicChat
import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.types.commands.BotCommandScope import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.toChatId import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.row import dev.inmo.tgbotapi.utils.*
import dev.inmo.tgbotapi.utils.mention
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapNotNull
sealed interface UserRetrievingStep : State {
data class RetrievingChannelChatState(
override val context: ChatId
) : UserRetrievingStep
data class RetrievingUserIdChatState(
override val context: ChatId,
val channelId: ChatId
) : UserRetrievingStep
data class RetrievingChatInfoDoneState(
override val context: ChatId,
val channelId: ChatId,
val userId: UserId
) : UserRetrievingStep
}
suspend fun main(args: Array<String>) { suspend fun main(args: Array<String>) {
val botToken = args.first() val botToken = args.first()
@ -60,20 +76,31 @@ suspend fun main(args: Array<String>) {
val otherMessagesToggleCommonData = "$commonDataPrefix other messages" val otherMessagesToggleCommonData = "$commonDataPrefix other messages"
val webPagePreviewToggleCommonData = "$commonDataPrefix web page preview" val webPagePreviewToggleCommonData = "$commonDataPrefix web page preview"
val adminRightsDataPrefix = "admin"
val refreshAdminRightsData = "${adminRightsDataPrefix}_refresh"
val postMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_post_messages"
val editMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_edit_messages"
val deleteMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_delete_messages"
val editStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_edit_stories"
val deleteStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_delete_stories"
val postStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_post_stories"
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? { suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
val chatMember = getChatMember(chatId, userId) val chatMember = getChatMember(chatId, userId)
return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember { return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember {
getChat(chatId).extendedGroupChatOrNull() ?.permissions getChat(chatId).extendedGroupChatOrNull() ?.permissions
} }
} }
fun buildGranularKeyboard(
suspend fun BehaviourContext.buildGranularKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? { permissions: ChatPermissions
val permissions = getUserChatPermissions(chatId, userId) ?: return null ): InlineKeyboardMarkup {
return inlineKeyboard { return inlineKeyboard {
row { row {
dataButton("Send messages${permissions.canSendMessages.allowedSymbol()}", messagesToggleGranularData) dataButton("Send messages${permissions.canSendMessages.allowedSymbol()}", messagesToggleGranularData)
dataButton("Send other messages${permissions.canSendOtherMessages.allowedSymbol()}", otherMessagesToggleGranularData) dataButton(
"Send other messages${permissions.canSendOtherMessages.allowedSymbol()}",
otherMessagesToggleGranularData
)
} }
row { row {
dataButton("Send audios${permissions.canSendAudios.allowedSymbol()}", audiosToggleGranularData) dataButton("Send audios${permissions.canSendAudios.allowedSymbol()}", audiosToggleGranularData)
@ -81,11 +108,17 @@ suspend fun main(args: Array<String>) {
} }
row { row {
dataButton("Send videos${permissions.canSendVideos.allowedSymbol()}", videosToggleGranularData) dataButton("Send videos${permissions.canSendVideos.allowedSymbol()}", videosToggleGranularData)
dataButton("Send video notes${permissions.canSendVideoNotes.allowedSymbol()}", videoNotesToggleGranularData) dataButton(
"Send video notes${permissions.canSendVideoNotes.allowedSymbol()}",
videoNotesToggleGranularData
)
} }
row { row {
dataButton("Send photos${permissions.canSendPhotos.allowedSymbol()}", photosToggleGranularData) dataButton("Send photos${permissions.canSendPhotos.allowedSymbol()}", photosToggleGranularData)
dataButton("Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}", webPagePreviewToggleGranularData) dataButton(
"Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}",
webPagePreviewToggleGranularData
)
} }
row { row {
dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleGranularData) dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleGranularData)
@ -93,6 +126,41 @@ suspend fun main(args: Array<String>) {
} }
} }
} }
fun buildAdminRightsKeyboard(
permissions: AdministratorChatMember?,
channelId: ChatId,
userId: UserId
): InlineKeyboardMarkup {
return inlineKeyboard {
permissions ?.also {
row {
dataButton("Refresh", "$refreshAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
row {
dataButton("Edit messages${permissions.canEditMessages.allowedSymbol()}", "$editMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
dataButton("Delete messages${permissions.canRemoveMessages.allowedSymbol()}", "$deleteMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
row {
dataButton("Post messages${permissions.canPostMessages.allowedSymbol()}", "$postMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
row {
dataButton("Edit stories${permissions.canEditStories.allowedSymbol()}", "$editStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
dataButton("Delete stories${permissions.canDeleteStories.allowedSymbol()}", "$deleteStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
row {
dataButton("Post stories${permissions.canPostStories.allowedSymbol()}", "$postStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
} ?: row {
dataButton("Promote to admin", "$postMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
}
}
}
suspend fun BehaviourContext.buildGranularKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? {
return buildGranularKeyboard(
getUserChatPermissions(chatId, userId) ?: return null
)
}
suspend fun BehaviourContext.buildCommonKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? { suspend fun BehaviourContext.buildCommonKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? {
val permissions = getUserChatPermissions(chatId, userId) ?: return null val permissions = getUserChatPermissions(chatId, userId) ?: return null
@ -110,23 +178,32 @@ suspend fun main(args: Array<String>) {
} }
} }
bot.buildBehaviourWithLongPolling( bot.buildBehaviourWithFSMAndStartLongPolling<UserRetrievingStep>(
defaultExceptionsHandler = { defaultExceptionsHandler = {
it.printStackTrace() it.printStackTrace()
} },
) { ) {
onCommand("simple", initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull() ?.user ?.id == allowedAdmin }) { onCommand(
"simple",
initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin }) {
val replyMessage = it.replyTo val replyMessage = it.replyTo
val userInReply = replyMessage ?.fromUserMessageOrNull() ?.user ?.id ?: return@onCommand val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
reply( reply(
replyMessage, replyMessage,
"Manage keyboard:", "Manage keyboard:",
replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
) )
} }
onCommand("granular", initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull() ?.user ?.id == allowedAdmin }) { onCommand(
"granular",
initialFilter = {
it.chat is ChannelChat || (it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin)
}
) {
val replyMessage = it.replyTo val replyMessage = it.replyTo
val userInReply = replyMessage ?.fromUserMessageOrNull() ?.user ?.id ?: return@onCommand val usernameInText = it.content.textSources.firstNotNullOfOrNull { it.mentionTextSourceOrNull() } ?.username
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
reply( reply(
replyMessage, replyMessage,
"Manage keyboard:", "Manage keyboard:",
@ -138,60 +215,72 @@ suspend fun main(args: Array<String>) {
Regex("^${granularDataPrefix}.*"), Regex("^${granularDataPrefix}.*"),
initialFilter = { it.user.id == allowedAdmin } initialFilter = { it.user.id == allowedAdmin }
) { ) {
val messageReply = it.message.commonMessageOrNull() ?.replyTo ?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery val messageReply =
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
val userId = messageReply.user.id val userId = messageReply.user.id
val permissions = getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery val permissions =
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
val newPermission = when (it.data) { val newPermission = when (it.data) {
messagesToggleGranularData -> { messagesToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendMessages = permissions.canSendMessages ?.let { !it } ?: false canSendMessages = permissions.canSendMessages?.let { !it } ?: false
) )
} }
otherMessagesToggleGranularData -> { otherMessagesToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendOtherMessages = permissions.canSendOtherMessages ?.let { !it } ?: false canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
) )
} }
audiosToggleGranularData -> { audiosToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendAudios = permissions.canSendAudios ?.let { !it } ?: false canSendAudios = permissions.canSendAudios?.let { !it } ?: false
) )
} }
voicesToggleGranularData -> { voicesToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendVoiceNotes = permissions.canSendVoiceNotes ?.let { !it } ?: false canSendVoiceNotes = permissions.canSendVoiceNotes?.let { !it } ?: false
) )
} }
videosToggleGranularData -> { videosToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendVideos = permissions.canSendVideos ?.let { !it } ?: false canSendVideos = permissions.canSendVideos?.let { !it } ?: false
) )
} }
videoNotesToggleGranularData -> { videoNotesToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendVideoNotes = permissions.canSendVideoNotes ?.let { !it } ?: false canSendVideoNotes = permissions.canSendVideoNotes?.let { !it } ?: false
) )
} }
photosToggleGranularData -> { photosToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendPhotos = permissions.canSendPhotos ?.let { !it } ?: false canSendPhotos = permissions.canSendPhotos?.let { !it } ?: false
) )
} }
webPagePreviewToggleGranularData -> { webPagePreviewToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canAddWebPagePreviews = permissions.canAddWebPagePreviews ?.let { !it } ?: false canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
) )
} }
pollsToggleGranularData -> { pollsToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendPolls = permissions.canSendPolls ?.let { !it } ?: false canSendPolls = permissions.canSendPolls?.let { !it } ?: false
) )
} }
documentsToggleGranularData -> { documentsToggleGranularData -> {
permissions.copyGranular( permissions.copyGranular(
canSendDocuments = permissions.canSendDocuments ?.let { !it } ?: false canSendDocuments = permissions.canSendDocuments?.let { !it } ?: false
) )
} }
else -> permissions.copyGranular() else -> permissions.copyGranular()
} }
@ -204,7 +293,8 @@ suspend fun main(args: Array<String>) {
edit( edit(
it.message, it.message,
replyMarkup = buildGranularKeyboard(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery replyMarkup = buildGranularKeyboard(it.message.chat.id.toChatId(), userId)
?: return@onMessageDataCallbackQuery
) )
} }
@ -212,25 +302,30 @@ suspend fun main(args: Array<String>) {
Regex("^${commonDataPrefix}.*"), Regex("^${commonDataPrefix}.*"),
initialFilter = { it.user.id == allowedAdmin } initialFilter = { it.user.id == allowedAdmin }
) { ) {
val messageReply = it.message.commonMessageOrNull() ?.replyTo ?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery val messageReply =
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
val userId = messageReply.user.id val userId = messageReply.user.id
val permissions = getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery val permissions =
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
val newPermission = when (it.data) { val newPermission = when (it.data) {
pollsToggleCommonData -> { pollsToggleCommonData -> {
permissions.copyCommon( permissions.copyCommon(
canSendPolls = permissions.canSendPolls ?.let { !it } ?: false canSendPolls = permissions.canSendPolls?.let { !it } ?: false
) )
} }
otherMessagesToggleCommonData -> { otherMessagesToggleCommonData -> {
permissions.copyCommon( permissions.copyCommon(
canSendOtherMessages = permissions.canSendOtherMessages ?.let { !it } ?: false canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
) )
} }
webPagePreviewToggleCommonData -> { webPagePreviewToggleCommonData -> {
permissions.copyCommon( permissions.copyCommon(
canAddWebPagePreviews = permissions.canAddWebPagePreviews ?.let { !it } ?: false canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
) )
} }
else -> permissions.copyCommon() else -> permissions.copyCommon()
} }
@ -243,13 +338,169 @@ suspend fun main(args: Array<String>) {
edit( edit(
it.message, it.message,
replyMarkup = buildCommonKeyboard(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery replyMarkup = buildCommonKeyboard(it.message.chat.id.toChatId(), userId)
?: return@onMessageDataCallbackQuery
) )
} }
onMessageDataCallbackQuery(
Regex("^${adminRightsDataPrefix}.*"),
initialFilter = { it.user.id == allowedAdmin }
) {
val (channelIdString, userIdString) = it.data.split(" ").drop(1)
val channelId = ChatId(channelIdString.toLong())
val userId = ChatId(userIdString.toLong())
val chatMember = getChatMember(channelId, userId)
val asAdmin = chatMember.administratorChatMemberOrNull()
val asMember = chatMember.memberChatMemberOrNull()
val realData = it.data.takeWhile { it != ' ' }
fun Boolean?.toggleIfData(data: String) = if (realData == data) {
!(this ?: false)
} else {
null
}
if (realData != refreshAdminRightsData) {
promoteChannelAdministrator(
channelId,
userId,
canPostMessages = asAdmin ?.canPostMessages.toggleIfData(postMessagesToggleAdminRightsData),
canEditMessages = asAdmin ?.canEditMessages.toggleIfData(editMessagesToggleAdminRightsData),
canDeleteMessages = asAdmin ?.canRemoveMessages.toggleIfData(deleteMessagesToggleAdminRightsData),
canEditStories = asAdmin ?.canEditStories.toggleIfData(editStoriesToggleAdminRightsData),
canDeleteStories = asAdmin ?.canDeleteStories.toggleIfData(deleteStoriesToggleAdminRightsData),
canPostStories = asAdmin ?.canPostStories.toggleIfData(postStoriesToggleAdminRightsData),
)
}
edit(
it.message,
replyMarkup = buildAdminRightsKeyboard(
getChatMember(
channelId,
userId
).administratorChatMemberOrNull(),
channelId,
userId
)
)
}
strictlyOn<UserRetrievingStep.RetrievingChannelChatState> { state ->
val requestId = RequestId.random()
send(
state.context,
replyMarkup = replyKeyboard(
oneTimeKeyboard = true,
resizeKeyboard = true
) {
row {
requestChatButton(
"Choose channel",
requestId = requestId,
isChannel = true,
botIsMember = true,
botRightsInChat = ChatCommonAdministratorRights(
canPromoteMembers = true,
canRestrictMembers = true
),
userRightsInChat = ChatCommonAdministratorRights(
canPromoteMembers = true,
canRestrictMembers = true
)
)
}
}
) {
regular("Ok, send me the channel in which you wish to manage user, or use ")
botCommand("cancel")
regular(" to cancel the request")
}
firstOf {
include {
val chatId = waitChatSharedEventsMessages().mapNotNull {
it.chatEvent.chatId.takeIf { _ ->
it.chatEvent.requestId == requestId && it.sameChat(state.context)
}
}.first()
UserRetrievingStep.RetrievingUserIdChatState(state.context, chatId)
}
include {
waitCommandMessage("cancel").filter { it.sameChat(state.context) }.first()
null
}
}
}
strictlyOn<UserRetrievingStep.RetrievingUserIdChatState> { state ->
val requestId = RequestId.random()
send(
state.context,
replyMarkup = replyKeyboard(
oneTimeKeyboard = true,
resizeKeyboard = true
) {
row {
requestUserButton(
"Choose user",
requestId = requestId
)
}
}
) {
regular("Ok, send me the user for which you wish to change rights, or use ")
botCommand("cancel")
regular(" to cancel the request")
}
firstOf {
include {
val userContactChatId = waitUserSharedEventsMessages().filter {
it.sameChat(state.context)
}.first().chatEvent.chatId
UserRetrievingStep.RetrievingChatInfoDoneState(
state.context,
state.channelId,
userContactChatId
)
}
include {
waitCommandMessage("cancel").filter { it.sameChat(state.context) }.first()
null
}
}
}
strictlyOn<UserRetrievingStep.RetrievingChatInfoDoneState> { state ->
val chatMember = getChatMember(state.channelId, state.userId).administratorChatMemberOrNull()
if (chatMember == null) {
return@strictlyOn null
}
send(
state.context,
replyMarkup = buildAdminRightsKeyboard(
chatMember,
state.channelId,
state.userId
)
) {
regular("Rights of ")
mentionln(chatMember.user)
regular("Please, remember, that to be able to change user rights bot must promote user by itself to admin")
}
null
}
onCommand("rights_in_channel") {
startChain(UserRetrievingStep.RetrievingChannelChatState(it.chat.id.toChatId()))
}
setMyCommands( setMyCommands(
BotCommand("simple", "Trigger simple keyboard. Use with reply to user"), BotCommand("simple", "Trigger simple keyboard. Use with reply to user"),
BotCommand("granular", "Trigger granular keyboard. Use with reply to user"), BotCommand("granular", "Trigger granular keyboard. Use with reply to user"),
BotCommand("rights_in_channel", "Trigger granular keyboard. Use with reply to user"),
scope = BotCommandScope.AllGroupChats scope = BotCommandScope.AllGroupChats
) )
}.join() }.join()

View File

@ -1,6 +1,7 @@
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.types.webAppQueryIdField import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.webapps.* import dev.inmo.tgbotapi.webapps.*
import dev.inmo.tgbotapi.webapps.cloud.*
import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackStyle import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackStyle
import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackType import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackType
import dev.inmo.tgbotapi.webapps.popup.* import dev.inmo.tgbotapi.webapps.popup.*
@ -14,8 +15,11 @@ import kotlinx.browser.window
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.dom.appendElement import kotlinx.dom.appendElement
import kotlinx.dom.appendText import kotlinx.dom.appendText
import kotlinx.dom.clear
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.w3c.dom.HTMLElement import org.w3c.dom.*
import kotlin.random.Random
import kotlin.random.nextUBytes
fun HTMLElement.log(text: String) { fun HTMLElement.log(text: String) {
appendText(text) appendText(text)
@ -70,6 +74,9 @@ fun main() {
appendText("Exit button") appendText("Exit button")
} ?: window.alert("Unable to load body") } ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendText("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
document.body ?.appendElement("p", {}) document.body ?.appendElement("p", {})
document.body ?.appendText("Alerts:") document.body ?.appendText("Alerts:")
@ -110,6 +117,32 @@ fun main() {
appendText("Alert") appendText("Alert")
} ?: window.alert("Unable to load body") } ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestWriteAccess() })
appendText("Request write access without callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestWriteAccess { document.body ?.log("Write access request result: $it") } })
appendText("Request write access with callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestContact() })
appendText("Request contact without callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestContact { document.body ?.log("Contact request result: $it") } })
appendText("Request contact with callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") { document.body ?.appendElement("button") {
addEventListener("click", { addEventListener("click", {
webApp.showConfirm( webApp.showConfirm(
@ -142,6 +175,91 @@ fun main() {
document.body ?.appendElement("p", {}) document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
fun updateHeaderColor() {
val (r, g, b) = Random.nextUBytes(3)
val hex = Color.Hex(r, g, b)
webApp.setHeaderColor(hex)
(this as? HTMLButtonElement) ?.style ?.backgroundColor = hex.value
textContent = "Header color: ${hex.value.uppercase()} (click to change)"
}
addEventListener("click", {
updateHeaderColor()
})
updateHeaderColor()
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
fun Element.updateCloudStorageContent() {
clear()
webApp.cloudStorage.getAll {
it.onSuccess {
document.body ?.log(it.toString())
appendElement("label") { textContent = "Cloud storage" }
appendElement("p", {})
it.forEach { (k, v) ->
appendElement("div") {
val kInput = appendElement("input", {}) as HTMLInputElement
val vInput = appendElement("input", {}) as HTMLInputElement
kInput.value = k.key
vInput.value = v.value
appendElement("button") {
addEventListener("click", {
if (k.key == kInput.value) {
webApp.cloudStorage.set(k.key, vInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
} else {
webApp.cloudStorage.remove(k.key) {
it.onSuccess {
webApp.cloudStorage.set(kInput.value, vInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
}
}
}
})
this.textContent = "Save"
}
}
appendElement("p", {})
}
appendElement("label") { textContent = "Cloud storage: add new" }
appendElement("p", {})
appendElement("div") {
val kInput = appendElement("input", {}) as HTMLInputElement
appendElement("button") {
textContent = "Add key"
addEventListener("click", {
webApp.cloudStorage.set(kInput.value, kInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
})
}
}
appendElement("p", {})
}.onFailure {
document.body ?.log(it.stackTraceToString())
}
}
}
val cloudStorageContentDiv = document.body ?.appendElement("div") {} as HTMLDivElement
document.body ?.appendElement("p", {})
webApp.apply { webApp.apply {
onThemeChanged { onThemeChanged {
document.body ?.log("Theme changed: ${webApp.themeParams}") document.body ?.log("Theme changed: ${webApp.themeParams}")
@ -171,8 +289,18 @@ fun main() {
onSettingsButtonClicked { onSettingsButtonClicked {
document.body ?.log("Settings button clicked") document.body ?.log("Settings button clicked")
} }
onWriteAccessRequested {
document.body ?.log("Write access request result: $it")
}
onContactRequested {
document.body ?.log("Contact request result: $it")
}
} }
webApp.ready() webApp.ready()
document.body ?.appendElement("input", {
(this as HTMLInputElement).value = window.location.href
})
cloudStorageContentDiv.updateCloudStorageContent()
}.onFailure { }.onFailure {
window.alert(it.stackTraceToString()) window.alert(it.stackTraceToString())
} }

View File

@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx2g
kotlin_version=1.8.22 kotlin_version=1.8.22
telegram_bot_api_version=9.1.2 telegram_bot_api_version=9.2.0
micro_utils_version=0.19.9 micro_utils_version=0.19.9
serialization_version=1.5.1 serialization_version=1.5.1
ktor_version=2.3.3 ktor_version=2.3.3