Compare commits

..

2 Commits

Author SHA1 Message Date
f54a05b3e9 update dependendencies 2022-11-10 17:22:53 +06:00
d4ceafaa3d start update up to 4.1.0 2022-11-10 16:55:45 +06:00
62 changed files with 92 additions and 2440 deletions

View File

@@ -8,12 +8,9 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install dependencies - name: Set up JDK 11
run: |
sudo apt install -y libcurl4-openssl-dev
- name: Set up JDK 17
uses: actions/setup-java@v1 uses: actions/setup-java@v1
with: with:
java-version: 17 java-version: 11
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build

3
.gitignore vendored
View File

@@ -10,6 +10,3 @@ build/
out/ out/
kotlin-js-store/ kotlin-js-store/
local.*
local.*/

View File

@@ -1,9 +0,0 @@
# UserChatShared
Showing info about boosts
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="BoostsInfoKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,65 +0,0 @@
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.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.get.getUserChatBoosts
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatBoostUpdated
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.regular
import korlibs.time.DateFormat
import korlibs.time.format
suspend fun main(args: Array<String>) {
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 requestChatId = RequestId(1)
val bot = telegramBot(args.first())
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
onChatBoostUpdated {
println(it)
}
onCommand("start") {
reply(
it,
replyMarkup = flatReplyKeyboard {
requestChannelButton(
"Click me :)",
requestChatId,
botIsMember = true
)
}
) {
regular("Select chat to get know about your boosts")
}
}
onChatShared(initialFilter = { it.chatEvent.requestId == requestChatId }) {
val boosts = getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
reply(
it
) {
boosts.boosts.forEach {
regular("Boost added: ${DateFormat.FORMAT1.format(it.addDate.asDate)}; Boost expire: ${DateFormat.FORMAT1.format(it.expirationDate.asDate)}; Unformatted: $it") + "\n"
}
}
}
}.join()
}

View File

@@ -17,17 +17,12 @@ suspend fun main(vararg args: String) {
telegramBotWithBehaviourAndLongPolling(botToken) { telegramBotWithBehaviourAndLongPolling(botToken) {
val me = bot.getMe() val me = bot.getMe()
val username = me.username
println(me) println(me)
if (username == null) {
error("Unable to start bot work: it have no username")
}
onText( onText(
initialFilter = { it.content.textSources.none { it is BotCommandTextSource } } // excluding messages with commands initialFilter = { it.content.textSources.none { it is BotCommandTextSource } } // excluding messages with commands
) { ) {
reply(it, makeTelegramDeepLink(username, it.content.text)) reply(it, makeTelegramDeepLink(me.username, it.content.text))
} }
onCommand("start", requireOnlyCommandInMessage = true) { // handling of `start` without args onCommand("start", requireOnlyCommandInMessage = true) { // handling of `start` without args

View File

@@ -8,7 +8,7 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread
import dev.inmo.tgbotapi.extensions.utils.formatting.* import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.IdChatIdentifier import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageThreadId import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
@@ -19,8 +19,8 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
sealed interface BotState : State sealed interface BotState : State
data class ExpectContentOrStopState(override val context: IdChatIdentifier, val sourceMessage: CommonMessage<TextContent>) : BotState data class ExpectContentOrStopState(override val context: Pair<ChatId, MessageThreadId?>, val sourceMessage: CommonMessage<TextContent>) : BotState
data class StopState(override val context: IdChatIdentifier) : BotState data class StopState(override val context: Pair<ChatId, MessageThreadId?>) : BotState
suspend fun main(args: Array<String>) { suspend fun main(args: Array<String>) {
val botToken = args.first() val botToken = args.first()
@@ -43,12 +43,13 @@ suspend fun main(args: Array<String>) {
) { ) {
strictlyOn<ExpectContentOrStopState> { strictlyOn<ExpectContentOrStopState> {
send( send(
it.context, it.context.first,
threadId = it.context.second
) { ) {
+"Send me some content or " + botCommand("stop") + " if you want to stop sending" +"Send me some content or " + botCommand("stop") + " if you want to stop sending"
} }
val contentMessage = waitAnyContentMessage().filter { message -> val contentMessage = waitContentMessage().filter { message ->
message.sameThread(it.sourceMessage) message.sameThread(it.sourceMessage)
}.first() }.first()
val content = contentMessage.content val content = contentMessage.content
@@ -56,13 +57,13 @@ suspend fun main(args: Array<String>) {
when { when {
content is TextContent && content.parseCommandsWithParams().keys.contains("stop") -> StopState(it.context) content is TextContent && content.parseCommandsWithParams().keys.contains("stop") -> StopState(it.context)
else -> { else -> {
execute(content.createResend(it.context)) execute(content.createResend(it.context.first, messageThreadId = it.context.second))
it it
} }
} }
} }
strictlyOn<StopState> { strictlyOn<StopState> {
send(it.context) { +"You have stopped sending of content" } send(it.context.first, threadId = it.context.second) { +"You have stopped sending of content" }
null null
} }
@@ -70,7 +71,7 @@ suspend fun main(args: Array<String>) {
command( command(
"start" "start"
) { ) {
startChain(ExpectContentOrStopState(it.chat.id, it)) startChain(ExpectContentOrStopState(it.chat.id to it.threadIdOrNull, it))
} }
}.second.join() }.second.join()
} }

View File

@@ -1,39 +1,9 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.files.downloadFile import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.replyWithAnimation
import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio
import dev.inmo.tgbotapi.extensions.api.send.replyWithDocument
import dev.inmo.tgbotapi.extensions.api.send.replyWithMediaGroup
import dev.inmo.tgbotapi.extensions.api.send.replyWithPhoto
import dev.inmo.tgbotapi.extensions.api.send.replyWithSticker
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideo
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideoNote
import dev.inmo.tgbotapi.extensions.api.send.replyWithVoice
import dev.inmo.tgbotapi.extensions.api.send.withAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.send.SendAction
import dev.inmo.tgbotapi.types.actions.BotAction
import dev.inmo.tgbotapi.types.actions.TypingAction
import dev.inmo.tgbotapi.types.media.TelegramMediaAudio
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
import dev.inmo.tgbotapi.types.message.content.AnimationContent
import dev.inmo.tgbotapi.types.message.content.AudioContent
import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.PhotoContent
import dev.inmo.tgbotapi.types.message.content.StickerContent
import dev.inmo.tgbotapi.types.message.content.VideoContent
import dev.inmo.tgbotapi.types.message.content.VideoNoteContent
import dev.inmo.tgbotapi.types.message.content.VoiceContent
import dev.inmo.tgbotapi.utils.filenameFromUrl import dev.inmo.tgbotapi.utils.filenameFromUrl
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -48,76 +18,16 @@ suspend fun main(args: Array<String>) {
directoryOrFile.mkdirs() directoryOrFile.mkdirs()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) { telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
onCommand("start") {
reply(it, "Send me any media (like photo or video) to download it")
}
onMedia(initialFilter = null) { onMedia(initialFilter = null) {
val content = it.content val pathedFile = bot.getFileAdditionalInfo(it.content.media)
val pathedFile = bot.getFileAdditionalInfo(content.media)
val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl) val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl)
runCatching { runCatching {
bot.downloadFile(content.media, outFile) bot.downloadFile(it.content.media, outFile)
}.onFailure { }.onFailure {
it.printStackTrace() it.printStackTrace()
}.onSuccess { _ ->
reply(it, "Saved to ${outFile.absolutePath}")
withAction(it.chat.id, TypingAction) {
when (content) {
is PhotoContent -> replyWithPhoto(
it,
outFile.asMultipartFile()
)
is AnimationContent -> replyWithAnimation(
it,
outFile.asMultipartFile()
)
is VideoContent -> replyWithVideo(
it,
outFile.asMultipartFile()
)
is StickerContent -> replyWithSticker(
it,
outFile.asMultipartFile()
)
is MediaGroupContent<*> -> replyWithMediaGroup(
it,
content.group.map {
when (val innerContent = it.content) {
is AudioContent -> TelegramMediaAudio(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is DocumentContent -> TelegramMediaDocument(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is PhotoContent -> TelegramMediaPhoto(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is VideoContent -> TelegramMediaVideo(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
}
}
)
is AudioContent -> replyWithAudio(
it,
outFile.asMultipartFile()
)
is DocumentContent -> replyWithDocument(
it,
outFile.asMultipartFile()
)
is VoiceContent -> replyWithVoice(
it,
outFile.asMultipartFile()
)
is VideoNoteContent -> replyWithVideoNote(
it,
outFile.asMultipartFile()
)
}
}
} }
reply(it, "Saved to ${outFile.absolutePath}")
} }
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) } onContentMessage { println(it) }
}.second.join() }.second.join()
} }

View File

@@ -1,14 +1,12 @@
import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.formatting.makeLink import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.chat.CommonBot import dev.inmo.tgbotapi.types.chat.CommonBot
import dev.inmo.tgbotapi.types.chat.CommonUser import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.ExtendedBot import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.message.* import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.code import dev.inmo.tgbotapi.utils.code
import dev.inmo.tgbotapi.utils.link
import dev.inmo.tgbotapi.utils.regular import dev.inmo.tgbotapi.utils.regular
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -42,14 +40,7 @@ suspend fun main(vararg args: String) {
is ExtendedBot -> regular("Bot ") is ExtendedBot -> regular("Bot ")
} + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username?.username ?: "Without username"})" } + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username?.username ?: "Without username"})"
} }
is ForwardInfo.PublicChat.FromChannel -> { is ForwardInfo.PublicChat.FromChannel -> regular("Channel (") + code(forwardInfo.channelChat.title) + ")"
regular("Channel (") + (forwardInfo.channelChat.username ?.let {
link(
forwardInfo.channelChat.title,
makeLink(it)
)
} ?: code(forwardInfo.channelChat.title)) + ")"
}
is ForwardInfo.PublicChat.FromSupergroup -> regular("Supergroup (") + code(forwardInfo.group.title) + ")" is ForwardInfo.PublicChat.FromSupergroup -> regular("Supergroup (") + code(forwardInfo.group.title) + ")"
is ForwardInfo.PublicChat.SentByChannel -> regular("Sent by channel (") + code(forwardInfo.channelChat.title) + ")" is ForwardInfo.PublicChat.SentByChannel -> regular("Sent by channel (") + code(forwardInfo.channelChat.title) + ")"
} }

