Compare commits

..

259 Commits

Author SHA1 Message Date
renovate[bot]
ddd5af4c3f Merge 537c781716 into a008d861da 2024-01-31 14:05:16 +00:00
renovate[bot]
537c781716 Update dependency dev.inmo:micro_utils.ktor.server to v0.20.31 2024-01-31 14:05:12 +00:00
a008d861da Merge pull request #248 from InsanusMokrassar/10.0.0
10.0.0
2024-01-12 14:57:51 +06:00
6f3766dff6 fixes in samples and update up to 10.0.0 2024-01-12 14:23:27 +06:00
fda366d820 add boosts sample 2024-01-12 00:49:59 +06:00
578887ac63 update userChatShared bot 2024-01-12 00:29:48 +06:00
6a04b3980c improvements in users requests 2024-01-10 23:10:28 +06:00
984ffb8bae update dependencies 2024-01-10 16:47:31 +06:00
2bcec6487d fixes 2024-01-09 18:30:54 +06:00
a5e3bfc3fe update dependency of tgbotapi 2024-01-09 18:05:30 +06:00
941afd0902 update LinkPreviewsBot 2024-01-09 17:57:49 +06:00
94c014b308 add LinkPreviewsBot 2024-01-09 17:56:33 +06:00
538cc9d44f improvement of ReactionsInfoBot 2024-01-09 13:55:35 +06:00
cb29726487 add showing of reactions count in println 2024-01-09 13:06:18 +06:00
262ef26239 updates and fixes 2024-01-08 19:23:06 +06:00
41efe5e141 update tgbotapi version 2024-01-08 15:55:05 +06:00
05e289975a add reactions info bot 2024-01-08 15:46:23 +06:00
753d686fab build fixes 2024-01-08 13:21:41 +06:00
281243c7e5 update dependencies 2024-01-08 10:14:08 +06:00
3609ae6bc2 Merge pull request #240 from InsanusMokrassar/renovate/serialization_version
Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.2
2023-12-25 09:00:04 +06:00
4f128f3421 Merge pull request #239 from InsanusMokrassar/renovate/io.ktor-ktor-client-logging-jvm-2.x
Update dependency io.ktor:ktor-client-logging-jvm to v2.3.7
2023-12-25 08:59:47 +06:00
ada6cd61d7 Merge pull request #247 from InsanusMokrassar/renovate/kotlin-monorepo
Update kotlin monorepo to v1.9.22
2023-12-25 08:59:28 +06:00
renovate[bot]
051d647004 Update kotlin monorepo to v1.9.22 2023-12-21 14:11:32 +00:00
renovate[bot]
d21606860a Update dependency io.ktor:ktor-client-logging-jvm to v2.3.7 2023-12-07 12:54:06 +00:00
renovate[bot]
93c0fcb5bd Update dependency org.jetbrains.kotlinx:kotlinx-serialization-json to v1.6.2 2023-11-30 16:56:11 +00:00
b1b8d0eb75 Merge pull request #243 from InsanusMokrassar/9.4.0
9.4.0
2023-11-26 18:17:08 +06:00
2ac23f70ab Update gradle.properties 2023-11-26 15:28:56 +06:00
e155373655 small improvement in GetMe 2023-11-25 12:57:11 +06:00
d842dab5b8 update dependencies 2023-11-25 12:44:05 +06:00
7186d5e624 Merge pull request #215 from InsanusMokrassar/renovate/io.ktor-ktor-client-logging-jvm-2.x
Update dependency io.ktor:ktor-client-logging-jvm to v2.3.5
2023-11-06 13:27:49 +06:00
renovate[bot]
8fefb17599 Update dependency io.ktor:ktor-client-logging-jvm to v2.3.5 2023-11-06 07:23:57 +00:00
bcf4ae5888 Merge pull request #237 from InsanusMokrassar/9.3.0
9.3.0
2023-11-06 13:23:44 +06:00
7090db148e Update gradle.properties 2023-11-05 13:53:10 +06:00
7d786f0e06 improvements 2023-11-05 12:33:45 +06:00
c88f84011f Update build.yml 2023-11-05 03:41:21 +06:00
b8cc8854ea Update gradle-wrapper.properties 2023-11-05 02:43:40 +06:00
13470999e8 Update gradle.properties 2023-11-05 02:43:13 +06:00
af04a854ef fixes 2023-10-25 15:33:05 +06:00
44e86c9349 small fixes in ResenderBot Lib 2023-10-21 00:50:03 +06:00
65c32d97d5 update native buildings configuration 2023-10-21 00:28:12 +06:00
9b7605591e update dependencies to work with linux arm 64 2023-10-20 22:50:36 +06:00
89d5a4f911 add support of arch target 2023-10-20 22:29:59 +06:00
53cf212175 fixes 2023-10-17 23:28:13 +06:00
28301a92c9 update webapp sample 2023-10-17 23:23:24 +06:00
f814b11777 update up to tgbotapi 9.3.0 2023-10-15 23:29:02 +06:00
9773a74890 update ktgbotapi version 9.2.2 2023-10-11 15:21:44 +06:00
a81cfaaba9 Merge pull request #234 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v9.2.1
2023-09-30 07:26:47 +06:00
renovate[bot]
ee599611f3 Update telegram_bot_api_version to v9.2.1 2023-09-29 19:24:07 +00:00
d3d6cd16c6 Merge pull request #235 from InsanusMokrassar/9.2.0
Migration onto 9.2.0
2023-09-28 19:37:41 +06:00
02c3d3da1a update webapp sample to use cloud storage 2023-09-25 23:20:48 +06:00
0ad8e61c0c one more improvement 2023-09-25 16:30:17 +06:00
8f80b7e066 small fixes and improvements 2023-09-25 16:28:49 +06:00
48d1077ce4 update webapp 2023-09-25 16:20:25 +06:00
6922a6d667 fixes in rights bot 2023-09-25 14:55:37 +06:00
676ce0df80 start adding channels rights changer 2023-09-25 00:19:34 +06:00
d97c2a0562 start migration onto 9.2.0 2023-09-23 01:51:04 +06:00
35e0cb4a46 update tgbotapi 2023-09-07 22:41:02 +06:00
30f5513f54 Merge pull request #230 from InsanusMokrassar/9.1.0
Update up to 9.1.0
2023-08-20 14:31:47 +06:00
fff8edde5f update PollsBot 2023-08-20 14:30:55 +06:00
e28a795796 Update PollsBot.kt 2023-08-20 11:38:56 +06:00
d289c2101d add polls sample bot 2023-08-20 02:31:23 +06:00
2ce47074d8 update dependencies 2023-08-19 18:32:43 +06:00
281f0840eb Merge pull request #224 from InsanusMokrassar/9.0.0
9.0.0
2023-07-01 16:26:53 +06:00
34ed962104 Update gradle-wrapper.properties 2023-07-01 16:26:45 +06:00
aa3337bf3a update tgbotapi version 2023-07-01 13:52:48 +06:00
31d29712be small improvements 2023-07-01 03:54:42 +06:00
88b348376f add local folders and files into gitignore 2023-06-30 22:53:12 +06:00
0d9e295baa start migration onto 9.0.0 2023-06-30 17:49:19 +06:00
ea08bac6e8 Merge pull request #214 from InsanusMokrassar/8.0.0
8.0.0
2023-06-09 01:54:39 +06:00
a85fdc227e Update gradle.properties 2023-06-08 22:47:48 +06:00
43482ee94e start 8.0.0 2023-05-28 21:22:40 +06:00
4addb6c755 Merge pull request #212 from InsanusMokrassar/7.1.3
7.1.3
2023-05-20 22:13:25 +06:00
7d958b6edb Update gradle.properties 2023-05-20 22:12:52 +06:00
323c21f415 upgrade of hello bot 2023-05-19 22:45:46 +06:00
6350581739 Merge pull request #209 from InsanusMokrassar/7.1.2
7.1.2
2023-05-06 15:38:14 +06:00
ea1d40fd05 downgrade microutils 2023-05-06 13:27:34 +06:00
8cee63a0fb update dependencies 2023-05-06 13:13:38 +06:00
d42ef2c6cb Merge pull request #208 from InsanusMokrassar/7.1.1
7.1.1
2023-05-04 08:42:52 +06:00
c7fe90ddd7 update dependencies 2023-05-01 02:16:20 +06:00
acb382d3f7 Merge pull request #206 from InsanusMokrassar/7.1.0
7.1.0
2023-04-22 20:31:55 +06:00
0cfe60fd77 Update gradle.properties 2023-04-22 20:31:41 +06:00
6719b9e17c Update Bot.kt 2023-04-22 16:39:59 +06:00
8d33dc0ab2 Update README.md 2023-04-22 16:36:14 +06:00
3e2ccf9cf1 add answerInlineQuery with WebAppInfo 2023-04-22 11:06:03 +06:00
eccbe71e68 finish checking update 2023-04-22 00:17:39 +06:00
24c74f3b1a in keyboards bot add sample with sending of inline query 2023-04-22 00:11:56 +06:00
d7a7e7153e add inline queries sample 2023-04-21 23:21:15 +06:00
0b37acb7a9 Merge pull request #202 from InsanusMokrassar/7.0.2
7.0.2
2023-04-20 03:55:59 +06:00
3925ef9423 add opportunity to set port different with 8080 in WebAppServer sample 2023-04-19 21:46:35 +06:00
c6019b1862 complete sample with native 2023-04-19 20:20:11 +06:00
7b996fe1de rollback ktor version in native sample 2023-04-18 11:31:28 +06:00
4e3c186952 update dependencies 2023-04-18 03:27:56 +06:00
8fdf715419 Update build.yml 2023-04-04 09:57:28 +06:00
fca8704cec Update build.yml 2023-04-04 09:56:41 +06:00
bf499ee780 Update gradle.properties 2023-04-04 01:45:15 +06:00
9b10749411 update dependencies and other attributes 2023-04-04 01:02:18 +06:00
d3cb8a32ef add partially working native sample 2023-04-04 01:01:22 +06:00
0f0ad5a1af Update gradle.properties 2023-03-18 19:24:40 +06:00
c3bc55a15c Merge pull request #200 from InsanusMokrassar/7.0.0
7.0.0
2023-03-11 23:18:32 +06:00
253328f49a complete sample with sticker set handler example 2023-03-11 21:39:08 +06:00
8ef50537ae some functionality of sticker set handler bot 2023-03-11 15:55:09 +06:00
7e7bbfaa93 update version of telegram bot api and start including of stickers sets handling bot 2023-03-11 15:24:18 +06:00
f152ede9b5 improve sticker info bot 2023-03-11 01:00:31 +06:00
fcedbf30da Merge pull request #186 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.17.3
2023-03-08 08:40:26 +06:00
renovate[bot]
fd030a92e3 Update dependency dev.inmo:micro_utils.ktor.server to v0.17.3 2023-03-07 20:44:36 +00:00
d54abf0b32 Merge pull request #196 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v6.0.3
2023-03-03 10:56:54 +06:00
renovate[bot]
877a20188f Update telegram_bot_api_version to v6.0.3 2023-03-02 21:17:02 +00:00
d0151ff048 Update gradle.properties 2023-02-28 22:57:26 +06:00
f8f517cfbb Merge pull request #195 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.2.4
2023-02-28 22:57:06 +06:00
renovate[bot]
b3cbbac917 Update dependency io.ktor:ktor-server-cio to v2.2.4 2023-02-28 13:44:59 +00:00
3e85bb4b22 Merge pull request #194 from InsanusMokrassar/6.0.0
6.0.0
2023-02-28 14:05:54 +06:00
4e0fb1c137 update dependencies 2023-02-27 22:39:17 +06:00
d8c90ef377 check and a little update 2023-02-27 22:38:05 +06:00
cb84fd0884 update dependencies 2023-02-27 20:36:03 +06:00
4379862c78 Merge pull request #188 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v5.2.1
2023-02-22 19:06:01 +06:00
renovate[bot]
ac1d812db0 Update telegram_bot_api_version to v5.2.1 2023-02-21 17:43:27 +00:00
f52590868c fix according to update of tgbotapi version 2023-02-17 16:04:32 +06:00
51c300c734 add LiveLocationsBot sample and update dependencies 2023-02-17 15:58:25 +06:00
f6082cff30 Merge pull request #189 from InsanusMokrassar/5.1.0
5.1.0
2023-02-06 15:47:11 +06:00
a40c16fe05 improvements and fixes 2023-02-06 14:07:44 +06:00
a7fe62f4af fix readmes 2023-02-06 12:11:02 +06:00
b9c745a21e add RightsChanger bot 2023-02-06 12:08:25 +06:00
1c2b068a94 start rights checker 2023-02-06 08:42:20 +06:00
51c2cb1b0e complete sample with request buttons 2023-02-05 23:43:27 +06:00
cfd4e2fcd5 Merge pull request #185 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v5.0.1
2023-01-19 00:29:38 +06:00
renovate[bot]
76ceeac757 Update telegram_bot_api_version to v5.0.1 2023-01-18 18:28:56 +00:00
340de11b0a Merge pull request #179 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.16.6
2023-01-19 00:28:23 +06:00
68a59ca5c8 Merge pull request #184 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.2.2
2023-01-19 00:27:56 +06:00
renovate[bot]
cdb8581318 Update dependency dev.inmo:micro_utils.ktor.server to v0.16.6 2023-01-18 16:58:56 +00:00
renovate[bot]
8b3a2ac1ed Update dependency io.ktor:ktor-server-cio to v2.2.2 2023-01-03 18:54:03 +00:00
a5b925fc59 Merge pull request #182 from InsanusMokrassar/0.5.0
0.5.0
2023-01-01 22:53:07 +06:00
9ec9f7a68c Update gradle.properties 2022-12-31 16:29:22 +06:00
0f2829945f add topics example 2022-12-31 15:45:48 +06:00
4eb80ea53c start 0.5.0 2022-12-30 22:12:12 +06:00
17cff21847 Update gradle.properties 2022-12-18 10:25:51 +06:00
431069d190 Merge pull request #178 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.16.2
2022-12-18 10:24:54 +06:00
renovate[bot]
fdbac78603 Update dependency dev.inmo:micro_utils.ktor.server to v0.16.2 2022-12-16 09:24:13 +00:00
da73acd379 Merge pull request #175 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.16.1
2022-12-13 09:00:47 +06:00
renovate[bot]
6e3880f152 Update dependency dev.inmo:micro_utils.ktor.server to v0.16.1 2022-12-09 14:50:53 +00:00
1ede6e58e6 Update gradle.properties 2022-12-08 11:27:03 +06:00
0e46f176fb Merge pull request #169 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v4.2.0
2022-12-05 12:29:53 +06:00
renovate[bot]
2bd449b8b8 Update telegram_bot_api_version to v4.2.0 2022-12-05 06:28:52 +00:00
82f9da0529 Merge pull request #172 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.15.0
2022-12-05 12:06:41 +06:00
78b7d468f2 Merge pull request #174 from InsanusMokrassar/renovate/kotlin-monorepo
Update kotlin monorepo to v1.7.22
2022-12-05 12:06:26 +06:00
renovate[bot]
08059f8174 Update dependency dev.inmo:micro_utils.ktor.server to v0.15.0 2022-12-04 17:47:48 +00:00
renovate[bot]
16766046d7 Update kotlin monorepo to v1.7.22 2022-11-28 14:43:44 +00:00
91ea20a269 Merge pull request #171 from InsanusMokrassar/4.1.3
4.1.3
2022-11-28 18:19:45 +06:00
11e280d177 Update gradle-wrapper.properties 2022-11-28 18:16:41 +06:00
a8d4a307ef Update gradle.properties 2022-11-28 16:32:46 +06:00
2bd2328a38 temporal update of version 2022-11-18 23:10:24 +06:00
139de35db9 Merge pull request #170 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.14.2
2022-11-15 21:32:13 +06:00
renovate[bot]
5dd22e1da2 Update dependency dev.inmo:micro_utils.ktor.server to v0.14.2 2022-11-15 07:32:25 +00:00
4186ab8270 Merge pull request #168 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v4.1.0
2022-11-11 00:41:58 +06:00
5aa69d7990 Update SimpleFSMBot.kt 2022-11-11 00:35:16 +06:00
renovate[bot]
df952c69b2 Update telegram_bot_api_version to v4.1.0 2022-11-10 18:28:56 +00:00
9a03a02bac Merge pull request #166 from InsanusMokrassar/renovate/kotlin-monorepo
Update kotlin monorepo to v1.7.21
2022-11-11 00:28:38 +06:00
0e7c050e9e Merge pull request #164 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.14.1
2022-11-11 00:27:53 +06:00
renovate[bot]
bdec902b58 Update dependency dev.inmo:micro_utils.ktor.server to v0.14.1 2022-11-10 14:27:54 +00:00
renovate[bot]
cc3c87590d Update kotlin monorepo to v1.7.21 2022-11-09 10:48:11 +00:00
910f892b89 Merge pull request #165 from InsanusMokrassar/4.0.0
4.0.0
2022-11-09 13:40:37 +06:00
8232cb4d62 updates 2022-11-08 17:49:41 +06:00
3b26971152 fixes 2022-11-08 17:00:53 +06:00
c0019bcbf8 update up to 4.0.0 2022-11-08 12:19:23 +06:00
c3dcb4d738 Merge pull request #163 from InsanusMokrassar/3.3.1
3.3.1
2022-11-01 16:49:56 +06:00
6a9921d4bd Update gradle.properties 2022-11-01 16:45:05 +06:00
33b14f320c update up to 3.3.1 2022-10-30 19:57:11 +06:00
50ad281132 Merge pull request #160 from InsanusMokrassar/3.3.0
3.3.0
2022-10-23 11:41:44 +06:00
6abfc3d369 3.3.0 2022-10-22 17:48:35 +06:00
f73620afec Merge pull request #154 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.12.17
2022-10-06 11:56:00 +06:00
a3d5112c83 Merge pull request #156 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.1.2
2022-10-06 11:55:26 +06:00
renovate[bot]
23abae07b7 Update dependency dev.inmo:micro_utils.ktor.server to v0.12.17 2022-10-06 05:55:14 +00:00
c5a9f657cf Merge pull request #157 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v3.2.7
2022-10-06 11:54:59 +06:00
renovate[bot]
51f7715915 Update telegram_bot_api_version to v3.2.7 2022-10-01 20:54:04 +00:00
renovate[bot]
bdfb900ce3 Update dependency io.ktor:ktor-server-cio to v2.1.2 2022-09-30 09:29:21 +00:00
27790a2576 Merge pull request #153 from InsanusMokrassar/3.2.6
3.2.6
2022-09-19 16:03:13 +06:00
e722055871 several small improvements 2022-09-19 14:58:43 +06:00
29e1552618 migration 2022-09-19 14:37:01 +06:00
7ef942a2b4 start migration onto 3.2.6 2022-09-19 11:19:21 +06:00
fdd4dfdbcf Merge pull request #151 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v3.2.4
2022-09-17 09:36:13 +06:00
renovate[bot]
ba88205249 Update telegram_bot_api_version to v3.2.4 2022-09-16 20:14:33 +00:00
9a38fe51f9 Merge pull request #150 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v3.2.3
2022-09-15 19:32:43 +06:00
renovate[bot]
c172bd1fa7 Update telegram_bot_api_version to v3.2.3 2022-09-15 12:48:34 +00:00
7392e85f1b Merge pull request #149 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.12.13
2022-09-15 10:07:05 +06:00
renovate[bot]
f2c7fd79d9 Update dependency dev.inmo:micro_utils.ktor.server to v0.12.13 2022-09-14 22:47:35 +00:00
9d77b61ea3 Merge pull request #148 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.12.12
2022-09-14 10:53:26 +06:00
renovate[bot]
d567b1382b Update dependency dev.inmo:micro_utils.ktor.server to v0.12.12 2022-09-13 19:26:38 +00:00
557d4ad3ca Merge pull request #147 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.12.11
2022-09-11 14:15:31 +06:00
renovate[bot]
f070fb804b Update dependency dev.inmo:micro_utils.ktor.server to v0.12.11 2022-09-08 22:34:27 +00:00
53de520c3a Merge pull request #135 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.12.10
2022-09-08 23:45:47 +06:00
f164ba901d Merge pull request #145 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.1.1
2022-09-08 23:45:28 +06:00
renovate[bot]
2a04b4979d Update dependency dev.inmo:micro_utils.ktor.server to v0.12.10 2022-09-08 17:45:25 +00:00
4a0279c89e Merge pull request #146 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v3.2.1
2022-09-08 23:45:08 +06:00
renovate[bot]
fcef9f1f64 Update telegram_bot_api_version to v3.2.1 2022-09-08 17:10:59 +00:00
renovate[bot]
33e191816a Update dependency io.ktor:ktor-server-cio to v2.1.1 2022-09-06 16:51:02 +00:00
e0b707ceba Update gradle.properties 2022-09-04 02:02:04 +06:00
115c76a9d4 Merge pull request #143 from InsanusMokrassar/3.2.0
3.2.0
2022-08-26 18:41:39 +06:00
0ebfb3c44a small fixes 2022-08-26 16:35:36 +06:00
9fa3690db6 add DeepLinkBot 2022-08-26 12:21:24 +06:00
a8c5d403c6 Merge pull request #141 from InsanusMokrassar/3.1.1
3.1.1
2022-08-15 09:41:20 +06:00
0c5186a37d add toggle closing confirmation button 2022-08-15 01:28:59 +06:00
7e9968ced9 add work with alerts in webapp 2022-08-15 01:12:39 +06:00
f1e8ed88a8 Merge pull request #139 from InsanusMokrassar/3.1.0
3.1.0
2022-08-13 16:58:34 +06:00
068dc79ac8 migration onto 3.1.0 and including new example 2022-08-13 14:39:24 +06:00
36273adfd1 Merge pull request #136 from InsanusMokrassar/3.0.0
3.0.0
2022-08-06 08:33:15 +06:00
bca2ae905b Update gradle-wrapper.properties 2022-08-06 08:19:13 +06:00
19a713a3e3 Update gradle.properties 2022-08-06 08:18:55 +06:00
e039f90961 updates according to 3.0.0 2022-08-05 19:45:52 +06:00
21692d16ca fixes in forward chat info bot 2022-08-04 22:32:36 +06:00
d547dce2ab update dependencies 2022-08-04 21:41:55 +06:00
9bfe88a79c Merge pull request #134 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v2.2.2
2022-07-31 21:48:05 +06:00
renovate[bot]
bfa327acf3 Update telegram_bot_api_version to v2.2.2 2022-07-31 15:35:38 +00:00
88a031b05a Update gradle.properties 2022-07-22 20:26:06 +06:00
20e942b2ac Merge pull request #133 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.13
2022-07-22 20:14:02 +06:00
renovate[bot]
3e20cbd22c Update dependency dev.inmo:micro_utils.ktor.server to v0.11.13 2022-07-22 14:10:56 +00:00
bcd35de038 Merge pull request #131 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v2.2.0
2022-07-11 12:05:16 +06:00
renovate[bot]
2aec45d453 Update telegram_bot_api_version to v2.2.0 2022-07-11 06:03:16 +00:00
ee55378e7a Update gradle.properties 2022-07-10 00:34:53 +06:00
b402c7b6e7 Merge pull request #130 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.12
2022-07-02 12:07:54 +06:00
renovate[bot]
32b7c7b9a4 Update dependency dev.inmo:micro_utils.ktor.server to v0.11.12 2022-07-02 06:02:52 +00:00
0b2d6b20de Merge pull request #129 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.10
2022-06-30 13:50:34 +06:00
renovate[bot]
182ee7a865 Update dependency dev.inmo:micro_utils.ktor.server to v0.11.10 2022-06-30 05:26:51 +00:00
0652a95d11 Merge pull request #128 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v2.1.2
2022-06-29 18:41:36 +06:00
renovate[bot]
f6066c60c0 Update telegram_bot_api_version to v2.1.2 2022-06-29 12:41:23 +00:00
2fa340292c Merge pull request #127 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.0.3
2022-06-29 18:41:04 +06:00
7d94007905 Merge pull request #126 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.9
2022-06-29 18:40:53 +06:00
renovate[bot]
9638174d48 Update dependency dev.inmo:micro_utils.ktor.server to v0.11.9 2022-06-29 11:53:40 +00:00
renovate[bot]
18bacaea2e Update dependency io.ktor:ktor-server-cio to v2.0.3 2022-06-28 16:35:40 +00:00
0f2b3760dd Merge pull request #125 from InsanusMokrassar/2.1.1
2.1.1
2022-06-26 15:24:30 +06:00
730923f55c actualization 2022-06-26 13:25:26 +06:00
9cf8bd9f28 migration onto 2.1.1 2022-06-26 13:03:52 +06:00
370fa45dba Merge pull request #124 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.6
2022-06-24 08:27:13 +06:00
renovate[bot]
72e7a73e40 Update dependency dev.inmo:micro_utils.ktor.server to v0.11.6 2022-06-23 23:54:36 +00:00
10dd9bd851 Merge pull request #123 from InsanusMokrassar/2.1.0
2.1.0
2022-06-22 00:43:36 +06:00
0217f97014 checked 2.1.0 and Bot API 6.1 + update several examples 2022-06-21 19:45:24 +06:00
4ae700b58a Merge pull request #121 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.3
2022-06-17 17:44:48 +06:00
renovate[bot]
804fb1e5ee Update dependency dev.inmo:micro_utils.ktor.server to v0.11.3 2022-06-17 11:44:38 +00:00
d443c39813 Merge pull request #122 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v2.0.3
2022-06-17 17:43:10 +06:00
renovate[bot]
cd96547d15 Update telegram_bot_api_version to v2.0.3 2022-06-17 11:42:07 +00:00
f107b4144d Update gradle.properties 2022-06-05 21:25:59 +06:00
39f4196b76 Merge pull request #119 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.11.0
2022-06-05 21:25:40 +06:00
Renovate Bot
22b9776c26 Update dependency dev.inmo:micro_utils.ktor.server to v0.11.0 2022-06-05 05:10:25 +00:00
2f8f6f3169 Merge pull request #118 from Akkihi/master
fixing example webapp mobile view with html meta
2022-06-04 16:49:52 +06:00
f2b6a50cb5 fixing example webapp mobile view with html meta 2022-06-04 14:46:28 +04:00
acd602d6fa Merge pull request #116 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.10.8
2022-06-03 10:33:33 +06:00
fdb80bf471 Merge pull request #115 from InsanusMokrassar/renovate/ktor_version
Update dependency io.ktor:ktor-server-cio to v2.0.2
2022-06-03 08:57:38 +06:00
Renovate Bot
78c58db4b7 Update dependency dev.inmo:micro_utils.ktor.server to v0.10.8 2022-06-03 02:57:34 +00:00
edd6399bcc Merge pull request #117 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v2.0.1
2022-06-03 08:56:53 +06:00
Renovate Bot
9513e77ba3 Update telegram_bot_api_version to v2.0.1 2022-05-29 20:26:33 +00:00
Renovate Bot
ac75f6487e Update dependency io.ktor:ktor-server-cio to v2.0.2 2022-05-27 18:22:47 +00:00
07750a71c0 Merge pull request #114 from InsanusMokrassar/2.0.0
2.0.0
2022-05-22 14:55:10 +06:00
0bf7e33df3 migration onto 2.0.0 and several improvements 2022-05-22 12:57:53 +06:00
7fb308ea4b Merge pull request #113 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v1.1.3
2022-05-19 14:57:05 -04:00
Renovate Bot
75b403ac98 Update telegram_bot_api_version to v1.1.3 2022-05-19 18:56:32 +00:00
5976e37046 Merge pull request #112 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.10.5
2022-05-19 14:56:10 -04:00
Renovate Bot
c8a94496c7 Update dependency dev.inmo:micro_utils.ktor.server to v0.10.5 2022-05-19 17:40:28 +00:00
f6550bd401 Merge pull request #110 from InsanusMokrassar/1.1.2
1.1.2
2022-05-18 10:24:09 -04:00
389d96f323 fix of checkWebAppData method call 2022-05-18 17:46:50 +06:00
803c5fd664 add handling of errors in FSMBot and update up to tgbotapi 1.1.2 2022-05-18 17:46:04 +06:00
b6eb4fe134 Merge pull request #108 from InsanusMokrassar/1.1.1
1.1.1
2022-05-17 16:05:35 -04:00
75 changed files with 2964 additions and 150 deletions

