mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-03-15 15:32:28 +00:00
Compare commits
181 Commits
11.0.0
...
renovate/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f934a286a3 | ||
| 29ad52b506 | |||
|
|
b848c6bfad | ||
| 6642b95af2 | |||
| 828ab43317 | |||
| 1a4533221c | |||
| e304a5ecab | |||
| 600ac8ebbf | |||
| 07403546f4 | |||
| 9d4b7b5a50 | |||
| e1f5e40143 | |||
| 186a0f7abf | |||
| e660f06edf | |||
| fb6ed8b7ae | |||
| 9981e82a10 | |||
| bef86042f9 | |||
| d1791b3058 | |||
| 0432611f85 | |||
| 6b27aa01fb | |||
| 523e428bcb | |||
| 0e8714cf2b | |||
| ea74f884bf | |||
| 69cbc257b5 | |||
| d3e6014e06 | |||
| 1d260f82e9 | |||
| 5d0b48c4b7 | |||
| 34be1a25b2 | |||
| 990614e257 | |||
| 0d1bcf05fd | |||
| 7d5cb58a3f | |||
| 261df14412 | |||
| 81ba5831c3 | |||
| 0b9c715e25 | |||
| 0216919145 | |||
| e2d56a4d80 | |||
| 70aca52960 | |||
| 6c0d961339 | |||
|
|
a3cdf693f2 | ||
|
|
e378c6630c | ||
| 707ad9a160 | |||
| 68e9830a8f | |||
| 55ebdeadbc | |||
|
|
d4f3d4bc68 | ||
| b3d06c9773 | |||
| e6e3eabf97 | |||
| 47efedf311 | |||
| 8423b1377b | |||
| d0029603ce | |||
| 8d8fa74779 | |||
| 459a70c47b | |||
| 88102f3afa | |||
| a621058fdd | |||
| 56e072aabe | |||
| 73f05bbcd7 | |||
| f053013360 | |||
| bc39279c6c | |||
| ad8fa92e87 | |||
| b0554adb7f | |||
| ad90180def | |||
| 69eda92bc7 | |||
| aee070c6c6 | |||
| 36163d5619 | |||
| 92d1c7a402 | |||
| 7ce784d0a2 | |||
| d203d48391 | |||
| 9352bb0090 | |||
| b1bb11d826 | |||
| 349517462e | |||
| 1708cad654 | |||
| f87a9c5c66 | |||
| a7b54e4b63 | |||
|
|
436213492d | ||
|
|
0c2110a71d | ||
| 949fa1a429 | |||
| 97cdd5a95f | |||
|
|
0cb116acef | ||
|
|
a0332c4efd | ||
| f6bce640da | |||
| d22a99da19 | |||
| 467a3a1710 | |||
| 5810bc5930 | |||
| 2cf2c4264e | |||
|
|
3d5c2ee4b8 | ||
| 360c6b4364 | |||
|
|
bb6a0a125a | ||
| 6a61da2eb7 | |||
| 8cd75673f5 | |||
| d294d0ef59 | |||
| 2ab8ccbfdf | |||
| d12e9aa032 | |||
| 76f151586e | |||
| 1c437690e4 | |||
| 222c7ec8ee | |||
| 59778a3add | |||
| 3e20835bc6 | |||
| c3ad2d4319 | |||
| 59fca968d7 | |||
| f03ba5f177 | |||
|
|
855d2c1296 | ||
| 280f5abce0 | |||
|
|
ed81e76ef8 | ||
| 541b76b292 | |||
| 5b580b5a15 | |||
| 86790ee414 | |||
| 0bbe430374 | |||
| b7d53a7410 | |||
| 73064db226 | |||
| a50eda366d | |||
| e34f0ec9d8 | |||
| c2237f7e87 | |||
| 0bbc6a9555 | |||
| d4d8508abf | |||
| 9acb64fda9 | |||
|
|
760ae36207 | ||
| 5c6b1b7171 | |||
| 6e06357541 | |||
| 38f46dfa3b | |||
|
|
e7f7ef16ac | ||
|
|
d100a5a336 | ||
| 5f0f2ce76d | |||
| 14235e7bd4 | |||
| 6eafd89542 | |||
| ed2922045c | |||
| 21ec50c773 | |||
| ab362e8c3b | |||
| 346755b41c | |||
|
|
a601674d71 | ||
|
|
cea610a0f8 | ||
| b6c92f754f | |||
| 023b810d07 | |||
| 0ec543d5c5 | |||
| 777604e5a0 | |||
| 999c33b2f5 | |||
| ca0427bfdd | |||
| a62a14a599 | |||
|
|
3efd3463a3 | ||
| 590f9ec6d8 | |||
| acdbd4d2ea | |||
| d2d913fca8 | |||
| 75726cac89 | |||
| 71b64689d0 | |||
| 5ba2fc5bab | |||
| 51a5bfb81a | |||
| 35e330c016 | |||
| 90d447fbcf | |||
| 2c5da5da9f | |||
| f79e43364a | |||
| f5a9efa3e7 | |||
| b70b6d1e2b | |||
|
|
3f36a04ac2 | ||
| 62b830d31b | |||
| 06459ebc0a | |||
| 673424b234 | |||
|
|
5d156f6708 | ||
|
|
529f4156fd | ||
|
|
7842ac0dac | ||
|
|
358f2d27d3 | ||
|
|
7964dc4eea | ||
| 9fb6570d21 | |||
| f750589fd3 | |||
| 481533bee2 | |||
| a1a4338869 | |||
| d8e5825ccf | |||
| 3a4c0c4226 | |||
| b85d7a697c | |||
| ad57e4142c | |||
| c7068182e3 | |||
| a5740e6315 | |||
| 3a35995bc7 | |||
| 41fc5a9a4c | |||
| 79700f24e5 | |||
| 73c1af15b3 | |||
| aa9ca976f0 | |||
| 238533a350 | |||
| 6f2a8bb0be | |||
| 99232b53d7 | |||
| 30358f7d2f | |||
| 11a97c520a | |||
| d6a6ad8d37 | |||
| c04a367375 | |||
| b660bf5f42 |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -10,10 +10,11 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
sudo apt update
|
||||||
sudo apt install -y libcurl4-openssl-dev
|
sudo apt install -y libcurl4-openssl-dev
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build --no-daemon
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.idea
|
.idea
|
||||||
|
.kotlin
|
||||||
out/*
|
out/*
|
||||||
*.iml
|
*.iml
|
||||||
target
|
target
|
||||||
|
|||||||
2
.template/bot/.env
Normal file
2
.template/bot/.env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
title=$prompt
|
||||||
|
subtitle=Subtitle of {{$title}}
|
||||||
9
.template/bot/{{$title}}/README.md
Normal file
9
.template/bot/{{$title}}/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# {{$title}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
22
.template/bot/{{$title}}/build.gradle
Normal file
22
.template/bot/{{$title}}/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="{{$title}}Kt"
|
||||||
|
{{$subtitle}}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
34
.template/bot/{{$title}}/src/main/kotlin/{{$title}}.kt
Normal file
34
.template/bot/{{$title}}/src/main/kotlin/{{$title}}.kt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This place can be the playground for your code.
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
208
.template/module_generator.main.kts
Executable file
208
.template/module_generator.main.kts
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env kotlin
|
||||||
|
/**
|
||||||
|
* Generates files and folders as they have been put in the folder. Envs uses common syntax, but
|
||||||
|
* values may contains {{${'$'}sampleVariable}} parts, where {{${'$'}sampleVariable}} will be replaced with variable value.
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* .env:
|
||||||
|
* sampleVariable=${'$'}prompt # require request from command line
|
||||||
|
* sampleVariable2=just some value
|
||||||
|
* sampleVariable3=${'$'}{sampleVariable}.${'$'}{sampleVariable2}
|
||||||
|
*
|
||||||
|
* Result variables:
|
||||||
|
* sampleVariable=your input in console # lets imagine you typed it
|
||||||
|
* sampleVariable2=just some value
|
||||||
|
* sampleVariable3=your input in console.just some value
|
||||||
|
*
|
||||||
|
* To use these variables in template, you will need to write {{${'$'}sampleVariable}}.
|
||||||
|
* You may use it in text of files as well as in files/folders names.
|
||||||
|
*
|
||||||
|
* Usage: kotlin generator.kts [args] folders...
|
||||||
|
* Args:
|
||||||
|
* -e, --env: Path to file with args for generation; Use "${'$'}prompt" as values to read variable value from console
|
||||||
|
* -o, --outputFolder: Folder where templates should be used. Folder of calling by default
|
||||||
|
* folders: Folders-templates
|
||||||
|
*/
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val console = System.console()
|
||||||
|
|
||||||
|
fun String.replaceWithVariables(envs: Map<String, String>): String {
|
||||||
|
var currentString = this
|
||||||
|
var changed = false
|
||||||
|
|
||||||
|
do {
|
||||||
|
changed = false
|
||||||
|
envs.forEach { (k, v) ->
|
||||||
|
val previousString = currentString
|
||||||
|
currentString = currentString.replace("{{$${k}}}", v)
|
||||||
|
changed = changed || currentString != previousString
|
||||||
|
}
|
||||||
|
} while (changed)
|
||||||
|
|
||||||
|
return currentString
|
||||||
|
}
|
||||||
|
|
||||||
|
fun requestVariable(variableName: String, defaultValue: String?): String {
|
||||||
|
console.printf("Enter value for variable $variableName${defaultValue ?.let { " [$it]" } ?: ""}: ")
|
||||||
|
return console.readLine().ifEmpty { defaultValue } ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readEnvs(content: String, presets: Map<String, String>): Map<String, String> {
|
||||||
|
val initialEnvs = mutableMapOf<String, String>()
|
||||||
|
content.split("\n").forEach {
|
||||||
|
val withoutComment = it.replace(Regex("\\#.*"), "")
|
||||||
|
|
||||||
|
runCatching {
|
||||||
|
val (key, value) = withoutComment.split("=")
|
||||||
|
val existsValue = presets[key]
|
||||||
|
if (value == "\$prompt") {
|
||||||
|
initialEnvs[key] = requestVariable(key, existsValue)
|
||||||
|
} else {
|
||||||
|
initialEnvs[key] = requestVariable(key, value.replaceWithVariables(initialEnvs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var i = 0
|
||||||
|
val readEnvs = initialEnvs.toMutableMap()
|
||||||
|
while (i < readEnvs.size) {
|
||||||
|
val key = readEnvs.keys.elementAt(i)
|
||||||
|
val currentValue = readEnvs.getValue(key)
|
||||||
|
val withReplaced = currentValue.replaceWithVariables(readEnvs)
|
||||||
|
var changed = false
|
||||||
|
if (withReplaced != currentValue) {
|
||||||
|
i = 0
|
||||||
|
readEnvs[key] = withReplaced
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return presets + readEnvs
|
||||||
|
}
|
||||||
|
|
||||||
|
var envFile: File? = null
|
||||||
|
var outputFolder: File = File("./") // current folder by default
|
||||||
|
val templatesFolders = mutableListOf<File>()
|
||||||
|
var extensions: List<String>? = null
|
||||||
|
|
||||||
|
fun readParameters() {
|
||||||
|
var i = 0
|
||||||
|
while (i < args.size) {
|
||||||
|
val arg = args[i]
|
||||||
|
when (arg) {
|
||||||
|
"--env",
|
||||||
|
"-e" -> {
|
||||||
|
i++
|
||||||
|
envFile = File(args[i])
|
||||||
|
}
|
||||||
|
"--extensions",
|
||||||
|
"-ex" -> {
|
||||||
|
i++
|
||||||
|
extensions = args[i].split(",")
|
||||||
|
}
|
||||||
|
"--outputFolder",
|
||||||
|
"-o" -> {
|
||||||
|
i++
|
||||||
|
outputFolder = File(args[i])
|
||||||
|
}
|
||||||
|
"--help",
|
||||||
|
"-h" -> {
|
||||||
|
println("""
|
||||||
|
Generates files and folders as the have been put in the folder. Envs uses common syntax, but
|
||||||
|
values may contains {{${'$'}sampleVariable}} parts, where {{${'$'}sampleVariable}} will be replaced with variable value.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.env:
|
||||||
|
sampleVariable=${'$'}prompt # require request from command line
|
||||||
|
sampleVariable2=just some value
|
||||||
|
sampleVariable3=${'$'}{sampleVariable}.${'$'}{sampleVariable2}
|
||||||
|
|
||||||
|
Result variables:
|
||||||
|
sampleVariable=your input in console # lets imagine you typed it
|
||||||
|
sampleVariable2=just some value
|
||||||
|
sampleVariable3=your input in console.just some value
|
||||||
|
|
||||||
|
To use these variables in template, you will need to write {{${'$'}sampleVariable}}.
|
||||||
|
You may use it in text of files as well as in files/folders names.
|
||||||
|
|
||||||
|
Usage: kotlin generator.kts [args] folders...
|
||||||
|
Args:
|
||||||
|
-e, --env: Path to file with args for generation; Use "${'$'}prompt" as values to read variable value from console
|
||||||
|
-o, --outputFolder: Folder where templates should be used. Folder of calling by default
|
||||||
|
folders: Folders-templates
|
||||||
|
""".trimIndent())
|
||||||
|
Runtime.getRuntime().exit(0)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val potentialFile = File(arg)
|
||||||
|
println("Potential file/folder as template: ${potentialFile.absolutePath}")
|
||||||
|
runCatching {
|
||||||
|
if (potentialFile.exists()) {
|
||||||
|
println("Adding file/folder as template: ${potentialFile.absolutePath}")
|
||||||
|
templatesFolders.add(potentialFile)
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
println("Unable to use folder $arg as template folder")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readParameters()
|
||||||
|
|
||||||
|
val envs: MutableMap<String, String> = envFile ?.let { readEnvs(it.readText(), emptyMap()) } ?.toMutableMap() ?: mutableMapOf()
|
||||||
|
|
||||||
|
println(
|
||||||
|
"""
|
||||||
|
Result environments:
|
||||||
|
${envs.toList().joinToString("\n ") { (k, v) -> "$k=$v" }}
|
||||||
|
Result extensions:
|
||||||
|
${extensions ?.joinToString()}
|
||||||
|
Input folders:
|
||||||
|
${templatesFolders.joinToString("\n ") { it.absolutePath }}
|
||||||
|
Output folder:
|
||||||
|
${outputFolder.absolutePath}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun File.handleTemplate(targetFolder: File, envs: Map<String, String>) {
|
||||||
|
println("Handling $absolutePath")
|
||||||
|
val localEnvs = File(absolutePath, ".env").takeIf { it.exists() } ?.let {
|
||||||
|
println("Reading .env in ${absolutePath}")
|
||||||
|
readEnvs(it.readText(), envs)
|
||||||
|
} ?: envs
|
||||||
|
println(
|
||||||
|
"""
|
||||||
|
Local environments:
|
||||||
|
${localEnvs.toList().joinToString("\n ") { (k, v) -> "$k=$v" }}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
val newName = name.replaceWithVariables(localEnvs)
|
||||||
|
println("New name $newName")
|
||||||
|
when {
|
||||||
|
!exists() -> return
|
||||||
|
isFile -> {
|
||||||
|
val content = useLines {
|
||||||
|
it.map { it.replaceWithVariables(localEnvs) }.toList()
|
||||||
|
}.joinToString("\n")
|
||||||
|
val targetFile = File(targetFolder, newName)
|
||||||
|
targetFile.writeText(content)
|
||||||
|
println("Target file: ${targetFile.absolutePath}")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val folder = File(targetFolder, newName)
|
||||||
|
println("Target folder: ${folder.absolutePath}")
|
||||||
|
folder.mkdirs()
|
||||||
|
listFiles() ?.forEach { fileOrFolder ->
|
||||||
|
fileOrFolder.handleTemplate(folder, localEnvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templatesFolders.forEach { folderOrFile ->
|
||||||
|
folderOrFile.handleTemplate(outputFolder, envs)
|
||||||
|
}
|
||||||
@@ -4,13 +4,13 @@ import dev.inmo.kslog.common.defaultMessageFormatter
|
|||||||
import dev.inmo.kslog.common.setDefaultKSLog
|
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.get.getUserChatBoosts
|
import dev.inmo.tgbotapi.extensions.api.get.getUserChatBoosts
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
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.onChatBoostUpdated
|
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.onChatShared
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatReplyKeyboard
|
||||||
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestChannelButton
|
||||||
import dev.inmo.tgbotapi.types.request.RequestId
|
import dev.inmo.tgbotapi.types.request.RequestId
|
||||||
import dev.inmo.tgbotapi.utils.regular
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
import korlibs.time.DateFormat
|
import korlibs.time.DateFormat
|
||||||
@@ -52,12 +52,19 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onChatShared(initialFilter = { it.chatEvent.requestId == requestChatId }) {
|
onChatShared(initialFilter = { it.chatEvent.requestId == requestChatId }) {
|
||||||
val boosts = getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
|
val boostsInfoContrainer = runCatching {
|
||||||
reply(
|
getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
|
||||||
it
|
}.getOrNull()
|
||||||
) {
|
|
||||||
boosts.boosts.forEach {
|
reply(it) {
|
||||||
regular("Boost added: ${DateFormat.FORMAT1.format(it.addDate.asDate)}; Boost expire: ${DateFormat.FORMAT1.format(it.expirationDate.asDate)}; Unformatted: $it") + "\n"
|
when {
|
||||||
|
boostsInfoContrainer == null -> +"Unable to take info about boosts in shared chat"
|
||||||
|
boostsInfoContrainer.boosts.isEmpty() -> +"There is no any boosts in passed chat"
|
||||||
|
else -> {
|
||||||
|
boostsInfoContrainer.boosts.forEach {
|
||||||
|
regular("Boost added: ${DateFormat.FORMAT1.format(it.addDate.asDate)}; Boost expire: ${DateFormat.FORMAT1.format(it.expirationDate.asDate)}; Unformatted: $it") + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
BusinessConnectionsBot/README.md
Normal file
9
BusinessConnectionsBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# BusinessConnectionBotBot
|
||||||
|
|
||||||
|
When bot connected or disconnected to the business chat, it will notify this chat
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
BusinessConnectionsBot/build.gradle
Normal file
21
BusinessConnectionsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="BusinessConnectionsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
487
BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt
Normal file
487
BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt
Normal file
@@ -0,0 +1,487 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import dev.inmo.micro_utils.common.Percentage
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountStarBalance
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.deleteBusinessMessages
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGifts
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.readBusinessMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.removeBusinessAccountProfilePhoto
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountBio
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountName
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountProfilePhoto
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.setBusinessAccountUsername
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.transferBusinessAccountStars
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.modify.pinChatMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.modify.unpinChatMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getBusinessConnection
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.stories.deleteStory
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.stories.postStory
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.commonMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extendedPrivateChatOrThrow
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.ifAccessibleMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.ifBusinessContentMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.textContentOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.multipartFile
|
||||||
|
import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto
|
||||||
|
import dev.inmo.tgbotapi.requests.stories.PostStory
|
||||||
|
import dev.inmo.tgbotapi.types.ChatId
|
||||||
|
import dev.inmo.tgbotapi.types.MessageId
|
||||||
|
import dev.inmo.tgbotapi.types.RawChatId
|
||||||
|
import dev.inmo.tgbotapi.types.business_connection.BusinessConnectionId
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.PhotoContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.StoryContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.TextContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.VideoContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.VisualMediaGroupPartContent
|
||||||
|
import dev.inmo.tgbotapi.types.stories.InputStoryContent
|
||||||
|
import dev.inmo.tgbotapi.types.stories.StoryArea
|
||||||
|
import dev.inmo.tgbotapi.types.stories.StoryAreaPosition
|
||||||
|
import dev.inmo.tgbotapi.types.stories.StoryAreaType
|
||||||
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
|
import dev.inmo.tgbotapi.utils.code
|
||||||
|
import dev.inmo.tgbotapi.utils.extensions.splitForText
|
||||||
|
import dev.inmo.tgbotapi.utils.row
|
||||||
|
import korlibs.time.seconds
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val isDebug = args.getOrNull(1) == "debug"
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val businessConnectionsChats = mutableMapOf<BusinessConnectionId, ChatId>()
|
||||||
|
val chatsBusinessConnections = mutableMapOf<ChatId, BusinessConnectionId>()
|
||||||
|
val businessConnectionsChatsMutex = Mutex()
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
flushAccumulatedUpdates()
|
||||||
|
|
||||||
|
onBusinessConnectionEnabled {
|
||||||
|
businessConnectionsChatsMutex.withLock {
|
||||||
|
businessConnectionsChats[it.id] = it.userChatId
|
||||||
|
chatsBusinessConnections[it.userChatId] = it.id
|
||||||
|
}
|
||||||
|
send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been enabled")
|
||||||
|
}
|
||||||
|
onBusinessConnectionDisabled {
|
||||||
|
businessConnectionsChatsMutex.withLock {
|
||||||
|
businessConnectionsChats.remove(it.id)
|
||||||
|
chatsBusinessConnections.remove(it.userChatId)
|
||||||
|
}
|
||||||
|
send(it.userChatId, "Business connection ${it.businessConnectionId.string} has been disabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentMessage {
|
||||||
|
it.ifBusinessContentMessage { businessContentMessage ->
|
||||||
|
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/pin") == true) {
|
||||||
|
businessContentMessage.replyTo ?.ifAccessibleMessage {
|
||||||
|
pinChatMessage(it)
|
||||||
|
return@ifBusinessContentMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (businessContentMessage.content.textContentOrNull() ?.text ?.startsWith("/unpin") == true) {
|
||||||
|
businessContentMessage.replyTo ?.ifAccessibleMessage {
|
||||||
|
unpinChatMessage(it)
|
||||||
|
return@ifBusinessContentMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val sent = execute(it.content.createResend(businessContentMessage.from.id))
|
||||||
|
if (businessContentMessage.sentByBusinessConnectionOwner) {
|
||||||
|
reply(sent, "You have sent this message to the ${businessContentMessage.businessConnectionId.string} related chat")
|
||||||
|
} else {
|
||||||
|
reply(
|
||||||
|
to = sent,
|
||||||
|
text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
chatId = businessConnectionsChats[it.businessConnectionId] ?: return@ifBusinessContentMessage,
|
||||||
|
text = "User have sent this message to you in the ${businessContentMessage.businessConnectionId.string} related chat",
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
row {
|
||||||
|
dataButton("Read message", "read ${it.chat.id.chatId.long} ${it.messageId.long}")
|
||||||
|
dataButton("Delete message", "delete ${it.chat.id.chatId.long} ${it.messageId.long}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onEditedContentMessage {
|
||||||
|
it.ifBusinessContentMessage { businessContentMessage ->
|
||||||
|
val sent = execute(businessContentMessage.content.createResend(businessContentMessage.from.id))
|
||||||
|
if (businessContentMessage.sentByBusinessConnectionOwner) {
|
||||||
|
reply(sent, "You have edited this message in the ${businessContentMessage.businessConnectionId.string} related chat")
|
||||||
|
} else {
|
||||||
|
reply(sent, "User have edited this message to you in the ${businessContentMessage.businessConnectionId.string} related chat")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBusinessMessagesDeleted {
|
||||||
|
var businessConnectionOwnerChat = businessConnectionsChatsMutex.withLock {
|
||||||
|
businessConnectionsChats[it.businessConnectionId]
|
||||||
|
}
|
||||||
|
if (businessConnectionOwnerChat == null) {
|
||||||
|
val businessConnection = getBusinessConnection(it.businessConnectionId)
|
||||||
|
businessConnectionsChatsMutex.withLock {
|
||||||
|
businessConnectionsChats[businessConnection.businessConnectionId] = businessConnection.userChatId
|
||||||
|
}
|
||||||
|
businessConnectionOwnerChat = businessConnection.userChatId
|
||||||
|
}
|
||||||
|
send(businessConnectionOwnerChat, "There are several removed messages in chat ${it.chat.id}: ${it.messageIds}")
|
||||||
|
}
|
||||||
|
onCommand("get_business_account_info", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id]
|
||||||
|
val businessConnectionInfo = businessConnectionId ?.let { getBusinessConnection(it) }
|
||||||
|
reply(it) {
|
||||||
|
if (businessConnectionInfo == null) {
|
||||||
|
+"There is no business connection for current chat"
|
||||||
|
} else {
|
||||||
|
+(Json { prettyPrint = true; encodeDefaults = true }.encodeToString(businessConnectionInfo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMessageDataCallbackQuery(Regex("read \\d+ \\d+")) {
|
||||||
|
val (_, chatIdString, messageIdString) = it.data.split(" ")
|
||||||
|
val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
|
||||||
|
|
||||||
|
val readResponse = businessConnectionId ?.let { readBusinessMessage(it, chatId, messageId) }
|
||||||
|
answer(
|
||||||
|
it,
|
||||||
|
if (readResponse == null) {
|
||||||
|
"There is no business connection for current chat"
|
||||||
|
} else {
|
||||||
|
"Message has been read"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onMessageDataCallbackQuery(Regex("delete \\d+ \\d+")) {
|
||||||
|
val (_, chatIdString, messageIdString) = it.data.split(" ")
|
||||||
|
val chatId = chatIdString.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val messageId = messageIdString.toLongOrNull() ?.let(::MessageId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.message.chat.id]
|
||||||
|
|
||||||
|
val readResponse = businessConnectionId ?.let { deleteBusinessMessages(it, listOf(messageId)) }
|
||||||
|
answer(
|
||||||
|
it,
|
||||||
|
if (readResponse == null) {
|
||||||
|
"There is no business connection for current chat"
|
||||||
|
} else {
|
||||||
|
"Message has been deleted"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onCommandWithArgs("set_business_account_name", initialFilter = { it.chat is PrivateChat }) { it, args ->
|
||||||
|
val firstName = args[0]
|
||||||
|
val secondName = args.getOrNull(1)
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
|
||||||
|
val set = runCatching {
|
||||||
|
setBusinessAccountName(
|
||||||
|
businessConnectionId,
|
||||||
|
firstName,
|
||||||
|
secondName
|
||||||
|
)
|
||||||
|
}.getOrElse { false }
|
||||||
|
reply(it) {
|
||||||
|
if (set) {
|
||||||
|
+"Account name has been set"
|
||||||
|
} else {
|
||||||
|
+"Account name has not been set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommandWithArgs("set_business_account_username", initialFilter = { it.chat is PrivateChat }) { it, args ->
|
||||||
|
val username = args[0]
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
|
||||||
|
val set = runCatching {
|
||||||
|
setBusinessAccountUsername(
|
||||||
|
businessConnectionId,
|
||||||
|
username
|
||||||
|
)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (set) {
|
||||||
|
+"Account username has been set"
|
||||||
|
} else {
|
||||||
|
+"Account username has not been set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("get_business_account_star_balance", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
||||||
|
val starAmount = runCatching {
|
||||||
|
getBusinessAccountStarBalance(businessConnectionId)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (starAmount != null) {
|
||||||
|
+"Account stars amount: $starAmount"
|
||||||
|
} else {
|
||||||
|
+"Account stars amount has not been got"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommandWithArgs("transfer_business_account_stars", initialFilter = { it.chat is PrivateChat }) { it, args ->
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommandWithArgs
|
||||||
|
val count = args.firstOrNull() ?.toIntOrNull() ?: reply(it) {
|
||||||
|
"Pass amount of stars to transfer to bot with command"
|
||||||
|
}.let {
|
||||||
|
return@onCommandWithArgs
|
||||||
|
}
|
||||||
|
val transferred = runCatching {
|
||||||
|
transferBusinessAccountStars(businessConnectionId, count)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (transferred) {
|
||||||
|
+"Stars have been transferred"
|
||||||
|
} else {
|
||||||
|
+"Stars have not been transferred"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("get_business_account_gifts", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
||||||
|
val giftsFlow = runCatching {
|
||||||
|
getBusinessAccountGiftsFlow(businessConnectionId)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (giftsFlow == null) {
|
||||||
|
reply(it) {
|
||||||
|
+"Error in receiving of gifts"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
giftsFlow.collect { giftsPage ->
|
||||||
|
giftsPage.gifts.joinToString {
|
||||||
|
it.toString()
|
||||||
|
}.splitForText().forEach { message ->
|
||||||
|
reply(it, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("set_business_account_bio", requireOnlyCommandInMessage = false, initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val initialBio = getChat(it.chat).extendedPrivateChatOrThrow().bio
|
||||||
|
val bio = it.content.text.removePrefix("/set_business_account_bio").trim()
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
||||||
|
val set = runCatching {
|
||||||
|
setBusinessAccountBio(
|
||||||
|
businessConnectionId,
|
||||||
|
bio
|
||||||
|
)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (set) {
|
||||||
|
+"Account bio has been set. It will be reset within 15 seconds.\n\nInitial bio: " + code(initialBio)
|
||||||
|
} else {
|
||||||
|
+"Account bio has not been set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(15.seconds)
|
||||||
|
val reset = runCatching {
|
||||||
|
setBusinessAccountBio(
|
||||||
|
businessConnectionId,
|
||||||
|
initialBio
|
||||||
|
)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (reset) {
|
||||||
|
+"Account bio has been reset"
|
||||||
|
} else {
|
||||||
|
+"Account bio has not been set. Set it manually: " + code(initialBio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suspend fun handleSetProfilePhoto(it: CommonMessage<TextContent>, isPublic: Boolean) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@handleSetProfilePhoto
|
||||||
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<PhotoContent>()
|
||||||
|
if (replyTo == null) {
|
||||||
|
reply(it) {
|
||||||
|
+"Reply to photo for using of this command"
|
||||||
|
}
|
||||||
|
return@handleSetProfilePhoto
|
||||||
|
}
|
||||||
|
|
||||||
|
val set = runCatching {
|
||||||
|
val file = downloadFileToTemp(replyTo.content)
|
||||||
|
setBusinessAccountProfilePhoto(
|
||||||
|
businessConnectionId,
|
||||||
|
InputProfilePhoto.Static(
|
||||||
|
file.multipartFile()
|
||||||
|
),
|
||||||
|
isPublic = isPublic
|
||||||
|
)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (set) {
|
||||||
|
+"Account profile photo has been set. It will be reset within 15 seconds"
|
||||||
|
} else {
|
||||||
|
+"Account profile photo has not been set"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (set == false) { return@handleSetProfilePhoto }
|
||||||
|
delay(15.seconds)
|
||||||
|
val reset = runCatching {
|
||||||
|
removeBusinessAccountProfilePhoto(
|
||||||
|
businessConnectionId,
|
||||||
|
isPublic = isPublic
|
||||||
|
)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (reset) {
|
||||||
|
+"Account profile photo has been reset"
|
||||||
|
} else {
|
||||||
|
+"Account profile photo has not been set. Set it manually"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("set_business_account_profile_photo", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
handleSetProfilePhoto(it, false)
|
||||||
|
}
|
||||||
|
onCommand("set_business_account_profile_photo_public", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
handleSetProfilePhoto(it, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("post_story", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
||||||
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<VisualMediaGroupPartContent>()
|
||||||
|
if (replyTo == null) {
|
||||||
|
reply(it) {
|
||||||
|
+"Reply to photo or video for using of this command"
|
||||||
|
}
|
||||||
|
return@onCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
val posted = runCatching {
|
||||||
|
val file = downloadFileToTemp(replyTo.content)
|
||||||
|
postStory(
|
||||||
|
businessConnectionId,
|
||||||
|
when (replyTo.content) {
|
||||||
|
is PhotoContent -> InputStoryContent.Photo(
|
||||||
|
file.multipartFile()
|
||||||
|
)
|
||||||
|
is VideoContent -> InputStoryContent.Video(
|
||||||
|
file.multipartFile()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
activePeriod = PostStory.ACTIVE_PERIOD_6_HOURS,
|
||||||
|
areas = listOf(
|
||||||
|
StoryArea(
|
||||||
|
StoryAreaPosition(
|
||||||
|
x = Percentage.of100(50.0),
|
||||||
|
y = Percentage.of100(50.0),
|
||||||
|
width = Percentage.of100(8.0),
|
||||||
|
height = Percentage.of100(8.0),
|
||||||
|
rotationAngle = 45.0,
|
||||||
|
cornerRadius = Percentage.of100(4.0),
|
||||||
|
),
|
||||||
|
StoryAreaType.Link(
|
||||||
|
"https://github.com/InsanusMokrassar/TelegramBotAPI-examples/blob/master/BusinessConnectionsBot/src/main/kotlin/BusinessConnectionsBot.kt"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
+"It is test of postStory :)"
|
||||||
|
}
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (posted != null) {
|
||||||
|
+"Story has been posted. You may unpost it with " + botCommand("remove_story")
|
||||||
|
} else {
|
||||||
|
+"Story has not been posted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("delete_story", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
val businessConnectionId = chatsBusinessConnections[it.chat.id] ?: return@onCommand
|
||||||
|
val replyTo = it.replyTo ?.commonMessageOrNull() ?.withContentOrNull<StoryContent>()
|
||||||
|
if (replyTo == null) {
|
||||||
|
reply(it) {
|
||||||
|
+"Reply to photo or video for using of this command"
|
||||||
|
}
|
||||||
|
return@onCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
val deleted = runCatching {
|
||||||
|
deleteStory(businessConnectionId, replyTo.content.story.id)
|
||||||
|
}.getOrElse {
|
||||||
|
it.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
reply(it) {
|
||||||
|
if (deleted) {
|
||||||
|
+"Story has been deleted"
|
||||||
|
} else {
|
||||||
|
+"Story has not been deleted"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will work when some premium user sending to some other user checklist
|
||||||
|
onChecklistContent {
|
||||||
|
execute(
|
||||||
|
it.content.createResend(
|
||||||
|
it.chat.id,
|
||||||
|
businessConnectionId = it.chat.id.businessConnectionId ?: chatsBusinessConnections[it.chat.id] ?: return@onChecklistContent
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ import dev.inmo.tgbotapi.extensions.api.files.downloadFile
|
|||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
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.onPhoto
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto
|
||||||
import dev.inmo.tgbotapi.extensions.utils.*
|
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
val bot = telegramBot(args.first())
|
val bot = telegramBot(args.first())
|
||||||
|
|||||||
21
ChecklistsBot/build.gradle
Normal file
21
ChecklistsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="ChecklistsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
120
ChecklistsBot/src/main/kotlin/ChecklistsBot.kt
Normal file
120
ChecklistsBot/src/main/kotlin/ChecklistsBot.kt
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
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.runCatchingLogging
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.resend
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.suggested.approveSuggestedPost
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostApproved
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostDeclined
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChannelDirectMessagesConfigurationChanged
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChecklistContent
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChecklistTasksAdded
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChecklistTasksDone
|
||||||
|
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.onSuggestedPostApprovalFailed
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApproved
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostDeclined
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostPaid
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostRefunded
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.channelDirectMessagesContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.previewChannelDirectMessagesChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.suggestedChannelDirectMessagesContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.types.checklists.ChecklistTaskId
|
||||||
|
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.ChecklistContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
|
||||||
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
|
import dev.inmo.tgbotapi.utils.bold
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import dev.inmo.tgbotapi.utils.code
|
||||||
|
import dev.inmo.tgbotapi.utils.firstOf
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
botToken,
|
||||||
|
CoroutineScope(Dispatchers.Default),
|
||||||
|
testServer = isTestServer,
|
||||||
|
) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
fun ChecklistContent.textBuilderTextSources(): TextSourcesList {
|
||||||
|
return buildEntities {
|
||||||
|
+checklist.textSources + "\n\n"
|
||||||
|
checklist.tasks.forEach { task ->
|
||||||
|
+"• "
|
||||||
|
code(
|
||||||
|
if (task.completionDate != null) {
|
||||||
|
"[x] "
|
||||||
|
} else {
|
||||||
|
"[ ] "
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
bold(task.textSources) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChecklistContent { messageWithContent ->
|
||||||
|
reply(messageWithContent) {
|
||||||
|
+messageWithContent.content.textBuilderTextSources()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChecklistTasksDone { eventMessage ->
|
||||||
|
reply(
|
||||||
|
eventMessage,
|
||||||
|
checklistTaskId = eventMessage.chatEvent.markedAsDone ?.firstOrNull()
|
||||||
|
) {
|
||||||
|
eventMessage.chatEvent.checklistMessage.content.checklist
|
||||||
|
+eventMessage.chatEvent.checklistMessage.content.textBuilderTextSources()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChecklistTasksAdded { messageWithContent ->
|
||||||
|
reply(
|
||||||
|
messageWithContent.chatEvent.checklistMessage,
|
||||||
|
checklistTaskId = messageWithContent.chatEvent.tasks.firstOrNull() ?.id
|
||||||
|
) {
|
||||||
|
+messageWithContent.chatEvent.checklistMessage.content.textBuilderTextSources()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
9
CustomBot/README.md
Normal file
9
CustomBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# CustomBot
|
||||||
|
|
||||||
|
This bot basically have no any useful behaviour, but you may customize it as a playground
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
CustomBot/build.gradle
Normal file
21
CustomBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="CustomBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
136
CustomBot/src/main/kotlin/CustomBot.kt
Normal file
136
CustomBot/src/main/kotlin/CustomBot.kt
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
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.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getUserProfileAudios
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.media.sendPaidMedia
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.replyWithPlaylist
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChannelDirectMessagesConfigurationChanged
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerChanged
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatOwnerLeft
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto
|
||||||
|
import dev.inmo.tgbotapi.types.media.AudioMediaGroupMemberTelegramMedia
|
||||||
|
import dev.inmo.tgbotapi.types.media.toTelegramMediaAudio
|
||||||
|
import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaPhoto
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
||||||
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
private var BehaviourContextData.update: Update?
|
||||||
|
get() = get("update") as? Update
|
||||||
|
set(value) = set("update", value)
|
||||||
|
|
||||||
|
private var BehaviourContextData.commonMessage: CommonMessage<*>?
|
||||||
|
get() = get("commonMessage") as? CommonMessage<*>
|
||||||
|
set(value) = set("commonMessage", value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This place can be the playground for your code.
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
botToken,
|
||||||
|
CoroutineScope(Dispatchers.IO),
|
||||||
|
testServer = isTestServer,
|
||||||
|
builder = {
|
||||||
|
includeMiddlewares {
|
||||||
|
addMiddleware {
|
||||||
|
doOnRequestReturnResult { result, request, _ ->
|
||||||
|
println("Result of $request:\n\n$result")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
subcontextInitialAction = buildSubcontextInitialAction {
|
||||||
|
add {
|
||||||
|
data.update = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
onCommand("start") {
|
||||||
|
println(data.update)
|
||||||
|
println(data.commonMessage)
|
||||||
|
println(getChat(it.chat))
|
||||||
|
var currentOffset = 0
|
||||||
|
val pageSize = 2
|
||||||
|
do {
|
||||||
|
val userAudios = getUserProfileAudios(userId = it.chat.id, offset = currentOffset, limit = pageSize)
|
||||||
|
currentOffset += pageSize
|
||||||
|
println(userAudios)
|
||||||
|
when (userAudios.audios.size) {
|
||||||
|
1 -> {
|
||||||
|
replyWithAudio(
|
||||||
|
it,
|
||||||
|
userAudios.audios.first().fileId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
0 -> {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
replyWithPlaylist(
|
||||||
|
it,
|
||||||
|
userAudios.audios.map {
|
||||||
|
it.toTelegramMediaAudio()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (currentOffset < userAudios.totalCount && userAudios.audios.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand(
|
||||||
|
"additional_command",
|
||||||
|
additionalSubcontextInitialAction = { update, commonMessage ->
|
||||||
|
data.commonMessage = commonMessage
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
println(data.update)
|
||||||
|
println(data.commonMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("getMyStarBalance") {
|
||||||
|
reply(
|
||||||
|
to = it,
|
||||||
|
text = getMyStarBalance().toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onChannelDirectMessagesConfigurationChanged {
|
||||||
|
println(it.chatEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelySkippingExceptions
|
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
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.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
|
||||||
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.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDeepLink
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramDeepLink
|
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramDeepLink
|
||||||
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
|
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
|
||||||
|
|
||||||
|
|||||||
9
DraftsBot/README.md
Normal file
9
DraftsBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Drafts bot
|
||||||
|
|
||||||
|
The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
DraftsBot/build.gradle
Normal file
21
DraftsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
86
DraftsBot/src/main/kotlin/DraftsBot.kt
Normal file
86
DraftsBot/src/main/kotlin/DraftsBot.kt
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import com.benasher44.uuid.uuid4
|
||||||
|
import dev.inmo.kslog.common.w
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingLogging
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.forum.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlowWithTexts
|
||||||
|
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.onForumTopicClosed
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicEdited
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicReopened
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicHidden
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicUnhidden
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicEdited
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.forumChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.forumContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.privateChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.privateForumChatOrNull
|
||||||
|
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.chat.PrivateChat
|
||||||
|
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
|
||||||
|
const val testText = """
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
"""
|
||||||
|
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
args.first(),
|
||||||
|
CoroutineScope(Dispatchers.Default),
|
||||||
|
defaultExceptionsHandler = {
|
||||||
|
it.printStackTrace()
|
||||||
|
},
|
||||||
|
builder = {
|
||||||
|
client = client.config {
|
||||||
|
install(HttpTimeout) {
|
||||||
|
requestTimeoutMillis = 30000
|
||||||
|
socketTimeoutMillis = 30000
|
||||||
|
connectTimeoutMillis = 30000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
onCommand("test_draft_flow") {
|
||||||
|
sendMessageDraftFlowWithTexts(
|
||||||
|
it.chat.id,
|
||||||
|
flow<String> {
|
||||||
|
val step = 50
|
||||||
|
var currentLength = step
|
||||||
|
while (isActive && testText.length > currentLength) {
|
||||||
|
delay(500L)
|
||||||
|
emit(testText.take(currentLength))
|
||||||
|
currentLength += step
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
send(it.chat, testText)
|
||||||
|
}
|
||||||
|
|
||||||
|
setMyCommands(
|
||||||
|
BotCommand("test_draft_flow", "Start draft testing with flow"),
|
||||||
|
scope = BotCommandScope.AllGroupChats
|
||||||
|
)
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.AccumulatorFlow
|
import dev.inmo.micro_utils.coroutines.awaitFirst
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.fsm.common.State
|
import dev.inmo.micro_utils.fsm.common.State
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.send
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContentMessage
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitCommandMessage
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndFSMAndStartLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.command
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.containsCommand
|
||||||
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
|
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
|
||||||
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.textContentOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
||||||
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
|
||||||
import dev.inmo.tgbotapi.utils.botCommand
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
|
import dev.inmo.tgbotapi.utils.firstOf
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
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: IdChatIdentifier, val sourceMessage: CommonMessage<TextContent>) : BotState
|
||||||
@@ -48,18 +53,29 @@ suspend fun main(args: Array<String>) {
|
|||||||
+"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 = firstOf(
|
||||||
message.sameThread(it.sourceMessage)
|
{
|
||||||
}.first()
|
waitCommandMessage("stop").filter { message ->
|
||||||
|
message.sameThread(it.sourceMessage)
|
||||||
|
}.first()
|
||||||
|
null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
waitAnyContentMessage().filter { message ->
|
||||||
|
message.sameThread(it.sourceMessage)
|
||||||
|
}.filter {
|
||||||
|
containsCommand(
|
||||||
|
"stop",
|
||||||
|
it.withContentOrNull<TextContent>() ?.content ?.textSources ?: return@filter false
|
||||||
|
) == false
|
||||||
|
}.first()
|
||||||
|
}
|
||||||
|
) ?: return@strictlyOn StopState(it.context)
|
||||||
|
|
||||||
val content = contentMessage.content
|
val content = contentMessage.content
|
||||||
|
|
||||||
when {
|
execute(content.createResend(it.context))
|
||||||
content is TextContent && content.parseCommandsWithArgs().keys.contains("stop") -> StopState(it.context)
|
it
|
||||||
else -> {
|
|
||||||
execute(content.createResend(it.context))
|
|
||||||
it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
strictlyOn<StopState> {
|
strictlyOn<StopState> {
|
||||||
send(it.context) { +"You have stopped sending of content" }
|
send(it.context) { +"You have stopped sending of content" }
|
||||||
@@ -72,5 +88,17 @@ suspend fun main(args: Array<String>) {
|
|||||||
) {
|
) {
|
||||||
startChain(ExpectContentOrStopState(it.chat.id, it))
|
startChain(ExpectContentOrStopState(it.chat.id, it))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContentMessage(
|
||||||
|
{
|
||||||
|
it.content.textContentOrNull() ?.text == "/start"
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
startChain(ExpectContentOrStopState(it.chat.id, it.withContentOrNull() ?: return@onContentMessage))
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,38 +2,17 @@ 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.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.*
|
||||||
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.onCommand
|
||||||
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.abstracts.asMultipartFile
|
||||||
import dev.inmo.tgbotapi.requests.send.SendAction
|
import dev.inmo.tgbotapi.types.actions.*
|
||||||
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.TelegramMediaAudio
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
|
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
|
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
|
||||||
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
|
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
|
||||||
import dev.inmo.tgbotapi.types.message.content.AnimationContent
|
import dev.inmo.tgbotapi.types.message.content.*
|
||||||
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
|
||||||
@@ -55,13 +34,27 @@ suspend fun main(args: Array<String>) {
|
|||||||
val content = it.content
|
val content = it.content
|
||||||
val pathedFile = bot.getFileAdditionalInfo(content.media)
|
val pathedFile = bot.getFileAdditionalInfo(content.media)
|
||||||
val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl)
|
val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl)
|
||||||
runCatching {
|
withTypingAction(it.chat.id) {
|
||||||
bot.downloadFile(content.media, outFile)
|
runCatching {
|
||||||
}.onFailure {
|
bot.downloadFile(content.media, outFile)
|
||||||
it.printStackTrace()
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.onSuccess { _ ->
|
||||||
|
reply(it, "Saved to ${outFile.absolutePath}")
|
||||||
|
}
|
||||||
}.onSuccess { _ ->
|
}.onSuccess { _ ->
|
||||||
reply(it, "Saved to ${outFile.absolutePath}")
|
val action = when (content) {
|
||||||
withAction(it.chat.id, TypingAction) {
|
is PhotoContent -> UploadPhotoAction
|
||||||
|
is AnimationContent,
|
||||||
|
is VideoContent -> UploadVideoAction
|
||||||
|
is StickerContent -> ChooseStickerAction
|
||||||
|
is MediaGroupContent<*> -> UploadPhotoAction
|
||||||
|
is DocumentContent -> UploadDocumentAction
|
||||||
|
is VoiceContent,
|
||||||
|
is AudioContent -> RecordVoiceAction
|
||||||
|
is VideoNoteContent -> UploadVideoNoteAction
|
||||||
|
}
|
||||||
|
withAction(it.chat.id, action) {
|
||||||
when (content) {
|
when (content) {
|
||||||
is PhotoContent -> replyWithPhoto(
|
is PhotoContent -> replyWithPhoto(
|
||||||
it,
|
it,
|
||||||
|
|||||||
@@ -1,31 +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.filter.filtered
|
|
||||||
import dev.inmo.kslog.common.setDefaultKSLog
|
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
|
||||||
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is one of the most easiest bot - it will just print information about itself
|
|
||||||
*/
|
|
||||||
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)
|
|
||||||
|
|
||||||
val me = bot.getMe()
|
|
||||||
println(me)
|
|
||||||
println(bot.getChat(me))
|
|
||||||
}
|
|
||||||
21
GiftsBot/build.gradle
Normal file
21
GiftsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="GiftsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
112
GiftsBot/src/main/kotlin/GiftsBot.kt
Normal file
112
GiftsBot/src/main/kotlin/GiftsBot.kt
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
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.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.gifts.getChatGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.gifts.getUserGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
|
||||||
|
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.onGiveawayCompleted
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayContent
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayWinners
|
||||||
|
import dev.inmo.tgbotapi.types.chat.BusinessChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PublicChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.UnknownChatType
|
||||||
|
import dev.inmo.tgbotapi.types.gifts.OwnedGift
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.splitForText
|
||||||
|
import dev.inmo.tgbotapi.utils.bold
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, testServer = isTestServer) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
onCommand("start") {
|
||||||
|
val giftsFlow = when (val chat = it.chat) {
|
||||||
|
is BusinessChat -> {
|
||||||
|
getBusinessAccountGiftsFlow(
|
||||||
|
chat.id.businessConnectionId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is PrivateChat -> {
|
||||||
|
getUserGiftsFlow(it.chat.id)
|
||||||
|
}
|
||||||
|
is UnknownChatType,
|
||||||
|
is PublicChat -> {
|
||||||
|
getChatGiftsFlow(it.chat.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withTypingAction(it.chat) {
|
||||||
|
val texts = buildEntities {
|
||||||
|
giftsFlow.collect { ownedGifts ->
|
||||||
|
ownedGifts.gifts.forEach {
|
||||||
|
when (it) {
|
||||||
|
is OwnedGift.Regular.Common -> {
|
||||||
|
bold("Type") + ": Regular common\n"
|
||||||
|
bold("Id") + ": ${it.gift.id.string}\n"
|
||||||
|
bold("Text") + ": ${it.text ?: "(None)"}\n"
|
||||||
|
bold("Stars cost") + ": ${it.gift.starCount}\n"
|
||||||
|
}
|
||||||
|
is OwnedGift.Unique.Common -> {
|
||||||
|
bold("Type") + ": Unique common\n"
|
||||||
|
bold("Id") + ": ${it.gift.id ?.string ?: "(None)"}\n"
|
||||||
|
bold("Name") + ": ${it.gift.name.value}\n"
|
||||||
|
bold("Model") + ": ${it.gift.model.name}\n"
|
||||||
|
bold("Number") + ": ${it.gift.number}\n"
|
||||||
|
}
|
||||||
|
is OwnedGift.Regular.OwnedByBusinessAccount -> {
|
||||||
|
bold("Type") + ": Regular owned by business\n"
|
||||||
|
bold("Id") + ": ${it.gift.id.string}\n"
|
||||||
|
bold("Text") + ": ${it.text ?: "(None)"}\n"
|
||||||
|
bold("Stars cost") + ": ${it.gift.starCount}\n"
|
||||||
|
}
|
||||||
|
is OwnedGift.Unique.OwnedByBusinessAccount -> {
|
||||||
|
bold("Type") + ": Unique owned by business\n"
|
||||||
|
bold("Id") + ": ${it.gift.id ?.string ?: "(None)"}\n"
|
||||||
|
bold("Name") + ": ${it.gift.name.value}\n"
|
||||||
|
bold("Model") + ": ${it.gift.model.name}\n"
|
||||||
|
bold("Number") + ": ${it.gift.number}\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val preparedTexts = texts.splitForText()
|
||||||
|
if (preparedTexts.isEmpty()) {
|
||||||
|
reply(it, "This chat have no any gifts")
|
||||||
|
} else {
|
||||||
|
preparedTexts.forEach { preparedText -> reply(it, preparedText) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
// println(it)
|
||||||
|
// }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
9
GiveawaysBot/README.md
Normal file
9
GiveawaysBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# CustomBot
|
||||||
|
|
||||||
|
Printing giveaways
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
GiveawaysBot/build.gradle
Normal file
21
GiveawaysBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="GiveawaysBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
57
GiveawaysBot/src/main/kotlin/GiveawaysBot.kt
Normal file
57
GiveawaysBot/src/main/kotlin/GiveawaysBot.kt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCompleted
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayContent
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayWinners
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This place can be the playground for your code.
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, testServer = isTestServer) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
onGiveawayCreated {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onGiveawayCompleted {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onGiveawayWinners {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onGiveawayContent {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
// println(it)
|
||||||
|
// }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,23 +1,21 @@
|
|||||||
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.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.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.behaviour_builder.triggers_handling.onMentionWithAnyContent
|
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.extensions.raw.text
|
||||||
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
|
||||||
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
|
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
|
||||||
import dev.inmo.tgbotapi.types.chat.*
|
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.message.MarkdownV2
|
||||||
import dev.inmo.tgbotapi.utils.PreviewFeature
|
import dev.inmo.tgbotapi.utils.PreviewFeature
|
||||||
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
|
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
|
* The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
|
||||||
@@ -28,24 +26,35 @@ suspend fun main(vararg args: String) {
|
|||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
val me = getMe()
|
val me = getMe()
|
||||||
onMentionWithAnyContent(me) { message ->
|
onContentMessage(
|
||||||
val chat = message.chat
|
initialFilter = initialFilter@{ it.text ?.contains(me.username ?.full ?: return@initialFilter false) == true }
|
||||||
|
) { message ->
|
||||||
val answerText = when (val chat = message.chat) {
|
val answerText = when (val chat = message.chat) {
|
||||||
is PreviewChannelChat -> {
|
is PreviewChannelChat -> {
|
||||||
val answer = "Hi everybody in this channel \"${chat.title}\""
|
val sender = message.sender_chat
|
||||||
reply(message, answer, MarkdownV2)
|
val answer = "Hi everybody in this channel \"${chat.title}\"" + if (sender != null) {
|
||||||
return@onMentionWithAnyContent
|
" and you, " + when (sender) {
|
||||||
|
is BusinessChat -> "business chat (wat) ${sender.original}"
|
||||||
|
is PrivateChat -> "${sender.lastName} ${sender.firstName}"
|
||||||
|
is GroupChat -> "group ${sender.title}"
|
||||||
|
is ChannelChat -> "channel ${sender.title}"
|
||||||
|
is UnknownChatType -> "wat chat (${sender})"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
reply(message, answer.escapeMarkdownV2Common(), MarkdownV2)
|
||||||
|
return@onContentMessage
|
||||||
}
|
}
|
||||||
is PreviewPrivateChat -> {
|
is PreviewPrivateChat -> {
|
||||||
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 PreviewGroupChat -> {
|
||||||
message.ifFromChannelGroupContentMessage {
|
message.ifFromChannelGroupContentMessage<Unit> {
|
||||||
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,6 +65,10 @@ suspend fun main(vararg args: String) {
|
|||||||
} ?: chat.title
|
} ?: chat.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is PreviewBusinessChat -> {
|
||||||
|
reply(message, "Hi, " + "${chat.original.firstName} ${chat.original.lastName} (as business chat :) )".textMentionMarkdownV2(chat.original.id), MarkdownV2)
|
||||||
|
return@onContentMessage
|
||||||
|
}
|
||||||
is UnknownChatType -> "Unknown :(".escapeMarkdownV2Common()
|
is UnknownChatType -> "Unknown :(".escapeMarkdownV2Common()
|
||||||
}
|
}
|
||||||
reply(
|
reply(
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName="InlineQueriesBotKt"
|
|
||||||
|
|
||||||
apply from: "$nativePartTemplate"
|
apply from: "$nativePartTemplate"
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
mainClass.set("InlineQueriesBotKt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
@@ -27,12 +29,9 @@ kotlin {
|
|||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
api "io.ktor:ktor-client-logging:$ktor_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation 'io.ktor:ktor-client-logging-jvm:2.3.7'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
@@ -59,7 +59,7 @@ suspend fun doInlineQueriesBot(token: String) {
|
|||||||
reply(message, deepLink)
|
reply(message, deepLink)
|
||||||
}
|
}
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import dev.inmo.micro_utils.common.MPPFile
|
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
doInlineQueriesBot(args.first())
|
doInlineQueriesBot(args.first())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
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.getMe
|
||||||
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.edit.text.editMessageText
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
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.CustomEmojiId
|
||||||
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.InlineQueryId
|
import dev.inmo.tgbotapi.types.InlineQueryId
|
||||||
|
import dev.inmo.tgbotapi.types.buttons.KeyboardButtonStyle
|
||||||
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.PreviewFeature
|
||||||
import kotlinx.coroutines.*
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
private const val nextPageData = "next"
|
import dev.inmo.tgbotapi.utils.row
|
||||||
private const val previousPageData = "previous"
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
||||||
fun String.parsePageAndCount(): Pair<Int, Int>? {
|
fun String.parsePageAndCount(): Pair<Int, Int>? {
|
||||||
val (pageString, countString) = split(" ").takeIf { it.count() > 1 } ?: return null
|
val (pageString, countString) = split(" ").takeIf { it.count() > 1 } ?: return null
|
||||||
@@ -45,20 +47,23 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
row {
|
||||||
|
copyTextButton("Command copy button", "/inline $page $count")
|
||||||
|
}
|
||||||
|
|
||||||
row {
|
row {
|
||||||
if (page - 1 > 2) {
|
if (page - 1 > 2) {
|
||||||
dataButton("<<", "1 $count")
|
dataButton("<<", "1 $count", style = KeyboardButtonStyle.Danger)
|
||||||
}
|
}
|
||||||
if (page - 1 > 1) {
|
if (page - 1 > 1) {
|
||||||
dataButton("<", "${page - 2} $count")
|
dataButton("<", "${page - 2} $count", style = KeyboardButtonStyle.Primary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page + 1 < count) {
|
if (page + 1 < count) {
|
||||||
dataButton(">", "${page + 2} $count")
|
dataButton(">", "${page + 2} $count", style = KeyboardButtonStyle.Success)
|
||||||
}
|
}
|
||||||
if (page + 2 < count) {
|
if (page + 2 < count) {
|
||||||
dataButton(">>", "$count $count")
|
dataButton(">>", "$count $count", style = KeyboardButtonStyle.Danger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
@@ -73,6 +78,7 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
suspend fun activateKeyboardsBot(
|
suspend fun activateKeyboardsBot(
|
||||||
token: String,
|
token: String,
|
||||||
print: (Any) -> Unit
|
print: (Any) -> Unit
|
||||||
@@ -83,11 +89,13 @@ suspend fun activateKeyboardsBot(
|
|||||||
|
|
||||||
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
||||||
onCommandWithArgs("inline") { message, args ->
|
onCommandWithArgs("inline") { message, args ->
|
||||||
val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10
|
val numberArgs = args.mapNotNull { it.toIntOrNull() }
|
||||||
|
val numberOfPages = numberArgs.getOrNull(1) ?: numberArgs.firstOrNull() ?: 10
|
||||||
|
val page = numberArgs.firstOrNull()?.takeIf { numberArgs.size > 1 }?.coerceAtLeast(1) ?: 1
|
||||||
reply(
|
reply(
|
||||||
message,
|
message,
|
||||||
replyMarkup = inlineKeyboard {
|
replyMarkup = inlineKeyboard {
|
||||||
includePageButtons(1, numberOfPages)
|
includePageButtons(page, numberOfPages)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
regular("Your inline keyboard with $numberOfPages pages")
|
regular("Your inline keyboard with $numberOfPages pages")
|
||||||
@@ -132,7 +140,8 @@ suspend fun activateKeyboardsBot(
|
|||||||
|
|
||||||
onBaseInlineQuery {
|
onBaseInlineQuery {
|
||||||
val page = it.query.takeWhile { it.isDigit() }.toIntOrNull() ?: return@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
|
val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }
|
||||||
|
.toIntOrNull() ?: return@onBaseInlineQuery
|
||||||
|
|
||||||
answer(
|
answer(
|
||||||
it,
|
it,
|
||||||
@@ -154,7 +163,7 @@ suspend fun activateKeyboardsBot(
|
|||||||
it,
|
it,
|
||||||
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
|
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
|
||||||
row {
|
row {
|
||||||
simpleButton("/inline")
|
simpleButton("/inline", style = KeyboardButtonStyle.Primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
@@ -164,7 +173,7 @@ suspend fun activateKeyboardsBot(
|
|||||||
|
|
||||||
setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))
|
setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
}.join()
|
}.join()
|
||||||
|
|||||||
@@ -1,7 +1,21 @@
|
|||||||
|
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 kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.IO) { // IO for inheriting of it in side of activateKeyboardsBot
|
withContext(Dispatchers.IO) { // IO for inheriting of it in side of activateKeyboardsBot
|
||||||
activateKeyboardsBot(args.first()) {
|
activateKeyboardsBot(args.first()) {
|
||||||
println(it)
|
println(it)
|
||||||
|
|||||||
@@ -3,24 +3,15 @@ import dev.inmo.kslog.common.LogLevel
|
|||||||
import dev.inmo.kslog.common.defaultMessageFormatter
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
import dev.inmo.kslog.common.setDefaultKSLog
|
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.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.reply
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.send
|
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.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.behaviour_builder.triggers_handling.onContentMessage
|
||||||
import dev.inmo.tgbotapi.extensions.utils.textLinkTextSourceOrNull
|
import dev.inmo.tgbotapi.extensions.utils.textLinkTextSourceOrNull
|
||||||
import dev.inmo.tgbotapi.extensions.utils.uRLTextSourceOrNull
|
import dev.inmo.tgbotapi.extensions.utils.uRLTextSourceOrNull
|
||||||
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
import dev.inmo.tgbotapi.types.LinkPreviewOptions
|
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.message.content.TextedContent
|
||||||
import dev.inmo.tgbotapi.types.reactions.Reaction
|
|
||||||
import dev.inmo.tgbotapi.utils.customEmoji
|
|
||||||
import dev.inmo.tgbotapi.utils.regular
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,17 +32,17 @@ suspend fun main(vararg args: String) {
|
|||||||
val bot = telegramBot(botToken)
|
val bot = telegramBot(botToken)
|
||||||
|
|
||||||
bot.buildBehaviourWithLongPolling {
|
bot.buildBehaviourWithLongPolling {
|
||||||
onContentMessage {
|
onContentMessage { contentMessage ->
|
||||||
val url = it.withContentOrNull<TextedContent>() ?.let {
|
val url = contentMessage.withContentOrNull<TextedContent>() ?.let { message ->
|
||||||
it.content.textSources.firstNotNullOfOrNull {
|
message.content.textSources.firstNotNullOfOrNull {
|
||||||
it.textLinkTextSourceOrNull() ?.url ?: it.uRLTextSourceOrNull() ?.source
|
it.textLinkTextSourceOrNull() ?.url ?: it.uRLTextSourceOrNull() ?.source
|
||||||
}
|
}
|
||||||
} ?: null.apply {
|
} ?: null.apply {
|
||||||
reply(it) {
|
reply(contentMessage) {
|
||||||
regular("I am support only content with text contains url only")
|
regular("I am support only content with text contains url only")
|
||||||
}
|
}
|
||||||
} ?: return@onContentMessage
|
} ?: return@onContentMessage
|
||||||
it.withContentOrNull<TextedContent>() ?.let {
|
contentMessage.withContentOrNull<TextedContent>() ?.let {
|
||||||
send(
|
send(
|
||||||
it.chat,
|
it.chat,
|
||||||
it.content.textSources,
|
it.content.textSources,
|
||||||
|
|||||||
@@ -1,33 +1,15 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.EditLiveLocationInfo
|
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.edit.location.live.stopLiveLocation
|
||||||
import dev.inmo.tgbotapi.extensions.api.handleLiveLocation
|
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.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.telegramBotWithBehaviourAndLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
|
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.dataButton
|
||||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
|
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.abstracts.ContentMessage
|
||||||
import dev.inmo.tgbotapi.types.message.content.LiveLocationContent
|
|
||||||
import dev.inmo.tgbotapi.types.message.content.LocationContent
|
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.*
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -64,7 +46,7 @@ suspend fun main(vararg args: String) {
|
|||||||
handleLiveLocation(
|
handleLiveLocation(
|
||||||
it.chat.id,
|
it.chat.id,
|
||||||
locationsFlow,
|
locationsFlow,
|
||||||
sentMessageFlow = FlowCollector { currentMessageState.emit(it) }
|
sentMessageFlow = { currentMessageState.emit(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
MemberUpdatedWatcherBot/README.md
Normal file
10
MemberUpdatedWatcherBot/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# MemberUpdatedWatcherBot
|
||||||
|
|
||||||
|
This bot will watch for some ChatMemberUpdated events using new extensions from 18.0.0
|
||||||
|
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
MemberUpdatedWatcherBot/build.gradle
Normal file
21
MemberUpdatedWatcherBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="MemberUpdatedWatcherKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import dev.inmo.kslog.common.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictedFilter
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.chatMemberGotRestrictionsChangedFilter
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.*
|
||||||
|
import dev.inmo.tgbotapi.types.chat.member.*
|
||||||
|
import dev.inmo.tgbotapi.utils.*
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val token = args.first()
|
||||||
|
|
||||||
|
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 internalLogger = KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag ?: "ChatMemberUpdates", message, throwable))
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot = telegramBot(token)
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling {
|
||||||
|
val me = getMe()
|
||||||
|
val filterSelfUpdates = SimpleFilter<ChatMemberUpdated> {
|
||||||
|
it.member.id == me.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// This bot updates
|
||||||
|
onChatMemberJoined(initialFilter = filterSelfUpdates) {
|
||||||
|
internalLogger.i("Bot was added to chat")
|
||||||
|
send(it.chat.id, "I was added to chat. Please grant me admin permissions to make me able to watch other users' events")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberGotPromoted(initialFilter = filterSelfUpdates) {
|
||||||
|
internalLogger.i("Bot was granted admin permissions")
|
||||||
|
send(it.chat.id, "I was promoted to admin. I now can watch other users' events")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberGotDemoted(initialFilter = filterSelfUpdates) {
|
||||||
|
internalLogger.i("Admin permissions were revoked")
|
||||||
|
send(it.chat.id, "I'm no longer an admin. Admin permissions are required to watch other users' events")
|
||||||
|
}
|
||||||
|
|
||||||
|
// All users updates
|
||||||
|
onChatMemberJoined {
|
||||||
|
val member = it.member
|
||||||
|
internalLogger.i("${member.firstName} joined the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
|
||||||
|
send(it.chat.id, "Welcome ${member.firstName}")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberLeft {
|
||||||
|
val member = it.member
|
||||||
|
internalLogger.i("${member.firstName} left the chat: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
|
||||||
|
send(it.chat.id, "Goodbye ${member.firstName}")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberGotPromoted {
|
||||||
|
val newState = it.newChatMemberState.administratorChatMemberOrThrow()
|
||||||
|
internalLogger.i("${newState.user.firstName} got promoted to ${newState.customTitle ?: "Admin"}: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
|
||||||
|
send(it.chat.id, "${newState.user.firstName} is now an ${newState.customTitle ?: "Admin"}")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberGotDemoted {
|
||||||
|
val member = it.member
|
||||||
|
internalLogger.i("${member.firstName} got demoted: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}")
|
||||||
|
send(it.chat.id, "${member.firstName} is now got demoted back to member")
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberGotPromotionChanged {
|
||||||
|
val member = it.member
|
||||||
|
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
|
||||||
|
internalLogger.i(message)
|
||||||
|
send(it.chat.id, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatMemberUpdated(
|
||||||
|
initialFilter = chatMemberGotRestrictedFilter + chatMemberGotRestrictionsChangedFilter,
|
||||||
|
) {
|
||||||
|
val member = it.member
|
||||||
|
val message = "${member.firstName} has the permissions changed: ${it.oldChatMemberState::class.simpleName} => ${it.newChatMemberState::class.simpleName}"
|
||||||
|
internalLogger.i(message)
|
||||||
|
send(it.chat.id, message)
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
94
MyBot/src/main/kotlin/MyBot.kt
Normal file
94
MyBot/src/main/kotlin/MyBot.kt
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
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.launchLoggingDropExceptions
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingLogging
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.removeMyProfilePhoto
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyProfilePhoto
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.sendMessageDraftFlowWithTexts
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitPhotoMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
|
import dev.inmo.tgbotapi.requests.business_connection.InputProfilePhoto
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is one of the easiest bots - it will just print information about itself
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot = telegramBot(botToken)
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
botToken,
|
||||||
|
CoroutineScope(Dispatchers.Default),
|
||||||
|
testServer = isTestServer,
|
||||||
|
) {
|
||||||
|
val me = bot.getMe()
|
||||||
|
println(me)
|
||||||
|
println(bot.getChat(me))
|
||||||
|
|
||||||
|
onCommand("setMyProfilePhoto") { commandMessage ->
|
||||||
|
reply(commandMessage, "ok, send me new photo")
|
||||||
|
val newPhotoMessage = waitPhotoMessage().filter { potentialPhotoMessage ->
|
||||||
|
potentialPhotoMessage.sameChat(commandMessage)
|
||||||
|
}.first()
|
||||||
|
val draftMessagesChannel = Channel<String>(capacity = 1)
|
||||||
|
|
||||||
|
launchLoggingDropExceptions {
|
||||||
|
sendMessageDraftFlowWithTexts(commandMessage.chat.id, draftMessagesChannel.consumeAsFlow())
|
||||||
|
}.invokeOnCompletion {
|
||||||
|
draftMessagesChannel.close(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
draftMessagesChannel.send("Start downloading photo")
|
||||||
|
val photoFile = downloadFileToTemp(newPhotoMessage.content)
|
||||||
|
|
||||||
|
draftMessagesChannel.send("Photo file have been downloaded. Start set my profile photo")
|
||||||
|
|
||||||
|
val setResult = setMyProfilePhoto(
|
||||||
|
InputProfilePhoto.Static(
|
||||||
|
photoFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (setResult) {
|
||||||
|
reply(commandMessage, "New photo have been set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("removeMyProfilePhoto") {
|
||||||
|
runCatchingLogging {
|
||||||
|
if (removeMyProfilePhoto()) {
|
||||||
|
reply(it, "Photo have been removed")
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
e.printStackTrace()
|
||||||
|
reply(it, "Something web wrong. See logs for details.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,55 +1,78 @@
|
|||||||
import com.benasher44.uuid.uuid4
|
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.tgbotapi.extensions.api.bot.getMe
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
import dev.inmo.tgbotapi.extensions.api.send.polls.sendQuizPoll
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll
|
import dev.inmo.tgbotapi.extensions.api.send.polls.sendRegularPoll
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.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.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollAnswer
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollUpdates
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
|
import dev.inmo.tgbotapi.extensions.utils.customEmojiTextSourceOrNull
|
||||||
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
|
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources
|
||||||
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
import dev.inmo.tgbotapi.types.*
|
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
||||||
import dev.inmo.tgbotapi.types.chat.*
|
import dev.inmo.tgbotapi.types.PollId
|
||||||
import dev.inmo.tgbotapi.types.chat.GroupChat
|
import dev.inmo.tgbotapi.types.ReplyParameters
|
||||||
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
import dev.inmo.tgbotapi.types.polls.InputPollOption
|
||||||
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.PollAnswer
|
||||||
import dev.inmo.tgbotapi.types.polls.PollOption
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
import dev.inmo.tgbotapi.types.polls.RegularPoll
|
import dev.inmo.tgbotapi.utils.customEmoji
|
||||||
import dev.inmo.tgbotapi.utils.PreviewFeature
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
|
import dev.inmo.tgbotapi.utils.underline
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This bot will answer with anonymous or public poll and send message on
|
* This bot will answer with anonymous or public poll and send message on
|
||||||
* updates of any of it.
|
* any update.
|
||||||
*
|
*
|
||||||
* * Use `/anonymous` to take anonymous regular poll
|
* * Use `/anonymous` to take anonymous regular poll
|
||||||
* * Use `/public` to take public regular poll
|
* * Use `/public` to take public regular poll
|
||||||
*/
|
*/
|
||||||
@OptIn(PreviewFeature::class)
|
|
||||||
suspend fun main(vararg args: String) {
|
suspend fun main(vararg args: String) {
|
||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
val me = getMe()
|
|
||||||
|
|
||||||
val pollToChat = mutableMapOf<PollId, IdChatIdentifier>()
|
val pollToChat = mutableMapOf<PollId, IdChatIdentifier>()
|
||||||
val pollToChatMutex = Mutex()
|
val pollToChatMutex = Mutex()
|
||||||
|
|
||||||
onCommand("anonymous") {
|
onCommand("anonymous", requireOnlyCommandInMessage = false) {
|
||||||
|
val customEmoji = it.content.parseCommandsWithArgsSources()
|
||||||
|
.toList()
|
||||||
|
.firstOrNull { it.first.command == "anonymous" }
|
||||||
|
?.second
|
||||||
|
?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() }
|
||||||
val sentPoll = sendRegularPoll(
|
val sentPoll = sendRegularPoll(
|
||||||
it.chat,
|
it.chat.id,
|
||||||
"Test regular anonymous poll",
|
buildEntities {
|
||||||
|
regular("Test regular anonymous poll")
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
},
|
||||||
(1 .. 10).map {
|
(1 .. 10).map {
|
||||||
it.toString()
|
InputPollOption {
|
||||||
|
regular(it.toString()) + " "
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isAnonymous = true,
|
isAnonymous = true,
|
||||||
replyParameters = ReplyParameters(it)
|
replyParameters = ReplyParameters(it)
|
||||||
@@ -59,12 +82,27 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCommand("public") {
|
onCommand("public", requireOnlyCommandInMessage = false) {
|
||||||
|
val customEmoji = it.content.parseCommandsWithArgsSources()
|
||||||
|
.toList()
|
||||||
|
.firstOrNull { it.first.command == "public" }
|
||||||
|
?.second
|
||||||
|
?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() }
|
||||||
val sentPoll = sendRegularPoll(
|
val sentPoll = sendRegularPoll(
|
||||||
it.chat,
|
it.chat.id,
|
||||||
"Test regular anonymous poll",
|
buildEntities {
|
||||||
|
regular("Test regular non anonymous poll")
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
},
|
||||||
(1 .. 10).map {
|
(1 .. 10).map {
|
||||||
it.toString()
|
InputPollOption {
|
||||||
|
regular(it.toString()) + " "
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isAnonymous = false,
|
isAnonymous = false,
|
||||||
replyParameters = ReplyParameters(it)
|
replyParameters = ReplyParameters(it)
|
||||||
@@ -74,6 +112,44 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCommand("quiz", requireOnlyCommandInMessage = false) {
|
||||||
|
val customEmoji = it.content.parseCommandsWithArgsSources()
|
||||||
|
.toList()
|
||||||
|
.firstOrNull { it.first.command == "quiz" }
|
||||||
|
?.second
|
||||||
|
?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() }
|
||||||
|
val correctAnswer = Random.nextInt(10)
|
||||||
|
val sentPoll = sendQuizPoll(
|
||||||
|
it.chat.id,
|
||||||
|
questionEntities = buildEntities {
|
||||||
|
regular("Test quiz poll")
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(1 .. 10).map {
|
||||||
|
InputPollOption {
|
||||||
|
regular(it.toString()) + " "
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isAnonymous = false,
|
||||||
|
replyParameters = ReplyParameters(it),
|
||||||
|
correctOptionId = correctAnswer,
|
||||||
|
explanationTextSources = buildEntities {
|
||||||
|
regular("Random solved it to be ") + underline((correctAnswer + 1).toString()) + " "
|
||||||
|
if (customEmoji != null) {
|
||||||
|
customEmoji(customEmoji.customEmojiId, customEmoji.subsources)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
pollToChatMutex.withLock {
|
||||||
|
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onPollAnswer {
|
onPollAnswer {
|
||||||
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
|
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
|
||||||
|
|
||||||
@@ -92,6 +168,12 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMyCommands(
|
||||||
|
BotCommand("anonymous", "Create anonymous regular poll"),
|
||||||
|
BotCommand("public", "Create non anonymous regular poll"),
|
||||||
|
BotCommand("quiz", "Create quiz poll with random right answer"),
|
||||||
|
)
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
mainClassName="RandomFileSenderBotKt"
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
mainClass.set("RandomFileSenderBotKt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
|||||||
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.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
|
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.buildBehaviourWithLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
|
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.onChatMessageReactionsCountUpdated
|
||||||
@@ -49,6 +48,7 @@ suspend fun main(vararg args: String) {
|
|||||||
when (it) {
|
when (it) {
|
||||||
is Reaction.CustomEmoji -> regular("• ") + customEmoji(it.customEmojiId) + regular("(customEmojiId: ${it.customEmojiId})")
|
is Reaction.CustomEmoji -> regular("• ") + customEmoji(it.customEmojiId) + regular("(customEmojiId: ${it.customEmojiId})")
|
||||||
is Reaction.Emoji -> regular("• ${it.emoji}")
|
is Reaction.Emoji -> regular("• ${it.emoji}")
|
||||||
|
is Reaction.Paid -> regular("• Some paid reaction")
|
||||||
is Reaction.Unknown -> regular("• Unknown emoji ($it)")
|
is Reaction.Unknown -> regular("• Unknown emoji ($it)")
|
||||||
}
|
}
|
||||||
regular("\n")
|
regular("\n")
|
||||||
|
|||||||
@@ -1,27 +1,36 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.filter.filtered
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.media.*
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilterExcludeMediaGroups
|
|
||||||
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.telegramBotWithBehaviourAndLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.possiblyWithEffectMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
|
||||||
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
import dev.inmo.tgbotapi.types.ReplyParameters
|
import dev.inmo.tgbotapi.types.ReplyParameters
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.BusinessContentMessage
|
||||||
import dev.inmo.tgbotapi.types.message.content.TextContent
|
import dev.inmo.tgbotapi.types.message.content.TextContent
|
||||||
import dev.inmo.tgbotapi.types.quoteEntitiesField
|
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
|
||||||
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
|
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
||||||
suspend fun activateResenderBot(
|
suspend fun activateResenderBot(
|
||||||
token: String,
|
token: String,
|
||||||
print: (Any) -> Unit
|
print: (Any) -> Unit
|
||||||
) {
|
) {
|
||||||
telegramBotWithBehaviourAndLongPolling(token, scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
token,
|
||||||
|
scope = CoroutineScope(currentCoroutineContext() + SupervisorJob()),
|
||||||
|
) {
|
||||||
onContentMessage(
|
onContentMessage(
|
||||||
subcontextUpdatesFilter = MessageFilterByChat,
|
subcontextUpdatesFilter = MessageFilterByChat,
|
||||||
|
initialFilter = { it !is BusinessContentMessage<*> || !it.sentByBusinessConnectionOwner }
|
||||||
) {
|
) {
|
||||||
val chat = it.chat
|
val chat = it.chat
|
||||||
|
|
||||||
@@ -29,15 +38,15 @@ suspend fun activateResenderBot(
|
|||||||
executeUnsafe(
|
executeUnsafe(
|
||||||
it.content.createResend(
|
it.content.createResend(
|
||||||
chat.id,
|
chat.id,
|
||||||
messageThreadId = it.threadIdOrNull,
|
replyParameters = it.replyInfo?.messageMeta?.let { meta ->
|
||||||
replyParameters = it.replyInfo ?.messageMeta ?.let { meta ->
|
val quote = it.withContentOrNull<TextContent>()?.content?.quote
|
||||||
val quote = it.withContentOrNull<TextContent>() ?.content ?.quote
|
|
||||||
ReplyParameters(
|
ReplyParameters(
|
||||||
meta,
|
meta,
|
||||||
entities = quote ?.textSources ?: emptyList(),
|
entities = quote?.textSources ?: emptyList(),
|
||||||
quotePosition = quote ?.position
|
quotePosition = quote?.position
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
effectId = it.possiblyWithEffectMessageOrNull()?.effectId
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
it.forEach(print)
|
it.forEach(print)
|
||||||
@@ -47,7 +56,7 @@ suspend fun activateResenderBot(
|
|||||||
println("Answer info: $answer")
|
println("Answer info: $answer")
|
||||||
}
|
}
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
print(bot.getMe())
|
print(bot.getMe())
|
||||||
|
|||||||
@@ -1,4 +1,19 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
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))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
activateResenderBot(args.first()) {
|
activateResenderBot(args.first()) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
implementation 'io.ktor:ktor-client-logging-jvm:2.3.7'
|
implementation 'io.ktor:ktor-client-logging-jvm:3.2.3'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ import dev.inmo.tgbotapi.extensions.api.send.reply
|
|||||||
import dev.inmo.tgbotapi.extensions.api.send.send
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithFSMAndStartLongPolling
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithFSMAndStartLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitChatSharedEventsMessages
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitCommandMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitUserSharedEventsMessages
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMessageDataCallbackQuery
|
||||||
import dev.inmo.tgbotapi.extensions.utils.*
|
import dev.inmo.tgbotapi.extensions.utils.*
|
||||||
@@ -27,12 +29,12 @@ import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
|
|||||||
import dev.inmo.tgbotapi.types.chat.ChannelChat
|
import dev.inmo.tgbotapi.types.chat.ChannelChat
|
||||||
import dev.inmo.tgbotapi.types.chat.ChatPermissions
|
import dev.inmo.tgbotapi.types.chat.ChatPermissions
|
||||||
import dev.inmo.tgbotapi.types.chat.PublicChat
|
import dev.inmo.tgbotapi.types.chat.PublicChat
|
||||||
import dev.inmo.tgbotapi.types.chat.member.*
|
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
|
||||||
|
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
|
||||||
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||||
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
|
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
|
||||||
import dev.inmo.tgbotapi.types.request.RequestId
|
import dev.inmo.tgbotapi.types.request.RequestId
|
||||||
import dev.inmo.tgbotapi.utils.*
|
import dev.inmo.tgbotapi.utils.*
|
||||||
import dev.inmo.tgbotapi.utils.mention
|
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.mapNotNull
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
@@ -52,6 +54,7 @@ sealed interface UserRetrievingStep : State {
|
|||||||
) : UserRetrievingStep
|
) : UserRetrievingStep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
|
|
||||||
@@ -103,7 +106,7 @@ suspend fun main(args: Array<String>) {
|
|||||||
|
|
||||||
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
|
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
|
||||||
val chatMember = getChatMember(chatId, userId)
|
val chatMember = getChatMember(chatId, userId)
|
||||||
return chatMember.restrictedChatMemberOrNull() ?: chatMember.whenMemberChatMember {
|
return chatMember.restrictedMemberChatMemberOrNull() ?: chatMember.whenMemberChatMember {
|
||||||
getChat(chatId).extendedGroupChatOrNull() ?.permissions
|
getChat(chatId).extendedGroupChatOrNull() ?.permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +227,6 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val replyMessage = it.replyTo
|
val replyMessage = it.replyTo
|
||||||
val usernameInText = it.content.textSources.firstNotNullOfOrNull { it.mentionTextSourceOrNull() } ?.username
|
|
||||||
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
||||||
|
|
||||||
if (replyMessage is AccessibleMessage) {
|
if (replyMessage is AccessibleMessage) {
|
||||||
@@ -381,7 +383,6 @@ suspend fun main(args: Array<String>) {
|
|||||||
val userId = ChatId(RawChatId(userIdString.toLong()))
|
val userId = ChatId(RawChatId(userIdString.toLong()))
|
||||||
val chatMember = getChatMember(channelId, userId)
|
val chatMember = getChatMember(channelId, userId)
|
||||||
val asAdmin = chatMember.administratorChatMemberOrNull()
|
val asAdmin = chatMember.administratorChatMemberOrNull()
|
||||||
val asMember = chatMember.memberChatMemberOrNull()
|
|
||||||
|
|
||||||
val realData = it.data.takeWhile { it != ' ' }
|
val realData = it.data.takeWhile { it != ' ' }
|
||||||
|
|
||||||
@@ -504,7 +505,6 @@ suspend fun main(args: Array<String>) {
|
|||||||
strictlyOn<UserRetrievingStep.RetrievingChatInfoDoneState> { state ->
|
strictlyOn<UserRetrievingStep.RetrievingChatInfoDoneState> { state ->
|
||||||
val chatMember = getChatMember(state.channelId, state.userId).administratorChatMemberOrNull()
|
val chatMember = getChatMember(state.channelId, state.userId).administratorChatMemberOrNull()
|
||||||
if (chatMember == null) {
|
if (chatMember == null) {
|
||||||
|
|
||||||
return@strictlyOn null
|
return@strictlyOn null
|
||||||
}
|
}
|
||||||
send(
|
send(
|
||||||
|
|||||||
9
StarTransactionsBot/README.md
Normal file
9
StarTransactionsBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# StarTransactionsBot
|
||||||
|
|
||||||
|
This bot basically have no any useful behaviour, but you may customize it as a playground
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
StarTransactionsBot/build.gradle
Normal file
21
StarTransactionsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="StarTransactionsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
185
StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt
Normal file
185
StarTransactionsBot/src/main/kotlin/StarTransactionsBot.kt
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
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.tgbotapi.extensions.api.answers.payments.answerPreCheckoutQueryOk
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.edit.edit
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getStarTransactions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
|
import dev.inmo.tgbotapi.types.ChatId
|
||||||
|
import dev.inmo.tgbotapi.types.RawChatId
|
||||||
|
import dev.inmo.tgbotapi.types.UserId
|
||||||
|
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
|
||||||
|
import dev.inmo.tgbotapi.types.files.*
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramPaidMediaPhoto
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramPaidMediaVideo
|
||||||
|
import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaPhoto
|
||||||
|
import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaVideo
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.TextContent
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList
|
||||||
|
import dev.inmo.tgbotapi.types.payments.LabeledPrice
|
||||||
|
import dev.inmo.tgbotapi.types.payments.stars.StarTransaction
|
||||||
|
import dev.inmo.tgbotapi.types.request.RequestId
|
||||||
|
import dev.inmo.tgbotapi.utils.bold
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
import dev.inmo.tgbotapi.utils.row
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example bot that interacts with Telegram Stars API (used for payments)
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val adminUserId = args.getOrNull(1) ?.toLongOrNull() ?.let(::RawChatId) ?.let(::ChatId) ?: error("Pass user-admin for full access to the bot")
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO), testServer = isTestServer) {
|
||||||
|
val payload = "sample payload"
|
||||||
|
command("start") {
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
price = LabeledPrice("1", 1L),
|
||||||
|
title = "Sample",
|
||||||
|
description = "Sample description",
|
||||||
|
payload = payload,
|
||||||
|
replyMarkup = flatInlineKeyboard {
|
||||||
|
payButton("Pay")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreCheckoutQuery(initialFilter = { it.invoicePayload == payload }) {
|
||||||
|
answerPreCheckoutQueryOk(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val transactionsDataPrefix = "getStarTransactions"
|
||||||
|
fun buildTransactionsData(offset: Int, limit: Int = 10) = "$transactionsDataPrefix $offset $limit"
|
||||||
|
fun parseTransactionsData(data: String): Pair<Int, Int> = data.split(" ").drop(1).let {
|
||||||
|
it.first().toInt() to it.last().toInt()
|
||||||
|
}
|
||||||
|
suspend fun buildStarTransactionsPage(offset: Int, limit: Int = 10): Pair<TextSourcesList, InlineKeyboardMarkup> {
|
||||||
|
val transactions = getStarTransactions(offset, limit)
|
||||||
|
return buildEntities {
|
||||||
|
transactions.transactions.forEach {
|
||||||
|
regular("Transaction Id: ") + bold(it.id.string) + "\n"
|
||||||
|
regular("Date: ") + bold(it.date.asDate.toStringDefault()) + "\n"
|
||||||
|
regular("Amount: ") + bold(it.amount.toString()) + "\n"
|
||||||
|
when (it) {
|
||||||
|
is StarTransaction.Incoming -> {
|
||||||
|
regular("Type: ") + bold("incoming") + "\n"
|
||||||
|
regular("Partner: ") + bold(it.partner.type) + "\n"
|
||||||
|
}
|
||||||
|
is StarTransaction.Outgoing -> {
|
||||||
|
regular("Type: ") + bold("outgoing") + "\n"
|
||||||
|
regular("Partner: ") + bold(it.partner.type) + "\n"
|
||||||
|
}
|
||||||
|
is StarTransaction.Unknown -> {
|
||||||
|
regular("Type: ") + bold("unknown") + "\n"
|
||||||
|
regular("Partner: ") + bold(it.partner.type) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} to inlineKeyboard {
|
||||||
|
row {
|
||||||
|
val prevOffset = (offset - limit).coerceAtLeast(0)
|
||||||
|
if (prevOffset < offset) {
|
||||||
|
dataButton("<", buildTransactionsData(prevOffset, limit))
|
||||||
|
}
|
||||||
|
val nextOffset = (offset + limit)
|
||||||
|
dataButton(">", buildTransactionsData(nextOffset, limit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("transactions", initialFilter = { it.sameChat(adminUserId) }) {
|
||||||
|
val (text, keyboard) = buildStarTransactionsPage(0)
|
||||||
|
|
||||||
|
reply(it, text, replyMarkup = keyboard)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageDataCallbackQuery(Regex("$transactionsDataPrefix \\d+ \\d+")) {
|
||||||
|
val (offset, limit) = parseTransactionsData(it.data)
|
||||||
|
val (text, keyboard) = buildStarTransactionsPage(offset, limit)
|
||||||
|
edit(
|
||||||
|
it.message.withContentOrNull<TextContent>() ?: return@onMessageDataCallbackQuery,
|
||||||
|
text,
|
||||||
|
replyMarkup = keyboard,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisualGalleryMessages {
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
1,
|
||||||
|
it.content.group.mapNotNull {
|
||||||
|
val file = downloadFileToTemp(it.content.media)
|
||||||
|
when (it.content.media) {
|
||||||
|
is VideoFile -> {
|
||||||
|
TelegramPaidMediaVideo(
|
||||||
|
file.asMultipartFile()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is PhotoSize -> {
|
||||||
|
TelegramPaidMediaPhoto(
|
||||||
|
file.asMultipartFile()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
it.content.textSources,
|
||||||
|
showCaptionAboveMedia = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPhoto {
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
1,
|
||||||
|
listOf(it.content.media.toTelegramPaidMediaPhoto())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onVideo {
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
1,
|
||||||
|
listOf(it.content.media.toTelegramPaidMediaVideo())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaidMediaInfoContent {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onRefundedPayment {
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
"Received your refund: ${it.chatEvent.payment}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,29 +1,25 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
|
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.api.get.*
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
import dev.inmo.tgbotapi.extensions.api.get.getCustomEmojiStickerOrNull
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSetOrNull
|
||||||
import dev.inmo.tgbotapi.types.StickerFormat
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
|
||||||
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.CustomEmojiTextSource
|
||||||
import dev.inmo.tgbotapi.types.stickers.AnimatedStickerSet
|
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
|
||||||
import dev.inmo.tgbotapi.types.stickers.CustomEmojiSimpleStickerSet
|
import dev.inmo.tgbotapi.types.message.textsources.separateForText
|
||||||
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.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
||||||
fun StickerSet?.buildInfo() = buildEntities {
|
fun StickerSet?.buildInfo() = buildEntities {
|
||||||
if (this@buildInfo == null) {
|
if (this@buildInfo == null) {
|
||||||
@@ -31,12 +27,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"
|
||||||
@@ -65,14 +55,14 @@ suspend fun activateStickerInfoBot(
|
|||||||
withTypingAction(it.chat) {
|
withTypingAction(it.chat) {
|
||||||
it.content.textSources.mapNotNull {
|
it.content.textSources.mapNotNull {
|
||||||
if (it is CustomEmojiTextSource) {
|
if (it is CustomEmojiTextSource) {
|
||||||
getCustomEmojiStickerOrNull(it.customEmojiId) ?.stickerSetName
|
getCustomEmojiStickerOrNull(it.customEmojiId)?.stickerSetName
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}.distinct().map {
|
}.distinct().map {
|
||||||
getStickerSet(it)
|
getStickerSet(it)
|
||||||
}.distinct().flatMap {
|
}.distinct().flatMap {
|
||||||
it.buildInfo() + regular("\n")
|
it.buildInfo() + regularTextSource("\n")
|
||||||
}.separateForText().map { entities ->
|
}.separateForText().map { entities ->
|
||||||
reply(it, entities)
|
reply(it, entities)
|
||||||
}
|
}
|
||||||
@@ -86,7 +76,7 @@ suspend fun activateStickerInfoBot(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
}.join()
|
}.join()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
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.files.downloadFileToTemp
|
||||||
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
@@ -10,12 +10,14 @@ import dev.inmo.tgbotapi.extensions.api.stickers.deleteStickerSet
|
|||||||
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.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
|
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.abstracts.asMultipartFile
|
||||||
import dev.inmo.tgbotapi.requests.stickers.InputSticker
|
import dev.inmo.tgbotapi.requests.stickers.InputSticker
|
||||||
import dev.inmo.tgbotapi.types.StickerSetName
|
import dev.inmo.tgbotapi.types.StickerSetName
|
||||||
import dev.inmo.tgbotapi.types.chat.Chat
|
import dev.inmo.tgbotapi.types.chat.Chat
|
||||||
import dev.inmo.tgbotapi.types.files.*
|
import dev.inmo.tgbotapi.types.files.CustomEmojiSticker
|
||||||
|
import dev.inmo.tgbotapi.types.files.MaskSticker
|
||||||
|
import dev.inmo.tgbotapi.types.files.RegularSticker
|
||||||
|
import dev.inmo.tgbotapi.types.files.UnknownSticker
|
||||||
import dev.inmo.tgbotapi.types.toChatId
|
import dev.inmo.tgbotapi.types.toChatId
|
||||||
import dev.inmo.tgbotapi.utils.botCommand
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -56,16 +58,19 @@ suspend fun main(args: Array<String>) {
|
|||||||
val newSticker = when (sticker) {
|
val newSticker = when (sticker) {
|
||||||
is CustomEmojiSticker -> InputSticker.WithKeywords.CustomEmoji(
|
is CustomEmojiSticker -> InputSticker.WithKeywords.CustomEmoji(
|
||||||
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
emptyList()
|
emptyList()
|
||||||
)
|
)
|
||||||
is MaskSticker -> InputSticker.Mask(
|
is MaskSticker -> InputSticker.Mask(
|
||||||
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
sticker.maskPosition
|
sticker.maskPosition
|
||||||
)
|
)
|
||||||
is RegularSticker -> InputSticker.WithKeywords.Regular(
|
is RegularSticker -> InputSticker.WithKeywords.Regular(
|
||||||
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
emptyList()
|
emptyList()
|
||||||
)
|
)
|
||||||
@@ -74,18 +79,25 @@ suspend fun main(args: Array<String>) {
|
|||||||
runCatchingSafely {
|
runCatchingSafely {
|
||||||
getStickerSet(stickerSetName)
|
getStickerSet(stickerSetName)
|
||||||
}.onSuccess { stickerSet ->
|
}.onSuccess { stickerSet ->
|
||||||
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
|
runCatching {
|
||||||
|
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
getStickerSet(stickerSetName).stickers.last()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.onFailure { exception ->
|
||||||
|
exception.printStackTrace()
|
||||||
reply(
|
reply(
|
||||||
it,
|
it,
|
||||||
getStickerSet(stickerSetName).stickers.last()
|
"Unable to add sticker in stickerset"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.onFailure { _ ->
|
}.onFailure { exception ->
|
||||||
createNewStickerSet(
|
createNewStickerSet(
|
||||||
it.chat.id.toChatId(),
|
it.chat.id.toChatId(),
|
||||||
stickerSetName.string,
|
stickerSetName.string,
|
||||||
"Sticker set by ${me.firstName}",
|
"Sticker set by ${me.firstName}",
|
||||||
it.content.media.stickerFormat,
|
|
||||||
listOf(
|
listOf(
|
||||||
newSticker
|
newSticker
|
||||||
),
|
),
|
||||||
@@ -98,5 +110,9 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
9
SuggestedPosts/README.md
Normal file
9
SuggestedPosts/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# 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"
|
||||||
|
```
|
||||||
21
SuggestedPosts/build.gradle
Normal file
21
SuggestedPosts/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="SuggestedPostsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
140
SuggestedPosts/src/main/kotlin/SuggestedPostsBot.kt
Normal file
140
SuggestedPosts/src/main/kotlin/SuggestedPostsBot.kt
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
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.runCatchingLogging
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMyStarBalance
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.resend
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.suggested.approveSuggestedPost
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.suggested.declineSuggestedPost
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContextData
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildSubcontextInitialAction
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostApproved
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitSuggestedPostDeclined
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChannelDirectMessagesConfigurationChanged
|
||||||
|
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.onSuggestedPostApprovalFailed
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostApproved
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostDeclined
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostPaid
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSuggestedPostRefunded
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.channelDirectMessagesContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.previewChannelDirectMessagesChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.suggestedChannelDirectMessagesContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.types.message.SuggestedPostParameters
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.ChannelPaidPost
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
||||||
|
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||||
|
import dev.inmo.tgbotapi.utils.firstOf
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This place can be the playground for your code.
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
botToken,
|
||||||
|
CoroutineScope(Dispatchers.Default),
|
||||||
|
testServer = isTestServer,
|
||||||
|
) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
onCommand("start") {
|
||||||
|
println(getChat(it.chat))
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentMessage {
|
||||||
|
val message = it.channelDirectMessagesContentMessageOrNull() ?: return@onContentMessage
|
||||||
|
val chat = getChat(it.chat)
|
||||||
|
println(chat)
|
||||||
|
|
||||||
|
resend(
|
||||||
|
message.chat.id,
|
||||||
|
message.content,
|
||||||
|
suggestedPostParameters = SuggestedPostParameters()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentMessage(
|
||||||
|
subcontextUpdatesFilter = { _, _ -> true } // important to not miss updates in channel for waitSuggestedPost events
|
||||||
|
) { message ->
|
||||||
|
val suggestedPost = message.suggestedChannelDirectMessagesContentMessageOrNull() ?: return@onContentMessage
|
||||||
|
|
||||||
|
firstOf(
|
||||||
|
{
|
||||||
|
waitSuggestedPostApproved().filter {
|
||||||
|
it.suggestedPostMessage ?.chat ?.id == message.chat.id
|
||||||
|
}.first()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
waitSuggestedPostDeclined().filter {
|
||||||
|
it.suggestedPostMessage ?.chat ?.id == message.chat.id
|
||||||
|
}.first()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
for (i in 0 until 3) {
|
||||||
|
delay(1000L)
|
||||||
|
send(suggestedPost.chat, "${3 - i}")
|
||||||
|
}
|
||||||
|
declineSuggestedPost(suggestedPost)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentMessage(initialFilter = { it is ChannelPaidPost<*> }) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuggestedPostPaid {
|
||||||
|
println(it)
|
||||||
|
reply(it, "Paid")
|
||||||
|
}
|
||||||
|
onSuggestedPostApproved {
|
||||||
|
println(it)
|
||||||
|
reply(it, "Approved")
|
||||||
|
}
|
||||||
|
onSuggestedPostDeclined {
|
||||||
|
println(it)
|
||||||
|
reply(it, "Declined")
|
||||||
|
}
|
||||||
|
onSuggestedPostRefunded {
|
||||||
|
println(it)
|
||||||
|
reply(it, "Refunded")
|
||||||
|
}
|
||||||
|
onSuggestedPostApprovalFailed {
|
||||||
|
println(it)
|
||||||
|
reply(it, "Approval failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
21
TagsBot/build.gradle
Normal file
21
TagsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="TagsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
101
TagsBot/src/main/kotlin/TagsBot.kt
Normal file
101
TagsBot/src/main/kotlin/TagsBot.kt
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
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.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.abstracts.FromUser
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.business.getBusinessAccountGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChatAdministrator
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChatMember
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.setChatMemberTag
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.gifts.getChatGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.gifts.getUserGiftsFlow
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.withTypingAction
|
||||||
|
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.onGiveawayCompleted
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayContent
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGiveawayWinners
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_tag
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.fromUserOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.groupContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.idChatIdentifierOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.potentiallyFromUserGroupContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.types.UserTag
|
||||||
|
import dev.inmo.tgbotapi.types.chat.BusinessChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PublicChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.UnknownChatType
|
||||||
|
import dev.inmo.tgbotapi.types.gifts.OwnedGift
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.OptionallyFromUserMessage
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.splitForText
|
||||||
|
import dev.inmo.tgbotapi.utils.bold
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.any { it == "debug" }
|
||||||
|
val isTestServer = args.any { it == "testServer" }
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, testServer = isTestServer) {
|
||||||
|
// start here!!
|
||||||
|
val me = getMe()
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
onCommand("setChatMemberTag", requireOnlyCommandInMessage = false) {
|
||||||
|
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
|
||||||
|
val title = it.content.text.removePrefix("/setChatMemberTag").removePrefix(" ")
|
||||||
|
setChatMemberTag(
|
||||||
|
chatId = reply.chat.id,
|
||||||
|
userId = reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
|
||||||
|
tag = UserTag(title)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("setCanManageTags", requireOnlyCommandInMessage = false) {
|
||||||
|
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
|
||||||
|
val setOrUnset = it.content.text.removePrefix("/setCanManageTags").removePrefix(" ") == "true"
|
||||||
|
promoteChatAdministrator(
|
||||||
|
it.chat.id,
|
||||||
|
reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
|
||||||
|
canManageTags = setOrUnset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("removeChatMemberTag") {
|
||||||
|
val reply = it.replyTo ?.groupContentMessageOrNull() ?: return@onCommand
|
||||||
|
setChatMemberTag(
|
||||||
|
chatId = reply.chat.id,
|
||||||
|
userId = reply.fromUserOrNull() ?.user ?.id ?: return@onCommand,
|
||||||
|
tag = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onContentMessage {
|
||||||
|
val groupContentMessage = it.potentiallyFromUserGroupContentMessageOrNull() ?: return@onContentMessage
|
||||||
|
reply(it, "Tag after casting: ${groupContentMessage.senderTag}")
|
||||||
|
reply(it, "Tag by getting via risk API: ${it.sender_tag}")
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -1,25 +1,34 @@
|
|||||||
import com.benasher44.uuid.uuid4
|
import com.benasher44.uuid.uuid4
|
||||||
import dev.inmo.micro_utils.common.repeatOnFailure
|
import dev.inmo.kslog.common.w
|
||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingLogging
|
||||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
|
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.chat.forum.closeForumTopic
|
import dev.inmo.tgbotapi.extensions.api.chat.forum.*
|
||||||
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.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.onCommand
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicClosed
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicEdited
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onForumTopicReopened
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicHidden
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onGeneralForumTopicUnhidden
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicCreated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPrivateForumTopicEdited
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.forumChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.forumContentMessageOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.privateChatOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.privateForumChatOrNull
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.flushAccumulatedUpdates
|
||||||
import dev.inmo.tgbotapi.types.BotCommand
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
import dev.inmo.tgbotapi.types.ForumTopic
|
import dev.inmo.tgbotapi.types.ForumTopic
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||||
|
import io.ktor.client.plugins.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -30,13 +39,33 @@ suspend fun main(vararg args: String) {
|
|||||||
CoroutineScope(Dispatchers.Default),
|
CoroutineScope(Dispatchers.Default),
|
||||||
defaultExceptionsHandler = {
|
defaultExceptionsHandler = {
|
||||||
it.printStackTrace()
|
it.printStackTrace()
|
||||||
|
},
|
||||||
|
builder = {
|
||||||
|
client = client.config {
|
||||||
|
install(HttpTimeout) {
|
||||||
|
requestTimeoutMillis = 30000
|
||||||
|
socketTimeoutMillis = 30000
|
||||||
|
connectTimeoutMillis = 30000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
suspend fun TelegramBot.isPrivateForumsEnabled(): Boolean {
|
||||||
|
val me = getMe()
|
||||||
|
if (me.hasTopicsEnabled == false) {
|
||||||
|
Log.w("private forums are disabled. That means that they will not work in private chats")
|
||||||
|
}
|
||||||
|
return me.hasTopicsEnabled
|
||||||
|
}
|
||||||
|
println()
|
||||||
flushAccumulatedUpdates()
|
flushAccumulatedUpdates()
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
onCommand("start_test_topics") {
|
onCommand("start_test_topics") {
|
||||||
|
if (it.chat is PrivateChat && isPrivateForumsEnabled() == false) {
|
||||||
|
return@onCommand
|
||||||
|
}
|
||||||
val forumTopic = createForumTopic(
|
val forumTopic = createForumTopic(
|
||||||
it.chat,
|
it.chat,
|
||||||
"Test",
|
"Test",
|
||||||
@@ -54,21 +83,23 @@ suspend fun main(vararg args: String) {
|
|||||||
|
|
||||||
reply(it, "Test topic has changed its name to Test 01")
|
reply(it, "Test topic has changed its name to Test 01")
|
||||||
|
|
||||||
delay(1000L)
|
if (it.chat.privateChatOrNull() == null) { // For private forums it is prohibited to close or reopen topics
|
||||||
closeForumTopic(
|
delay(1000L)
|
||||||
it.chat.id,
|
closeForumTopic(
|
||||||
forumTopic.messageThreadId,
|
it.chat.id,
|
||||||
)
|
forumTopic.messageThreadId,
|
||||||
|
)
|
||||||
|
|
||||||
reply(it, "Test topic has been closed")
|
reply(it, "Test topic has been closed")
|
||||||
|
|
||||||
delay(1000L)
|
delay(1000L)
|
||||||
reopenForumTopic(
|
reopenForumTopic(
|
||||||
it.chat.id,
|
it.chat.id,
|
||||||
forumTopic.messageThreadId,
|
forumTopic.messageThreadId,
|
||||||
)
|
)
|
||||||
|
|
||||||
reply(it, "Test topic has been reopened")
|
reply(it, "Test topic has been reopened")
|
||||||
|
}
|
||||||
|
|
||||||
delay(1000L)
|
delay(1000L)
|
||||||
deleteForumTopic(
|
deleteForumTopic(
|
||||||
@@ -78,68 +109,111 @@ suspend fun main(vararg args: String) {
|
|||||||
|
|
||||||
reply(it, "Test topic has been deleted")
|
reply(it, "Test topic has been deleted")
|
||||||
|
|
||||||
delay(1000L)
|
if (it.chat.privateChatOrNull() == null) { // For private forums it is prohibited to close or reopen topics
|
||||||
hideGeneralForumTopic(
|
delay(1000L)
|
||||||
it.chat.id,
|
hideGeneralForumTopic(
|
||||||
)
|
it.chat.id,
|
||||||
|
)
|
||||||
|
|
||||||
reply(it, "General topic has been hidden")
|
reply(it, "General topic has been hidden")
|
||||||
|
|
||||||
delay(1000L)
|
delay(1000L)
|
||||||
unhideGeneralForumTopic(
|
unhideGeneralForumTopic(
|
||||||
it.chat.id
|
it.chat.id
|
||||||
)
|
)
|
||||||
|
|
||||||
reply(it, "General topic has been shown")
|
reply(it, "General topic has been shown")
|
||||||
|
|
||||||
delay(1000L)
|
delay(1000L)
|
||||||
runCatchingSafely(
|
runCatchingSafely(
|
||||||
{ _ ->
|
{ _ ->
|
||||||
reopenGeneralForumTopic(
|
reopenGeneralForumTopic(
|
||||||
it.chat.id
|
it.chat.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
closeGeneralForumTopic(
|
||||||
|
it.chat.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
closeGeneralForumTopic(
|
closeGeneralForumTopic(
|
||||||
it.chat.id
|
it.chat.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
closeGeneralForumTopic(
|
reply(it, "General topic has been closed")
|
||||||
|
|
||||||
|
delay(1000L)
|
||||||
|
reopenGeneralForumTopic(
|
||||||
it.chat.id
|
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
delay(1000L)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCommand("delete_topic") {
|
||||||
|
val chat = it.chat.forumChatOrNull() ?: return@onCommand
|
||||||
|
|
||||||
|
deleteForumTopic(chat, chat.id.threadId ?: return@onCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("unpin_all_forum_topic_messages") {
|
||||||
|
val chat = it.chat.forumChatOrNull() ?: return@onCommand
|
||||||
|
|
||||||
|
unpinAllForumTopicMessages(chat, chat.id.threadId ?: return@onCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
onForumTopicCreated {
|
||||||
|
reply(it, "Topic has been created")
|
||||||
|
}
|
||||||
|
onPrivateForumTopicCreated {
|
||||||
|
reply(it, "Private topic has been created")
|
||||||
|
}
|
||||||
|
|
||||||
|
onForumTopicEdited {
|
||||||
|
reply(it, "Topic has been edited")
|
||||||
|
}
|
||||||
|
onPrivateForumTopicEdited {
|
||||||
|
reply(it, "Private topic has been edited")
|
||||||
|
}
|
||||||
|
|
||||||
|
onForumTopicReopened {
|
||||||
|
reply(it, "Topic has been reopened")
|
||||||
|
}
|
||||||
|
onGeneralForumTopicHidden {
|
||||||
|
reply(it, "General topic has been hidden")
|
||||||
|
}
|
||||||
|
onGeneralForumTopicUnhidden {
|
||||||
|
reply(it, "General topic has been unhidden")
|
||||||
|
}
|
||||||
|
|
||||||
setMyCommands(
|
setMyCommands(
|
||||||
BotCommand("start_test_topics", "start test topics"),
|
BotCommand("start_test_topics", "start test topics"),
|
||||||
|
BotCommand("delete_topic", "delete topic where message have been sent"),
|
||||||
|
BotCommand("unpin_all_forum_topic_messages", "delete topic where message have been sent"),
|
||||||
scope = BotCommandScope.AllGroupChats
|
scope = BotCommandScope.AllGroupChats
|
||||||
)
|
)
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,35 @@
|
|||||||
|
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.runCatchingSafely
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.*
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
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.onChatShared
|
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.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.behaviour_builder.triggers_handling.onUsersShared
|
||||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
||||||
import dev.inmo.tgbotapi.types.BotCommand
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
import dev.inmo.tgbotapi.types.keyboardButtonRequestUserLimit
|
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.types.request.RequestId
|
||||||
|
import dev.inmo.tgbotapi.utils.mention
|
||||||
import dev.inmo.tgbotapi.utils.row
|
import dev.inmo.tgbotapi.utils.row
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<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)
|
||||||
|
|
||||||
@@ -55,35 +67,53 @@ suspend fun main(args: Array<String>) {
|
|||||||
row {
|
row {
|
||||||
requestUserOrBotButton(
|
requestUserOrBotButton(
|
||||||
"\uD83D\uDC64/\uD83E\uDD16 (1)",
|
"\uD83D\uDC64/\uD83E\uDD16 (1)",
|
||||||
requestIdUserOrBot
|
requestIdUserOrBot,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestUserButton(
|
requestUserButton(
|
||||||
"\uD83D\uDC64☆ (1)",
|
"\uD83D\uDC64☆ (1)",
|
||||||
requestIdUserNonPremium,
|
requestIdUserNonPremium,
|
||||||
premiumUser = false
|
premiumUser = false,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestUserButton(
|
requestUserButton(
|
||||||
"\uD83D\uDC64 (1)",
|
"\uD83D\uDC64 (1)",
|
||||||
requestIdUserAny,
|
requestIdUserAny,
|
||||||
premiumUser = null
|
premiumUser = null,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestUserButton(
|
requestUserButton(
|
||||||
"\uD83D\uDC64★ (1)",
|
"\uD83D\uDC64★ (1)",
|
||||||
requestIdUserPremium,
|
requestIdUserPremium,
|
||||||
premiumUser = true
|
premiumUser = true,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestBotButton(
|
requestBotButton(
|
||||||
"\uD83E\uDD16 (1)",
|
"\uD83E\uDD16 (1)",
|
||||||
requestIdBot
|
requestIdBot,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestUsersOrBotsButton(
|
requestUsersOrBotsButton(
|
||||||
"\uD83D\uDC64/\uD83E\uDD16",
|
"\uD83D\uDC64/\uD83E\uDD16",
|
||||||
requestIdUsersOrBots,
|
requestIdUsersOrBots,
|
||||||
maxCount = keyboardButtonRequestUserLimit.last
|
maxCount = keyboardButtonRequestUserLimit.last,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
@@ -91,101 +121,152 @@ suspend fun main(args: Array<String>) {
|
|||||||
"\uD83D\uDC64☆",
|
"\uD83D\uDC64☆",
|
||||||
requestIdUsersNonPremium,
|
requestIdUsersNonPremium,
|
||||||
premiumUser = false,
|
premiumUser = false,
|
||||||
maxCount = keyboardButtonRequestUserLimit.last
|
maxCount = keyboardButtonRequestUserLimit.last,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestUsersButton(
|
requestUsersButton(
|
||||||
"\uD83D\uDC64",
|
"\uD83D\uDC64",
|
||||||
requestIdUsersAny,
|
requestIdUsersAny,
|
||||||
premiumUser = null,
|
premiumUser = null,
|
||||||
maxCount = keyboardButtonRequestUserLimit.last
|
maxCount = keyboardButtonRequestUserLimit.last,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestUsersButton(
|
requestUsersButton(
|
||||||
"\uD83D\uDC64★",
|
"\uD83D\uDC64★",
|
||||||
requestIdUsersPremium,
|
requestIdUsersPremium,
|
||||||
premiumUser = true,
|
premiumUser = true,
|
||||||
maxCount = keyboardButtonRequestUserLimit.last
|
maxCount = keyboardButtonRequestUserLimit.last,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestBotsButton(
|
requestBotsButton(
|
||||||
"\uD83E\uDD16",
|
"\uD83E\uDD16",
|
||||||
requestIdBots,
|
requestIdBots,
|
||||||
maxCount = keyboardButtonRequestUserLimit.last
|
maxCount = keyboardButtonRequestUserLimit.last,
|
||||||
|
requestName = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestChatButton(
|
requestChatButton(
|
||||||
"\uD83D\uDDE3/\uD83D\uDC65",
|
"\uD83D\uDDE3/\uD83D\uDC65",
|
||||||
requestIdAnyChat
|
requestIdAnyChat,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestChatButton(
|
requestChatButton(
|
||||||
"\uD83D\uDDE3",
|
"\uD83D\uDDE3",
|
||||||
requestIdChannel,
|
requestIdChannel,
|
||||||
isChannel = true
|
isChannel = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestChatButton(
|
requestChatButton(
|
||||||
"\uD83D\uDDE3\uD83D\uDD17",
|
"\uD83D\uDDE3\uD83D\uDD17",
|
||||||
requestIdPublicChannel,
|
requestIdPublicChannel,
|
||||||
isChannel = true,
|
isChannel = true,
|
||||||
isPublic = true
|
isPublic = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestChatButton(
|
requestChatButton(
|
||||||
"\uD83D\uDDE3❌\uD83D\uDD17",
|
"\uD83D\uDDE3❌\uD83D\uDD17",
|
||||||
requestIdPrivateChannel,
|
requestIdPrivateChannel,
|
||||||
isChannel = true,
|
isChannel = true,
|
||||||
isPublic = false
|
isPublic = false,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestChatButton(
|
requestChatButton(
|
||||||
"\uD83D\uDDE3\uD83D\uDC6E",
|
"\uD83D\uDDE3\uD83D\uDC6E",
|
||||||
requestIdChannelUserOwner,
|
requestIdChannelUserOwner,
|
||||||
isChannel = true,
|
isChannel = true,
|
||||||
isOwnedBy = true
|
isOwnedBy = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"👥",
|
"👥",
|
||||||
requestIdGroup
|
requestIdGroup,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"👥\uD83D\uDD17",
|
"👥\uD83D\uDD17",
|
||||||
requestIdPublicGroup,
|
requestIdPublicGroup,
|
||||||
isPublic = true
|
isPublic = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"👥❌\uD83D\uDD17",
|
"👥❌\uD83D\uDD17",
|
||||||
requestIdPrivateGroup,
|
requestIdPrivateGroup,
|
||||||
isPublic = false
|
isPublic = false,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"👥\uD83D\uDC6E",
|
"👥\uD83D\uDC6E",
|
||||||
requestIdGroupUserOwner,
|
requestIdGroupUserOwner,
|
||||||
isOwnedBy = true
|
isOwnedBy = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
row {
|
row {
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"🏛",
|
"🏛",
|
||||||
requestIdForum,
|
requestIdForum,
|
||||||
isForum = true
|
isForum = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"🏛\uD83D\uDD17",
|
"🏛\uD83D\uDD17",
|
||||||
requestIdPublicForum,
|
requestIdPublicForum,
|
||||||
isPublic = true,
|
isPublic = true,
|
||||||
isForum = true
|
isForum = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"🏛❌\uD83D\uDD17",
|
"🏛❌\uD83D\uDD17",
|
||||||
requestIdPrivateForum,
|
requestIdPrivateForum,
|
||||||
isPublic = false,
|
isPublic = false,
|
||||||
isForum = true
|
isForum = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
requestGroupButton(
|
requestGroupButton(
|
||||||
"🏛\uD83D\uDC6E",
|
"🏛\uD83D\uDC6E",
|
||||||
requestIdForumUserOwner,
|
requestIdForumUserOwner,
|
||||||
isOwnedBy = true,
|
isOwnedBy = true,
|
||||||
isForum = true
|
isForum = true,
|
||||||
|
requestTitle = true,
|
||||||
|
requestUsername = true,
|
||||||
|
requestPhoto = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +287,7 @@ suspend fun main(args: Array<String>) {
|
|||||||
it,
|
it,
|
||||||
) {
|
) {
|
||||||
+"You have shared "
|
+"You have shared "
|
||||||
+mention(
|
mention(
|
||||||
when (it.chatEvent.requestId) {
|
when (it.chatEvent.requestId) {
|
||||||
requestIdUserOrBot -> "user or bot"
|
requestIdUserOrBot -> "user or bot"
|
||||||
requestIdUserNonPremium -> "non premium user"
|
requestIdUserNonPremium -> "non premium user"
|
||||||
|
|||||||
@@ -11,12 +11,19 @@ buildscript {
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
id "org.jetbrains.kotlin.plugin.serialization"
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
|
||||||
|
id "org.jetbrains.kotlin.plugin.compose" version "$kotlin_version"
|
||||||
|
id "org.jetbrains.compose" version "$compose_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'application'
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvm()
|
jvm {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
mainClass.set("WebAppServerKt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
js(IR) {
|
js(IR) {
|
||||||
browser()
|
browser()
|
||||||
binaries.executable()
|
binaries.executable()
|
||||||
@@ -27,12 +34,15 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('stdlib')
|
implementation kotlin('stdlib')
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
|
||||||
|
implementation "dev.inmo:tgbotapi.core:$telegram_bot_api_version"
|
||||||
|
implementation compose.runtime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jsMain {
|
jsMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "dev.inmo:tgbotapi.webapps:$telegram_bot_api_version"
|
implementation "dev.inmo:tgbotapi.webapps:$telegram_bot_api_version"
|
||||||
|
implementation compose.web.core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,15 +51,12 @@ kotlin {
|
|||||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
|
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
|
||||||
implementation "io.ktor:ktor-server-cio:$ktor_version"
|
implementation "io.ktor:ktor-server-cio:$ktor_version"
|
||||||
|
implementation compose.desktop.currentOs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
|
||||||
mainClassName = "WebAppServerKt"
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.getByName("compileKotlinJvm")
|
tasks.getByName("compileKotlinJvm")
|
||||||
.dependsOn(jsBrowserDistribution)
|
.dependsOn(jsBrowserDistribution)
|
||||||
tasks.getByName("compileKotlinJvm").configure {
|
tasks.getByName("compileKotlinJvm").configure {
|
||||||
|
|||||||
3
WebApp/src/commonMain/kotlin/CustomEmojiIdToSet.kt
Normal file
3
WebApp/src/commonMain/kotlin/CustomEmojiIdToSet.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import dev.inmo.tgbotapi.types.CustomEmojiId
|
||||||
|
|
||||||
|
val CustomEmojiIdToSet = CustomEmojiId("5424939566278649034")
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
|||||||
<title>Web App Example</title>
|
<title>Web App Example</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
<script type="application/javascript" src="https://telegram.org/js/telegram-web-app.js"></script>
|
<script type="application/javascript" src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||||
<script type="application/javascript" src="WebApp.js"></script>
|
<script type="application/javascript" src="WebApp.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,45 +1,43 @@
|
|||||||
import dev.inmo.kslog.common.KSLog
|
import dev.inmo.kslog.common.*
|
||||||
import dev.inmo.kslog.common.LogLevel
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
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.answerInlineQuery
|
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.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.set.setUserEmojiStatus
|
||||||
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.buildBehaviourWithLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramStartattach
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onUnhandledCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onWriteAccessAllowed
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.replyKeyboard
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.webAppButton
|
||||||
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
|
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
|
||||||
import dev.inmo.tgbotapi.types.BotCommand
|
import dev.inmo.tgbotapi.types.*
|
||||||
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.InlineQueryId
|
|
||||||
import dev.inmo.tgbotapi.types.LinkPreviewOptions
|
|
||||||
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.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
import io.ktor.server.application.call
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.http.content.*
|
import io.ktor.server.http.content.*
|
||||||
import io.ktor.server.request.receiveText
|
import io.ktor.server.request.*
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.post
|
import io.ktor.server.routing.*
|
||||||
import io.ktor.server.routing.routing
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts two parameters:
|
* Accepts two parameters:
|
||||||
*
|
*
|
||||||
* * Telegram Token
|
* * Telegram Token
|
||||||
* * URL where will be placed
|
* * URL where will be placed
|
||||||
|
* * Port (default 8080)
|
||||||
*
|
*
|
||||||
* Will start the server to share the static (index.html and WebApp.js) on 0.0.0.0:8080
|
* Will start the server to share the static (index.html and WebApp.js) on 0.0.0.0:8080
|
||||||
*/
|
*/
|
||||||
@@ -58,23 +56,37 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val initiationLogger = KSLog("Initialization")
|
||||||
|
|
||||||
val bot = telegramBot(telegramBotAPIUrlsKeeper)
|
val bot = telegramBot(telegramBotAPIUrlsKeeper)
|
||||||
createKtorServer(
|
createKtorServer(
|
||||||
"0.0.0.0",
|
"0.0.0.0",
|
||||||
args.getOrNull(2) ?.toIntOrNull() ?: 8080,
|
args.getOrNull(2) ?.toIntOrNull() ?: 8080
|
||||||
additionalEngineEnvironmentConfigurator = {
|
|
||||||
parentCoroutineContext += Dispatchers.IO
|
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
routing {
|
routing {
|
||||||
val baseJsFolder = File("WebApp/build/dist/js/")
|
val baseJsFolder = File("WebApp/build/dist/js/")
|
||||||
baseJsFolder.list() ?.forEach {
|
val prodSubFolder = File(baseJsFolder, "productionExecutable")
|
||||||
if (it == "productionExecutable" || it == "developmentExecutable") {
|
val devSubFolder = File(baseJsFolder, "developmentExecutable")
|
||||||
staticFiles("", File(baseJsFolder, it)) {
|
|
||||||
default("WebApp/build/dist/js/$it/index.html")
|
val staticFolder = when {
|
||||||
}
|
prodSubFolder.exists() -> {
|
||||||
|
initiationLogger.i("Folder for static is ${prodSubFolder.absolutePath}")
|
||||||
|
prodSubFolder
|
||||||
}
|
}
|
||||||
|
devSubFolder.exists() -> {
|
||||||
|
initiationLogger.i("Folder for static is ${devSubFolder.absolutePath}")
|
||||||
|
devSubFolder
|
||||||
|
}
|
||||||
|
else -> error("""
|
||||||
|
Unable to detect any folder with static. Current working directory: ${File("").absolutePath}.
|
||||||
|
Searched paths:
|
||||||
|
* ${prodSubFolder.absolutePath}
|
||||||
|
* ${devSubFolder.absolutePath}
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
staticFiles("", staticFolder) {
|
||||||
|
default("${staticFolder.absolutePath}${File.separator}index.html")
|
||||||
}
|
}
|
||||||
post("inline") {
|
post("inline") {
|
||||||
val requestBody = call.receiveText()
|
val requestBody = call.receiveText()
|
||||||
@@ -85,19 +97,38 @@ suspend fun main(vararg args: String) {
|
|||||||
}
|
}
|
||||||
post("check") {
|
post("check") {
|
||||||
val requestBody = call.receiveText()
|
val requestBody = call.receiveText()
|
||||||
val webAppCheckData = Json { }.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
|
val webAppCheckData = Json.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
|
||||||
|
|
||||||
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
|
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
|
||||||
|
|
||||||
call.respond(HttpStatusCode.OK, isSafe.toString())
|
call.respond(HttpStatusCode.OK, isSafe.toString())
|
||||||
}
|
}
|
||||||
|
post("setCustomEmoji") {
|
||||||
|
val requestBody = call.receiveText()
|
||||||
|
val webAppCheckData = Json.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
|
||||||
|
|
||||||
|
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
|
||||||
|
val rawUserId = call.parameters[userIdField] ?.toLongOrNull() ?.let(::RawChatId) ?: error("$userIdField should be presented as long value")
|
||||||
|
|
||||||
|
val set = if (isSafe) {
|
||||||
|
runCatching {
|
||||||
|
bot.setUserEmojiStatus(
|
||||||
|
UserId(rawUserId),
|
||||||
|
CustomEmojiIdToSet
|
||||||
|
)
|
||||||
|
}.getOrElse { false }
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
call.respond(HttpStatusCode.OK, set.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.start(false)
|
}.start(false)
|
||||||
|
|
||||||
bot.buildBehaviourWithLongPolling(
|
bot.buildBehaviourWithLongPolling(
|
||||||
defaultExceptionsHandler = { it.printStackTrace() }
|
defaultExceptionsHandler = { it.printStackTrace() }
|
||||||
) {
|
) {
|
||||||
val me = getMe()
|
|
||||||
onCommand("reply_markup") {
|
onCommand("reply_markup") {
|
||||||
reply(
|
reply(
|
||||||
it,
|
it,
|
||||||
@@ -165,7 +196,7 @@ suspend fun main(vararg args: String) {
|
|||||||
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"),
|
||||||
)
|
)
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
println(getMe())
|
println(getMe())
|
||||||
|
|||||||
28
WebHooks/README.md
Normal file
28
WebHooks/README.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# WebHooks
|
||||||
|
|
||||||
|
Launches webhook-based simple bot. Use `/start` with bot to get simple info about webhooks
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN https://sample.com it/is/subpath 8080 debug"
|
||||||
|
```
|
||||||
|
|
||||||
|
Required arguments:
|
||||||
|
|
||||||
|
1. Token
|
||||||
|
2. Arguments starting with `https://`
|
||||||
|
|
||||||
|
Optional arguments:
|
||||||
|
|
||||||
|
* Any argument == `debug` to enable debug mode
|
||||||
|
* Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
|
||||||
|
subroute to place listening of webhooks)
|
||||||
|
* Any argument as number of port
|
||||||
|
|
||||||
|
Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
|
||||||
|
|
||||||
|
* `TOKEN` used as token
|
||||||
|
* Bot will set up its webhook info as `https://sample.com/it/is/subpath`
|
||||||
|
* Bot will set up to listen webhooks on route `it/is/subpath`
|
||||||
|
* Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`
|
||||||
23
WebHooks/build.gradle
Normal file
23
WebHooks/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
mainClassName="WebHooksKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
|
||||||
|
implementation "io.ktor:ktor-server-cio:$ktor_version"
|
||||||
|
}
|
||||||
87
WebHooks/src/main/kotlin/WebHooks.kt
Normal file
87
WebHooks/src/main/kotlin/WebHooks.kt
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
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.ktor.server.createKtorServer
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.webhook.setWebhookInfo
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.includeWebhookHandlingInRoute
|
||||||
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PrivateChat
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import io.ktor.server.routing.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches webhook-based simple bot. Required arguments:
|
||||||
|
*
|
||||||
|
* 1. Token
|
||||||
|
* *. Arguments starting with `https://`
|
||||||
|
*
|
||||||
|
* Optional arguments:
|
||||||
|
*
|
||||||
|
* *. Any argument == `debug` to enable debug mode
|
||||||
|
* *. Any argument **not** starting with `https://` and **not** equal to `debug` as **subpath** (will be used as
|
||||||
|
* subroute to place listening of webhooks)
|
||||||
|
* *. Any argument as number of port
|
||||||
|
*
|
||||||
|
* Sample: `TOKEN https://sample.com it/is/subpath 8080` will result to:
|
||||||
|
*
|
||||||
|
* * `TOKEN` used as token
|
||||||
|
* * Bot will set up its webhook info as `https://sample.com/it/is/subpath`
|
||||||
|
* * Bot will set up to listen webhooks on route `it/is/subpath`
|
||||||
|
* * Bot will start to listen any incoming request on port `8080` and url `0.0.0.0`
|
||||||
|
*/
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val address = args.first { it.startsWith("https://") }
|
||||||
|
val subpath = args.drop(1).firstOrNull { it != address && it != "debug" }
|
||||||
|
val port = args.firstNotNullOfOrNull { it.toIntOrNull() } ?: 8080
|
||||||
|
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(botToken)
|
||||||
|
|
||||||
|
val behaviourContext = bot.buildBehaviour (defaultExceptionsHandler = { it.printStackTrace() }) {
|
||||||
|
onCommand("start", initialFilter = { it.chat is PrivateChat }) {
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
buildEntities {
|
||||||
|
+"Url: $address" + "\n"
|
||||||
|
+"Listening server: 0.0.0.0" + "\n"
|
||||||
|
+"Listening port: $port"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setMyCommands(BotCommand("start", "Get webhook info"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val webhookInfoSubpath = subpath ?.let { "/" + it.removePrefix("/") } ?: "" // drop leading `/` to add it in the beginning for correct construction of subpath
|
||||||
|
bot.setWebhookInfo(address + webhookInfoSubpath)
|
||||||
|
|
||||||
|
createKtorServer(
|
||||||
|
"0.0.0.0",
|
||||||
|
port,
|
||||||
|
) {
|
||||||
|
routing {
|
||||||
|
if (subpath == null) {
|
||||||
|
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
|
||||||
|
} else {
|
||||||
|
route(subpath) {
|
||||||
|
includeWebhookHandlingInRoute(behaviourContext, block = behaviourContext.asUpdateReceiver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start(true)
|
||||||
|
}
|
||||||
10
build.gradle
10
build.gradle
@@ -14,8 +14,8 @@ allprojects {
|
|||||||
nativePartTemplate = "${rootProject.projectDir.absolutePath}/native_template.gradle"
|
nativePartTemplate = "${rootProject.projectDir.absolutePath}/native_template.gradle"
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
google()
|
||||||
if (project.hasProperty("GITHUB_USER") && project.hasProperty("GITHUB_TOKEN")) {
|
if (project.hasProperty("GITHUB_USER") && project.hasProperty("GITHUB_TOKEN")) {
|
||||||
maven {
|
maven {
|
||||||
url "https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"
|
url "https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"
|
||||||
@@ -26,6 +26,12 @@ allprojects {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
|
maven { url "https://proxy.nexus.inmo.dev/repository/maven-releases/" }
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix of https://youtrack.jetbrains.com/issue/KTOR-7912/Module-not-found-errors-when-executing-browserProductionWebpack-task-since-3.0.2
|
||||||
|
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin.class) {
|
||||||
|
rootProject.kotlinYarn.resolution("ws", "8.18.0")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
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=-Xmx3148m
|
||||||
|
kotlin.daemon.jvmargs=-Xmx3g -Xms500m
|
||||||
|
|
||||||
|
|
||||||
kotlin_version=1.9.23
|
kotlin_version=2.2.21
|
||||||
telegram_bot_api_version=11.0.0
|
telegram_bot_api_version=31.2.0
|
||||||
micro_utils_version=0.20.39
|
micro_utils_version=0.26.9
|
||||||
serialization_version=1.6.3
|
serialization_version=1.10.0
|
||||||
ktor_version=2.3.9
|
ktor_version=3.3.2
|
||||||
|
compose_version=1.8.2
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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-8.13-bin.zip
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ include ":HelloBot"
|
|||||||
|
|
||||||
include ":PollsBot"
|
include ":PollsBot"
|
||||||
|
|
||||||
include ":GetMeBot"
|
include ":MyBot"
|
||||||
|
|
||||||
include ":DeepLinksBot"
|
include ":DeepLinksBot"
|
||||||
|
|
||||||
@@ -47,3 +47,25 @@ include ":ReactionsInfoBot"
|
|||||||
include ":LinkPreviewsBot"
|
include ":LinkPreviewsBot"
|
||||||
|
|
||||||
include ":BoostsInfoBot"
|
include ":BoostsInfoBot"
|
||||||
|
|
||||||
|
include ":BusinessConnectionsBot"
|
||||||
|
|
||||||
|
include ":StarTransactionsBot"
|
||||||
|
|
||||||
|
include ":GiveawaysBot"
|
||||||
|
|
||||||
|
include ":CustomBot"
|
||||||
|
|
||||||
|
include ":MemberUpdatedWatcherBot"
|
||||||
|
|
||||||
|
include ":WebHooks"
|
||||||
|
|
||||||
|
include ":SuggestedPosts"
|
||||||
|
|
||||||
|
include ":ChecklistsBot"
|
||||||
|
|
||||||
|
include ":DraftsBot"
|
||||||
|
|
||||||
|
include ":GiftsBot"
|
||||||
|
|
||||||
|
include ":TagsBot"
|
||||||
|
|||||||
Reference in New Issue
Block a user