View File

@@ -1,31 +1,24 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.filter.filtered
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat import dev.inmo.tgbotapi.extensions.api.chat.forum.closeForumTopic
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog import dev.inmo.tgbotapi.extensions.api.chat.forum.createForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.deleteForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.reopenForumTopic
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicClosed
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.CustomEmojiId
import dev.inmo.tgbotapi.types.ForumTopic
import kotlinx.coroutines.delay
/** /**
* This is one of the most easiest bot - it will just print information about itself * This is one of the most easiest bot - it will just print information about itself
*/ */
suspend fun main(vararg args: String) { suspend fun main(vararg args: String) {
val botToken = args.first() 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 bot = telegramBot(botToken) val bot = telegramBot(botToken)
val me = bot.getMe() println(bot.getMe())
println(me)
println(bot.getChat(me))
} }

View File

@@ -1,10 +1,8 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.* import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMentionWithAnyContent
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2 import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2 import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
@@ -27,25 +25,24 @@ suspend fun main(vararg args: String) {
val botToken = args.first() val botToken = args.first()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) { telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe() onContentMessage { message ->
onMentionWithAnyContent(me) { message ->
val chat = message.chat val chat = message.chat
val answerText = when (val chat = message.chat) { val answerText = when (val chat = message.chat) {
is PreviewChannelChat -> { is ChannelChat -> {
val answer = "Hi everybody in this channel \"${chat.title}\"" val answer = "Hi everybody in this channel \"${chat.title}\""
reply(message, answer, MarkdownV2) reply(message, answer, MarkdownV2)
return@onMentionWithAnyContent return@onContentMessage
} }
is PreviewPrivateChat -> { is PrivateChat -> {
reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2) reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2)
return@onMentionWithAnyContent return@onContentMessage
} }
is PreviewGroupChat -> { is GroupChat -> {
message.ifFromChannelGroupContentMessage { message.ifFromChannelGroupContentMessage {
val answer = "Hi, ${it.senderChat.title}" val answer = "Hi, ${it.senderChat.title}"
reply(message, answer, MarkdownV2) reply(message, answer, MarkdownV2)
return@onMentionWithAnyContent return@onContentMessage
} }
"Oh, hi, " + when (chat) { "Oh, hi, " + when (chat) {
is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let { is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let {
@@ -56,7 +53,9 @@ suspend fun main(vararg args: String) {
} ?: chat.title } ?: chat.title
} }
} }
is UnknownExtendedChat,
is UnknownChatType -> "Unknown :(".escapeMarkdownV2Common() is UnknownChatType -> "Unknown :(".escapeMarkdownV2Common()
else -> error("Something went wrong: unknown type of chat $chat")
} }
reply( reply(
message, message,

View File

@@ -1,9 +0,0 @@
# InlineQueriesBot
This bot will form the inline queries for you. For that feature you should explicitly enable inline queries in bot settings
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,38 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
}
apply plugin: 'application'
mainClassName="InlineQueriesBotKt"
apply from: "$nativePartTemplate"
kotlin {
jvm()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
}
}
}
}
dependencies {
implementation 'io.ktor:ktor-client-logging-jvm:2.3.10'
}

View File