View File

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

3
.gitignore vendored
View File

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

9
BoostsInfoBot/README.md Normal file
View File

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

View 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"
}

View File

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

View File

@@ -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"
```

View 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"
}

View 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.extensions.utils.*
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import kotlinx.coroutines.*
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()
}

9
DeepLinksBot/README.md Normal file
View 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
View 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"
}

View File

@@ -0,0 +1,44 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelySkippingExceptions
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.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.*
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()
}

View File

@@ -1,33 +1,56 @@
import dev.inmo.micro_utils.coroutines.AccumulatorFlow import dev.inmo.micro_utils.coroutines.AccumulatorFlow
import dev.inmo.micro_utils.fsm.common.State import dev.inmo.micro_utils.fsm.common.State
import dev.inmo.tgbotapi.extensions.api.send.send
import dev.inmo.tgbotapi.extensions.api.send.sendMessage import dev.inmo.tgbotapi.extensions.api.send.sendMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.* import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams import dev.inmo.tgbotapi.extensions.utils.extensions.parseCommandsWithParams
import dev.inmo.tgbotapi.extensions.utils.extensions.sameThread
import dev.inmo.tgbotapi.extensions.utils.formatting.* import dev.inmo.tgbotapi.extensions.utils.formatting.*
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.botCommand
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
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")
}
}
e.printStackTrace()
state
}
) {
strictlyOn<ExpectContentOrStopState> {
send(
it.context,
) {
+"Send me some content or " + botCommand("stop") + " if you want to stop sending"
}
val contentMessage = waitContentMessage().first() val contentMessage = waitAnyContentMessage().filter { message ->
message.sameThread(it.sourceMessage)
}.first()
val content = contentMessage.content val content = contentMessage.content
when { when {
@@ -39,12 +62,14 @@ suspend fun main(args: Array<String>) {
} }
} }
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))
} }
}.second.join() }.second.join()

View File

@@ -1,9 +1,39 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.files.downloadFile import dev.inmo.tgbotapi.extensions.api.files.downloadFile
import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp
import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo import dev.inmo.tgbotapi.extensions.api.get.getFileAdditionalInfo
import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.replyWithAnimation
import dev.inmo.tgbotapi.extensions.api.send.replyWithAudio
import dev.inmo.tgbotapi.extensions.api.send.replyWithDocument
import dev.inmo.tgbotapi.extensions.api.send.replyWithMediaGroup
import dev.inmo.tgbotapi.extensions.api.send.replyWithPhoto
import dev.inmo.tgbotapi.extensions.api.send.replyWithSticker
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideo
import dev.inmo.tgbotapi.extensions.api.send.replyWithVideoNote
import dev.inmo.tgbotapi.extensions.api.send.replyWithVoice
import dev.inmo.tgbotapi.extensions.api.send.withAction
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.send.SendAction
import dev.inmo.tgbotapi.types.actions.BotAction
import dev.inmo.tgbotapi.types.actions.TypingAction
import dev.inmo.tgbotapi.types.media.TelegramMediaAudio
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.media.TelegramMediaPhoto
import dev.inmo.tgbotapi.types.media.TelegramMediaVideo
import dev.inmo.tgbotapi.types.message.content.AnimationContent
import dev.inmo.tgbotapi.types.message.content.AudioContent
import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import dev.inmo.tgbotapi.types.message.content.PhotoContent
import dev.inmo.tgbotapi.types.message.content.StickerContent
import dev.inmo.tgbotapi.types.message.content.VideoContent
import dev.inmo.tgbotapi.types.message.content.VideoNoteContent
import dev.inmo.tgbotapi.types.message.content.VoiceContent
import dev.inmo.tgbotapi.utils.filenameFromUrl import dev.inmo.tgbotapi.utils.filenameFromUrl
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -14,18 +44,80 @@ 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)
runCatching {
bot.downloadFile(content.media, outFile)
}.onFailure {
it.printStackTrace()
}.onSuccess { _ ->
reply(it, "Saved to ${outFile.absolutePath}")
withAction(it.chat.id, TypingAction) {
when (content) {
is PhotoContent -> replyWithPhoto(
it,
outFile.asMultipartFile()
)
is AnimationContent -> replyWithAnimation(
it,
outFile.asMultipartFile()
)
is VideoContent -> replyWithVideo(
it,
outFile.asMultipartFile()
)
is StickerContent -> replyWithSticker(
it,
outFile.asMultipartFile()
)
is MediaGroupContent<*> -> replyWithMediaGroup(
it,
content.group.map {
when (val innerContent = it.content) {
is AudioContent -> TelegramMediaAudio(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is DocumentContent -> TelegramMediaDocument(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is PhotoContent -> TelegramMediaPhoto(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
is VideoContent -> TelegramMediaVideo(
downloadFileToTemp(innerContent.media).asMultipartFile()
)
}
}
)
is AudioContent -> replyWithAudio(
it,
outFile.asMultipartFile()
)
is DocumentContent -> replyWithDocument(
it,
outFile.asMultipartFile()
)
is VoiceContent -> replyWithVoice(
it,
outFile.asMultipartFile()
)
is VideoNoteContent -> replyWithVideoNote(
it,
outFile.asMultipartFile()
)
}
}
}
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join() }.second.join()
} }

View File

@@ -1,11 +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.chat.CommonBot import dev.inmo.tgbotapi.types.chat.CommonBot
import dev.inmo.tgbotapi.types.chat.CommonUser import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.ExtendedBot import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.message.* import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.code
import dev.inmo.tgbotapi.utils.link
import dev.inmo.tgbotapi.utils.regular
import kotlinx.coroutines.* import kotlinx.coroutines.*
/** /**
@@ -16,23 +20,38 @@ 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)

View File

@@ -11,7 +11,7 @@ buildscript {
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'application' apply plugin: 'application'
mainClassName="HelloBotKt" mainClassName="GetMeBotKt"
dependencies { dependencies {

View File

@@ -1,13 +1,31 @@
import dev.inmo.kslog.common.KSLog
import dev.inmo.kslog.common.LogLevel
import dev.inmo.kslog.common.defaultMessageFormatter
import dev.inmo.kslog.common.filter.filtered
import dev.inmo.kslog.common.setDefaultKSLog
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChat
import dev.inmo.tgbotapi.utils.DefaultKTgBotAPIKSLog
/** /**
* This is one of the most easiest bot - it will just print information about itself * This is one of the most easiest bot - it will just print information about itself
*/ */
suspend fun main(vararg args: String) { suspend fun main(vararg args: String) {
val botToken = args.first() val botToken = args.first()
val isDebug = args.getOrNull(1) == "debug"
if (isDebug) {
setDefaultKSLog(
KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? ->
println(defaultMessageFormatter(level, tag, message, throwable))
}
)
}
val bot = telegramBot(botToken) val bot = telegramBot(botToken)
println(bot.getMe()) val me = bot.getMe()
println(me)
println(bot.getChat(me))
} }

View File

@@ -1,11 +1,15 @@
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.*
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.formatting.linkMarkdownV2 import dev.inmo.tgbotapi.extensions.utils.formatting.linkMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2 import dev.inmo.tgbotapi.extensions.utils.formatting.textMentionMarkdownV2
import dev.inmo.tgbotapi.extensions.utils.ifChannelChat
import dev.inmo.tgbotapi.extensions.utils.ifFromChannelGroupContentMessage
import dev.inmo.tgbotapi.types.chat.* import dev.inmo.tgbotapi.types.chat.*
import dev.inmo.tgbotapi.types.chat.GroupChat import dev.inmo.tgbotapi.types.chat.GroupChat
import dev.inmo.tgbotapi.types.chat.PrivateChat import dev.inmo.tgbotapi.types.chat.PrivateChat
@@ -23,23 +27,36 @@ 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()
onMentionWithAnyContent(me) { message ->
val chat = message.chat val chat = message.chat
if (chat is ChannelChat) {
val answer = "Hi everybody in this channel \"${chat.title}\"" val answerText = when (val chat = message.chat) {
sendTextMessage(chat, answer, MarkdownV2) is PreviewChannelChat -> {
return@onContentMessage val answer = "Hi everybody in this channel \"${chat.title}\""
} reply(message, answer, MarkdownV2)
val answerText = "Oh, hi, " + when (chat) { return@onMentionWithAnyContent
is User -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id) }
is PrivateChat -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id) is PreviewPrivateChat -> {
is SupergroupChat -> (chat.username ?.username ?: getChat(chat).inviteLink) ?.let { reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2)
chat.title.linkMarkdownV2(it) return@onMentionWithAnyContent
} ?: chat.title }
is GroupChat -> bot.getChat(chat).inviteLink ?.let { is PreviewGroupChat -> {
chat.title.linkMarkdownV2(it) message.ifFromChannelGroupContentMessage {
} ?: chat.title val answer = "Hi, ${it.senderChat.title}"
else -> "Unknown :(".escapeMarkdownV2Common() reply(message, answer, MarkdownV2)
return@onMentionWithAnyContent
}
"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 UnknownChatType -> "Unknown :(".escapeMarkdownV2Common()
} }
reply( reply(
message, message,

View 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"
```

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,16 +3,20 @@ import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.bot.ktor.telegramBot import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.answers.answer import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.edit.edit
import dev.inmo.tgbotapi.extensions.api.edit.editMessageText
import dev.inmo.tgbotapi.extensions.api.edit.reply_markup.editMessageReplyMarkup
import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText import dev.inmo.tgbotapi.extensions.api.edit.text.editMessageText
import dev.inmo.tgbotapi.extensions.api.send.* import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.* import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.formatting.botCommand
import dev.inmo.tgbotapi.extensions.utils.formatting.buildEntities
import dev.inmo.tgbotapi.extensions.utils.types.buttons.* import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.withContent import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.types.BotCommand import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
private const val nextPageData = "next" private const val nextPageData = "next"
@@ -56,6 +60,16 @@ fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
dataButton(">>", "$count $count") dataButton(">>", "$count $count")
} }
} }
row {
inlineQueryInChosenChatButton(
"Send somebody page",
query = "$page $count",
allowUsers = true,
allowBots = true,
allowGroups = true,
allowChannels = true,
)
}
} }
suspend fun activateKeyboardsBot( suspend fun activateKeyboardsBot(
@@ -71,13 +85,12 @@ suspend fun activateKeyboardsBot(
val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10 val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10
reply( reply(
message, message,
"Your inline keyboard with $numberOfPages pages",
replyMarkup = inlineKeyboard { replyMarkup = inlineKeyboard {
row { includePageButtons(1, numberOfPages)
includePageButtons(1, numberOfPages)
}
} }
) ) {
regular("Your inline keyboard with $numberOfPages pages")
}
} }
onMessageDataCallbackQuery { onMessageDataCallbackQuery {
@@ -86,34 +99,66 @@ suspend fun activateKeyboardsBot(
return@onMessageDataCallbackQuery return@onMessageDataCallbackQuery
} }
val text = "This is $page of $count" edit(
editMessageText(
it.message.withContent<TextContent>() ?: it.let { it.message.withContent<TextContent>() ?: it.let {
answer(it, "Unsupported message type :(") answer(it, "Unsupported message type :(")
return@onMessageDataCallbackQuery return@onMessageDataCallbackQuery
}, },
text,
replyMarkup = inlineKeyboard { replyMarkup = inlineKeyboard {
row { includePageButtons(page, count)
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(
it.query,
"Send buttons",
InputTextMessageContent("It is sent via inline mode inline buttons"),
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
)
)
) )
} }
onUnhandledCommand { onUnhandledCommand {
reply( reply(
it, it,
buildEntities {
+"Use " + botCommand("inline") + " to get pagination inline keyboard"
},
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) { replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
row { row {
simpleButton("/inline") simpleButton("/inline")
} }
} }
) ) {
+"Use " + botCommand("inline") + " to get pagination inline keyboard"
}
} }
setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard")) setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))

