mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2025-09-08 17:59:17 +00:00
411 lines
19 KiB
Kotlin
411 lines
19 KiB
Kotlin
import dev.inmo.kslog.common.KSLog
|
|
import dev.inmo.kslog.common.LogLevel
|
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
|
import dev.inmo.kslog.common.setDefaultKSLog
|
|
import dev.inmo.micro_utils.common.Percentage
|
|
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
import dev.inmo.tgbotapi.extensions.api.business.deleteBusinessMessages
|
|
import dev.inmo.tgbotapi.extensions.api.business.readBusinessMessage
|
|
import dev.inmo.tgbotapi.extensions.api.business.removeBusinessAccountProfilePhoto
|
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountBio
|
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountName
|
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountProfilePhoto
|
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountUsername
|
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
|
import dev.inmo.tgbotapi.extensions.api.chat.modify.pinChatMessage
|
|
import dev.inmo.tgbotapi.extensions.api.chat.modify.unpinChatMessage
|
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
|
|
import dev.inmo.tgbotapi.extensions.api.get.getBusinessConnection
|
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
|
import dev.inmo.tgbotapi.extensions.api.stories.deleteStory
|
|
import dev.inmo.tgbotapi.extensions.api.stories.postStory
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
|
import dev.inmo.tgbotapi.extensions.utils.commonMessageOrNull
|
|
import dev.inmo.tgbotapi.extensions.utils.extendedPrivateChatOrThrow
|
|
import dev.inmo.tgbotapi.extensions.utils.ifAccessibleMessage
|
|
import dev.inmo.tgbotapi.extensions.utils.ifBusinessContentMessage
|
|
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
|
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
|
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
|
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
|
import dev.inmo.tgbotapi.requests.abstracts.multipartFile
|
|
import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto
|
|
import dev.inmo.tgbotapi.requests.stories.PostStory
|
|
import dev.inmo.tgbotapi.types.ChatId
|
|
import dev.inmo.tgbotapi.types.MessageId
|
|
import dev.inmo.tgbotapi.types.RawChatId
|
|
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
|
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
|
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
|
import dev.inmo.tgbotapi.types.message.content.PhotoContent
|
|
import dev.inmo.tgbotapi.types.message.content.StoryContent
|
|
import dev.inmo.tgbotapi.types.message.content.TextContent
|
|
import dev.inmo.tgbotapi.types.message.content.VideoContent
|
|
import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupPartContent
|
|
import dev.inmo.tgbotapi.types.stories.InputStoryContent
|
|
import dev.inmo.tgbotapi.types.stories.StoryArea
|
|
import dev.inmo.tgbotapi.types.stories.StoryAreaPosition
|
|
import dev.inmo.tgbotapi.types.stories.StoryAreaType
|
|
import dev.inmo.tgbotapi.utils.botCommand
|
|
import dev.inmo.tgbotapi.utils.code
|
|
import dev.inmo.tgbotapi.utils.row
|
|
import korlibs.time.seconds
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.delay
|
|
import kotlinx.coroutines.sync.Mutex
|
|
import kotlinx.coroutines.sync.withLock
|
|
import kotlinx.serialization.json.Json
|
|
|
|
suspend fun main(args: Array<String>) {
|
|
val botToken = args.first()
|
|
val isDebug = args.getOrNull(1) == "debug"
|
|
|
|
if (isDebug) {
|
|
setDefaultKSLog(
|
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
|
}
|
|
)
|
|
}
|
|
|
|
val businessConnectionsChats = mutableMapOf<BusinessConnectionId, ChatId>()
|
|
val chatsBusinessConnections = mutableMapOf<ChatId, BusinessConnectionId>()
|
|
val businessConnectionsChatsMutex = Mutex()
|
|
|
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
|
val me = getMe()
|
|
println(me)
|
|
|
|
onBusinessConnectionEnabled {
|
|
businessConnectionsChatsMutex.withLock {
|
|
businessConnectionsChats[it.id] = it.userChatId
|
|
chatsBusinessConnections[it.userChatId] = it.id
|
|
}
|
|
send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been enabled")
|
|
}
|
|
onBusinessConnectionDisabled {
|
|
businessConnectionsChatsMutex.withLock {
|
|
businessConnectionsChats.remove(it.id)
|
|
chatsBusinessConnections.remove(it.userChatId)
|
|
}
|
|
send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been disabled")
|
|
}
|
|
|
|
onContentMessage {
|
|
it.ifBusinessContentMessage { businessContentMessage ->
|
|
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/pin") == true) {
|
|
businessContentMessage.replyTo ?.ifAccessibleMessage {
|
|
pinChatMessage(it)
|
|
return@ifBusinessContentMessage
|
|
}
|
|
}
|
|
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/unpin") == true) {
|
|
businessContentMessage.replyTo ?.ifAccessibleMessage {
|
|
unpinChatMessage(it)
|
|
return@ifBusinessContentMessage
|
|
}
|
|
}
|
|
val sent = execute(it.content.createResend(businessContentMessage.from.id))
|
|
if (businessContentMessage.sentByBusinessConnectionOwner) {
|
|
reply(sent, "You have sent this message to the ${businessContentMessage.businessConnectionId.string} related chat")
|
|
} else {
|
|
reply(
|
|
to = sent,
|
|
text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
|
|
)
|
|
send(
|
|
chatId = businessConnectionsChats[it.businessConnectionId] ?: return@ifBusinessContentMessage,
|
|
text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
|
|
replyMarkup = inlineKeyboard {
|
|
row {
|
|
dataButton("Read message", "read ${it.chat.id.chatId.long} ${it.messageId.long}")
|
|
dataButton("Delete message", "delete ${it.chat.id.chatId.long} ${it.messageId.long}")
|
|
}
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
onEditedContentMessage {
|
|
it.ifBusinessContentMessage { businessContentMessage ->
|
|
val sent = execute(businessContentMessage.content.createResend(businessContentMessage.from.id))
|
|
if (businessContentMessage.sentByBusinessConnectionOwner) {
|
|
reply(sent, "You have edited this message in the ${businessContentMessage.businessConnectionId.string} related chat")
|
|
} else {
|
|
reply(sent, "User have edited this message to you in the ${businessContentMessage.businessConnectionId.string} related chat")
|
|
}
|
|
}
|
|
}
|
|
onBusinessMessagesDeleted {
|
|
var businessConnectionOwnerChat = businessConnectionsChatsMutex.withLock {
|
|
businessConnectionsChats[it.businessConnectionId]
|
|
}
|
|
if (businessConnectionOwnerChat == null) {
|
|
val businessConnection = getBusinessConnection(it.businessConnectionId)
|
|
businessConnectionsChatsMutex.withLock {
|
|
businessConnectionsChats[businessConnection.businessConnectionId] = businessConnection.userChatId
|
|
}
|
|
businessConnectionOwnerChat = businessConnection.userChatId
|
|
}
|
|
send(businessConnectionOwnerChat, "There are several removed messages in chat ${it.chat.id}: ${it.messageIds}")
|
|
}
|
|
onCommand("get_business_account_info", initialFilter = { it.chat is PrivateChat }) {
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id]
|
|
val businessConnectionInfo = businessConnectionId ?.let { getBusinessConnection(it) }
|
|
reply(it) {
|
|
if (businessConnectionInfo == null) {
|
|
+"There is no business connection for current chat"
|
|
} else {
|
|
+(Json { prettyPrint = true; encodeDefaults = true }.encodeToString(businessConnectionInfo))
|
|
}
|
|
}
|
|
}
|
|
onMessageDataCallbackQuery(Regex("read \\d+ \\d+")) {
|
|
val (_, chatIdString, messageIdString) = it.data.split(" ")
|
|
val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
|
|
val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
|
|
val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
|
|
|
|
val readResponse = businessConnectionId ?.let { readBusinessMessage(it, chatId, messageId) }
|
|
answer(
|
|
it,
|
|
if (readResponse == null) {
|
|
"There is no business connection for current chat"
|
|
} else {
|
|
"Message has been read"
|
|
}
|
|
)
|
|
}
|
|
onMessageDataCallbackQuery(Regex("delete \\d+ \\d+")) {
|
|
val (_, chatIdString, messageIdString) = it.data.split(" ")
|
|
val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
|
|
val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
|
|
val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
|
|
|
|
val readResponse = businessConnectionId ?.let { deleteBusinessMessages(it, listOf(messageId)) }
|
|
answer(
|
|
it,
|
|
if (readResponse == null) {
|
|
"There is no business connection for current chat"
|
|
} else {
|
|
"Message has been deleted"
|
|
}
|
|
)
|
|
}
|
|
onCommandWithArgs("set_business_account_name", initialFilter = { it.chat is PrivateChat }) { it, args ->
|
|
val firstName = args[0]
|
|
val secondName = args.getOrNull(1)
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
|
|
val set = runCatching {
|
|
setBusinessAccountName(
|
|
businessConnectionId,
|
|
firstName,
|
|
secondName
|
|
)
|
|
}.getOrElse { false }
|
|
reply(it) {
|
|
if (set) {
|
|
+"Account name has been set"
|
|
} else {
|
|
+"Account name has not been set"
|
|
}
|
|
}
|
|
}
|
|
onCommandWithArgs("set_business_account_username", initialFilter = { it.chat is PrivateChat }) { it, args ->
|
|
val username = args[0]
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
|
|
val set = runCatching {
|
|
setBusinessAccountUsername(
|
|
businessConnectionId,
|
|
username
|
|
)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (set) {
|
|
+"Account username has been set"
|
|
} else {
|
|
+"Account username has not been set"
|
|
}
|
|
}
|
|
}
|
|
onCommand("set_business_account_bio", requireOnlyCommandInMessage = false, initialFilter = { it.chat is PrivateChat }) {
|
|
val initialBio = getChat(it.chat).extendedPrivateChatOrThrow().bio
|
|
val bio = it.content.text.removePrefix("/set_business_account_bio").trim()
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
|
val set = runCatching {
|
|
setBusinessAccountBio(
|
|
businessConnectionId,
|
|
bio
|
|
)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (set) {
|
|
+"Account bio has been set. It will be reset within 15 seconds.\n\nInitial bio: " + code(initialBio)
|
|
} else {
|
|
+"Account bio has not been set"
|
|
}
|
|
}
|
|
delay(15.seconds)
|
|
val reset = runCatching {
|
|
setBusinessAccountBio(
|
|
businessConnectionId,
|
|
initialBio
|
|
)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (reset) {
|
|
+"Account bio has been reset"
|
|
} else {
|
|
+"Account bio has not been set. Set it manually: " + code(initialBio)
|
|
}
|
|
}
|
|
}
|
|
suspend fun handleSetProfilePhoto(it: CommonMessage<TextContent>, isPublic: Boolean) {
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@handleSetProfilePhoto
|
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<PhotoContent>()
|
|
if (replyTo == null) {
|
|
reply(it) {
|
|
+"Reply to photo for using of this command"
|
|
}
|
|
return@handleSetProfilePhoto
|
|
}
|
|
|
|
val set = runCatching {
|
|
val file = downloadFileToTemp(replyTo.content)
|
|
setBusinessAccountProfilePhoto(
|
|
businessConnectionId,
|
|
InputProfilePhoto.Static(
|
|
file.multipartFile()
|
|
),
|
|
isPublic = isPublic
|
|
)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (set) {
|
|
+"Account profile photo has been set. It will be reset within 15 seconds"
|
|
} else {
|
|
+"Account profile photo has not been set"
|
|
}
|
|
}
|
|
if (set == false) { return@handleSetProfilePhoto }
|
|
delay(15.seconds)
|
|
val reset = runCatching {
|
|
removeBusinessAccountProfilePhoto(
|
|
businessConnectionId,
|
|
isPublic = isPublic
|
|
)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (reset) {
|
|
+"Account profile photo has been reset"
|
|
} else {
|
|
+"Account profile photo has not been set. Set it manually"
|
|
}
|
|
}
|
|
}
|
|
onCommand("set_business_account_profile_photo", initialFilter = { it.chat is PrivateChat }) {
|
|
handleSetProfilePhoto(it, false)
|
|
}
|
|
onCommand("set_business_account_profile_photo_public", initialFilter = { it.chat is PrivateChat }) {
|
|
handleSetProfilePhoto(it, true)
|
|
}
|
|
|
|
onCommand("post_story", initialFilter = { it.chat is PrivateChat }) {
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<VisualMediaGroupPartContent>()
|
|
if (replyTo == null) {
|
|
reply(it) {
|
|
+"Reply to photo or video for using of this command"
|
|
}
|
|
return@onCommand
|
|
}
|
|
|
|
val posted = runCatching {
|
|
val file = downloadFileToTemp(replyTo.content)
|
|
postStory(
|
|
businessConnectionId,
|
|
when (replyTo.content) {
|
|
is PhotoContent -> InputStoryContent.Photo(
|
|
file.multipartFile()
|
|
)
|
|
is VideoContent -> InputStoryContent.Video(
|
|
file.multipartFile()
|
|
)
|
|
},
|
|
activePeriod = PostStory.ACTIVE_PERIOD_6_HOURS,
|
|
areas = listOf(
|
|
StoryArea(
|
|
StoryAreaPosition(
|
|
x = Percentage.of100(50.0),
|
|
y = Percentage.of100(50.0),
|
|
width = Percentage.of100(8.0),
|
|
height = Percentage.of100(8.0),
|
|
rotationAngle = 45.0,
|
|
cornerRadius = Percentage.of100(4.0),
|
|
),
|
|
StoryAreaType.Link(
|
|
"https://github.com/InsanusMokrassar/TelegramBotAPI-examples/blob/master/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt"
|
|
)
|
|
)
|
|
)
|
|
) {
|
|
+"It is test of postStory :)"
|
|
}
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
null
|
|
}
|
|
reply(it) {
|
|
if (posted != null) {
|
|
+"Story has been posted. You may unpost it with " + botCommand("remove_story")
|
|
} else {
|
|
+"Story has not been posted"
|
|
}
|
|
}
|
|
}
|
|
|
|
onCommand("delete_story", initialFilter = { it.chat is PrivateChat }) {
|
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<StoryContent>()
|
|
if (replyTo == null) {
|
|
reply(it) {
|
|
+"Reply to photo or video for using of this command"
|
|
}
|
|
return@onCommand
|
|
}
|
|
|
|
val deleted = runCatching {
|
|
deleteStory(businessConnectionId, replyTo.content.story.id)
|
|
}.getOrElse {
|
|
it.printStackTrace()
|
|
false
|
|
}
|
|
reply(it) {
|
|
if (deleted) {
|
|
+"Story has been deleted"
|
|
} else {
|
|
+"Story has not been deleted"
|
|
}
|
|
}
|
|
}
|
|
}.second.join()
|
|
} |