@@ -1,67 +0,0 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDeepLink
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.inlineQueryAnswerResultsLimit
import dev.inmo.tgbotapi.utils.buildEntities
/**
* Thi bot will create inline query answers. You
* should enable inline queries in bot settings
*/
suspend fun doInlineQueriesBot(token: String) {
val bot = telegramBot(token)
bot.buildBehaviourWithLongPolling(
defaultExceptionsHandler = { it.printStackTrace() },
) {
onBaseInlineQuery {
val page = it.offset.toIntOrNull() ?: 0
val results = (0 until inlineQueryAnswerResultsLimit.last).map {
(page * inlineQueryAnswerResultsLimit.last) + it
}
answer(
it,
results = results.map { resultNumber ->
val resultAsString = resultNumber.toString()
InlineQueryResultArticle(
resultAsString,
"Title $resultNumber",
InputTextMessageContent(
buildEntities {
+"Result text of " + resultNumber.toString() + " result:\n"
+it.query
}
),
description = "Description of $resultNumber result"
)
},
cachedTime = 0,
isPersonal = true,
button = InlineQueryResultsButton.Start(
"Text of button with page $page",
"deep_link_for_page_$page"
),
nextOffset = (page + 1).toString()
)
}
onDeepLink { (message, deepLink) ->
reply(message, deepLink)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
println(getMe())
}.join()
}

View File

@@ -1,5 +0,0 @@
import dev.inmo.micro_utils.common.MPPFile
suspend fun main(args: Array<String>) {
doInlineQueriesBot(args.first())
}

View File

@@ -1,7 +0,0 @@
import kotlinx.coroutines.runBlocking
fun main(args: Array<String>) {
runBlocking {
doInlineQueriesBot(args.first())
}
}

View File

@@ -4,17 +4,12 @@ import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.answers.answer import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.edit.edit import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.editMessageText
import dev.inmo.tgbotapi.extensions.api.edit.reply_markup.editMessageReplyMarkup
import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText
import dev.inmo.tgbotapi.extensions.api.send.* import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.withContent import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.* import dev.inmo.tgbotapi.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -60,16 +55,6 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
dataButton(">>", "$count $count") dataButton(">>", "$count $count")
} }
} }
row {
inlineQueryInChosenChatButton(
"Send somebody page",
query = "$page $count",
allowUsers = true,
allowBots = true,
allowGroups = true,
allowChannels = true,
)
}
} }
suspend fun activateKeyboardsBot( suspend fun activateKeyboardsBot(
@@ -86,7 +71,9 @@ suspend fun activateKeyboardsBot(
reply( reply(
message, message,
replyMarkup = inlineKeyboard { replyMarkup = inlineKeyboard {
includePageButtons(1, numberOfPages) row {
includePageButtons(1, numberOfPages)
}
} }
) { ) {
regular("Your inline keyboard with $numberOfPages pages") regular("Your inline keyboard with $numberOfPages pages")
@@ -105,48 +92,15 @@ suspend fun activateKeyboardsBot(
return@onMessageDataCallbackQuery return@onMessageDataCallbackQuery
}, },
replyMarkup = inlineKeyboard { replyMarkup = inlineKeyboard {
includePageButtons(page, count) row {
includePageButtons(page, count)
}
} }
) { ) {
regular("This is $page of $count") regular("This is $page of $count")
} }
answer(it) answer(it)
} }
onInlineMessageIdDataCallbackQuery {
val (page, count) = it.data.parsePageAndCount() ?: it.let {
answer(it, "Unsupported data :(")
return@onInlineMessageIdDataCallbackQuery
}
editMessageText(
it.inlineMessageId,
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
) {
regular("This is $page of $count")
}
answer(it)
}
onBaseInlineQuery {
val page = it.query.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
answer(
it,
results = listOf(
InlineQueryResultArticle(
it.query,
"Send buttons",
InputTextMessageContent("It is sent via inline mode inline buttons"),
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
)
)
)
}
onUnhandledCommand { onUnhandledCommand {
reply( reply(

View File

@@ -1,9 +0,0 @@
# ReactionsInfoBot
This bot will resend messages with links with all variants of `LinkPreviewOptions`
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="LinkPreviewsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,93 +0,0 @@
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.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.copyMessage
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionsCountUpdated
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.textLinkTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.uRLTextSourceOrNull
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.LinkPreviewOptions
import dev.inmo.tgbotapi.types.chat.ExtendedChat
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.message.content.TextedContent
import dev.inmo.tgbotapi.types.reactions.Reaction
import dev.inmo.tgbotapi.utils.customEmoji
import dev.inmo.tgbotapi.utils.regular
/**
* This bot will reply with the same
*/
suspend fun main(vararg args: 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 bot = telegramBot(botToken)
bot.buildBehaviourWithLongPolling {
onContentMessage {
val url = it.withContentOrNull<TextedContent>() ?.let {
it.content.textSources.firstNotNullOfOrNull {
it.textLinkTextSourceOrNull() ?.url ?: it.uRLTextSourceOrNull() ?.source
}
} ?: null.apply {
reply(it) {
regular("I am support only content with text contains url only")
}
} ?: return@onContentMessage
it.withContentOrNull<TextedContent>() ?.let {
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Disabled
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Large(url, showAboveText = true)
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Large(url, showAboveText = false)
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Small(url, showAboveText = true)
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Small(url, showAboveText = false)
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Default(url, showAboveText = true)
)
send(
it.chat,
it.content.textSources,
linkPreviewOptions = LinkPreviewOptions.Default(url, showAboveText = false)
)
}
}
}.join()
}

View File

@@ -1,9 +0,0 @@
# LiveLocationsBot
This bot will send you live location and update it from time to time
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="LiveLocationsBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,85 +0,0 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.EditLiveLocationInfo
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.location.live.stopLiveLocation
import dev.inmo.tgbotapi.extensions.api.handleLiveLocation
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.oneOf
import dev.inmo.tgbotapi.extensions.behaviour_builder.parallel
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.chat.SupergroupChat
import dev.inmo.tgbotapi.types.location.LiveLocation
import dev.inmo.tgbotapi.types.message.MarkdownV2
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.LiveLocationContent
import dev.inmo.tgbotapi.types.message.content.LocationContent
import dev.inmo.tgbotapi.utils.PreviewFeature
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
/**
* This bot will send you live location and update it from time to time
*/
suspend fun main(vararg args: String) {
val botToken = args.first()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val locationsFlow = flow {
var i = 0
while (isActive) {
val newInfo = EditLiveLocationInfo(
latitude = i.toDouble(),
longitude = i.toDouble(),
replyMarkup = flatInlineKeyboard {
dataButton("Cancel", "cancel")
}
)
emit(newInfo)
i++
delay(3000L) // 3 seconds
}
}
onCommand("start") {
// in this flow will be actual message with live location
val currentMessageState = MutableStateFlow<ContentMessage<LocationContent>?>(null)
val sendingJob = launch {
handleLiveLocation(
it.chat.id,
locationsFlow,
sentMessageFlow = FlowCollector { currentMessageState.emit(it) }
)
}
waitMessageDataCallbackQuery().filter {
it.message.sameMessage(
currentMessageState.value ?: return@filter false
) && it.data == "cancel"
}.first()
sendingJob.cancel() // ends live location
currentMessageState.value ?.let {
stopLiveLocation(it, replyMarkup = null)
}
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

View File

@@ -1,11 +0,0 @@
# PollsBot
This bot will send test poll in the chat where commands will be received. Commands:
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="HelloBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,100 +0,0 @@
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.PollIdentifier
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.chat.SupergroupChat
import dev.inmo.tgbotapi.types.message.MarkdownV2
import dev.inmo.tgbotapi.types.polls.Poll
import dev.inmo.tgbotapi.types.polls.PollAnswer
import dev.inmo.tgbotapi.types.polls.PollOption
import dev.inmo.tgbotapi.types.polls.RegularPoll
import dev.inmo.tgbotapi.utils.PreviewFeature
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* This bot will answer with anonymous or public poll and send message on
* updates of any of it.
*
* * Use `/anonymous` to take anonymous regular poll
* * Use `/public` to take public regular poll
*/
@OptIn(PreviewFeature::class)
suspend fun main(vararg args: String) {
val botToken = args.first()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
val pollToChat = mutableMapOf<PollIdentifier, IdChatIdentifier>()
val pollToChatMutex = Mutex()
onCommand("anonymous") {
val sentPoll = sendRegularPoll(
it.chat,
"Test regular anonymous poll",
(1 .. 10).map {
it.toString()
},
isAnonymous = true,
replyParameters = ReplyParameters(it)
)
pollToChatMutex.withLock {
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
}
}
onCommand("public") {
val sentPoll = sendRegularPoll(
it.chat,
"Test regular anonymous poll",
(1 .. 10).map {
it.toString()
},
isAnonymous = false,
replyParameters = ReplyParameters(it)
)
pollToChatMutex.withLock {
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
}
}
onPollAnswer {
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
when(it) {
is PollAnswer.Public -> send(chatId, "[onPollAnswer] User ${it.user} have answered")
is PollAnswer.Anonymous -> send(chatId, "[onPollAnswer] Chat ${it.voterChat} have answered")
}
}
onPollUpdates {
val chatId = pollToChat[it.id] ?: return@onPollUpdates
when(it.isAnonymous) {
false -> send(chatId, "[onPollUpdates] Public poll updated: ${it.options.joinToString()}")
true -> send(chatId, "[onPollUpdates] Anonymous poll updated: ${it.options.joinToString()}")
}
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

View File

@@ -4,8 +4,6 @@ This repository contains several examples of simple bots which are using Telegra
## How to use this repository ## How to use this repository
***TO RUN NATIVE TARGETS ON LINUX YOU SHOULD INSTALL CURL LIBRARY. FOR EXAMPLE: `sudo apt install libcurl4-gnutls-dev`***
This repository contains several important things: This repository contains several important things:
* Example subprojects * Example subprojects

View File

@@ -8,27 +8,14 @@ buildscript {
} }
} }
plugins { apply plugin: 'kotlin'
id "org.jetbrains.kotlin.multiplatform"
}
apply plugin: 'application' apply plugin: 'application'
mainClassName="RandomFileSenderBotKt" mainClassName="RandomFileSenderBotKt"
kotlin {
jvm()
sourceSets { dependencies {
commonMain { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
dependencies {
implementation kotlin('stdlib')
api "dev.inmo:tgbotapi:$telegram_bot_api_version" implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}
}
}
} }
apply from: "$nativePartTemplate"

View File

@@ -1,10 +0,0 @@
import dev.inmo.micro_utils.common.MPPFile
import java.io.File
actual fun pickFile(currentRoot: MPPFile): File? {
if (currentRoot.isFile) {
return currentRoot
} else {
return pickFile(currentRoot.listFiles() ?.takeIf { it.isNotEmpty() } ?.random() ?: return null)
}
}

View File

@@ -1,5 +0,0 @@
import dev.inmo.micro_utils.common.MPPFile
suspend fun main(args: Array<String>) {
doRandomFileSenderBot(args.first(), MPPFile(args.getOrNull(1) ?: ""))
}

View File

@@ -1,25 +1,23 @@
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filesize import dev.inmo.micro_utils.common.filesize
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocument import dev.inmo.tgbotapi.extensions.api.send.media.sendDocument
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.withUploadDocumentAction
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.files.DocumentFile
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
import java.io.File
private const val command = "send_file" private const val command = "send_file"
expect fun pickFile(currentRoot: MPPFile): MPPFile?
/** /**
* This bot will send files inside of working directory OR from directory in the second argument. * This bot will send files inside of working directory OR from directory in the second argument.
* You may send /send_file command to this bot to get random file from the directory OR * You may send /send_file command to this bot to get random file from the directory OR
@@ -27,10 +25,19 @@ expect fun pickFile(currentRoot: MPPFile): MPPFile?
* /send_file and `/send_file 1` will have the same effect - bot will send one random file. * /send_file and `/send_file 1` will have the same effect - bot will send one random file.
* But if you will send `/send_file 5` it will choose 5 random files and send them as group * But if you will send `/send_file 5` it will choose 5 random files and send them as group
*/ */
suspend fun doRandomFileSenderBot(token: String, folder: MPPFile) { suspend fun main(args: Array<String>) {
val bot = telegramBot(token) val botToken = args.first()
val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("")
suspend fun TelegramBot.sendFiles(chat: Chat, files: List<MPPFile>) { fun pickFile(currentRoot: File = directoryOrFile): File? {
if (currentRoot.isFile) {
return currentRoot
} else {
return pickFile(currentRoot.listFiles() ?.takeIf { it.isNotEmpty() } ?.random() ?: return null)
}
}
suspend fun TelegramBot.sendFiles(chat: Chat, files: List<File>) {
when (files.size) { when (files.size) {
1 -> sendDocument( 1 -> sendDocument(
chat.id, chat.id,
@@ -45,6 +52,8 @@ suspend fun doRandomFileSenderBot(token: String, folder: MPPFile) {
} }
} }
val bot = telegramBot(botToken)
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) { bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommandWithArgs(command) { message, args -> onCommandWithArgs(command) { message, args ->
@@ -53,10 +62,10 @@ suspend fun doRandomFileSenderBot(token: String, folder: MPPFile) {
var sent = false var sent = false
var left = count var left = count
val chosen = mutableListOf<MPPFile>() val chosen = mutableListOf<File>()
while (left > 0) { while (left > 0) {
val picked = pickFile(folder) ?.takeIf { it.filesize > 0 } ?: continue val picked = pickFile() ?.takeIf { it.filesize > 0 } ?: continue
chosen.add(picked) chosen.add(picked)
left-- left--
if (chosen.size >= mediaCountInMediaGroup.last) { if (chosen.size >= mediaCountInMediaGroup.last) {

View File

@@ -1,10 +0,0 @@
import dev.inmo.micro_utils.common.MPPFile
import okio.FileSystem
actual fun pickFile(currentRoot: MPPFile): MPPFile? {
if (FileSystem.SYSTEM.exists(currentRoot) && FileSystem.SYSTEM.listOrNull(currentRoot) == null) {
return currentRoot
} else {
return pickFile(FileSystem.SYSTEM.list(currentRoot).takeIf { it.isNotEmpty() } ?.random() ?: return null)
}
}

View File

@@ -1,8 +0,0 @@
import kotlinx.coroutines.runBlocking
import okio.Path.Companion.toPath
fun main(args: Array<String>) {
runBlocking {
doRandomFileSenderBot(args.first(), args.getOrNull(1) ?.toPath() ?: "".toPath())
}
}

View File

@@ -1,9 +0,0 @@
# ReactionsInfoBot
This bot will send info about user reactions in his PM with reply to message user reacted to
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="ReactionsInfoBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,68 +0,0 @@
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.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
import dev.inmo.tgbotapi.extensions.api.send.setMessageReactions
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionsCountUpdated
import dev.inmo.tgbotapi.types.chat.ExtendedChat
import dev.inmo.tgbotapi.types.reactions.Reaction
import dev.inmo.tgbotapi.utils.customEmoji
import dev.inmo.tgbotapi.utils.regular
/**
* This bot will send info about user reactions in his PM with reply to message user reacted to
*/
suspend fun main(vararg args: 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 bot = telegramBot(botToken)
bot.buildBehaviourWithLongPolling {
onChatMessageReactionUpdatedByUser {
setMessageReaction(
it.chat.id,
it.messageId,
""
)
val replyResult = reply(
it.chat.id,
it.messageId,
replyInChatId = it.reactedUser.id
) {
regular("Current reactions for message in reply:\n")
it.new.forEach {
when (it) {
is Reaction.CustomEmoji -> regular("") + customEmoji(it.customEmojiId) + regular("(customEmojiId: ${it.customEmojiId})")
is Reaction.Emoji -> regular("${it.emoji}")
is Reaction.Unknown -> regular("• Unknown emoji ($it)")
}
regular("\n")
}
}
setMessageReaction(
it.chat.id,
it.messageId,
)
}
onChatMessageReactionsCountUpdated {
val extendedChat: ExtendedChat = getChat(it.chat)
println(extendedChat)
println(it)
}
}.join()
}

View File

@@ -20,9 +20,6 @@ kotlin {
browser() browser()
binaries.executable() binaries.executable()
} }
linuxX64()
mingwX64()
linuxArm64()
sourceSets { sourceSets {
commonMain { commonMain {

View File

@@ -8,10 +8,6 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilte
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.shortcuts.* import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.quoteEntitiesField
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -19,7 +15,11 @@ suspend fun activateResenderBot(
token: String, token: String,
print: (Any) -> Unit print: (Any) -> Unit
) { ) {
telegramBotWithBehaviourAndLongPolling(token, scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())) { val bot = telegramBot(token)
print(bot.getMe())
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onContentMessage( onContentMessage(
subcontextUpdatesFilter = MessageFilterByChat, subcontextUpdatesFilter = MessageFilterByChat,
) { ) {
@@ -30,14 +30,7 @@ suspend fun activateResenderBot(
it.content.createResend( it.content.createResend(
chat.id, chat.id,
messageThreadId = it.threadIdOrNull, messageThreadId = it.threadIdOrNull,
replyParameters = it.replyInfo ?.messageMeta ?.let { meta -> replyToMessageId = it.messageId
val quote = it.withContentOrNull<TextContent>() ?.content ?.quote
ReplyParameters(
meta,
entities = quote ?.textSources ?: emptyList(),
quotePosition = quote ?.position
)
}
) )
) { ) {
it.forEach(print) it.forEach(print)
@@ -50,6 +43,5 @@ suspend fun activateResenderBot(
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it) println(it)
} }
print(bot.getMe()) }.join()
}.second.join()
} }

View File

@@ -1,28 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
}
apply from: "$nativePartTemplate"
kotlin {
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api project(":ResenderBot:ResenderBotLib")
}
}
}
}

View File

@@ -1,9 +0,0 @@
import kotlinx.coroutines.runBlocking
fun main(vararg args: String) {
runBlocking {
activateResenderBot(args.first()) {
println(it)
}
}
}

View File

@@ -1,12 +0,0 @@
# RightsChanger
All the commands should be called with reply to some common user.
* Use `/simple` with bot to get request buttons for non-independent permissions change
* Use `/granular` with bot to get request buttons for independent permissions change
## Launch
```bash
../gradlew run --args="BOT_TOKEN allowed_user_id_long"
```

View File

@@ -1,22 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="RightsChangerKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation 'io.ktor:ktor-client-logging-jvm:2.3.10'
}

View File

@@ -1,540 +0,0 @@
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.coroutines.firstOf
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
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.promoteChannelAdministrator
import dev.inmo.tgbotapi.extensions.api.chat.members.restrictChatMember
import dev.inmo.tgbotapi.extensions.api.edit.edit
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.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.onMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.*
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.PublicChat
import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
import dev.inmo.tgbotapi.types.request.RequestId
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>) {
val botToken = args.first()
val isDebug = args.getOrNull(2) == "debug"
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val bot = telegramBot(botToken)
val allowedAdmin = ChatId(args[1].toLong())
fun Boolean?.allowedSymbol() = when (this) {
true -> ""
false -> ""
null -> ""
}
val granularDataPrefix = "granular"
val messagesToggleGranularData = "$granularDataPrefix messages"
val otherMessagesToggleGranularData = "$granularDataPrefix other messages"
val audiosToggleGranularData = "$granularDataPrefix audios"
val voicesToggleGranularData = "$granularDataPrefix voices"
val videosToggleGranularData = "$granularDataPrefix videos"
val videoNotesToggleGranularData = "$granularDataPrefix video notes"
val photosToggleGranularData = "$granularDataPrefix photos"
val webPagePreviewToggleGranularData = "$granularDataPrefix web page preview"
val pollsToggleGranularData = "$granularDataPrefix polls"
val documentsToggleGranularData = "$granularDataPrefix documents"
val commonDataPrefix = "common"
val pollsToggleCommonData = "$commonDataPrefix polls"
val otherMessagesToggleCommonData = "$commonDataPrefix other messages"
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? {
val chatMember = getChatMember(chatId, userId)
return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember {
getChat(chatId).extendedGroupChatOrNull() ?.permissions
}
}
fun buildGranularKeyboard(
permissions: ChatPermissions
): InlineKeyboardMarkup {
return inlineKeyboard {
row {
dataButton("Send messages${permissions.canSendMessages.allowedSymbol()}", messagesToggleGranularData)
dataButton(
"Send other messages${permissions.canSendOtherMessages.allowedSymbol()}",
otherMessagesToggleGranularData
)
}
row {
dataButton("Send audios${permissions.canSendAudios.allowedSymbol()}", audiosToggleGranularData)
dataButton("Send voices${permissions.canSendVoiceNotes.allowedSymbol()}", voicesToggleGranularData)
}
row {
dataButton("Send videos${permissions.canSendVideos.allowedSymbol()}", videosToggleGranularData)
dataButton(
"Send video notes${permissions.canSendVideoNotes.allowedSymbol()}",
videoNotesToggleGranularData
)
}
row {
dataButton("Send photos${permissions.canSendPhotos.allowedSymbol()}", photosToggleGranularData)
dataButton(
"Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}",
webPagePreviewToggleGranularData
)
}
row {
dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleGranularData)
dataButton("Send documents${permissions.canSendDocuments.allowedSymbol()}", documentsToggleGranularData)
}
}
}
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? {
val permissions = getUserChatPermissions(chatId, userId) ?: return null
return inlineKeyboard {
row {
dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleCommonData)
}
row {
dataButton("Send other messages${permissions.canSendOtherMessages.allowedSymbol()}", otherMessagesToggleCommonData)
}
row {
dataButton("Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}", webPagePreviewToggleCommonData)
}
}
}
bot.buildBehaviourWithFSMAndStartLongPolling<UserRetrievingStep>(
defaultExceptionsHandler = {
it.printStackTrace()
},
) {
onCommand(
"simple",
initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin }
) {
val replyMessage = it.replyTo
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
if (replyMessage is AccessibleMessage) {
reply(
replyMessage,
"Manage keyboard:",
replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
)
} else {
reply(it) {
regular("Reply to somebody's message to get hist/her rights keyboard")
}
}
}
onCommand(
"granular",
initialFilter = {
it.chat is ChannelChat || (it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin)
}
) {
val replyMessage = it.replyTo
val usernameInText = it.content.textSources.firstNotNullOfOrNull { it.mentionTextSourceOrNull() } ?.username
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
if (replyMessage is AccessibleMessage) {
reply(
replyMessage,
"Manage keyboard:",
replyMarkup = buildGranularKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
)
} else {
reply(it) {
regular("Reply to somebody's message to get hist/her rights keyboard")
}
}
}
onMessageDataCallbackQuery(
Regex("^${granularDataPrefix}.*"),
initialFilter = { it.user.id == allowedAdmin }
) {
val messageReply =
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
val userId = messageReply.user.id
val permissions =
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
val newPermission = when (it.data) {
messagesToggleGranularData -> {
permissions.copyGranular(
canSendMessages = permissions.canSendMessages?.let { !it } ?: false
)
}
otherMessagesToggleGranularData -> {
permissions.copyGranular(
canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
)
}
audiosToggleGranularData -> {
permissions.copyGranular(
canSendAudios = permissions.canSendAudios?.let { !it } ?: false
)
}
voicesToggleGranularData -> {
permissions.copyGranular(
canSendVoiceNotes = permissions.canSendVoiceNotes?.let { !it } ?: false
)
}
videosToggleGranularData -> {
permissions.copyGranular(
canSendVideos = permissions.canSendVideos?.let { !it } ?: false
)
}
videoNotesToggleGranularData -> {
permissions.copyGranular(
canSendVideoNotes = permissions.canSendVideoNotes?.let { !it } ?: false
)
}
photosToggleGranularData -> {
permissions.copyGranular(
canSendPhotos = permissions.canSendPhotos?.let { !it } ?: false
)
}
webPagePreviewToggleGranularData -> {
permissions.copyGranular(
canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
)
}
pollsToggleGranularData -> {
permissions.copyGranular(
canSendPolls = permissions.canSendPolls?.let { !it } ?: false
)
}
documentsToggleGranularData -> {
permissions.copyGranular(
canSendDocuments = permissions.canSendDocuments?.let { !it } ?: false
)
}
else -> permissions.copyGranular()
}
restrictChatMember(
it.message.chat.id,
userId,
permissions = newPermission,
useIndependentChatPermissions = true
)
edit(
it.message,
replyMarkup = buildGranularKeyboard(it.message.chat.id.toChatId(), userId)
?: return@onMessageDataCallbackQuery
)
}
onMessageDataCallbackQuery(
Regex("^${commonDataPrefix}.*"),
initialFilter = { it.user.id == allowedAdmin }
) {
val messageReply =
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
val userId = messageReply.user.id
val permissions =
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
val newPermission = when (it.data) {
pollsToggleCommonData -> {
permissions.copyCommon(
canSendPolls = permissions.canSendPolls?.let { !it } ?: false
)
}
otherMessagesToggleCommonData -> {
permissions.copyCommon(
canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
)
}
webPagePreviewToggleCommonData -> {
permissions.copyCommon(
canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
)
}
else -> permissions.copyCommon()
}
restrictChatMember(
it.message.chat.id,
userId,
permissions = newPermission,
useIndependentChatPermissions = false
)
edit(
it.message,
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(
BotCommand("simple", "Trigger simple 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
)
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
}.join()
}

View File

@@ -6,21 +6,9 @@ import dev.inmo.tgbotapi.extensions.api.get.*
import dev.inmo.tgbotapi.extensions.api.send.* import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.types.StickerFormat
import dev.inmo.tgbotapi.types.StickerType import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.message.textsources.* import dev.inmo.tgbotapi.types.message.textsources.*
import dev.inmo.tgbotapi.types.stickers.AnimatedStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.StickerSet import dev.inmo.tgbotapi.types.stickers.StickerSet
import dev.inmo.tgbotapi.types.stickers.UnknownStickerSet
import dev.inmo.tgbotapi.utils.bold import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities import dev.inmo.tgbotapi.utils.buildEntities
import kotlinx.coroutines.* import kotlinx.coroutines.*
@@ -31,12 +19,6 @@ fun StickerSet?.buildInfo() = buildEntities {
} else { } else {
bold("StickerSet name: ") + "${name}\n" bold("StickerSet name: ") + "${name}\n"
bold("StickerSet title: ") + "${title}\n" bold("StickerSet title: ") + "${title}\n"
bold("Sticker format: ") + when (stickerFormat) {
StickerFormat.Animated -> "Animated"
StickerFormat.Static -> "Static"
is StickerFormat.Unknown -> stickerFormat.type
StickerFormat.Video -> "Video"
} + "\n"
bold( bold(
when (stickerType) { when (stickerType) {
StickerType.CustomEmoji -> "Custom emoji" StickerType.CustomEmoji -> "Custom emoji"

View File

@@ -1,9 +0,0 @@
# StickerSetHandler
Send sticker to this bot to form your own stickers set. Send /delete to delete this sticker set
## How to run
```bash
./gradlew run --args="TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="StickerSetHandlerBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,101 +0,0 @@
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.stickers.addStickerToSet
import dev.inmo.tgbotapi.extensions.api.stickers.createNewStickerSet
import dev.inmo.tgbotapi.extensions.api.stickers.deleteStickerSet
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sticker
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.stickers.InputSticker
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.files.*
import dev.inmo.tgbotapi.types.toChatId
import dev.inmo.tgbotapi.utils.botCommand
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
/**
* Send sticker to this bot to form your own stickers set. Send /delete to delete this sticker set
*/
suspend fun main(args: Array<String>) {
telegramBotWithBehaviourAndLongPolling(
args.first(),
scope = CoroutineScope(Dispatchers.IO),
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
val me = getMe()
fun Chat.stickerSetName() = "s${id.chatId}_by_${me.username ?.usernameWithoutAt}"
onCommand("start") {
reply(it) {
botCommand("delete") + " - to clear stickers"
}
}
onCommand("delete") {
val deleted = runCatchingSafely {
deleteStickerSet(it.chat.stickerSetName())
}.getOrElse { false }
if (deleted) {
reply(it, "Deleted")
} else {
reply(it, "Can't delete for some of reason")
}
}
onSticker {
val stickerSetName = it.chat.stickerSetName()
val sticker = it.content.media
val newSticker = when (sticker) {
is CustomEmojiSticker -> InputSticker.WithKeywords.CustomEmoji(
downloadFileToTemp(sticker.fileId).asMultipartFile(),
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
emptyList()
)
is MaskSticker -> InputSticker.Mask(
downloadFileToTemp(sticker.fileId).asMultipartFile(),
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
sticker.maskPosition
)
is RegularSticker -> InputSticker.WithKeywords.Regular(
downloadFileToTemp(sticker.fileId).asMultipartFile(),
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
emptyList()
)
is UnknownSticker -> return@onSticker
}
runCatchingSafely {
getStickerSet(stickerSetName)
}.onSuccess { stickerSet ->
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
reply(
it,
getStickerSet(stickerSetName).stickers.last()
)
}
}.onFailure { _ ->
createNewStickerSet(
it.chat.id.toChatId(),
stickerSetName,
"Sticker set by ${me.firstName}",
it.content.media.stickerFormat,
listOf(
newSticker
),
(sticker as? CustomEmojiSticker) ?.needsRepainting ?: false
).also { _ ->
reply(
it,
getStickerSet(stickerSetName).stickers.first()
)
}
}
}
}.second.join()
}

View File

@@ -1,9 +0,0 @@
# HelloBot
The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="TopicsHandlingKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,145 +0,0 @@
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.common.repeatOnFailure
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.forum.closeForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.closeGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.createForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.deleteForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.editForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.editGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.hideGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.reopenForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.reopenGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.chat.forum.unhideGeneralForumTopic
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.ForumTopic
import dev.inmo.tgbotapi.types.commands.BotCommandScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
suspend fun main(vararg args: String) {
telegramBotWithBehaviourAndLongPolling(
args.first(),
CoroutineScope(Dispatchers.Default),
defaultExceptionsHandler = {
it.printStackTrace()
}
) {
flushAccumulatedUpdates()
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
onCommand("start_test_topics") {
val forumTopic = createForumTopic(
it.chat,
"Test",
ForumTopic.GREEN
)
reply(it, "Test topic has been created")
delay(1000L)
editForumTopic(
it.chat.id,
forumTopic.messageThreadId,
"Test 01"
)
reply(it, "Test topic has changed its name to Test 01")
delay(1000L)
closeForumTopic(
it.chat.id,
forumTopic.messageThreadId,
)
reply(it, "Test topic has been closed")
delay(1000L)
reopenForumTopic(
it.chat.id,
forumTopic.messageThreadId,
)
reply(it, "Test topic has been reopened")
delay(1000L)
deleteForumTopic(
it.chat.id,
forumTopic.messageThreadId,
)
reply(it, "Test topic has been deleted")
delay(1000L)
hideGeneralForumTopic(
it.chat.id,
)
reply(it, "General topic has been hidden")
delay(1000L)
unhideGeneralForumTopic(
it.chat.id
)
reply(it, "General topic has been shown")
delay(1000L)
runCatchingSafely(
{ _ ->
reopenGeneralForumTopic(
it.chat.id
)
closeGeneralForumTopic(
it.chat.id
)
}
) {
closeGeneralForumTopic(
it.chat.id
)
}
reply(it, "General topic has been closed")
delay(1000L)
reopenGeneralForumTopic(
it.chat.id
)
reply(it, "General topic has been opened")
delay(1000L)
editGeneralForumTopic(
it.chat.id,
uuid4().toString().take(10)
)
reply(it, "General topic has been renamed")
delay(1000L)
editGeneralForumTopic(
it.chat.id,
"Main topic"
)
reply(it, "General topic has been renamed")
delay(1000L)
}
setMyCommands(
BotCommand("start_test_topics", "start test topics"),
scope = BotCommandScope.AllGroupChats
)
}.second.join()
}

View File

@@ -1,9 +0,0 @@
# UserChatShared
Use `/start` with bot to get request buttons. Bot will ask you to choose user/chat from your list and send it to him.
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

View File

@@ -1,21 +0,0 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="UserChatSharedKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,254 +0,0 @@
import dev.inmo.micro_utils.coroutines.runCatchingSafely
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUserShared
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUsersShared
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.PrivateChat
import dev.inmo.tgbotapi.types.keyboardButtonRequestUserLimit
import dev.inmo.tgbotapi.types.message.textsources.mention
import dev.inmo.tgbotapi.types.request.RequestId
import dev.inmo.tgbotapi.utils.row
suspend fun main(args: Array<String>) {
val botToken = args.first()
val bot = telegramBot(botToken)
val requestIdUserOrBot = RequestId(0)
val requestIdUserNonPremium = RequestId(1)
val requestIdUserAny = RequestId(2)
val requestIdUserPremium = RequestId(3)
val requestIdBot = RequestId(4)
val requestIdUsersOrBots = RequestId(5)
val requestIdUsersNonPremium = RequestId(6)
val requestIdUsersAny = RequestId(7)
val requestIdUsersPremium = RequestId(8)
val requestIdBots = RequestId(9)
val requestIdAnyChat = RequestId(10)
val requestIdChannel = RequestId(11)
val requestIdPublicChannel = RequestId(12)
val requestIdPrivateChannel = RequestId(13)
val requestIdChannelUserOwner = RequestId(14)
val requestIdGroup = RequestId(15)
val requestIdPublicGroup = RequestId(16)
val requestIdPrivateGroup = RequestId(17)
val requestIdGroupUserOwner = RequestId(18)
val requestIdForum = RequestId(19)
val requestIdPublicForum = RequestId(20)
val requestIdPrivateForum = RequestId(21)
val requestIdForumUserOwner = RequestId(22)
val keyboard = replyKeyboard(
resizeKeyboard = true,
) {
row {
requestUserOrBotButton(
"\uD83D\uDC64/\uD83E\uDD16 (1)",
requestIdUserOrBot
)
}
row {
requestUserButton(
"\uD83D\uDC64☆ (1)",
requestIdUserNonPremium,
premiumUser = false
)
requestUserButton(
"\uD83D\uDC64 (1)",
requestIdUserAny,
premiumUser = null
)
requestUserButton(
"\uD83D\uDC64★ (1)",
requestIdUserPremium,
premiumUser = true
)
requestBotButton(
"\uD83E\uDD16 (1)",
requestIdBot
)
}
row {
requestUsersOrBotsButton(
"\uD83D\uDC64/\uD83E\uDD16",
requestIdUsersOrBots,
maxCount = keyboardButtonRequestUserLimit.last
)
}
row {
requestUsersButton(
"\uD83D\uDC64",
requestIdUsersNonPremium,
premiumUser = false,
maxCount = keyboardButtonRequestUserLimit.last
)
requestUsersButton(
"\uD83D\uDC64",
requestIdUsersAny,
premiumUser = null,
maxCount = keyboardButtonRequestUserLimit.last
)
requestUsersButton(
"\uD83D\uDC64",
requestIdUsersPremium,
premiumUser = true,
maxCount = keyboardButtonRequestUserLimit.last
)
requestBotsButton(
"\uD83E\uDD16",
requestIdBots,
maxCount = keyboardButtonRequestUserLimit.last
)
}
row {
requestChatButton(
"\uD83D\uDDE3/\uD83D\uDC65",
requestIdAnyChat
)
}
row {
requestChatButton(
"\uD83D\uDDE3",
requestIdChannel,
isChannel = true
)
requestChatButton(
"\uD83D\uDDE3\uD83D\uDD17",
requestIdPublicChannel,
isChannel = true,
isPublic = true
)
requestChatButton(
"\uD83D\uDDE3\uD83D\uDD17",
requestIdPrivateChannel,
isChannel = true,
isPublic = false
)
requestChatButton(
"\uD83D\uDDE3\uD83D\uDC6E",
requestIdChannelUserOwner,
isChannel = true,
isOwnedBy = true
)
}
row {
requestGroupButton(
"👥",
requestIdGroup
)
requestGroupButton(
"👥\uD83D\uDD17",
requestIdPublicGroup,
isPublic = true
)
requestGroupButton(
"👥❌\uD83D\uDD17",
requestIdPrivateGroup,
isPublic = false
)
requestGroupButton(
"👥\uD83D\uDC6E",
requestIdGroupUserOwner,
isOwnedBy = true
)
}
row {
requestGroupButton(
"🏛",
requestIdForum,
isForum = true
)
requestGroupButton(
"🏛\uD83D\uDD17",
requestIdPublicForum,
isPublic = true,
isForum = true
)
requestGroupButton(
"🏛❌\uD83D\uDD17",
requestIdPrivateForum,
isPublic = false,
isForum = true
)
requestGroupButton(
"🏛\uD83D\uDC6E",
requestIdForumUserOwner,
isOwnedBy = true,
isForum = true
)
}
}
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommand("start", initialFilter = { it.chat is PrivateChat }) {
reply(
it,
"Here possible requests buttons:",
replyMarkup = keyboard
)
}
onUsersShared {
it.chatEvent.userIds.forEach { userId ->
val userInfo = runCatchingSafely { getChat(userId) }.getOrNull()
reply(
it,
) {
+"You have shared "
+mention(
when (it.chatEvent.requestId) {
requestIdUserOrBot -> "user or bot"
requestIdUserNonPremium -> "non premium user"
requestIdUserAny -> "any user"
requestIdUserPremium -> "premium user"
requestIdBot -> "bot"
else -> "somebody O.o"
},
userId
)
+" (user info: $userInfo; user id: $userId)"
}
}
}
onChatShared {
val chatId = it.chatEvent.chatId
val chatInfo = runCatchingSafely { getChat(chatId) }.getOrNull()
reply(
it,
) {
+"You have shared "
+when (it.chatEvent.requestId) {
requestIdAnyChat -> "some chat"
requestIdChannel -> "any channel"
requestIdPublicChannel -> "public channel"
requestIdPrivateChannel -> "private channel"
requestIdChannelUserOwner -> "channel owned by you"
requestIdGroup -> "any group"
requestIdPublicGroup -> "public group"
requestIdPrivateGroup -> "private group"
requestIdGroupUserOwner -> "group owned by you"
requestIdForum -> "any forum"
requestIdPublicForum -> "public forum"
requestIdPrivateForum -> "private forum"
requestIdForumUserOwner -> "forum owned by you"
else -> "some chat O.o"
}
+" (chat info: $chatInfo; chat id: $chatId)"
}
}
setMyCommands(BotCommand("start", "Trigger buttons"))
}.join()
}

View File

@@ -12,6 +12,6 @@ What is there in this module:
## How to run ## How to run
```bash ```kotlin
./gradlew run --args="TOKEN WEB_APP_ADDRESS" ./gradlew run --args="TOKEN WEB_APP_ADDRESS"
``` ```

View File

@@ -1,7 +1,6 @@
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.*
@@ -15,11 +14,8 @@ 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.* import org.w3c.dom.HTMLElement
import kotlin.random.Random
import kotlin.random.nextUBytes
fun HTMLElement.log(text: String) { fun HTMLElement.log(text: String) {
appendText(text) appendText(text)
@@ -71,12 +67,9 @@ fun main() {
} }
} }
}) })
appendText("Answer in chat button") appendText("Example 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:")
@@ -117,32 +110,6 @@ 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(
@@ -175,91 +142,6 @@ 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}")
@@ -289,18 +171,8 @@ 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

@@ -1,24 +1,16 @@
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.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.ktor.server.createKtorServer import dev.inmo.micro_utils.ktor.server.createKtorServer
import dev.inmo.tgbotapi.extensions.api.answers.answer import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery
import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.* import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.telegramBot import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramStartattach
import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.LinkPreviewOptions
import dev.inmo.tgbotapi.types.webAppQueryIdField import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.types.webapps.WebAppInfo import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.* import dev.inmo.tgbotapi.utils.*
@@ -48,32 +40,18 @@ suspend fun main(vararg args: String) {
args.first(), args.first(),
testServer = args.any { it == "testServer" } testServer = args.any { it == "testServer" }
) )
val isDebug = args.any { it == "debug" }
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val bot = telegramBot(telegramBotAPIUrlsKeeper) val bot = telegramBot(telegramBotAPIUrlsKeeper)
createKtorServer( createKtorServer(
"0.0.0.0", "0.0.0.0",
args.getOrNull(2) ?.toIntOrNull() ?: 8080, 8080,
additionalEngineEnvironmentConfigurator = { additionalEngineEnvironmentConfigurator = {
parentCoroutineContext += Dispatchers.IO parentCoroutineContext += Dispatchers.IO
} }
) { ) {
routing { routing {
val baseJsFolder = File("WebApp/build/dist/js/") static {
baseJsFolder.list() ?.forEach { files(File("WebApp/build/distributions"))
if (it == "productionExecutable" || it == "developmentExecutable") { default("WebApp/build/distributions/index.html")
staticFiles("", File(baseJsFolder, it)) {
default("WebApp/build/dist/js/$it/index.html")
}
}
} }
post("inline") { post("inline") {
val requestBody = call.receiveText() val requestBody = call.receiveText()
@@ -96,7 +74,6 @@ suspend fun main(vararg args: String) {
bot.buildBehaviourWithLongPolling( bot.buildBehaviourWithLongPolling(
defaultExceptionsHandler = { it.printStackTrace() } defaultExceptionsHandler = { it.printStackTrace() }
) { ) {
val me = getMe()
onCommand("reply_markup") { onCommand("reply_markup") {
reply( reply(
it, it,
@@ -117,35 +94,8 @@ suspend fun main(vararg args: String) {
row { row {
webAppButton("Open WebApp", WebAppInfo(args[1])) webAppButton("Open WebApp", WebAppInfo(args[1]))
} }
}, }
linkPreviewOptions = LinkPreviewOptions.Small(
args[1],
showAboveText = false
)
)
}
onCommand("attachment_menu") {
reply(
it,
"Button",
replyMarkup = inlineKeyboard {
row {
webAppButton("Open WebApp", WebAppInfo(args[1]))
}
},
linkPreviewOptions = LinkPreviewOptions.Large(
args[1],
showAboveText = true
)
)
}
onBaseInlineQuery {
answerInlineQuery(
it,
button = InlineQueryResultsButton.invoke(
"Open webApp",
WebAppInfo(args[1])
)
) )
} }
onUnhandledCommand { onUnhandledCommand {
@@ -157,9 +107,6 @@ suspend fun main(vararg args: String) {
} }
) )
} }
onWriteAccessAllowed(initialFilter = { it.chatEvent.webAppName != null }) {
send(it.chat, "Thanks for adding ${it.chatEvent.webAppName} to the attachment menu")
}
setMyCommands( setMyCommands(
BotCommand("reply_markup", "Use to get reply markup keyboard with web app trigger"), BotCommand("reply_markup", "Use to get reply markup keyboard with web app trigger"),
BotCommand("inline", "Use to get inline keyboard with web app trigger"), BotCommand("inline", "Use to get inline keyboard with web app trigger"),

View File

@@ -10,9 +10,6 @@ buildscript {
} }
allprojects { allprojects {
ext {
nativePartTemplate = "${rootProject.projectDir.absolutePath}/native_template.gradle"
}
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
@@ -25,7 +22,5 @@ allprojects {
} }
} }
} }
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
} }
} }

View File

@@ -1,11 +1,11 @@
kotlin.code.style=official kotlin.code.style=official
org.gradle.parallel=true org.gradle.parallel=true
# Due to parallel compilation project require next amount of memory on full build # Due to parallel compilation project require next amount of memory on full build
org.gradle.jvmargs=-Xmx2344m org.gradle.jvmargs=-Xmx768m
kotlin_version=1.9.22 kotlin_version=1.7.21
telegram_bot_api_version=10.1.1 telegram_bot_api_version=4.1.0
micro_utils_version=0.20.37 micro_utils_version=0.14.1
serialization_version=1.6.3 serialization_version=1.4.1
ktor_version=2.3.8 ktor_version=2.1.3

View File

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

View File

@@ -1,20 +0,0 @@
kotlin {
def hostOs = System.getProperty("os.name")
def isMingwX64 = hostOs.startsWith("Windows")
def isArch64 = System.getProperty("os.arch") == "aarch64"
def nativeTarget
if (hostOs == "Linux") {
if (isArch64) {
nativeTarget = linuxArm64("native") { binaries { executable() } }
} else {
nativeTarget = linuxX64("native") { binaries { executable() } }
}
} else {
if (isMingwX64) {
nativeTarget = mingwX64("native") { binaries { executable() } }
} else {
throw new GradleException("Host OS is not supported in Kotlin/Native.")
}
}
}

View File

@@ -4,8 +4,6 @@ include ":RandomFileSenderBot"
include ":HelloBot" include ":HelloBot"
include ":PollsBot"
include ":GetMeBot" include ":GetMeBot"
include ":DeepLinksBot" include ":DeepLinksBot"
@@ -14,7 +12,6 @@ include ":FilesLoaderBot"
include ":ResenderBot:ResenderBotLib" include ":ResenderBot:ResenderBotLib"
include ":ResenderBot:jvm_launcher" include ":ResenderBot:jvm_launcher"
include ":ResenderBot:native_launcher"
include ":KeyboardsBot:KeyboardsBotLib" include ":KeyboardsBot:KeyboardsBotLib"
include ":KeyboardsBot:jvm_launcher" include ":KeyboardsBot:jvm_launcher"
@@ -29,21 +26,3 @@ include ":ChatAvatarSetter"
include ":WebApp" include ":WebApp"
include ":FSMBot" include ":FSMBot"
include ":TopicsHandling"
include ":UserChatShared"
include ":RightsChangerBot"
include ":LiveLocationsBot"
include ":StickerSetHandler"
include ":InlineQueriesBot"
include ":ReactionsInfoBot"
include ":LinkPreviewsBot"
include ":BoostsInfoBot"