View 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"
```

View 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"
}

View File

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

View File

@@ -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"
```

View 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"
}

View File

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

11
PollsBot/README.md Normal file
View 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"
```

21
PollsBot/build.gradle Normal file
View 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="HelloBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

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

View File

@@ -4,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

View File

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

View File

@@ -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,6 +7,7 @@ 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.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
@@ -13,10 +15,11 @@ import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.Chat import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
import java.io.File
private const val command = "send_file" private const val command = "send_file"
expect fun pickFile(currentRoot: MPPFile): MPPFile?
/** /**
* This bot will send files inside of working directory OR from directory in the second argument. * This bot will send files inside of working directory OR from directory in the second argument.
* You may send /send_file command to this bot to get random file from the directory OR * You may send /send_file command to this bot to get random file from the directory OR
@@ -24,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,
@@ -51,8 +45,6 @@ suspend fun main(args: Array<String>) {
} }
} }
val bot = telegramBot(botToken)
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) { bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommandWithArgs(command) { message, args -> onCommandWithArgs(command) { message, args ->
@@ -61,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()
@@ -80,7 +72,7 @@ suspend fun main(args: Array<String>) {
} }
if (!sent) { if (!sent) {
bot.reply(message, "Nothing selected :(") reply(message, "Nothing selected :(")
} }
} }
} }

View 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)
}
}

View File

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

View 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)
}
}

View File

@@ -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())
}
}

View 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"
```

