mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-03-15 15:32:28 +00:00
Compare commits
478 Commits
0.38.10
...
renovate/k
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30b6e0d8be | ||
| 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 | |||
| 57dd2380cd | |||
| fbb41c7714 | |||
| 9170d30b2f | |||
| 2f3fd2e53b | |||
| 88697fb5a6 | |||
| 578d00cac6 | |||
| 13ecb3f0df | |||
| a008d861da | |||
| 6f3766dff6 | |||
| fda366d820 | |||
| 578887ac63 | |||
| 6a04b3980c | |||
| 984ffb8bae | |||
| 2bcec6487d | |||
| a5e3bfc3fe | |||
| 941afd0902 | |||
| 94c014b308 | |||
| 538cc9d44f | |||
| cb29726487 | |||
| 262ef26239 | |||
| 41efe5e141 | |||
| 05e289975a | |||
| 753d686fab | |||
| 281243c7e5 | |||
| 3609ae6bc2 | |||
| 4f128f3421 | |||
| ada6cd61d7 | |||
|
|
051d647004 | ||
|
|
d21606860a | ||
|
|
93c0fcb5bd | ||
| b1b8d0eb75 | |||
| 2ac23f70ab | |||
| e155373655 | |||
| d842dab5b8 | |||
| 7186d5e624 | |||
|
|
8fefb17599 | ||
| bcf4ae5888 | |||
| 7090db148e | |||
| 7d786f0e06 | |||
| c88f84011f | |||
| b8cc8854ea | |||
| 13470999e8 | |||
| af04a854ef | |||
| 44e86c9349 | |||
| 65c32d97d5 | |||
| 9b7605591e | |||
| 89d5a4f911 | |||
| 53cf212175 | |||
| 28301a92c9 | |||
| f814b11777 | |||
| 9773a74890 | |||
| a81cfaaba9 | |||
|
|
ee599611f3 | ||
| d3d6cd16c6 | |||
| 02c3d3da1a | |||
| 0ad8e61c0c | |||
| 8f80b7e066 | |||
| 48d1077ce4 | |||
| 6922a6d667 | |||
| 676ce0df80 | |||
| d97c2a0562 | |||
| 35e0cb4a46 | |||
| 30f5513f54 | |||
| fff8edde5f | |||
| e28a795796 | |||
| d289c2101d | |||
| 2ce47074d8 | |||
| 281f0840eb | |||
| 34ed962104 | |||
| aa3337bf3a | |||
| 31d29712be | |||
| 88b348376f | |||
| 0d9e295baa | |||
| ea08bac6e8 | |||
| a85fdc227e | |||
| 43482ee94e | |||
| 4addb6c755 | |||
| 7d958b6edb | |||
| 323c21f415 | |||
| 6350581739 | |||
| ea1d40fd05 | |||
| 8cee63a0fb | |||
| d42ef2c6cb | |||
| c7fe90ddd7 | |||
| acb382d3f7 | |||
| 0cfe60fd77 | |||
| 6719b9e17c | |||
| 8d33dc0ab2 | |||
| 3e2ccf9cf1 | |||
| eccbe71e68 | |||
| 24c74f3b1a | |||
| d7a7e7153e | |||
| 0b37acb7a9 | |||
| 3925ef9423 | |||
| c6019b1862 | |||
| 7b996fe1de | |||
| 4e3c186952 | |||
| 8fdf715419 | |||
| fca8704cec | |||
| bf499ee780 | |||
| 9b10749411 | |||
| d3cb8a32ef | |||
| 0f0ad5a1af | |||
| c3bc55a15c | |||
| 253328f49a | |||
| 8ef50537ae | |||
| 7e7bbfaa93 | |||
| f152ede9b5 | |||
| fcedbf30da | |||
|
|
fd030a92e3 | ||
| d54abf0b32 | |||
|
|
877a20188f | ||
| d0151ff048 | |||
| f8f517cfbb | |||
|
|
b3cbbac917 | ||
| 3e85bb4b22 | |||
| 4e0fb1c137 | |||
| d8c90ef377 | |||
| cb84fd0884 | |||
| 4379862c78 | |||
|
|
ac1d812db0 | ||
| f52590868c | |||
| 51c300c734 | |||
| f6082cff30 | |||
| a40c16fe05 | |||
| a7fe62f4af | |||
| b9c745a21e | |||
| 1c2b068a94 | |||
| 51c2cb1b0e | |||
| cfd4e2fcd5 | |||
|
|
76ceeac757 | ||
| 340de11b0a | |||
| 68a59ca5c8 | |||
|
|
cdb8581318 | ||
|
|
8b3a2ac1ed | ||
| a5b925fc59 | |||
| 9ec9f7a68c | |||
| 0f2829945f | |||
| 4eb80ea53c | |||
| 17cff21847 | |||
| 431069d190 | |||
|
|
fdbac78603 | ||
| da73acd379 | |||
|
|
6e3880f152 | ||
| 1ede6e58e6 | |||
| 0e46f176fb | |||
|
|
2bd449b8b8 | ||
| 82f9da0529 | |||
| 78b7d468f2 | |||
|
|
08059f8174 | ||
|
|
16766046d7 | ||
| 91ea20a269 | |||
| 11e280d177 | |||
| a8d4a307ef | |||
| 2bd2328a38 | |||
| 139de35db9 | |||
|
|
5dd22e1da2 | ||
| 4186ab8270 | |||
| 5aa69d7990 | |||
|
|
df952c69b2 | ||
| 9a03a02bac | |||
| 0e7c050e9e | |||
|
|
bdec902b58 | ||
|
|
cc3c87590d | ||
| 910f892b89 | |||
| 8232cb4d62 | |||
| 3b26971152 | |||
| c0019bcbf8 | |||
| c3dcb4d738 | |||
| 6a9921d4bd | |||
| 33b14f320c | |||
| 50ad281132 | |||
| 6abfc3d369 | |||
| f73620afec | |||
| a3d5112c83 | |||
|
|
23abae07b7 | ||
| c5a9f657cf | |||
|
|
51f7715915 | ||
|
|
bdfb900ce3 | ||
| 27790a2576 | |||
| e722055871 | |||
| 29e1552618 | |||
| 7ef942a2b4 | |||
| fdd4dfdbcf | |||
|
|
ba88205249 | ||
| 9a38fe51f9 | |||
|
|
c172bd1fa7 | ||
| 7392e85f1b | |||
|
|
f2c7fd79d9 | ||
| 9d77b61ea3 | |||
|
|
d567b1382b | ||
| 557d4ad3ca | |||
|
|
f070fb804b | ||
| 53de520c3a | |||
| f164ba901d | |||
|
|
2a04b4979d | ||
| 4a0279c89e | |||
|
|
fcef9f1f64 | ||
|
|
33e191816a | ||
| e0b707ceba | |||
| 115c76a9d4 | |||
| 0ebfb3c44a | |||
| 9fa3690db6 | |||
| a8c5d403c6 | |||
| 0c5186a37d | |||
| 7e9968ced9 | |||
| f1e8ed88a8 | |||
| 068dc79ac8 | |||
| 36273adfd1 | |||
| bca2ae905b | |||
| 19a713a3e3 | |||
| e039f90961 | |||
| 21692d16ca | |||
| d547dce2ab | |||
| 9bfe88a79c | |||
|
|
bfa327acf3 | ||
| 88a031b05a | |||
| 20e942b2ac | |||
|
|
3e20cbd22c | ||
| bcd35de038 | |||
|
|
2aec45d453 | ||
| ee55378e7a | |||
| b402c7b6e7 | |||
|
|
32b7c7b9a4 | ||
| 0b2d6b20de | |||
|
|
182ee7a865 | ||
| 0652a95d11 | |||
|
|
f6066c60c0 | ||
| 2fa340292c | |||
| 7d94007905 | |||
|
|
9638174d48 | ||
|
|
18bacaea2e | ||
| 0f2b3760dd | |||
| 730923f55c | |||
| 9cf8bd9f28 | |||
| 370fa45dba | |||
|
|
72e7a73e40 | ||
| 10dd9bd851 | |||
| 0217f97014 | |||
| 4ae700b58a | |||
|
|
804fb1e5ee | ||
| d443c39813 | |||
|
|
cd96547d15 | ||
| f107b4144d | |||
| 39f4196b76 | |||
|
|
22b9776c26 | ||
| 2f8f6f3169 | |||
| f2b6a50cb5 | |||
| acd602d6fa | |||
| fdb80bf471 | |||
|
|
78c58db4b7 | ||
| edd6399bcc | |||
|
|
9513e77ba3 | ||
|
|
ac75f6487e | ||
| 07750a71c0 | |||
| 0bf7e33df3 | |||
| 7fb308ea4b | |||
|
|
75b403ac98 | ||
| 5976e37046 | |||
|
|
c8a94496c7 | ||
| f6550bd401 | |||
| 389d96f323 | |||
| 803c5fd664 | |||
| b6eb4fe134 | |||
| 9a4bd55ef6 | |||
| 4dac411693 | |||
| d386d50f1c | |||
|
|
1b846a6383 | ||
| 568b845890 | |||
|
|
3ebca4a058 | ||
| 2b9af2f667 | |||
| 16410debff | |||
| a18f22fe8f | |||
| 0489f217b3 | |||
| e772cfda3b | |||
| 9d42385662 | |||
| b740203116 | |||
| 149717301d | |||
| 60e72be844 | |||
| 831b558724 | |||
| df778b4e93 | |||
| bf0c6497fe | |||
|
|
71a047f867 | ||
| 2ae3e1165d | |||
| 9a5d02512b | |||
| dd2c528006 | |||
| 1c16a9f868 | |||
| 00c3aba12b | |||
| a547bbce65 | |||
| 0b7d8c087f | |||
| 8ec282e3d5 | |||
|
|
e8433cd8ac | ||
| 222134836a | |||
| c18e02dcb3 | |||
| f9050061d1 | |||
| a3e3f6c22c | |||
| 516cc7bfcb |
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -8,9 +8,13 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up JDK 1.8
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y libcurl4-openssl-dev
|
||||||
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 1.8
|
java-version: 17
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build --no-daemon
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.idea
|
.idea
|
||||||
|
.kotlin
|
||||||
out/*
|
out/*
|
||||||
*.iml
|
*.iml
|
||||||
target
|
target
|
||||||
@@ -10,3 +11,6 @@ build/
|
|||||||
out/
|
out/
|
||||||
|
|
||||||
kotlin-js-store/
|
kotlin-js-store/
|
||||||
|
|
||||||
|
local.*
|
||||||
|
local.*/
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
9
BoostsInfoBot/README.md
Normal file
9
BoostsInfoBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# UserChatShared
|
||||||
|
|
||||||
|
Showing info about boosts
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
BoostsInfoBot/build.gradle
Normal file
21
BoostsInfoBot/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="BoostsInfoKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
72
BoostsInfoBot/src/main/kotlin/BoostsInfo.kt
Normal file
72
BoostsInfoBot/src/main/kotlin/BoostsInfo.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getUserChatBoosts
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatBoostUpdated
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatShared
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatReplyKeyboard
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestChannelButton
|
||||||
|
import dev.inmo.tgbotapi.types.request.RequestId
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
import korlibs.time.DateFormat
|
||||||
|
import korlibs.time.format
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val isDebug = args.getOrNull(1) == "debug"
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestChatId = RequestId(1)
|
||||||
|
|
||||||
|
val bot = telegramBot(args.first())
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
|
||||||
|
onChatBoostUpdated {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("start") {
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
replyMarkup = flatReplyKeyboard {
|
||||||
|
requestChannelButton(
|
||||||
|
"Click me :)",
|
||||||
|
requestChatId,
|
||||||
|
botIsMember = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("Select chat to get know about your boosts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChatShared(initialFilter = { it.chatEvent.requestId == requestChatId }) {
|
||||||
|
val boostsInfoContrainer = runCatching {
|
||||||
|
getUserChatBoosts(it.chatEvent.chatId, it.chat.id)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
|
reply(it) {
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
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()
|
||||||
|
}
|
||||||
9
ChatAvatarSetter/README.md
Normal file
9
ChatAvatarSetter/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ChatAvatarSetter
|
||||||
|
|
||||||
|
This bot will set the chat avatar based on the image sent to bot
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
ChatAvatarSetter/build.gradle
Normal file
21
ChatAvatarSetter/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="ChatAvatarSetterKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
36
ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt
Normal file
36
ChatAvatarSetter/src/main/kotlin/ChatAvatarSetter.kt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.modify.setChatPhoto
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPhoto
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val bot = telegramBot(args.first())
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling(scope = CoroutineScope(Dispatchers.IO)) {
|
||||||
|
onPhoto {
|
||||||
|
val bytes = downloadFile(it.content)
|
||||||
|
runCatchingSafely {
|
||||||
|
setChatPhoto(
|
||||||
|
it.chat.id,
|
||||||
|
bytes.asMultipartFile("sample.jpg")
|
||||||
|
)
|
||||||
|
}.onSuccess { b ->
|
||||||
|
if (b) {
|
||||||
|
reply(it, "Done")
|
||||||
|
} else {
|
||||||
|
reply(it, "Something went wrong")
|
||||||
|
}
|
||||||
|
}.onFailure { e ->
|
||||||
|
e.printStackTrace()
|
||||||
|
|
||||||
|
reply(it, "Something went wrong (see logs)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
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()
|
||||||
|
}
|
||||||
9
DeepLinksBot/README.md
Normal file
9
DeepLinksBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# DeepLinksBot
|
||||||
|
|
||||||
|
This bot will send you deeplink to this bot when you send some text message and react on the `start` button
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
DeepLinksBot/build.gradle
Normal file
21
DeepLinksBot/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="DeepLinksBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
44
DeepLinksBot/src/main/kotlin/DeepLinksBot.kt
Normal file
44
DeepLinksBot/src/main/kotlin/DeepLinksBot.kt
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitDeepLinks
|
||||||
|
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.onDeepLink
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onText
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramDeepLink
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.BotCommandTextSource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bot will send you deeplink to this bot when you send some text message and react on the `start` button
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken) {
|
||||||
|
val me = bot.getMe()
|
||||||
|
val username = me.username
|
||||||
|
println(me)
|
||||||
|
|
||||||
|
if (username == null) {
|
||||||
|
error("Unable to start bot work: it have no username")
|
||||||
|
}
|
||||||
|
|
||||||
|
onText(
|
||||||
|
initialFilter = { it.content.textSources.none { it is BotCommandTextSource } } // excluding messages with commands
|
||||||
|
) {
|
||||||
|
reply(it, makeTelegramDeepLink(username, it.content.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("start", requireOnlyCommandInMessage = true) { // handling of `start` without args
|
||||||
|
reply(it, "Hi :) Send me any text and I will try hard to create deeplink for you")
|
||||||
|
}
|
||||||
|
onDeepLink { (it, deepLink) ->
|
||||||
|
reply(it, "Ok, I got deep link \"${deepLink}\" in trigger")
|
||||||
|
}
|
||||||
|
waitDeepLinks().subscribeSafelyWithoutExceptions(this) { (it, deepLink) ->
|
||||||
|
reply(it, "Ok, I got deep link \"${deepLink}\" in waiter")
|
||||||
|
println(triggersHolder.handleableCommandsHolder.handleable)
|
||||||
|
}
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
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,48 +1,104 @@
|
|||||||
|
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.sendMessage
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitAnyContentMessage
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitCommandMessage
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndFSMAndStartLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.command
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.*
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
import dev.inmo.tgbotapi.types.ChatId
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.containsCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgs
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread
|
||||||
|
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.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 kotlinx.coroutines.*
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
|
import dev.inmo.tgbotapi.utils.firstOf
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
sealed interface BotState : State
|
sealed interface BotState : State
|
||||||
data class ExpectContentOrStopState(override val context: ChatId, val sourceMessage: CommonMessage<TextContent>) : BotState
|
data class ExpectContentOrStopState(override val context: IdChatIdentifier, val sourceMessage: CommonMessage<TextContent>) : BotState
|
||||||
data class StopState(override val context: ChatId) : BotState
|
data class StopState(override val context: IdChatIdentifier) : BotState
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
|
|
||||||
telegramBotWithBehaviourAndFSMAndStartLongPolling<BotState>(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndFSMAndStartLongPolling<BotState>(
|
||||||
strictlyOn<ExpectContentOrStopState> {
|
botToken,
|
||||||
sendMessage(
|
CoroutineScope(Dispatchers.IO),
|
||||||
it.context,
|
onStateHandlingErrorHandler = { state, e ->
|
||||||
buildEntities {
|
when (state) {
|
||||||
+"Send me some content or " + botCommand("stop") + " if you want to stop sending"
|
is ExpectContentOrStopState -> {
|
||||||
|
println("Thrown error on ExpectContentOrStopState")
|
||||||
}
|
}
|
||||||
)
|
is StopState -> {
|
||||||
|
println("Thrown error on StopState")
|
||||||
val content = waitContentMessage().first()
|
|
||||||
when {
|
|
||||||
content is TextContent && content.parseCommandsWithParams().keys.contains("stop") -> StopState(it.context)
|
|
||||||
else -> {
|
|
||||||
execute(content.createResend(it.context))
|
|
||||||
it
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
e.printStackTrace()
|
||||||
|
state
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
strictlyOn<ExpectContentOrStopState> {
|
||||||
|
send(
|
||||||
|
it.context,
|
||||||
|
) {
|
||||||
|
+"Send me some content or " + botCommand("stop") + " if you want to stop sending"
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentMessage = firstOf(
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
execute(content.createResend(it.context))
|
||||||
|
it
|
||||||
}
|
}
|
||||||
strictlyOn<StopState> {
|
strictlyOn<StopState> {
|
||||||
sendMessage(it.context, "You have stopped sending of content")
|
send(it.context) { +"You have stopped sending of content" }
|
||||||
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
command("start") {
|
command(
|
||||||
|
"start"
|
||||||
|
) {
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFile
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
|
||||||
import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
|
import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.*
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviour
|
|
||||||
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.onCommand
|
||||||
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.types.actions.*
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramMediaAudio
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
|
||||||
|
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.*
|
||||||
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
|
||||||
@@ -15,18 +23,94 @@ import java.io.File
|
|||||||
*/
|
*/
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("")
|
val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("/tmp/")
|
||||||
directoryOrFile.mkdirs()
|
directoryOrFile.mkdirs()
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
onMedia(initialFilter = null) {
|
onCommand("start") {
|
||||||
val pathedFile = bot.getFileAdditionalInfo(it.content.media)
|
reply(it, "Send me any media (like photo or video) to download it")
|
||||||
val file = File(directoryOrFile, pathedFile.filePath.filenameFromUrl).apply {
|
|
||||||
createNewFile()
|
|
||||||
writeBytes(bot.downloadFile(pathedFile))
|
|
||||||
}
|
|
||||||
reply(it, "Saved to ${file.absolutePath}")
|
|
||||||
}
|
}
|
||||||
onContentMessage { println(it) }
|
onMedia(initialFilter = null) {
|
||||||
|
val content = it.content
|
||||||
|
val pathedFile = bot.getFileAdditionalInfo(content.media)
|
||||||
|
val outFile = File(directoryOrFile, pathedFile.filePath.filenameFromUrl)
|
||||||
|
withTypingAction(it.chat.id) {
|
||||||
|
runCatching {
|
||||||
|
bot.downloadFile(content.media, outFile)
|
||||||
|
}.onFailure {
|
||||||
|
it.printStackTrace()
|
||||||
|
}.onSuccess { _ ->
|
||||||
|
reply(it, "Saved to ${outFile.absolutePath}")
|
||||||
|
}
|
||||||
|
}.onSuccess { _ ->
|
||||||
|
val action = when (content) {
|
||||||
|
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) {
|
||||||
|
is PhotoContent -> replyWithPhoto(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is AnimationContent -> replyWithAnimation(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is VideoContent -> replyWithVideo(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is StickerContent -> replyWithSticker(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is MediaGroupContent<*> -> replyWithMediaGroup(
|
||||||
|
it,
|
||||||
|
content.group.map {
|
||||||
|
when (val innerContent = it.content) {
|
||||||
|
is AudioContent -> TelegramMediaAudio(
|
||||||
|
downloadFileToTemp(innerContent.media).asMultipartFile()
|
||||||
|
)
|
||||||
|
is DocumentContent -> TelegramMediaDocument(
|
||||||
|
downloadFileToTemp(innerContent.media).asMultipartFile()
|
||||||
|
)
|
||||||
|
is PhotoContent -> TelegramMediaPhoto(
|
||||||
|
downloadFileToTemp(innerContent.media).asMultipartFile()
|
||||||
|
)
|
||||||
|
is VideoContent -> TelegramMediaVideo(
|
||||||
|
downloadFileToTemp(innerContent.media).asMultipartFile()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
is AudioContent -> replyWithAudio(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is DocumentContent -> replyWithDocument(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is VoiceContent -> replyWithVoice(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
is VideoNoteContent -> replyWithVideoNote(
|
||||||
|
it,
|
||||||
|
outFile.asMultipartFile()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
import dev.inmo.tgbotapi.extensions.utils.formatting.*
|
import dev.inmo.tgbotapi.extensions.utils.formatting.makeLink
|
||||||
import dev.inmo.tgbotapi.types.*
|
import dev.inmo.tgbotapi.types.chat.CommonBot
|
||||||
|
import dev.inmo.tgbotapi.types.chat.CommonUser
|
||||||
|
import dev.inmo.tgbotapi.types.chat.ExtendedBot
|
||||||
import dev.inmo.tgbotapi.types.message.*
|
import dev.inmo.tgbotapi.types.message.*
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import dev.inmo.tgbotapi.utils.code
|
||||||
|
import dev.inmo.tgbotapi.utils.link
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,28 +20,41 @@ suspend fun main(vararg args: String) {
|
|||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
onContentMessage(subcontextUpdatesFilter = { _, _ -> true }) {
|
onContentMessage {
|
||||||
val toAnswer = buildEntities {
|
val toAnswer = buildEntities {
|
||||||
when (val forwardInfo = it.forwardInfo) {
|
when (val forwardInfo = it.forwardInfo) {
|
||||||
null -> +"There is no forward info"
|
null -> +"There is no forward info"
|
||||||
is AnonymousForwardInfo -> {
|
is ForwardInfo.ByAnonymous -> {
|
||||||
regular("Anonymous user which signed as \"") + code(forwardInfo.senderName) + "\""
|
regular("Anonymous user which signed as \"") + code(forwardInfo.senderName) + "\""
|
||||||
}
|
}
|
||||||
is UserForwardInfo -> {
|
is ForwardInfo.ByUser -> {
|
||||||
val user = forwardInfo.from
|
val user = forwardInfo.from
|
||||||
when (user) {
|
when (user) {
|
||||||
is CommonUser -> regular("User ")
|
is CommonUser -> {
|
||||||
|
if (user.isPremium) {
|
||||||
|
regular("Premium user ")
|
||||||
|
} else {
|
||||||
|
regular("User ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is CommonBot,
|
is CommonBot,
|
||||||
is ExtendedBot -> regular("Bot ")
|
is ExtendedBot -> regular("Bot ")
|
||||||
} + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username ?.username ?: "Without username"})"
|
} + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username?.username ?: "Without username"})"
|
||||||
}
|
}
|
||||||
is ForwardFromChannelInfo -> regular("Channel (") + code((forwardInfo.channelChat).title) + ")"
|
is ForwardInfo.PublicChat.FromChannel -> {
|
||||||
is ForwardFromSupergroupInfo -> regular("Supergroup (") + code((forwardInfo.group).title) + ")"
|
regular("Channel (") + (forwardInfo.channelChat.username ?.let {
|
||||||
|
link(
|
||||||
|
forwardInfo.channelChat.title,
|
||||||
|
makeLink(it)
|
||||||
|
)
|
||||||
|
} ?: code(forwardInfo.channelChat.title)) + ")"
|
||||||
|
}
|
||||||
|
is ForwardInfo.PublicChat.FromSupergroup -> regular("Supergroup (") + code(forwardInfo.group.title) + ")"
|
||||||
|
is ForwardInfo.PublicChat.SentByChannel -> regular("Sent by channel (") + code(forwardInfo.channelChat.title) + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reply(it, toAnswer)
|
reply(it, toAnswer)
|
||||||
coroutineContext.job.invokeOnCompletion { println("completance of onContentMessage") }
|
|
||||||
}
|
}
|
||||||
coroutineContext.job.invokeOnCompletion { println("Completed :)") }
|
|
||||||
}.second.join()
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 bot = telegramBot(botToken)
|
|
||||||
|
|
||||||
println(bot.getMe())
|
|
||||||
}
|
|
||||||
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,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -1,41 +1,75 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.sendTextMessage
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMentionWithAnyContent
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.sender_chat
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.raw.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.types.ParseMode.MarkdownV2
|
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
|
||||||
import dev.inmo.tgbotapi.types.User
|
import dev.inmo.tgbotapi.types.chat.*
|
||||||
import dev.inmo.tgbotapi.types.chat.abstracts.*
|
import dev.inmo.tgbotapi.types.message.MarkdownV2
|
||||||
|
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
|
||||||
*/
|
*/
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
suspend fun main(vararg args: String) {
|
suspend fun main(vararg args: String) {
|
||||||
val botToken = args.first()
|
val botToken = args.first()
|
||||||
|
|
||||||
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
onContentMessage { message ->
|
val me = getMe()
|
||||||
val chat = message.chat
|
onContentMessage(
|
||||||
if (chat is ChannelChat) {
|
initialFilter = initialFilter@{ it.text ?.contains(me.username ?.full ?: return@initialFilter false) == true }
|
||||||
val answer = "Hi everybody in this channel \"${chat.title}\""
|
) { message ->
|
||||||
sendTextMessage(chat, answer, MarkdownV2)
|
val answerText = when (val chat = message.chat) {
|
||||||
return@onContentMessage
|
is PreviewChannelChat -> {
|
||||||
}
|
val sender = message.sender_chat
|
||||||
val answerText = "Oh, hi, " + when (chat) {
|
val answer = "Hi everybody in this channel \"${chat.title}\"" + if (sender != null) {
|
||||||
is PrivateChat -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
|
" and you, " + when (sender) {
|
||||||
is User -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
|
is BusinessChat -> "business chat (wat) ${sender.original}"
|
||||||
is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let {
|
is PrivateChat -> "${sender.lastName} ${sender.firstName}"
|
||||||
chat.title.linkMarkdownV2(it)
|
is GroupChat -> "group ${sender.title}"
|
||||||
} ?: chat.title
|
is ChannelChat -> "channel ${sender.title}"
|
||||||
is GroupChat -> bot.getChat(chat).inviteLink ?.let {
|
is UnknownChatType -> "wat chat (${sender})"
|
||||||
chat.title.linkMarkdownV2(it)
|
}
|
||||||
} ?: chat.title
|
} else {
|
||||||
else -> "Unknown :(".escapeMarkdownV2Common()
|
""
|
||||||
|
}
|
||||||
|
reply(message, answer.escapeMarkdownV2Common(), MarkdownV2)
|
||||||
|
return@onContentMessage
|
||||||
|
}
|
||||||
|
is PreviewPrivateChat -> {
|
||||||
|
reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2)
|
||||||
|
return@onContentMessage
|
||||||
|
}
|
||||||
|
is PreviewGroupChat -> {
|
||||||
|
message.ifFromChannelGroupContentMessage<Unit> {
|
||||||
|
val answer = "Hi, ${it.senderChat.title}"
|
||||||
|
reply(message, answer, MarkdownV2)
|
||||||
|
return@onContentMessage
|
||||||
|
}
|
||||||
|
"Oh, hi, " + when (chat) {
|
||||||
|
is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let {
|
||||||
|
chat.title.linkMarkdownV2(it)
|
||||||
|
} ?: chat.title
|
||||||
|
else -> bot.getChat(chat).inviteLink ?.let {
|
||||||
|
chat.title.linkMarkdownV2(it)
|
||||||
|
} ?: 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()
|
||||||
}
|
}
|
||||||
reply(
|
reply(
|
||||||
message,
|
message,
|
||||||
|
|||||||
9
InlineQueriesBot/README.md
Normal file
9
InlineQueriesBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# InlineQueriesBot
|
||||||
|
|
||||||
|
This bot will form the inline queries for you. For that feature you should explicitly enable inline queries in bot settings
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
37
InlineQueriesBot/build.gradle
Normal file
37
InlineQueriesBot/build.gradle
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$nativePartTemplate"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
mainClass.set("InlineQueriesBotKt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
|
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
api "io.ktor:ktor-client-logging:$ktor_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
68
InlineQueriesBot/src/commonMain/kotlin/Bot.kt
Normal file
68
InlineQueriesBot/src/commonMain/kotlin/Bot.kt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.answers.answer
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDeepLink
|
||||||
|
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
|
||||||
|
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
|
||||||
|
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
|
||||||
|
import dev.inmo.tgbotapi.types.InlineQueryId
|
||||||
|
import dev.inmo.tgbotapi.types.inlineQueryAnswerResultsLimit
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thi bot will create inline query answers. You
|
||||||
|
* should enable inline queries in bot settings
|
||||||
|
*/
|
||||||
|
suspend fun doInlineQueriesBot(token: String) {
|
||||||
|
val bot = telegramBot(token)
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling(
|
||||||
|
defaultExceptionsHandler = { it.printStackTrace() },
|
||||||
|
) {
|
||||||
|
onBaseInlineQuery {
|
||||||
|
val page = it.offset.toIntOrNull() ?: 0
|
||||||
|
val results = (0 until inlineQueryAnswerResultsLimit.last).map {
|
||||||
|
(page * inlineQueryAnswerResultsLimit.last) + it
|
||||||
|
}
|
||||||
|
|
||||||
|
answer(
|
||||||
|
it,
|
||||||
|
results = results.map { resultNumber ->
|
||||||
|
val inlineQueryId = InlineQueryId(resultNumber.toString())
|
||||||
|
InlineQueryResultArticle(
|
||||||
|
inlineQueryId,
|
||||||
|
"Title $resultNumber",
|
||||||
|
InputTextMessageContent(
|
||||||
|
buildEntities {
|
||||||
|
+"Result text of " + resultNumber.toString() + " result:\n"
|
||||||
|
+it.query
|
||||||
|
}
|
||||||
|
),
|
||||||
|
description = "Description of $resultNumber result"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
cachedTime = 0,
|
||||||
|
isPersonal = true,
|
||||||
|
button = InlineQueryResultsButton.Start(
|
||||||
|
"Text of button with page $page",
|
||||||
|
"deep_link_for_page_$page"
|
||||||
|
),
|
||||||
|
nextOffset = (page + 1).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeepLink { (message, deepLink) ->
|
||||||
|
reply(message, deepLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
println(getMe())
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
3
InlineQueriesBot/src/jvmMain/kotlin/InlineQueriesBot.kt
Normal file
3
InlineQueriesBot/src/jvmMain/kotlin/InlineQueriesBot.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
doInlineQueriesBot(args.first())
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
runBlocking {
|
||||||
|
doInlineQueriesBot(args.first())
|
||||||
|
}
|
||||||
|
}
|
||||||
32
KeyboardsBot/KeyboardsBotLib/build.gradle
Normal file
32
KeyboardsBot/KeyboardsBotLib/build.gradle
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
js(IR) {
|
||||||
|
browser()
|
||||||
|
binaries.executable()
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
|
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
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.edit.edit
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContent
|
||||||
|
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.InputMessageContent.InputTextMessageContent
|
||||||
|
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.utils.PreviewFeature
|
||||||
|
import dev.inmo.tgbotapi.utils.botCommand
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
import dev.inmo.tgbotapi.utils.row
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
||||||
|
fun String.parsePageAndCount(): Pair<Int, Int>? {
|
||||||
|
val (pageString, countString) = split(" ").takeIf { it.count() > 1 } ?: return null
|
||||||
|
return Pair(
|
||||||
|
pageString.toIntOrNull() ?: return null,
|
||||||
|
countString.toIntOrNull() ?: return null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
|
||||||
|
val numericButtons = listOfNotNull(
|
||||||
|
page - 1,
|
||||||
|
page,
|
||||||
|
page + 1,
|
||||||
|
)
|
||||||
|
row {
|
||||||
|
val numbersRange = 1 .. count
|
||||||
|
numericButtons.forEach {
|
||||||
|
if (it in numbersRange) {
|
||||||
|
dataButton(it.toString(), "$it $count")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
copyTextButton("Command copy button", "/inline $page $count")
|
||||||
|
}
|
||||||
|
|
||||||
|
row {
|
||||||
|
if (page - 1 > 2) {
|
||||||
|
dataButton("<<", "1 $count", style = KeyboardButtonStyle.Danger)
|
||||||
|
}
|
||||||
|
if (page - 1 > 1) {
|
||||||
|
dataButton("<", "${page - 2} $count", style = KeyboardButtonStyle.Primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page + 1 < count) {
|
||||||
|
dataButton(">", "${page + 2} $count", style = KeyboardButtonStyle.Success)
|
||||||
|
}
|
||||||
|
if (page + 2 < count) {
|
||||||
|
dataButton(">>", "$count $count", style = KeyboardButtonStyle.Danger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
inlineQueryInChosenChatButton(
|
||||||
|
"Send somebody page",
|
||||||
|
query = "$page $count",
|
||||||
|
allowUsers = true,
|
||||||
|
allowBots = true,
|
||||||
|
allowGroups = true,
|
||||||
|
allowChannels = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
|
suspend fun activateKeyboardsBot(
|
||||||
|
token: String,
|
||||||
|
print: (Any) -> Unit
|
||||||
|
) {
|
||||||
|
val bot = telegramBot(token)
|
||||||
|
|
||||||
|
print(bot.getMe())
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
||||||
|
onCommandWithArgs("inline") { message, args ->
|
||||||
|
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(
|
||||||
|
message,
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
includePageButtons(page, numberOfPages)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("Your inline keyboard with $numberOfPages pages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageDataCallbackQuery {
|
||||||
|
val (page, count) = it.data.parsePageAndCount() ?: it.let {
|
||||||
|
answer(it, "Unsupported data :(")
|
||||||
|
return@onMessageDataCallbackQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(
|
||||||
|
it.message.withContent<TextContent>() ?: it.let {
|
||||||
|
answer(it, "Unsupported message type :(")
|
||||||
|
return@onMessageDataCallbackQuery
|
||||||
|
},
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
includePageButtons(page, count)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("This is $page of $count")
|
||||||
|
}
|
||||||
|
answer(it)
|
||||||
|
}
|
||||||
|
onInlineMessageIdDataCallbackQuery {
|
||||||
|
val (page, count) = it.data.parsePageAndCount() ?: it.let {
|
||||||
|
answer(it, "Unsupported data :(")
|
||||||
|
return@onInlineMessageIdDataCallbackQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
editMessageText(
|
||||||
|
it.inlineMessageId,
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
includePageButtons(page, count)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("This is $page of $count")
|
||||||
|
}
|
||||||
|
answer(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBaseInlineQuery {
|
||||||
|
val page = it.query.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
|
||||||
|
val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }
|
||||||
|
.toIntOrNull() ?: return@onBaseInlineQuery
|
||||||
|
|
||||||
|
answer(
|
||||||
|
it,
|
||||||
|
results = listOf(
|
||||||
|
InlineQueryResultArticle(
|
||||||
|
InlineQueryId(it.query),
|
||||||
|
"Send buttons",
|
||||||
|
InputTextMessageContent("It is sent via inline mode inline buttons"),
|
||||||
|
replyMarkup = inlineKeyboard {
|
||||||
|
includePageButtons(page, count)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnhandledCommand {
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
|
||||||
|
row {
|
||||||
|
simpleButton("/inline", style = KeyboardButtonStyle.Primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
+"Use " + botCommand("inline") + " to get pagination inline keyboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import kotlinx.browser.document
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.w3c.dom.*
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
{
|
||||||
|
val botsContainer = document.getElementById("bots_container") ?: return@addEventListener
|
||||||
|
|
||||||
|
(document.getElementById("bot_token_form") as? HTMLFormElement) ?.onsubmit = {
|
||||||
|
(document.getElementById("bot_token") as? HTMLInputElement) ?.value ?.let { token ->
|
||||||
|
val botContainer = document.createElement("div") as HTMLDivElement
|
||||||
|
botsContainer.append(botContainer)
|
||||||
|
|
||||||
|
val infoDiv = document.createElement("div") as HTMLDivElement
|
||||||
|
botContainer.append(infoDiv)
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
activateKeyboardsBot(token) {
|
||||||
|
infoDiv.innerHTML = it.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
16
KeyboardsBot/KeyboardsBotLib/src/jsMain/resources/index.html
Normal file
16
KeyboardsBot/KeyboardsBotLib/src/jsMain/resources/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Keyboards bot</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="bot_token_form">
|
||||||
|
<input type="text" id="bot_token">
|
||||||
|
<input type="submit" value="Start bot">
|
||||||
|
</form>
|
||||||
|
<div id="start_offer">Type your bot token to the input above to start its work</div>
|
||||||
|
<script type="text/javascript" src="KeyboardsBotLib.js"></script>
|
||||||
|
<div id="bots_container"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21
KeyboardsBot/jvm_launcher/build.gradle
Normal file
21
KeyboardsBot/jvm_launcher/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="KeyboardsBotJvmKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation project(":KeyboardsBot:KeyboardsBotLib")
|
||||||
|
}
|
||||||
24
KeyboardsBot/jvm_launcher/src/main/kotlin/KeyboardsBotJvm.kt
Normal file
24
KeyboardsBot/jvm_launcher/src/main/kotlin/KeyboardsBotJvm.kt
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
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
|
||||||
|
activateKeyboardsBot(args.first()) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
LinkPreviewsBot/README.md
Normal file
9
LinkPreviewsBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ReactionsInfoBot
|
||||||
|
|
||||||
|
This bot will resend messages with links with all variants of `LinkPreviewOptions`
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
LinkPreviewsBot/build.gradle
Normal file
21
LinkPreviewsBot/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="LinkPreviewsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
84
LinkPreviewsBot/src/main/kotlin/LinkPreviewsBot.kt
Normal file
84
LinkPreviewsBot/src/main/kotlin/LinkPreviewsBot.kt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.textLinkTextSourceOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.uRLTextSourceOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
|
import dev.inmo.tgbotapi.types.LinkPreviewOptions
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.TextedContent
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bot will reply with the same
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val isDebug = args.getOrNull(1) == "debug"
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot = telegramBot(botToken)
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling {
|
||||||
|
onContentMessage { contentMessage ->
|
||||||
|
val url = contentMessage.withContentOrNull<TextedContent>() ?.let { message ->
|
||||||
|
message.content.textSources.firstNotNullOfOrNull {
|
||||||
|
it.textLinkTextSourceOrNull() ?.url ?: it.uRLTextSourceOrNull() ?.source
|
||||||
|
}
|
||||||
|
} ?: null.apply {
|
||||||
|
reply(contentMessage) {
|
||||||
|
regular("I am support only content with text contains url only")
|
||||||
|
}
|
||||||
|
} ?: return@onContentMessage
|
||||||
|
contentMessage.withContentOrNull<TextedContent>() ?.let {
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Disabled
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Large(url, showAboveText = true)
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Large(url, showAboveText = false)
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Small(url, showAboveText = true)
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Small(url, showAboveText = false)
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Default(url, showAboveText = true)
|
||||||
|
)
|
||||||
|
send(
|
||||||
|
it.chat,
|
||||||
|
it.content.textSources,
|
||||||
|
linkPreviewOptions = LinkPreviewOptions.Default(url, showAboveText = false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
9
LiveLocationsBot/README.md
Normal file
9
LiveLocationsBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# LiveLocationsBot
|
||||||
|
|
||||||
|
This bot will send you live location and update it from time to time
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
LiveLocationsBot/build.gradle
Normal file
21
LiveLocationsBot/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="LiveLocationsBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
67
LiveLocationsBot/src/main/kotlin/LiveLocationsBot.kt
Normal file
67
LiveLocationsBot/src/main/kotlin/LiveLocationsBot.kt
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.EditLiveLocationInfo
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.edit.location.live.stopLiveLocation
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.handleLiveLocation
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
|
||||||
|
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.sameMessage
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||||
|
import dev.inmo.tgbotapi.types.message.content.LocationContent
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bot will send you live location and update it from time to time
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
|
||||||
|
val locationsFlow = flow {
|
||||||
|
var i = 0
|
||||||
|
while (isActive) {
|
||||||
|
val newInfo = EditLiveLocationInfo(
|
||||||
|
latitude = i.toDouble(),
|
||||||
|
longitude = i.toDouble(),
|
||||||
|
replyMarkup = flatInlineKeyboard {
|
||||||
|
dataButton("Cancel", "cancel")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
emit(newInfo)
|
||||||
|
i++
|
||||||
|
delay(3000L) // 3 seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("start") {
|
||||||
|
// in this flow will be actual message with live location
|
||||||
|
val currentMessageState = MutableStateFlow<ContentMessage<LocationContent>?>(null)
|
||||||
|
val sendingJob = launch {
|
||||||
|
handleLiveLocation(
|
||||||
|
it.chat.id,
|
||||||
|
locationsFlow,
|
||||||
|
sentMessageFlow = { currentMessageState.emit(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitMessageDataCallbackQuery().filter {
|
||||||
|
it.message.sameMessage(
|
||||||
|
currentMessageState.value ?: return@filter false
|
||||||
|
) && it.data == "cancel"
|
||||||
|
}.first()
|
||||||
|
|
||||||
|
sendingJob.cancel() // ends live location
|
||||||
|
currentMessageState.value ?.let {
|
||||||
|
stopLiveLocation(it, replyMarkup = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
}
|
||||||
21
MyBot/build.gradle
Normal file
21
MyBot/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="GetMeBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
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()
|
||||||
|
}
|
||||||
11
PollsBot/README.md
Normal file
11
PollsBot/README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# PollsBot
|
||||||
|
|
||||||
|
This bot will send test poll in the chat where commands will be received. Commands:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
179
PollsBot/src/main/kotlin/PollsBot.kt
Normal file
179
PollsBot/src/main/kotlin/PollsBot.kt
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
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.setMyCommands
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.polls.sendQuizPoll
|
||||||
|
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.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollAnswer
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPollUpdates
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.customEmojiTextSourceOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithArgsSources
|
||||||
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
|
import dev.inmo.tgbotapi.types.IdChatIdentifier
|
||||||
|
import dev.inmo.tgbotapi.types.PollId
|
||||||
|
import dev.inmo.tgbotapi.types.ReplyParameters
|
||||||
|
import dev.inmo.tgbotapi.types.polls.InputPollOption
|
||||||
|
import dev.inmo.tgbotapi.types.polls.PollAnswer
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import dev.inmo.tgbotapi.utils.customEmoji
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
import dev.inmo.tgbotapi.utils.underline
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bot will answer with anonymous or public poll and send message on
|
||||||
|
* any update.
|
||||||
|
*
|
||||||
|
* * Use `/anonymous` to take anonymous regular poll
|
||||||
|
* * Use `/public` to take public regular poll
|
||||||
|
*/
|
||||||
|
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)) {
|
||||||
|
val pollToChat = mutableMapOf<PollId, IdChatIdentifier>()
|
||||||
|
val pollToChatMutex = Mutex()
|
||||||
|
|
||||||
|
onCommand("anonymous", requireOnlyCommandInMessage = false) {
|
||||||
|
val customEmoji = it.content.parseCommandsWithArgsSources()
|
||||||
|
.toList()
|
||||||
|
.firstOrNull { it.first.command == "anonymous" }
|
||||||
|
?.second
|
||||||
|
?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() }
|
||||||
|
val sentPoll = sendRegularPoll(
|
||||||
|
it.chat.id,
|
||||||
|
buildEntities {
|
||||||
|
regular("Test regular anonymous 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 = true,
|
||||||
|
replyParameters = ReplyParameters(it)
|
||||||
|
)
|
||||||
|
pollToChatMutex.withLock {
|
||||||
|
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("public", requireOnlyCommandInMessage = false) {
|
||||||
|
val customEmoji = it.content.parseCommandsWithArgsSources()
|
||||||
|
.toList()
|
||||||
|
.firstOrNull { it.first.command == "public" }
|
||||||
|
?.second
|
||||||
|
?.firstNotNullOfOrNull { it.customEmojiTextSourceOrNull() }
|
||||||
|
val sentPoll = sendRegularPoll(
|
||||||
|
it.chat.id,
|
||||||
|
buildEntities {
|
||||||
|
regular("Test regular non anonymous 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)
|
||||||
|
)
|
||||||
|
pollToChatMutex.withLock {
|
||||||
|
pollToChat[sentPoll.content.poll.id] = sentPoll.chat.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
val chatId = pollToChat[it.pollId] ?: return@onPollAnswer
|
||||||
|
|
||||||
|
when(it) {
|
||||||
|
is PollAnswer.Public -> send(chatId, "[onPollAnswer] User ${it.user} have answered")
|
||||||
|
is PollAnswer.Anonymous -> send(chatId, "[onPollAnswer] Chat ${it.voterChat} have answered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPollUpdates {
|
||||||
|
val chatId = pollToChat[it.id] ?: return@onPollUpdates
|
||||||
|
|
||||||
|
when(it.isAnonymous) {
|
||||||
|
false -> send(chatId, "[onPollUpdates] Public poll updated: ${it.options.joinToString()}")
|
||||||
|
true -> send(chatId, "[onPollUpdates] Anonymous poll updated: ${it.options.joinToString()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}.second.join()
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ This repository contains several examples of simple bots which are using Telegra
|
|||||||
|
|
||||||
## How to use this repository
|
## How to use this repository
|
||||||
|
|
||||||
|
***TO RUN NATIVE TARGETS ON LINUX YOU SHOULD INSTALL CURL LIBRARY. FOR EXAMPLE: `sudo apt install libcurl4-gnutls-dev`***
|
||||||
|
|
||||||
This repository contains several important things:
|
This repository contains several important things:
|
||||||
|
|
||||||
* Example subprojects
|
* Example subprojects
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -8,14 +8,29 @@ buildscript {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'kotlin'
|
plugins {
|
||||||
apply plugin: 'application'
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
|
||||||
mainClassName="RandomFileSenderBotKt"
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
||||||
|
|
||||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm {
|
||||||
|
binaries {
|
||||||
|
executable {
|
||||||
|
mainClass.set("RandomFileSenderBotKt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
|
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$nativePartTemplate"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.filesize
|
||||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
@@ -6,22 +7,19 @@ import dev.inmo.tgbotapi.extensions.api.send.media.sendDocument
|
|||||||
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup
|
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.reply
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
import dev.inmo.tgbotapi.extensions.api.send.withUploadDocumentAction
|
import dev.inmo.tgbotapi.extensions.api.send.withUploadDocumentAction
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviour
|
import dev.inmo.tgbotapi.extensions.api.telegramBot
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
|
||||||
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
import dev.inmo.tgbotapi.requests.abstracts.toInputFile
|
|
||||||
import dev.inmo.tgbotapi.types.BotCommand
|
import dev.inmo.tgbotapi.types.BotCommand
|
||||||
import dev.inmo.tgbotapi.types.InputMedia.DocumentMediaGroupMemberInputMedia
|
import dev.inmo.tgbotapi.types.chat.Chat
|
||||||
import dev.inmo.tgbotapi.types.InputMedia.InputMediaDocument
|
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
|
||||||
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
|
|
||||||
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
|
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
private const val command = "send_file"
|
private const val command = "send_file"
|
||||||
|
|
||||||
|
expect fun pickFile(currentRoot: MPPFile): MPPFile?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This bot will send files inside of working directory OR from directory in the second argument.
|
* This bot will send files inside of working directory OR from directory in the second argument.
|
||||||
* You may send /send_file command to this bot to get random file from the directory OR
|
* You may send /send_file command to this bot to get random file from the directory OR
|
||||||
@@ -29,19 +27,10 @@ private const val command = "send_file"
|
|||||||
* /send_file and `/send_file 1` will have the same effect - bot will send one random file.
|
* /send_file and `/send_file 1` will have the same effect - bot will send one random file.
|
||||||
* But if you will send `/send_file 5` it will choose 5 random files and send them as group
|
* But if you will send `/send_file 5` it will choose 5 random files and send them as group
|
||||||
*/
|
*/
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun doRandomFileSenderBot(token: String, folder: MPPFile) {
|
||||||
val botToken = args.first()
|
val bot = telegramBot(token)
|
||||||
val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("")
|
|
||||||
|
|
||||||
fun pickFile(currentRoot: File = directoryOrFile): File? {
|
suspend fun TelegramBot.sendFiles(chat: Chat, files: List<MPPFile>) {
|
||||||
if (currentRoot.isFile) {
|
|
||||||
return currentRoot
|
|
||||||
} else {
|
|
||||||
return pickFile(currentRoot.listFiles() ?.takeIf { it.isNotEmpty() } ?.random() ?: return null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun TelegramBot.sendFiles(chat: Chat, files: List<File>) {
|
|
||||||
when (files.size) {
|
when (files.size) {
|
||||||
1 -> sendDocument(
|
1 -> sendDocument(
|
||||||
chat.id,
|
chat.id,
|
||||||
@@ -50,14 +39,12 @@ suspend fun main(args: Array<String>) {
|
|||||||
)
|
)
|
||||||
else -> sendDocumentsGroup(
|
else -> sendDocumentsGroup(
|
||||||
chat,
|
chat,
|
||||||
files.map { InputMediaDocument(it.asMultipartFile()) },
|
files.map { TelegramMediaDocument(it.asMultipartFile()) },
|
||||||
protectContent = true
|
protectContent = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val bot = telegramBot(botToken)
|
|
||||||
|
|
||||||
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
|
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
|
||||||
onCommandWithArgs(command) { message, args ->
|
onCommandWithArgs(command) { message, args ->
|
||||||
|
|
||||||
@@ -66,12 +53,12 @@ suspend fun main(args: Array<String>) {
|
|||||||
var sent = false
|
var sent = false
|
||||||
|
|
||||||
var left = count
|
var left = count
|
||||||
val chosen = mutableListOf<File>()
|
val chosen = mutableListOf<MPPFile>()
|
||||||
|
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
left--
|
val picked = pickFile(folder) ?.takeIf { it.filesize > 0 } ?: continue
|
||||||
val picked = pickFile() ?: continue
|
|
||||||
chosen.add(picked)
|
chosen.add(picked)
|
||||||
|
left--
|
||||||
if (chosen.size >= mediaCountInMediaGroup.last) {
|
if (chosen.size >= mediaCountInMediaGroup.last) {
|
||||||
sendFiles(message.chat, chosen)
|
sendFiles(message.chat, chosen)
|
||||||
chosen.clear()
|
chosen.clear()
|
||||||
@@ -85,7 +72,7 @@ suspend fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
bot.reply(message, "Nothing selected :(")
|
reply(message, "Nothing selected :(")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
RandomFileSenderBot/src/jvmMain/kotlin/ActualPickFile.kt
Normal file
10
RandomFileSenderBot/src/jvmMain/kotlin/ActualPickFile.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
actual fun pickFile(currentRoot: MPPFile): File? {
|
||||||
|
if (currentRoot.isFile) {
|
||||||
|
return currentRoot
|
||||||
|
} else {
|
||||||
|
return pickFile(currentRoot.listFiles() ?.takeIf { it.isNotEmpty() } ?.random() ?: return null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
doRandomFileSenderBot(args.first(), MPPFile(args.getOrNull(1) ?: ""))
|
||||||
|
}
|
||||||
10
RandomFileSenderBot/src/nativeMain/kotlin/ActualPickFile.kt
Normal file
10
RandomFileSenderBot/src/nativeMain/kotlin/ActualPickFile.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import okio.FileSystem
|
||||||
|
|
||||||
|
actual fun pickFile(currentRoot: MPPFile): MPPFile? {
|
||||||
|
if (FileSystem.SYSTEM.exists(currentRoot) && FileSystem.SYSTEM.listOrNull(currentRoot) == null) {
|
||||||
|
return currentRoot
|
||||||
|
} else {
|
||||||
|
return pickFile(FileSystem.SYSTEM.list(currentRoot).takeIf { it.isNotEmpty() } ?.random() ?: return null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okio.Path.Companion.toPath
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
runBlocking {
|
||||||
|
doRandomFileSenderBot(args.first(), args.getOrNull(1) ?.toPath() ?: "".toPath())
|
||||||
|
}
|
||||||
|
}
|
||||||
9
ReactionsInfoBot/README.md
Normal file
9
ReactionsInfoBot/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ReactionsInfoBot
|
||||||
|
|
||||||
|
This bot will send info about user reactions in his PM with reply to message user reacted to
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN"
|
||||||
|
```
|
||||||
21
ReactionsInfoBot/build.gradle
Normal file
21
ReactionsInfoBot/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="ReactionsInfoBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
68
ReactionsInfoBot/src/main/kotlin/ReactionsInfoBot.kt
Normal file
68
ReactionsInfoBot/src/main/kotlin/ReactionsInfoBot.kt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.setMessageReaction
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionUpdatedByUser
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMessageReactionsCountUpdated
|
||||||
|
import dev.inmo.tgbotapi.types.chat.ExtendedChat
|
||||||
|
import dev.inmo.tgbotapi.types.reactions.Reaction
|
||||||
|
import dev.inmo.tgbotapi.utils.customEmoji
|
||||||
|
import dev.inmo.tgbotapi.utils.regular
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bot will send info about user reactions in his PM with reply to message user reacted to
|
||||||
|
*/
|
||||||
|
suspend fun main(vararg args: String) {
|
||||||
|
val botToken = args.first()
|
||||||
|
val isDebug = args.getOrNull(1) == "debug"
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot = telegramBot(botToken)
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling {
|
||||||
|
onChatMessageReactionUpdatedByUser {
|
||||||
|
setMessageReaction(
|
||||||
|
it.chat.id,
|
||||||
|
it.messageId,
|
||||||
|
"✍"
|
||||||
|
)
|
||||||
|
val replyResult = reply(
|
||||||
|
it.chat.id,
|
||||||
|
it.messageId,
|
||||||
|
replyInChatId = it.reactedUser.id
|
||||||
|
) {
|
||||||
|
regular("Current reactions for message in reply:\n")
|
||||||
|
it.new.forEach {
|
||||||
|
when (it) {
|
||||||
|
is Reaction.CustomEmoji -> regular("• ") + customEmoji(it.customEmojiId) + regular("(customEmojiId: ${it.customEmojiId})")
|
||||||
|
is Reaction.Emoji -> regular("• ${it.emoji}")
|
||||||
|
is Reaction.Paid -> regular("• Some paid reaction")
|
||||||
|
is Reaction.Unknown -> regular("• Unknown emoji ($it)")
|
||||||
|
}
|
||||||
|
regular("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMessageReaction(
|
||||||
|
it.chat.id,
|
||||||
|
it.messageId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onChatMessageReactionsCountUpdated {
|
||||||
|
val extendedChat: ExtendedChat = getChat(it.chat)
|
||||||
|
println(extendedChat)
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -9,7 +9,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +20,9 @@ kotlin {
|
|||||||
browser()
|
browser()
|
||||||
binaries.executable()
|
binaries.executable()
|
||||||
}
|
}
|
||||||
|
linuxX64()
|
||||||
|
mingwX64()
|
||||||
|
linuxArm64()
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
|
|||||||
@@ -1,55 +1,64 @@
|
|||||||
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.types.message.abstracts.Message
|
import dev.inmo.tgbotapi.extensions.utils.possiblyWithEffectMessageOrNull
|
||||||
import kotlinx.coroutines.*
|
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
|
||||||
|
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.utils.DefaultKTgBotAPIKSLog
|
||||||
|
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
|
||||||
|
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
|
||||||
) {
|
) {
|
||||||
val bot = telegramBot(token)
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
token,
|
||||||
print(bot.getMe())
|
scope = CoroutineScope(currentCoroutineContext() + SupervisorJob()),
|
||||||
|
) {
|
||||||
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
|
||||||
onContentMessage(
|
onContentMessage(
|
||||||
initialFilter = CommonMessageFilterExcludeMediaGroups,
|
subcontextUpdatesFilter = MessageFilterByChat,
|
||||||
subcontextUpdatesFilter = MessageFilterByChat
|
initialFilter = { it !is BusinessContentMessage<*> || !it.sentByBusinessConnectionOwner }
|
||||||
) {
|
) {
|
||||||
val chat = it.chat
|
val chat = it.chat
|
||||||
withTypingAction(chat) {
|
|
||||||
executeUnsafe(it.content.createResend(chat.id, replyToMessageId = it.messageId))
|
val answer = withTypingAction(chat) {
|
||||||
}
|
executeUnsafe(
|
||||||
}
|
it.content.createResend(
|
||||||
onVisualGallery {
|
chat.id,
|
||||||
val chat = it.chat ?: return@onVisualGallery
|
replyParameters = it.replyInfo?.messageMeta?.let { meta ->
|
||||||
withUploadPhotoAction(chat) {
|
val quote = it.withContentOrNull<TextContent>()?.content?.quote
|
||||||
sendVisualMediaGroup(chat, it.map { it.content.toMediaGroupMemberInputMedia() })
|
ReplyParameters(
|
||||||
}
|
meta,
|
||||||
}
|
entities = quote?.textSources ?: emptyList(),
|
||||||
onPlaylist {
|
quotePosition = quote?.position
|
||||||
val chat = it.chat ?: return@onPlaylist
|
)
|
||||||
withUploadDocumentAction(chat) {
|
},
|
||||||
sendPlaylist(chat, it.map { it.content.toMediaGroupMemberInputMedia() })
|
effectId = it.possiblyWithEffectMessageOrNull()?.effectId
|
||||||
}
|
)
|
||||||
}
|
) {
|
||||||
onDocumentsGroup {
|
it.forEach(print)
|
||||||
val chat = it.chat ?: return@onDocumentsGroup
|
}
|
||||||
withUploadDocumentAction(chat) {
|
|
||||||
sendDocumentsGroup(chat, it.map { it.content.toMediaGroupMemberInputMedia() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Answer info: $answer")
|
||||||
}
|
}
|
||||||
|
|
||||||
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
}.join()
|
print(bot.getMe())
|
||||||
|
}.second.join()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
28
ResenderBot/native_launcher/build.gradle
Normal file
28
ResenderBot/native_launcher/build.gradle
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$nativePartTemplate"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
|
api project(":ResenderBot:ResenderBotLib")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
fun main(vararg args: String) {
|
||||||
|
runBlocking {
|
||||||
|
activateResenderBot(args.first()) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
RightsChangerBot/README.md
Normal file
12
RightsChangerBot/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# RightsChanger
|
||||||
|
|
||||||
|
All the commands should be called with reply to some common user.
|
||||||
|
|
||||||
|
* Use `/simple` with bot to get request buttons for non-independent permissions change
|
||||||
|
* Use `/granular` with bot to get request buttons for independent permissions change
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
../gradlew run --args="BOT_TOKEN allowed_user_id_long"
|
||||||
|
```
|
||||||
22
RightsChangerBot/build.gradle
Normal file
22
RightsChangerBot/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="RightsChangerKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
implementation 'io.ktor:ktor-client-logging-jvm:3.2.3'
|
||||||
|
}
|
||||||
540
RightsChangerBot/src/main/kotlin/RightsChanger.kt
Normal file
540
RightsChangerBot/src/main/kotlin/RightsChanger.kt
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
import dev.inmo.kslog.common.KSLog
|
||||||
|
import dev.inmo.kslog.common.LogLevel
|
||||||
|
import dev.inmo.kslog.common.defaultMessageFormatter
|
||||||
|
import dev.inmo.kslog.common.setDefaultKSLog
|
||||||
|
import dev.inmo.micro_utils.coroutines.firstOf
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
|
import dev.inmo.micro_utils.fsm.common.State
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.promoteChannelAdministrator
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.chat.members.restrictChatMember
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.edit.edit
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithFSMAndStartLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.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.onMessageDataCallbackQuery
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.*
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat
|
||||||
|
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
|
||||||
|
import dev.inmo.tgbotapi.types.*
|
||||||
|
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup
|
||||||
|
import dev.inmo.tgbotapi.types.chat.ChannelChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.ChatPermissions
|
||||||
|
import dev.inmo.tgbotapi.types.chat.PublicChat
|
||||||
|
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
|
||||||
|
import dev.inmo.tgbotapi.types.chat.member.ChatCommonAdministratorRights
|
||||||
|
import dev.inmo.tgbotapi.types.commands.BotCommandScope
|
||||||
|
import dev.inmo.tgbotapi.types.message.abstracts.AccessibleMessage
|
||||||
|
import dev.inmo.tgbotapi.types.request.RequestId
|
||||||
|
import dev.inmo.tgbotapi.utils.*
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
|
||||||
|
sealed interface UserRetrievingStep : State {
|
||||||
|
data class RetrievingChannelChatState(
|
||||||
|
override val context: ChatId
|
||||||
|
) : UserRetrievingStep
|
||||||
|
data class RetrievingUserIdChatState(
|
||||||
|
override val context: ChatId,
|
||||||
|
val channelId: ChatId
|
||||||
|
) : UserRetrievingStep
|
||||||
|
data class RetrievingChatInfoDoneState(
|
||||||
|
override val context: ChatId,
|
||||||
|
val channelId: ChatId,
|
||||||
|
val userId: UserId
|
||||||
|
) : UserRetrievingStep
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(PreviewFeature::class)
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
val botToken = args.first()
|
||||||
|
|
||||||
|
val isDebug = args.getOrNull(2) == "debug"
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
setDefaultKSLog(
|
||||||
|
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
|
||||||
|
println(defaultMessageFormatter(level, tag, message, throwable))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot = telegramBot(botToken)
|
||||||
|
|
||||||
|
val allowedAdmin = ChatId(RawChatId(args[1].toLong()))
|
||||||
|
|
||||||
|
fun Boolean?.allowedSymbol() = when (this) {
|
||||||
|
true -> "✅"
|
||||||
|
false -> "❌"
|
||||||
|
null -> ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val granularDataPrefix = "granular"
|
||||||
|
val messagesToggleGranularData = "$granularDataPrefix messages"
|
||||||
|
val otherMessagesToggleGranularData = "$granularDataPrefix other messages"
|
||||||
|
val audiosToggleGranularData = "$granularDataPrefix audios"
|
||||||
|
val voicesToggleGranularData = "$granularDataPrefix voices"
|
||||||
|
val videosToggleGranularData = "$granularDataPrefix videos"
|
||||||
|
val videoNotesToggleGranularData = "$granularDataPrefix video notes"
|
||||||
|
val photosToggleGranularData = "$granularDataPrefix photos"
|
||||||
|
val webPagePreviewToggleGranularData = "$granularDataPrefix web page preview"
|
||||||
|
val pollsToggleGranularData = "$granularDataPrefix polls"
|
||||||
|
val documentsToggleGranularData = "$granularDataPrefix documents"
|
||||||
|
|
||||||
|
val commonDataPrefix = "common"
|
||||||
|
val pollsToggleCommonData = "$commonDataPrefix polls"
|
||||||
|
val otherMessagesToggleCommonData = "$commonDataPrefix other messages"
|
||||||
|
val webPagePreviewToggleCommonData = "$commonDataPrefix web page preview"
|
||||||
|
|
||||||
|
val adminRightsDataPrefix = "admin"
|
||||||
|
val refreshAdminRightsData = "${adminRightsDataPrefix}_refresh"
|
||||||
|
val postMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_post_messages"
|
||||||
|
val editMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_edit_messages"
|
||||||
|
val deleteMessagesToggleAdminRightsData = "${adminRightsDataPrefix}_delete_messages"
|
||||||
|
val editStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_edit_stories"
|
||||||
|
val deleteStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_delete_stories"
|
||||||
|
val postStoriesToggleAdminRightsData = "${adminRightsDataPrefix}_post_stories"
|
||||||
|
|
||||||
|
suspend fun BehaviourContext.getUserChatPermissions(chatId: ChatId, userId: UserId): ChatPermissions? {
|
||||||
|
val chatMember = getChatMember(chatId, userId)
|
||||||
|
return chatMember.restrictedMemberChatMemberOrNull() ?: chatMember.whenMemberChatMember {
|
||||||
|
getChat(chatId).extendedGroupChatOrNull() ?.permissions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun buildGranularKeyboard(
|
||||||
|
permissions: ChatPermissions
|
||||||
|
): InlineKeyboardMarkup {
|
||||||
|
return inlineKeyboard {
|
||||||
|
row {
|
||||||
|
dataButton("Send messages${permissions.canSendMessages.allowedSymbol()}", messagesToggleGranularData)
|
||||||
|
dataButton(
|
||||||
|
"Send other messages${permissions.canSendOtherMessages.allowedSymbol()}",
|
||||||
|
otherMessagesToggleGranularData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Send audios${permissions.canSendAudios.allowedSymbol()}", audiosToggleGranularData)
|
||||||
|
dataButton("Send voices${permissions.canSendVoiceNotes.allowedSymbol()}", voicesToggleGranularData)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Send videos${permissions.canSendVideos.allowedSymbol()}", videosToggleGranularData)
|
||||||
|
dataButton(
|
||||||
|
"Send video notes${permissions.canSendVideoNotes.allowedSymbol()}",
|
||||||
|
videoNotesToggleGranularData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Send photos${permissions.canSendPhotos.allowedSymbol()}", photosToggleGranularData)
|
||||||
|
dataButton(
|
||||||
|
"Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}",
|
||||||
|
webPagePreviewToggleGranularData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleGranularData)
|
||||||
|
dataButton("Send documents${permissions.canSendDocuments.allowedSymbol()}", documentsToggleGranularData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun buildAdminRightsKeyboard(
|
||||||
|
permissions: AdministratorChatMember?,
|
||||||
|
channelId: ChatId,
|
||||||
|
userId: UserId
|
||||||
|
): InlineKeyboardMarkup {
|
||||||
|
return inlineKeyboard {
|
||||||
|
permissions ?.also {
|
||||||
|
row {
|
||||||
|
dataButton("Refresh", "$refreshAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Edit messages${permissions.canEditMessages.allowedSymbol()}", "$editMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
dataButton("Delete messages${permissions.canRemoveMessages.allowedSymbol()}", "$deleteMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Post messages${permissions.canPostMessages.allowedSymbol()}", "$postMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Edit stories${permissions.canEditStories.allowedSymbol()}", "$editStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
dataButton("Delete stories${permissions.canDeleteStories.allowedSymbol()}", "$deleteStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Post stories${permissions.canPostStories.allowedSymbol()}", "$postStoriesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
} ?: row {
|
||||||
|
dataButton("Promote to admin", "$postMessagesToggleAdminRightsData ${channelId.chatId} ${userId.chatId}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun BehaviourContext.buildGranularKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? {
|
||||||
|
return buildGranularKeyboard(
|
||||||
|
getUserChatPermissions(chatId, userId) ?: return null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun BehaviourContext.buildCommonKeyboard(chatId: ChatId, userId: UserId): InlineKeyboardMarkup? {
|
||||||
|
val permissions = getUserChatPermissions(chatId, userId) ?: return null
|
||||||
|
|
||||||
|
return inlineKeyboard {
|
||||||
|
row {
|
||||||
|
dataButton("Send polls${permissions.canSendPolls.allowedSymbol()}", pollsToggleCommonData)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Send other messages${permissions.canSendOtherMessages.allowedSymbol()}", otherMessagesToggleCommonData)
|
||||||
|
}
|
||||||
|
row {
|
||||||
|
dataButton("Add web preview${permissions.canAddWebPagePreviews.allowedSymbol()}", webPagePreviewToggleCommonData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.buildBehaviourWithFSMAndStartLongPolling<UserRetrievingStep>(
|
||||||
|
defaultExceptionsHandler = {
|
||||||
|
it.printStackTrace()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
onCommand(
|
||||||
|
"simple",
|
||||||
|
initialFilter = { it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin }
|
||||||
|
) {
|
||||||
|
val replyMessage = it.replyTo
|
||||||
|
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
||||||
|
if (replyMessage is AccessibleMessage) {
|
||||||
|
reply(
|
||||||
|
replyMessage,
|
||||||
|
"Manage keyboard:",
|
||||||
|
replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
reply(it) {
|
||||||
|
regular("Reply to somebody's message to get hist/her rights keyboard")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand(
|
||||||
|
"granular",
|
||||||
|
initialFilter = {
|
||||||
|
it.chat is ChannelChat || (it.chat is PublicChat && it.fromUserMessageOrNull()?.user?.id == allowedAdmin)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val replyMessage = it.replyTo
|
||||||
|
val userInReply = replyMessage?.fromUserMessageOrNull()?.user?.id ?: return@onCommand
|
||||||
|
|
||||||
|
if (replyMessage is AccessibleMessage) {
|
||||||
|
reply(
|
||||||
|
replyMessage,
|
||||||
|
"Manage keyboard:",
|
||||||
|
replyMarkup = buildGranularKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
reply(it) {
|
||||||
|
regular("Reply to somebody's message to get hist/her rights keyboard")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageDataCallbackQuery(
|
||||||
|
Regex("^${granularDataPrefix}.*"),
|
||||||
|
initialFilter = { it.user.id == allowedAdmin }
|
||||||
|
) {
|
||||||
|
val messageReply =
|
||||||
|
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
|
||||||
|
val userId = messageReply.user.id
|
||||||
|
val permissions =
|
||||||
|
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val newPermission = when (it.data) {
|
||||||
|
messagesToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendMessages = permissions.canSendMessages?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
otherMessagesToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
audiosToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendAudios = permissions.canSendAudios?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
voicesToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendVoiceNotes = permissions.canSendVoiceNotes?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
videosToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendVideos = permissions.canSendVideos?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
videoNotesToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendVideoNotes = permissions.canSendVideoNotes?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
photosToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendPhotos = permissions.canSendPhotos?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
webPagePreviewToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pollsToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendPolls = permissions.canSendPolls?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
documentsToggleGranularData -> {
|
||||||
|
permissions.copyGranular(
|
||||||
|
canSendDocuments = permissions.canSendDocuments?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> permissions.copyGranular()
|
||||||
|
}
|
||||||
|
|
||||||
|
restrictChatMember(
|
||||||
|
it.message.chat.id,
|
||||||
|
userId,
|
||||||
|
permissions = newPermission,
|
||||||
|
useIndependentChatPermissions = true
|
||||||
|
)
|
||||||
|
|
||||||
|
edit(
|
||||||
|
it.message,
|
||||||
|
replyMarkup = buildGranularKeyboard(it.message.chat.id.toChatId(), userId)
|
||||||
|
?: return@onMessageDataCallbackQuery
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageDataCallbackQuery(
|
||||||
|
Regex("^${commonDataPrefix}.*"),
|
||||||
|
initialFilter = { it.user.id == allowedAdmin }
|
||||||
|
) {
|
||||||
|
val messageReply =
|
||||||
|
it.message.commonMessageOrNull()?.replyTo?.fromUserMessageOrNull() ?: return@onMessageDataCallbackQuery
|
||||||
|
val userId = messageReply.user.id
|
||||||
|
val permissions =
|
||||||
|
getUserChatPermissions(it.message.chat.id.toChatId(), userId) ?: return@onMessageDataCallbackQuery
|
||||||
|
val newPermission = when (it.data) {
|
||||||
|
pollsToggleCommonData -> {
|
||||||
|
permissions.copyCommon(
|
||||||
|
canSendPolls = permissions.canSendPolls?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
otherMessagesToggleCommonData -> {
|
||||||
|
permissions.copyCommon(
|
||||||
|
canSendOtherMessages = permissions.canSendOtherMessages?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
webPagePreviewToggleCommonData -> {
|
||||||
|
permissions.copyCommon(
|
||||||
|
canAddWebPagePreviews = permissions.canAddWebPagePreviews?.let { !it } ?: false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> permissions.copyCommon()
|
||||||
|
}
|
||||||
|
|
||||||
|
restrictChatMember(
|
||||||
|
it.message.chat.id,
|
||||||
|
userId,
|
||||||
|
permissions = newPermission,
|
||||||
|
useIndependentChatPermissions = false
|
||||||
|
)
|
||||||
|
|
||||||
|
edit(
|
||||||
|
it.message,
|
||||||
|
replyMarkup = buildCommonKeyboard(it.message.chat.id.toChatId(), userId)
|
||||||
|
?: return@onMessageDataCallbackQuery
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageDataCallbackQuery(
|
||||||
|
Regex("^${adminRightsDataPrefix}.*"),
|
||||||
|
initialFilter = { it.user.id == allowedAdmin }
|
||||||
|
) {
|
||||||
|
val (channelIdString, userIdString) = it.data.split(" ").drop(1)
|
||||||
|
val channelId = ChatId(RawChatId(channelIdString.toLong()))
|
||||||
|
val userId = ChatId(RawChatId(userIdString.toLong()))
|
||||||
|
val chatMember = getChatMember(channelId, userId)
|
||||||
|
val asAdmin = chatMember.administratorChatMemberOrNull()
|
||||||
|
|
||||||
|
val realData = it.data.takeWhile { it != ' ' }
|
||||||
|
|
||||||
|
fun Boolean?.toggleIfData(data: String) = if (realData == data) {
|
||||||
|
!(this ?: false)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realData != refreshAdminRightsData) {
|
||||||
|
promoteChannelAdministrator(
|
||||||
|
channelId,
|
||||||
|
userId,
|
||||||
|
canPostMessages = asAdmin ?.canPostMessages.toggleIfData(postMessagesToggleAdminRightsData),
|
||||||
|
canEditMessages = asAdmin ?.canEditMessages.toggleIfData(editMessagesToggleAdminRightsData),
|
||||||
|
canDeleteMessages = asAdmin ?.canRemoveMessages.toggleIfData(deleteMessagesToggleAdminRightsData),
|
||||||
|
canEditStories = asAdmin ?.canEditStories.toggleIfData(editStoriesToggleAdminRightsData),
|
||||||
|
canDeleteStories = asAdmin ?.canDeleteStories.toggleIfData(deleteStoriesToggleAdminRightsData),
|
||||||
|
canPostStories = asAdmin ?.canPostStories.toggleIfData(postStoriesToggleAdminRightsData),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(
|
||||||
|
it.message,
|
||||||
|
replyMarkup = buildAdminRightsKeyboard(
|
||||||
|
getChatMember(
|
||||||
|
channelId,
|
||||||
|
userId
|
||||||
|
).administratorChatMemberOrNull(),
|
||||||
|
channelId,
|
||||||
|
userId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
strictlyOn<UserRetrievingStep.RetrievingChannelChatState> { state ->
|
||||||
|
val requestId = RequestId.random()
|
||||||
|
send(
|
||||||
|
state.context,
|
||||||
|
replyMarkup = replyKeyboard(
|
||||||
|
oneTimeKeyboard = true,
|
||||||
|
resizeKeyboard = true
|
||||||
|
) {
|
||||||
|
row {
|
||||||
|
requestChatButton(
|
||||||
|
"Choose channel",
|
||||||
|
requestId = requestId,
|
||||||
|
isChannel = true,
|
||||||
|
botIsMember = true,
|
||||||
|
botRightsInChat = ChatCommonAdministratorRights(
|
||||||
|
canPromoteMembers = true,
|
||||||
|
canRestrictMembers = true
|
||||||
|
),
|
||||||
|
userRightsInChat = ChatCommonAdministratorRights(
|
||||||
|
canPromoteMembers = true,
|
||||||
|
canRestrictMembers = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("Ok, send me the channel in which you wish to manage user, or use ")
|
||||||
|
botCommand("cancel")
|
||||||
|
regular(" to cancel the request")
|
||||||
|
}
|
||||||
|
firstOf {
|
||||||
|
include {
|
||||||
|
val chatId = waitChatSharedEventsMessages().mapNotNull {
|
||||||
|
it.chatEvent.chatId.takeIf { _ ->
|
||||||
|
it.chatEvent.requestId == requestId && it.sameChat(state.context)
|
||||||
|
}
|
||||||
|
}.first()
|
||||||
|
UserRetrievingStep.RetrievingUserIdChatState(state.context, chatId)
|
||||||
|
}
|
||||||
|
include {
|
||||||
|
waitCommandMessage("cancel").filter { it.sameChat(state.context) }.first()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strictlyOn<UserRetrievingStep.RetrievingUserIdChatState> { state ->
|
||||||
|
val requestId = RequestId.random()
|
||||||
|
send(
|
||||||
|
state.context,
|
||||||
|
replyMarkup = replyKeyboard(
|
||||||
|
oneTimeKeyboard = true,
|
||||||
|
resizeKeyboard = true
|
||||||
|
) {
|
||||||
|
row {
|
||||||
|
requestUserButton(
|
||||||
|
"Choose user",
|
||||||
|
requestId = requestId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
regular("Ok, send me the user for which you wish to change rights, or use ")
|
||||||
|
botCommand("cancel")
|
||||||
|
regular(" to cancel the request")
|
||||||
|
}
|
||||||
|
|
||||||
|
firstOf {
|
||||||
|
include {
|
||||||
|
val userContactChatId = waitUserSharedEventsMessages().filter {
|
||||||
|
it.sameChat(state.context)
|
||||||
|
}.first().chatEvent.chatId
|
||||||
|
UserRetrievingStep.RetrievingChatInfoDoneState(
|
||||||
|
state.context,
|
||||||
|
state.channelId,
|
||||||
|
userContactChatId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
include {
|
||||||
|
waitCommandMessage("cancel").filter { it.sameChat(state.context) }.first()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strictlyOn<UserRetrievingStep.RetrievingChatInfoDoneState> { state ->
|
||||||
|
val chatMember = getChatMember(state.channelId, state.userId).administratorChatMemberOrNull()
|
||||||
|
if (chatMember == null) {
|
||||||
|
return@strictlyOn null
|
||||||
|
}
|
||||||
|
send(
|
||||||
|
state.context,
|
||||||
|
replyMarkup = buildAdminRightsKeyboard(
|
||||||
|
chatMember,
|
||||||
|
state.channelId,
|
||||||
|
state.userId
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
regular("Rights of ")
|
||||||
|
mentionln(chatMember.user)
|
||||||
|
regular("Please, remember, that to be able to change user rights bot must promote user by itself to admin")
|
||||||
|
}
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommand("rights_in_channel") {
|
||||||
|
startChain(UserRetrievingStep.RetrievingChannelChatState(it.chat.id.toChatId()))
|
||||||
|
}
|
||||||
|
|
||||||
|
setMyCommands(
|
||||||
|
BotCommand("simple", "Trigger simple keyboard. Use with reply to user"),
|
||||||
|
BotCommand("granular", "Trigger granular keyboard. Use with reply to user"),
|
||||||
|
BotCommand("rights_in_channel", "Trigger granular keyboard. Use with reply to user"),
|
||||||
|
scope = BotCommandScope.AllGroupChats
|
||||||
|
)
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -1,35 +1,26 @@
|
|||||||
import dev.inmo.micro_utils.coroutines.safely
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
import dev.inmo.tgbotapi.bot.Ktor.telegramBot
|
|
||||||
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.triggers_handling.onDice
|
||||||
import dev.inmo.tgbotapi.extensions.utils.*
|
import dev.inmo.tgbotapi.extensions.utils.*
|
||||||
import dev.inmo.tgbotapi.extensions.utils.shortcuts.filterContentMessages
|
|
||||||
import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling
|
|
||||||
import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType
|
import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType
|
||||||
import dev.inmo.tgbotapi.types.message.content.DiceContent
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
|
|
||||||
suspend fun main(args: Array<String>) {
|
suspend fun main(args: Array<String>) {
|
||||||
val bot = telegramBot(args.first())
|
val bot = telegramBot(args.first())
|
||||||
|
|
||||||
val scope = CoroutineScope(Dispatchers.Default)
|
bot.buildBehaviourWithLongPolling(scope = CoroutineScope(Dispatchers.IO)) {
|
||||||
bot.longPolling(scope = scope) {
|
onDice {
|
||||||
filterContentMessages<DiceContent>(scope).onEach {
|
|
||||||
val content = it.content
|
val content = it.content
|
||||||
val dice = content.dice
|
val dice = content.dice
|
||||||
val diceType = dice.animationType
|
val diceType = dice.animationType
|
||||||
|
|
||||||
safely ({ it.printStackTrace() }) {
|
if (diceType == SlotMachineDiceAnimationType) {
|
||||||
if (diceType == SlotMachineDiceAnimationType) {
|
val result = dice.calculateSlotMachineResult() ?: return@onDice
|
||||||
val result = dice.calculateSlotMachineResult() ?: return@safely
|
reply(it, "${result.leftReel}|${result.centerReel}|${result.rightReel}")
|
||||||
bot.reply(it, "${result.leftReel}|${result.centerReel}|${result.rightReel}")
|
} else {
|
||||||
} else {
|
reply(it, "There is no slot machine dice in message")
|
||||||
bot.reply(it, "There is no slot machine dice in message")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.launchIn(scope)
|
}
|
||||||
}
|
}.join()
|
||||||
|
}
|
||||||
scope.coroutineContext[Job]!!.join()
|
|
||||||
}
|
|
||||||
|
|||||||
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()
|
||||||
|
}
|
||||||
33
StickerInfoBot/StickerInfoBotLib/build.gradle
Normal file
33
StickerInfoBot/StickerInfoBotLib/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
// js(LEGACY) {
|
||||||
|
js(IR) {
|
||||||
|
browser()
|
||||||
|
binaries.executable()
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
|
||||||
|
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
|
||||||
|
import dev.inmo.micro_utils.coroutines.subscribeLoggingDropExceptions
|
||||||
|
import dev.inmo.tgbotapi.bot.ktor.telegramBot
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getCustomEmojiStickerOrNull
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSetOrNull
|
||||||
|
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.message.textsources.CustomEmojiTextSource
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.regularTextSource
|
||||||
|
import dev.inmo.tgbotapi.types.message.textsources.separateForText
|
||||||
|
import dev.inmo.tgbotapi.types.stickers.StickerSet
|
||||||
|
import dev.inmo.tgbotapi.utils.bold
|
||||||
|
import dev.inmo.tgbotapi.utils.buildEntities
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
|
||||||
|
fun StickerSet?.buildInfo() = buildEntities {
|
||||||
|
if (this@buildInfo == null) {
|
||||||
|
bold("Looks like this stickerset has been removed")
|
||||||
|
} else {
|
||||||
|
bold("StickerSet name: ") + "${name}\n"
|
||||||
|
bold("StickerSet title: ") + "${title}\n"
|
||||||
|
bold(
|
||||||
|
when (stickerType) {
|
||||||
|
StickerType.CustomEmoji -> "Custom emoji"
|
||||||
|
StickerType.Mask -> "Mask"
|
||||||
|
StickerType.Regular -> "Regular"
|
||||||
|
is StickerType.Unknown -> "Unknown type \"${stickerType.type}\""
|
||||||
|
}
|
||||||
|
) + " sticker set with title " + bold(title) + " and name " + bold(name.string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun activateStickerInfoBot(
|
||||||
|
token: String,
|
||||||
|
print: (Any) -> Unit
|
||||||
|
) {
|
||||||
|
val bot = telegramBot(token)
|
||||||
|
|
||||||
|
print(bot.getMe())
|
||||||
|
|
||||||
|
defaultSafelyWithoutExceptionHandler = {
|
||||||
|
it.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
|
||||||
|
onText {
|
||||||
|
withTypingAction(it.chat) {
|
||||||
|
it.content.textSources.mapNotNull {
|
||||||
|
if (it is CustomEmojiTextSource) {
|
||||||
|
getCustomEmojiStickerOrNull(it.customEmojiId)?.stickerSetName
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}.distinct().map {
|
||||||
|
getStickerSet(it)
|
||||||
|
}.distinct().flatMap {
|
||||||
|
it.buildInfo() + regularTextSource("\n")
|
||||||
|
}.separateForText().map { entities ->
|
||||||
|
reply(it, entities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSticker {
|
||||||
|
val stickerSetInfo = getStickerSetOrNull(it.content.media)
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
stickerSetInfo.buildInfo()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeLoggingDropExceptions(scope = this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import kotlinx.browser.document
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import org.w3c.dom.*
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
document.addEventListener(
|
||||||
|
"DOMContentLoaded",
|
||||||
|
{
|
||||||
|
val botsContainer = document.getElementById("bots_container") ?: return@addEventListener
|
||||||
|
|
||||||
|
(document.getElementById("bot_token_form") as? HTMLFormElement) ?.onsubmit = {
|
||||||
|
(document.getElementById("bot_token") as? HTMLInputElement) ?.value ?.let { token ->
|
||||||
|
val botContainer = document.createElement("div") as HTMLDivElement
|
||||||
|
botsContainer.append(botContainer)
|
||||||
|
|
||||||
|
val infoDiv = document.createElement("div") as HTMLDivElement
|
||||||
|
botContainer.append(infoDiv)
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
activateStickerInfoBot(token) {
|
||||||
|
infoDiv.innerHTML = it.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Resender bot</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="bot_token_form">
|
||||||
|
<input type="text" id="bot_token">
|
||||||
|
<input type="submit" value="Start bot">
|
||||||
|
</form>
|
||||||
|
<div id="start_offer">Type your bot token to the input above to start its work</div>
|
||||||
|
<script type="text/javascript" src="StickerInfoBotLib.js"></script>
|
||||||
|
<div id="bots_container"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21
StickerInfoBot/jvm_launcher/build.gradle
Normal file
21
StickerInfoBot/jvm_launcher/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="StickerInfoBotJvmKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation project(":StickerInfoBot:StickerInfoBotLib")
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
activateStickerInfoBot(args.first()) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
StickerSetHandler/README.md
Normal file
9
StickerSetHandler/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
StickerSetHandler/build.gradle
Normal file
21
StickerSetHandler/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="StickerSetHandlerBotKt"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
|
|
||||||
|
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||||
|
}
|
||||||
118
StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt
Normal file
118
StickerSetHandler/src/main/kotlin/StickerSetHandlerBot.kt
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
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.files.downloadFileToTemp
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.get.getStickerSet
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.stickers.addStickerToSet
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.stickers.createNewStickerSet
|
||||||
|
import dev.inmo.tgbotapi.extensions.api.stickers.deleteStickerSet
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
||||||
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onSticker
|
||||||
|
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
|
||||||
|
import dev.inmo.tgbotapi.requests.stickers.InputSticker
|
||||||
|
import dev.inmo.tgbotapi.types.StickerSetName
|
||||||
|
import dev.inmo.tgbotapi.types.chat.Chat
|
||||||
|
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.utils.botCommand
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send sticker to this bot to form your own stickers set. Send /delete to delete this sticker set
|
||||||
|
*/
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
telegramBotWithBehaviourAndLongPolling(
|
||||||
|
args.first(),
|
||||||
|
scope = CoroutineScope(Dispatchers.IO),
|
||||||
|
defaultExceptionsHandler = {
|
||||||
|
it.printStackTrace()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
val me = getMe()
|
||||||
|
fun Chat.stickerSetName() = StickerSetName("s${id.chatId}_by_${me.username ?.withoutAt}")
|
||||||
|
onCommand("start") {
|
||||||
|
reply(it) {
|
||||||
|
botCommand("delete") + " - to clear stickers"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onCommand("delete") {
|
||||||
|
val deleted = runCatchingSafely {
|
||||||
|
deleteStickerSet(it.chat.stickerSetName())
|
||||||
|
}.getOrElse { false }
|
||||||
|
|
||||||
|
if (deleted) {
|
||||||
|
reply(it, "Deleted")
|
||||||
|
} else {
|
||||||
|
reply(it, "Can't delete for some of reason")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSticker {
|
||||||
|
val stickerSetName = it.chat.stickerSetName()
|
||||||
|
val sticker = it.content.media
|
||||||
|
val newSticker = when (sticker) {
|
||||||
|
is CustomEmojiSticker -> InputSticker.WithKeywords.CustomEmoji(
|
||||||
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
|
emptyList()
|
||||||
|
)
|
||||||
|
is MaskSticker -> InputSticker.Mask(
|
||||||
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
|
sticker.maskPosition
|
||||||
|
)
|
||||||
|
is RegularSticker -> InputSticker.WithKeywords.Regular(
|
||||||
|
downloadFileToTemp(sticker.fileId).asMultipartFile(),
|
||||||
|
sticker.stickerFormat,
|
||||||
|
listOf(sticker.emoji ?: "\uD83D\uDE0A"),
|
||||||
|
emptyList()
|
||||||
|
)
|
||||||
|
is UnknownSticker -> return@onSticker
|
||||||
|
}
|
||||||
|
runCatchingSafely {
|
||||||
|
getStickerSet(stickerSetName)
|
||||||
|
}.onSuccess { stickerSet ->
|
||||||
|
runCatching {
|
||||||
|
addStickerToSet(it.chat.id.toChatId(), stickerSet.name, newSticker).also { _ ->
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
getStickerSet(stickerSetName).stickers.last()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.onFailure { exception ->
|
||||||
|
exception.printStackTrace()
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
"Unable to add sticker in stickerset"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.onFailure { exception ->
|
||||||
|
createNewStickerSet(
|
||||||
|
it.chat.id.toChatId(),
|
||||||
|
stickerSetName.string,
|
||||||
|
"Sticker set by ${me.firstName}",
|
||||||
|
listOf(
|
||||||
|
newSticker
|
||||||
|
),
|
||||||
|
(sticker as? CustomEmojiSticker) ?.needsRepainting ?: false
|
||||||
|
).also { _ ->
|
||||||
|
reply(
|
||||||
|
it,
|
||||||
|
getStickerSet(stickerSetName).stickers.first()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
||||||
|
println(it)
|
||||||
|
}
|
||||||
|
}.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"
|
||||||
|
```
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user