View 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"
}

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

View File

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

View File

@@ -8,47 +8,48 @@ import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilte
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.shortcuts.* import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
import dev.inmo.tgbotapi.extensions.utils.withContentOrNull
import dev.inmo.tgbotapi.types.ReplyParameters
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.types.quoteEntitiesField
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.* import kotlinx.coroutines.*
suspend fun activateResenderBot( suspend fun activateResenderBot(
token: String, token: String,
print: (Any) -> Unit print: (Any) -> Unit
) { ) {
val bot = telegramBot(token) telegramBotWithBehaviourAndLongPolling(token, scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
print(bot.getMe())
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onContentMessage( onContentMessage(
initialFilter = CommonMessageFilterExcludeMediaGroups, subcontextUpdatesFilter = MessageFilterByChat,
subcontextUpdatesFilter = MessageFilterByChat
) { ) {
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 messageThreadId = it.threadIdOrNull,
withUploadPhotoAction(chat) { replyParameters = it.replyInfo ?.messageMeta ?.let { meta ->
sendVisualMediaGroup(chat, it.map { it.content.toMediaGroupMemberTelegramMedia() }) val quote = it.withContentOrNull<TextContent>() ?.content ?.quote
} ReplyParameters(
} meta,
onPlaylist { entities = quote ?.textSources ?: emptyList(),
val chat = it.chat ?: return@onPlaylist quotePosition = quote ?.position
withUploadDocumentAction(chat) { )
sendPlaylist(chat, it.map { it.content.toMediaGroupMemberTelegramMedia() }) }
} )
} ) {
onDocumentsGroup { it.forEach(print)
val chat = it.chat ?: return@onDocumentsGroup }
withUploadDocumentAction(chat) {
sendDocumentsGroup(chat, it.map { it.content.toMediaGroupMemberTelegramMedia() })
} }
println("Answer info: $answer")
} }
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it) println(it)
} }
}.join() print(bot.getMe())
}.second.join()
} }

View 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")
}
}
}
}

View File

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

View 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"
```

View 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:2.3.7'
}

View File

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

View File

@@ -17,9 +17,9 @@ suspend fun main(args: Array<String>) {
if (diceType == SlotMachineDiceAnimationType) { if (diceType == SlotMachineDiceAnimationType) {
val result = dice.calculateSlotMachineResult() ?: return@onDice val result = dice.calculateSlotMachineResult() ?: return@onDice
bot.reply(it, "${result.leftReel}|${result.centerReel}|${result.rightReel}") reply(it, "${result.leftReel}|${result.centerReel}|${result.rightReel}")
} else { } else {
bot.reply(it, "There is no slot machine dice in message") reply(it, "There is no slot machine dice in message")
} }
} }
}.join() }.join()

View 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"
}
}
}
}

View File

@@ -0,0 +1,93 @@
import dev.inmo.micro_utils.coroutines.defaultSafelyWithoutExceptionHandler
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.get.*
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.types.StickerFormat
import dev.inmo.tgbotapi.types.StickerType
import dev.inmo.tgbotapi.types.message.textsources.*
import dev.inmo.tgbotapi.types.stickers.AnimatedStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiStickerSet
import dev.inmo.tgbotapi.types.stickers.CustomEmojiVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskStickerSet
import dev.inmo.tgbotapi.types.stickers.MaskVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularSimpleStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularStickerSet
import dev.inmo.tgbotapi.types.stickers.RegularVideoStickerSet
import dev.inmo.tgbotapi.types.stickers.StickerSet
import dev.inmo.tgbotapi.types.stickers.UnknownStickerSet
import dev.inmo.tgbotapi.utils.bold
import dev.inmo.tgbotapi.utils.buildEntities
import kotlinx.coroutines.*
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("Sticker format: ") + when (stickerFormat) {
StickerFormat.Animated -> "Animated"
StickerFormat.Static -> "Static"
is StickerFormat.Unknown -> stickerFormat.type
StickerFormat.Video -> "Video"
} + "\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)
}
}
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() + regular("\n")
}.separateForText().map { entities ->
reply(it, entities)
}
}
}
onSticker {
val stickerSetInfo = getStickerSetOrNull(it.content.media)
reply(
it,
stickerSetInfo.buildInfo()
)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
}.join()
}

View File

@@ -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
}
}
)
}

View File

@@ -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>

View 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")
}

View File

@@ -0,0 +1,5 @@
suspend fun main(args: Array<String>) {
activateStickerInfoBot(args.first()) {
println(it)
}
}

View 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"
```

View 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"
}

View File

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

9
TopicsHandling/README.md Normal file
View File

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

View File

@@ -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"
}

View File

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

9
UserChatShared/README.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,10 @@
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.types.webAppQueryIdField import dev.inmo.tgbotapi.types.webAppQueryIdField
import dev.inmo.tgbotapi.webapps.* import dev.inmo.tgbotapi.webapps.*
import dev.inmo.tgbotapi.webapps.cloud.*
import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackStyle
import dev.inmo.tgbotapi.webapps.haptic.HapticFeedbackType
import dev.inmo.tgbotapi.webapps.popup.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
@@ -11,12 +15,15 @@ import kotlinx.browser.window
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.dom.appendElement import kotlinx.dom.appendElement
import kotlinx.dom.appendText import kotlinx.dom.appendText
import kotlinx.dom.clear
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.w3c.dom.HTMLElement import org.w3c.dom.*
import kotlin.random.Random
import kotlin.random.nextUBytes
fun HTMLElement.log(text: String) { fun HTMLElement.log(text: String) {
appendElement("p", {})
appendText(text) appendText(text)
appendElement("p", {})
} }
fun main() { fun main() {
@@ -39,13 +46,17 @@ fun main() {
} }
val dataIsSafe = response.bodyAsText().toBoolean() val dataIsSafe = response.bodyAsText().toBoolean()
document.body ?.appendElement("div") { document.body ?.log(
textContent = if (dataIsSafe) { if (dataIsSafe) {
"Data is safe" "Data is safe"
} else { } else {
"Data is unsafe" "Data is unsafe"
} }
} )
document.body ?.log(
webApp.initDataUnsafe.chat.toString()
)
} }
document.body ?.appendElement("button") { document.body ?.appendElement("button") {
@@ -60,8 +71,195 @@ fun main() {
} }
} }
}) })
appendText("Example button") appendText("Answer in chat button")
} ?: window.alert("Unable to load body") } ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendText("Allow to write in private messages: ${webApp.initDataUnsafe.user ?.allowsWriteToPM ?: "User unavailable"}")
document.body ?.appendElement("p", {})
document.body ?.appendText("Alerts:")
document.body ?.appendElement("button") {
addEventListener("click", {
webApp.showPopup(
PopupParams(
"It is sample title of default button",
"It is sample message of default button",
DefaultPopupButton("default", "Default button"),
OkPopupButton("ok"),
DestructivePopupButton("destructive", "Destructive button")
)
) {
document.body ?.log(
when (it) {
"default" -> "You have clicked default button in popup"
"ok" -> "You have clicked ok button in popup"
"destructive" -> "You have clicked destructive button in popup"
else -> "I can't imagine where you take button with id $it"
}
)
}
})
appendText("Popup")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("button") {
addEventListener("click", {
webApp.showAlert(
"This is alert message"
) {
document.body ?.log(
"You have closed alert"
)
}
})
appendText("Alert")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestWriteAccess() })
appendText("Request write access without callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestWriteAccess { document.body ?.log("Write access request result: $it") } })
appendText("Request write access with callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestContact() })
appendText("Request contact without callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("button") {
addEventListener("click", { webApp.requestContact { document.body ?.log("Contact request result: $it") } })
appendText("Request contact with callback")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
addEventListener("click", {
webApp.showConfirm(
"This is confirm message"
) {
document.body ?.log(
"You have pressed \"${if (it) "Ok" else "Cancel"}\" in confirm"
)
}
})
appendText("Confirm")
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
fun updateText() {
textContent = if (webApp.isClosingConfirmationEnabled) {
"Disable closing confirmation"
} else {
"Enable closing confirmation"
}
}
addEventListener("click", {
webApp.toggleClosingConfirmation()
updateText()
})
updateText()
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
document.body ?.appendElement("button") {
fun updateHeaderColor() {
val (r, g, b) = Random.nextUBytes(3)
val hex = Color.Hex(r, g, b)
webApp.setHeaderColor(hex)
(this as? HTMLButtonElement) ?.style ?.backgroundColor = hex.value
textContent = "Header color: ${hex.value.uppercase()} (click to change)"
}
addEventListener("click", {
updateHeaderColor()
})
updateHeaderColor()
} ?: window.alert("Unable to load body")
document.body ?.appendElement("p", {})
fun Element.updateCloudStorageContent() {
clear()
webApp.cloudStorage.getAll {
it.onSuccess {
document.body ?.log(it.toString())
appendElement("label") { textContent = "Cloud storage" }
appendElement("p", {})
it.forEach { (k, v) ->
appendElement("div") {
val kInput = appendElement("input", {}) as HTMLInputElement
val vInput = appendElement("input", {}) as HTMLInputElement
kInput.value = k.key
vInput.value = v.value
appendElement("button") {
addEventListener("click", {
if (k.key == kInput.value) {
webApp.cloudStorage.set(k.key, vInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
} else {
webApp.cloudStorage.remove(k.key) {
it.onSuccess {
webApp.cloudStorage.set(kInput.value, vInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
}
}
}
})
this.textContent = "Save"
}
}
appendElement("p", {})
}
appendElement("label") { textContent = "Cloud storage: add new" }
appendElement("p", {})
appendElement("div") {
val kInput = appendElement("input", {}) as HTMLInputElement
appendElement("button") {
textContent = "Add key"
addEventListener("click", {
webApp.cloudStorage.set(kInput.value, kInput.value) {
document.body ?.log(it.toString())
this@updateCloudStorageContent.updateCloudStorageContent()
}
})
}
}
appendElement("p", {})
}.onFailure {
document.body ?.log(it.stackTraceToString())
}
}
}
val cloudStorageContentDiv = document.body ?.appendElement("div") {} as HTMLDivElement
document.body ?.appendElement("p", {})
webApp.apply { webApp.apply {
onThemeChanged { onThemeChanged {
document.body ?.log("Theme changed: ${webApp.themeParams}") document.body ?.log("Theme changed: ${webApp.themeParams}")
@@ -69,8 +267,40 @@ fun main() {
onViewportChanged { onViewportChanged {
document.body ?.log("Viewport changed: ${it.isStateStable}") document.body ?.log("Viewport changed: ${it.isStateStable}")
} }
backButton.apply {
onClick {
document.body ?.log("Back button clicked")
hapticFeedback.impactOccurred(
HapticFeedbackStyle.Heavy
)
}
show()
}
mainButton.apply {
setText("Main button")
onClick {
document.body ?.log("Main button clicked")
hapticFeedback.notificationOccurred(
HapticFeedbackType.Success
)
}
show()
}
onSettingsButtonClicked {
document.body ?.log("Settings button clicked")
}
onWriteAccessRequested {
document.body ?.log("Write access request result: $it")
}
onContactRequested {
document.body ?.log("Contact request result: $it")
}
} }
webApp.ready() webApp.ready()
document.body ?.appendElement("input", {
(this as HTMLInputElement).value = window.location.href
})
cloudStorageContentDiv.updateCloudStorageContent()
}.onFailure { }.onFailure {
window.alert(it.stackTraceToString()) window.alert(it.stackTraceToString())
} }

View File

@@ -2,6 +2,11 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="format-detection" content="telephone=no"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="HandheldFriendly" content="True"/>
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<title>Web App Example</title> <title>Web App Example</title>
</head> </head>
<body> <body>

View File

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

View File

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

View File

@@ -1,11 +1,11 @@
kotlin.code.style=official kotlin.code.style=official
org.gradle.parallel=true org.gradle.parallel=true
# Due to parallel compilation project require next amount of memory on full build # Due to parallel compilation project require next amount of memory on full build
org.gradle.jvmargs=-Xmx768m org.gradle.jvmargs=-Xmx2344m
kotlin_version=1.6.21 kotlin_version=1.9.22
telegram_bot_api_version=1.1.1 telegram_bot_api_version=10.0.0
micro_utils_version=0.10.4 micro_utils_version=0.20.31
serialization_version=1.3.3 serialization_version=1.6.2
ktor_version=2.0.1 ktor_version=2.3.7

View File

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

20
native_template.gradle Normal file
View File

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

View File

@@ -4,18 +4,46 @@ include ":RandomFileSenderBot"
include ":HelloBot" include ":HelloBot"
include ":PollsBot"
include ":GetMeBot" include ":GetMeBot"
include ":DeepLinksBot"
include ":FilesLoaderBot" include ":FilesLoaderBot"
include ":ResenderBot:ResenderBotLib" include ":ResenderBot:ResenderBotLib"
include ":ResenderBot:jvm_launcher" include ":ResenderBot:jvm_launcher"
include ":ResenderBot:native_launcher"
include ":KeyboardsBot:KeyboardsBotLib" include ":KeyboardsBot:KeyboardsBotLib"
include ":KeyboardsBot:jvm_launcher" include ":KeyboardsBot:jvm_launcher"
include ":StickerInfoBot:StickerInfoBotLib"
include ":StickerInfoBot:jvm_launcher"
include ":SlotMachineDetectorBot" include ":SlotMachineDetectorBot"
include ":ChatAvatarSetter"
include ":WebApp" include ":WebApp"
include ":FSMBot" include ":FSMBot"
include ":TopicsHandling"
include ":UserChatShared"
include ":RightsChangerBot"
include ":LiveLocationsBot"
include ":StickerSetHandler"
include ":InlineQueriesBot"
include ":ReactionsInfoBot"
include ":LinkPreviewsBot"
include ":BoostsInfoBot"