Compare commits

...

360 Commits

Author SHA1 Message Date
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
9a4bd55ef6 update tgbotapi version 2022-05-17 19:55:42 +06:00
4dac411693 add support of data checking in webapp 2022-05-17 18:58:43 +06:00
d386d50f1c Merge pull request #107 from InsanusMokrassar/renovate/telegram_bot_api_version
Update telegram_bot_api_version to v1.1.0
2022-05-14 17:33:30 +06:00
Renovate Bot
1b846a6383 Update telegram_bot_api_version to v1.1.0 2022-05-14 11:33:22 +00:00
568b845890 Merge pull request #106 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.ktor.server to v0.10.4
2022-05-14 17:32:48 +06:00
Renovate Bot
3ebca4a058 Update dependency dev.inmo:micro_utils.ktor.server to v0.10.4 2022-05-12 15:37:39 +00:00
2b9af2f667 Merge pull request #104 from InsanusMokrassar/1.0.0
1.0.0
2022-05-12 17:05:10 +06:00
16410debff decrease xmx 2022-05-12 17:00:22 +06:00
a18f22fe8f update xmx for builds 2022-05-12 16:47:46 +06:00
0489f217b3 update examples 2022-05-11 12:15:18 +06:00
e772cfda3b start migration onto 1.0.0 2022-05-11 02:47:37 +06:00
9d42385662 1.0.0 migration preview 2022-05-11 00:23:14 +06:00
b740203116 0.38.23 2022-05-08 00:41:13 +06:00
149717301d Update gradle.properties 2022-05-05 15:16:24 +06:00
60e72be844 Merge pull request #103 from InsanusMokrassar/0.38.21
0.38.21
2022-05-04 14:54:45 +06:00
831b558724 fixes 2022-05-04 13:27:39 +06:00
df778b4e93 start migration onto 0.38.20 2022-05-04 12:22:49 +06:00
bf0c6497fe Merge pull request #101 from Akkihi/master
sendData does not work with inlineKeyboard
2022-05-03 23:57:07 +06:00
akkkihi
71a047f867 sendData does not work with inlineKeyboard 2022-05-03 17:55:08 +04:00
2ae3e1165d Update gradle.properties 2022-05-03 15:55:27 +06:00
9a5d02512b Update gradle.properties 2022-05-02 14:28:05 +06:00
dd2c528006 Update gradle.properties 2022-05-01 18:42:09 +06:00
1c16a9f868 Create README.md 2022-04-30 08:47:35 +06:00
00c3aba12b Merge pull request #97 from InsanusMokrassar/0.38.16
0.38.16
2022-04-30 08:40:17 +06:00
a547bbce65 Merge branch 'master' into 0.38.16 2022-04-30 08:39:01 +06:00
0b7d8c087f update tgbotapi and add webapps example 2022-04-29 20:57:16 +06:00
8ec282e3d5 Merge pull request #96 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.15
2022-04-26 15:48:10 +06:00
Renovate Bot
e8433cd8ac Update dependency dev.inmo:tgbotapi to v0.38.15 2022-04-26 09:46:32 +00:00
222134836a update up to 0.38.13 2022-04-17 00:26:04 +06:00
c18e02dcb3 Merge pull request #94 from InsanusMokrassar/0.38.12
Add keyboards and update tgbotapi up to 0.38.12
2022-04-16 23:52:43 +06:00
f9050061d1 add keyboards and update tgbotapi up to 0.38.12 2022-04-09 12:20:35 +06:00
a3e3f6c22c Update gradle.properties 2022-03-30 07:54:57 +06:00
516cc7bfcb Merge pull request #89 from InsanusMokrassar/0.38.10
Update up to 0.38.10
2022-03-25 21:26:18 +06:00
c707a11b66 update gradle 2022-03-24 16:53:32 +06:00
c6c418c393 update up to 0.38.10 2022-03-24 16:52:13 +06:00
e0795c3c14 update up to 0.38.8 2022-03-21 11:25:59 +06:00
dec55990dc Merge pull request #87 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.7
2022-03-12 22:59:42 +06:00
Renovate Bot
169f3f5bd5 Update dependency dev.inmo:tgbotapi to v0.38.7 2022-03-12 16:26:32 +00:00
e175bb6143 Update gradle-wrapper.properties 2022-02-28 09:07:15 +06:00
290d722ae6 Update gradle.properties 2022-02-28 09:06:50 +06:00
9ee85df1ad Merge pull request #85 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.6
2022-02-28 09:06:20 +06:00
Renovate Bot
2953c40963 Update dependency dev.inmo:tgbotapi to v0.38.6 2022-02-28 03:04:39 +00:00
512067b056 Merge pull request #84 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.5
2022-02-12 12:55:49 +06:00
Renovate Bot
0cb209dc6e Update dependency dev.inmo:tgbotapi to v0.38.5 2022-02-09 11:00:15 +00:00
7cf6814d89 update up to 0.38.4 2022-02-02 13:30:32 +06:00
09e8492439 Merge pull request #82 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.3
2022-01-14 23:19:51 +06:00
Renovate Bot
71222f42e5 Update dependency dev.inmo:tgbotapi to v0.38.3 2022-01-14 17:18:46 +00:00
426d649877 Merge pull request #81 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.2
2022-01-12 01:26:24 +06:00
Renovate Bot
93faf9654a Update dependency dev.inmo:tgbotapi to v0.38.2 2022-01-11 16:19:57 +00:00
288386e25a Update gradle.properties 2022-01-11 20:19:49 +06:00
99e83589a4 Merge pull request #80 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.38.1
2022-01-07 15:51:02 +06:00
Renovate Bot
fbea95fc0a Update dependency dev.inmo:tgbotapi to v0.38.1 2022-01-07 09:31:59 +00:00
0edaa28151 several small updates 2022-01-02 12:06:32 +06:00
df874b0783 update up to 0.38.0 2022-01-02 01:06:04 +06:00
5f5722398d update up to 0.37.3 2021-12-20 14:13:55 +06:00
cc29ec75ca Merge pull request #76 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.37.1
2021-11-15 09:57:36 +06:00
Renovate Bot
a42173acf6 Update dependency dev.inmo:tgbotapi to v0.37.1 2021-11-14 15:41:13 +00:00
d936bc4643 upgrade up to 0.37.0 2021-11-11 12:34:52 +06:00
1b340d8db5 Merge pull request #74 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.36.1
2021-10-24 11:52:00 +06:00
Renovate Bot
4cd1980778 Update dependency dev.inmo:tgbotapi to v0.36.1 2021-10-24 05:26:22 +00:00
d89202aebb update up to 0.36.0 2021-10-18 20:21:58 +06:00
be2de6e372 Merge pull request #71 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.fsm.common to v0.5.31
2021-10-01 20:30:11 +06:00
Renovate Bot
08796f18e8 Update dependency dev.inmo:micro_utils.fsm.common to v0.5.31 2021-10-01 14:28:11 +00:00
8bd7e116e3 Merge pull request #70 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.fsm.common to v0.5.30
2021-09-25 18:12:40 +06:00
Renovate Bot
39d7a95c95 Update dependency dev.inmo:micro_utils.fsm.common to v0.5.30 2021-09-25 12:11:44 +00:00
526ecac89a Merge pull request #68 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.fsm.common to v0.5.29
2021-09-23 17:35:05 +06:00
Renovate Bot
1e4e246fab Update dependency dev.inmo:micro_utils.fsm.common to v0.5.29 2021-09-23 10:02:04 +00:00
6aa7fe151e update up to 0.35.9 2021-09-21 23:22:13 +06:00
16c27fa493 Merge pull request #67 from InsanusMokrassar/renovate/micro_utils_version
Update dependency dev.inmo:micro_utils.fsm.common to v0.5.26
2021-09-09 17:32:00 +06:00
Renovate Bot
f6a811acca Update dependency dev.inmo:micro_utils.fsm.common to v0.5.26 2021-09-09 11:26:29 +00:00
17c08fc906 Update RandomFileSenderBot.kt 2021-09-09 09:20:17 +06:00
aee0943a5e update dependencies and RandomFileSenderBot example 2021-09-08 23:00:19 +06:00
c07f4f2ed1 Update gradle-wrapper.properties 2021-09-05 09:23:25 +06:00
817acb8faf Update gradle.properties 2021-09-05 09:23:00 +06:00
8b294a0cc0 Merge pull request #66 from InsanusMokrassar/renovate/telegram_bot_api_version
Update dependency dev.inmo:tgbotapi to v0.35.7
2021-09-05 09:22:26 +06:00
Renovate Bot
0139cc5dcc Update dependency dev.inmo:tgbotapi to v0.35.7 2021-09-04 12:47:15 +00:00
6d16508277 small actualizations 2021-08-25 15:58:27 +06:00
ce2051d32d Update gradle.properties 2021-08-25 15:52:00 +06:00
1abbcc8789 Update gradle.properties 2021-08-17 09:23:50 +06:00
c5e05dfce1 Update gradle.properties 2021-08-13 22:24:44 +06:00
c1d7129af8 update dependencies 2021-08-09 17:39:04 +06:00
496fc45d2d tgbotapi -> 0.35.3 2021-08-08 20:32:26 +06:00
cdb634e91a Update gradle-wrapper.properties 2021-07-30 13:14:55 +06:00
9d17311918 Update dependencies 2021-07-30 13:14:14 +06:00
12ef88c757 Merge pull request #56 from InsanusMokrassar/0.35.1
0.35.1
2021-06-30 20:42:03 +06:00
4435fdc80c Merge branch 'master' into 0.35.1 2021-06-30 20:28:22 +06:00
cd9eba0393 Update gradle.properties 2021-06-30 20:25:08 +06:00
ef74b33d68 fix main in FSMBot 2021-06-30 13:50:08 +06:00
522d1b55ba add example with oneof and fsm 2021-06-30 13:12:15 +06:00
d062ab86ae update up to 0.35.1 2021-06-28 01:14:16 +06:00
48292ecf72 Merge pull request #54 from InsanusMokrassar/tgbotapi/0.35.0
tgbotapi 0.35.0
2021-06-13 10:08:19 +06:00
22c0fcb439 Update gradle.properties 2021-06-07 19:34:21 +06:00
c90952d3ca update tgbotapi version 2021-05-06 18:24:31 +06:00
b9e9f981c4 Update up to 0.4.34 2021-04-18 17:26:17 +06:00
11836b1e17 Update gradle-wrapper.properties 2021-04-11 04:07:29 +06:00
d30e673539 Merge pull request #44 from InsanusMokrassar/renovate/dev.inmo-tgbotapi-0.x
Update dependency dev.inmo:tgbotapi to v0.33.3
2021-04-06 22:41:03 +06:00
Renovate Bot
5337f1e4f8 Update dependency dev.inmo:tgbotapi to v0.33.3 2021-04-06 16:35:58 +00:00
26fc3c5924 Update gradle.properties 2021-03-30 22:55:20 +06:00
9e0f43dcc0 update 2021-03-12 15:49:16 +06:00
aa89a289ce Update gradle-wrapper.properties 2021-03-04 15:44:47 +06:00
aeb75d48b9 Update gradle.properties 2021-03-04 15:43:33 +06:00
a67db1c214 Update gradle.properties 2021-02-22 14:42:22 +06:00
3c06da09c2 Update gradle.properties 2021-02-18 16:29:21 +06:00
c15c6d2cf1 Update gradle.properties 2021-02-10 01:19:59 +06:00
febfacc3d5 Update gradle-wrapper.properties 2021-02-10 01:18:49 +06:00
3c71bf287f Merge pull request #34 from InsanusMokrassar/renovate/org.jetbrains.kotlin-kotlin-gradle-plugin-1.x
Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v1.4.30
2021-02-10 01:18:11 +06:00
6f265640a7 update repositories definition 2021-02-04 15:19:36 +06:00
Renovate Bot
b9e8705b2e Update dependency org.jetbrains.kotlin:kotlin-gradle-plugin to v1.4.30 2021-02-03 16:27:00 +00:00
746f3e8a14 Create build.yml 2021-02-02 20:30:24 +06:00
bd621047ee Merge pull request #32 from InsanusMokrassar/renovate/dev.inmo-tgbotapi-0.x
Update dependency dev.inmo:tgbotapi to v0.32.3
2021-02-02 20:25:11 +06:00
Renovate Bot
98f14faeb2 Update dependency dev.inmo:tgbotapi to v0.32.3 2021-02-02 14:18:38 +00:00
ccb29d6977 update tgbotapi version up to 0.32.1, gradle wrapper and resender bot logic according to update of tgbotapi 2021-02-01 12:57:39 +06:00
f5b526e0c5 update up to tgbotapi 0.31.0 2021-01-18 23:32:47 +06:00
c1cfd05446 Update gradle-wrapper.properties 2021-01-09 15:04:58 +06:00
fb7795414b Merge pull request #27 from InsanusMokrassar/renovate/dev.inmo-tgbotapi-0.x
Update dependency dev.inmo:tgbotapi to v0.30.13
2021-01-09 15:01:15 +06:00
Renovate Bot
5cb998b390 Update dependency dev.inmo:tgbotapi to v0.30.13 2021-01-09 08:46:38 +00:00
6f182c774c update tgbotapi version up to 0.30.12, update of readme 2021-01-06 00:01:41 +06:00
79cc1a7c9a Update tgbotapi 0.30.8 -> 0.30.10 2020-12-09 18:21:48 +06:00
4ce79657ac update kotlin 2020-11-21 20:24:17 +06:00
9a86360682 Merge pull request #18 from InsanusMokrassar/renovate/dev.inmo-tgbotapi-0.x
Update dependency dev.inmo:tgbotapi to v0.30.8
2020-11-21 20:23:02 +06:00
50f8e0df61 update 2020-11-21 20:22:11 +06:00
bb846406da Merge pull request #14 from InsanusMokrassar/renovate/dev.inmo-tgbotapi-0.x
Update dependency dev.inmo:tgbotapi to v0.30.7
2020-11-20 16:42:14 +06:00
6766dc4a8f temporal fix for legacy in resender bot 2020-11-18 12:37:24 +06:00
b440757331 update 2020-11-18 12:35:45 +06:00
Renovate Bot
94bfa67eb3 Update dependency dev.inmo:tgbotapi to v0.30.4 2020-11-13 15:21:18 +00:00
6d066532fb rename forwarder bot 2020-11-02 22:44:15 +06:00
d49ce88a0b update and add SlotMachineDetectorBot 2020-11-02 22:35:28 +06:00
3820e29bf2 update tgbotapi version 2020-11-02 14:39:36 +06:00
7b66c89632 update version of telegram bot api 2020-10-27 16:23:27 +06:00
8f79b9e380 Merge pull request #11 from InsanusMokrassar/0.29.0
0.29.0
2020-10-04 23:50:55 +06:00
358bfdaa05 update hello example 2020-10-04 19:18:16 +06:00
dfae8b7f29 update dependencies 2020-10-04 19:11:48 +06:00
1eb9676c19 migration onto 0.29.0 2020-10-04 17:32:50 +06:00
86 changed files with 3441 additions and 288 deletions

19
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt install -y libcurl4-openssl-dev
- name: Set up JDK 17
uses: actions/setup-java@v1
with:
java-version: 17
- name: Build with Gradle
run: ./gradlew build

5
.gitignore vendored
View File

@@ -8,3 +8,8 @@ settings.xml
.gradle/
build/
out/
kotlin-js-store/
local.*
local.*/

View File

@@ -1,11 +0,0 @@
language: java
install: true
os: linux
dist: trusty
jdk: oraclejdk8
script:
- ./gradlew build -s

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

10
FSMBot/README.md Normal file
View File

@@ -0,0 +1,10 @@
# FSM
This bot contains an example of working with FSM included in project
[MicroUtils](https://github.com/InsanusMokrassar/MicroUtils)
## Launch
```bash
../gradlew run --args="BOT_TOKEN"
```

26
FSMBot/build.gradle Normal file
View File

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

View File

@@ -0,0 +1,76 @@
import dev.inmo.micro_utils.coroutines.AccumulatorFlow
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.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
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.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MessageThreadId
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
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.flow.filter
import kotlinx.coroutines.flow.first
sealed interface BotState : State
data class ExpectContentOrStopState(override val context: IdChatIdentifier, val sourceMessage: CommonMessage<TextContent>) : BotState
data class StopState(override val context: IdChatIdentifier) : BotState
suspend fun main(args: Array<String>) {
val botToken = args.first()
telegramBotWithBehaviourAndFSMAndStartLongPolling<BotState>(
botToken,
CoroutineScope(Dispatchers.IO),
onStateHandlingErrorHandler = { state, e ->
when (state) {
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 = waitAnyContentMessage().filter { message ->
message.sameThread(it.sourceMessage)
}.first()
val content = contentMessage.content
when {
content is TextContent && content.parseCommandsWithParams().keys.contains("stop") -> StopState(it.context)
else -> {
execute(content.createResend(it.context))
it
}
}
}
strictlyOn<StopState> {
send(it.context) { +"You have stopped sending of content" }
null
}
command(
"start"
) {
startChain(ExpectContentOrStopState(it.chat.id, it))
}
}.second.join()
}

View File

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -13,12 +13,8 @@ apply plugin: 'application'
mainClassName="FilesLoaderBotKt"
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,14 +1,42 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.downloadFile
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.get.getFileAdditionalInfo
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.flatMap
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.safely
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.shortcuts.*
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MediaContent
import com.github.insanusmokrassar.TelegramBotAPI.utils.filenameFromUrl
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
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.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.triggers_handling.onCommand
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onContentMessage
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onMedia
import dev.inmo.tgbotapi.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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import java.io.File
/**
@@ -16,27 +44,80 @@ import java.io.File
*/
suspend fun main(args: Array<String>) {
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()
val bot = telegramBot(botToken)
val scope = CoroutineScope(Dispatchers.Default)
bot.startGettingFlowsUpdatesByLongPolling(scope = scope) {
val flow = merge (
filterContentMessages<MediaContent>(),
mediaGroupMessages().flatMap()
)
flow.onEach {
safely({ it.printStackTrace() }) {
val pathedFile = bot.getFileAdditionalInfo(it.content.media)
File(directoryOrFile, pathedFile.filePath.filenameFromUrl).apply {
createNewFile()
writeBytes(bot.downloadFile(pathedFile))
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
onCommand("start") {
reply(it, "Send me any media (like photo or video) to download 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()
)
}
}
}
}.launchIn(scope)
}
scope.coroutineContext[Job]!!.join()
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

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="ForwardInfoSenderBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,60 @@
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.onContentMessage
import dev.inmo.tgbotapi.extensions.utils.formatting.makeLink
import dev.inmo.tgbotapi.types.chat.CommonBot
import dev.inmo.tgbotapi.types.chat.CommonUser
import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.message.*
import dev.inmo.tgbotapi.utils.buildEntities
import dev.inmo.tgbotapi.utils.code
import dev.inmo.tgbotapi.utils.link
import dev.inmo.tgbotapi.utils.regular
import kotlinx.coroutines.*
/**
* This bot will always return message about forwarder. In cases when sent message was not a forward message it will
* send suitable message
*/
suspend fun main(vararg args: String) {
val botToken = args.first()
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
onContentMessage {
val toAnswer = buildEntities {
when (val forwardInfo = it.forwardInfo) {
null -> +"There is no forward info"
is ForwardInfo.ByAnonymous -> {
regular("Anonymous user which signed as \"") + code(forwardInfo.senderName) + "\""
}
is ForwardInfo.ByUser -> {
val user = forwardInfo.from
when (user) {
is CommonUser -> {
if (user.isPremium) {
regular("Premium user ")
} else {
regular("User ")
}
}
is CommonBot,
is ExtendedBot -> regular("Bot ")
} + code(user.id.chatId.toString()) + " (${user.firstName} ${user.lastName}: ${user.username?.username ?: "Without username"})"
}
is ForwardInfo.PublicChat.FromChannel -> {
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)
}
}.second.join()
}

View File

@@ -1,24 +0,0 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="ForwarderBotKt"
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
}

View File

@@ -1,43 +0,0 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.send.sendTextMessage
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.formatting.codeMarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.formatting.regularMarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.safely
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.asContentMessagesFlow
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.MarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.types.message.*
import com.github.insanusmokrassar.TelegramBotAPI.types.message.abstracts.PossiblyForwardedMessage
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
/**
* This bot will always return message about forwarder. In cases when sent message was not a forward message it will
* send suitable message
*/
suspend fun main(vararg args: String) {
val botToken = args.first()
val bot = telegramBot(botToken)
val scope = CoroutineScope(Dispatchers.Default)
bot.startGettingFlowsUpdatesByLongPolling(scope = scope) {
messageFlow.asContentMessagesFlow().mapNotNull { it as? PossiblyForwardedMessage }.onEach { message ->
safely({ it.printStackTrace() }) {
val toAnswer = when (val forwardInfo = message.forwardInfo) {
null -> "There is no forward info"
is AnonymousForwardInfo -> "Anonymous user which signed as \"${forwardInfo.senderName.codeMarkdownV2()}\""
is UserForwardInfo -> forwardInfo.from.let { user ->
"User ${user.id.chatId.toString().codeMarkdownV2()} " + "(${user.firstName} ${user.lastName}: ${user.username ?.username ?: "Without username"})".regularMarkdownV2()
}
is ForwardFromChannelInfo -> "Channel (".regularMarkdownV2() + (forwardInfo.channelChat).title.codeMarkdownV2() + ")".regularMarkdownV2()
is ForwardFromSupergroupInfo -> "Supergroup (".regularMarkdownV2() + (forwardInfo.group).title.codeMarkdownV2() + ")".regularMarkdownV2()
}
bot.sendTextMessage(message.chat, toAnswer, MarkdownV2)
}
}.launchIn(scope)
}
scope.coroutineContext[Job]!!.join()
}

View File

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -11,14 +11,11 @@ buildscript {
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName="HelloBotKt"
mainClassName="GetMeBotKt"
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,5 +1,5 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.bot.getMe
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
/**
* This is one of the most easiest bot - it will just print information about itself
@@ -10,4 +10,4 @@ suspend fun main(vararg args: String) {
val bot = telegramBot(botToken)
println(bot.getMe())
}
}

View File

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -13,12 +13,9 @@ apply plugin: 'application'
mainClassName="HelloBotKt"
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -1,54 +1,69 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.chat.get.getChat
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.send.sendTextMessage
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.formatting.linkMarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.formatting.textMentionMarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.safely
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling
import com.github.insanusmokrassar.TelegramBotAPI.types.ParseMode.MarkdownV2
import com.github.insanusmokrassar.TelegramBotAPI.types.User
import com.github.insanusmokrassar.TelegramBotAPI.types.chat.abstracts.*
import com.github.insanusmokrassar.TelegramBotAPI.utils.extensions.escapeMarkdownV2Common
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.behaviour_builder.telegramBotWithBehaviourAndLongPolling
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.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.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.utils.PreviewFeature
import dev.inmo.tgbotapi.utils.extensions.escapeMarkdownV2Common
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
/**
* The main purpose of this bot is just to answer "Oh, hi, " and add user mention here
*/
@OptIn(PreviewFeature::class)
suspend fun main(vararg args: String) {
val botToken = args.first()
val bot = telegramBot(botToken)
telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO)) {
val me = getMe()
onMentionWithAnyContent(me) { message ->
val chat = message.chat
val scope = CoroutineScope(Dispatchers.Default)
bot.startGettingFlowsUpdatesByLongPolling(scope = scope) {
messageFlow.onEach {
safely {
val chat = it.data.chat
val message = "Oh, hi, " + when (chat) {
is PrivateChat -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
is User -> "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id)
is SupergroupChat -> (chat.username ?.username ?: bot.getChat(chat).inviteLink) ?.let {
chat.title.linkMarkdownV2(it)
} ?: chat.title
is GroupChat -> bot.getChat(chat).inviteLink ?.let {
chat.title.linkMarkdownV2(it)
} ?: chat.title
else -> "Unknown :(".escapeMarkdownV2Common()
val answerText = when (val chat = message.chat) {
is PreviewChannelChat -> {
val answer = "Hi everybody in this channel \"${chat.title}\""
reply(message, answer, MarkdownV2)
return@onMentionWithAnyContent
}
bot.sendTextMessage(chat, message, MarkdownV2)
is PreviewPrivateChat -> {
reply(message, "Hi, " + "${chat.firstName} ${chat.lastName}".textMentionMarkdownV2(chat.id), MarkdownV2)
return@onMentionWithAnyContent
}
is PreviewGroupChat -> {
message.ifFromChannelGroupContentMessage {
val answer = "Hi, ${it.senderChat.title}"
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()
}
}.launchIn(scope)
channelPostFlow.onEach {
safely {
val chat = it.data.chat
val message = "Hi everybody in this channel \"${(chat as ChannelChat).title}\""
bot.sendTextMessage(chat, message, MarkdownV2)
}
}.launchIn(scope)
}
scope.coroutineContext[Job]!!.join()
}
reply(
message,
answerText,
MarkdownV2
)
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) }
}.second.join()
}

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.0'
}

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

@@ -0,0 +1,32 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
}
kotlin {
jvm()
js(IR) {
browser()
binaries.executable()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
}
}
}
}

View File

@@ -0,0 +1,170 @@
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.answers.answer
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.send.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.extensions.utils.withContent
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.InlineQueries.InlineQueryResult.InlineQueryResultArticle
import dev.inmo.tgbotapi.types.InlineQueries.InputMessageContent.InputTextMessageContent
import dev.inmo.tgbotapi.types.message.content.TextContent
import dev.inmo.tgbotapi.utils.*
import kotlinx.coroutines.*
private const val nextPageData = "next"
private const val previousPageData = "previous"
fun String.parsePageAndCount(): Pair<Int, Int>? {
val (pageString, countString) = split(" ").takeIf { it.count() > 1 } ?: return null
return Pair(
pageString.toIntOrNull() ?: return null,
countString.toIntOrNull() ?: return null
)
}
fun InlineKeyboardBuilder.includePageButtons(page: Int, count: Int) {
val numericButtons = listOfNotNull(
page - 1,
page,
page + 1,
)
row {
val numbersRange = 1 .. count
numericButtons.forEach {
if (it in numbersRange) {
dataButton(it.toString(), "$it $count")
}
}
}
row {
if (page - 1 > 2) {
dataButton("<<", "1 $count")
}
if (page - 1 > 1) {
dataButton("<", "${page - 2} $count")
}
if (page + 1 < count) {
dataButton(">", "${page + 2} $count")
}
if (page + 2 < count) {
dataButton(">>", "$count $count")
}
}
row {
inlineQueryInChosenChatButton(
"Send somebody page",
query = "$page $count",
allowUsers = true,
allowBots = true,
allowGroups = true,
allowChannels = true,
)
}
}
suspend fun activateKeyboardsBot(
token: String,
print: (Any) -> Unit
) {
val bot = telegramBot(token)
print(bot.getMe())
bot.buildBehaviourWithLongPolling(CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onCommandWithArgs("inline") { message, args ->
val numberOfPages = args.firstOrNull() ?.toIntOrNull() ?: 10
reply(
message,
replyMarkup = inlineKeyboard {
includePageButtons(1, numberOfPages)
}
) {
regular("Your inline keyboard with $numberOfPages pages")
}
}
onMessageDataCallbackQuery {
val (page, count) = it.data.parsePageAndCount() ?: it.let {
answer(it, "Unsupported data :(")
return@onMessageDataCallbackQuery
}
edit(
it.message.withContent<TextContent>() ?: it.let {
answer(it, "Unsupported message type :(")
return@onMessageDataCallbackQuery
},
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
) {
regular("This is $page of $count")
}
answer(it)
}
onInlineMessageIdDataCallbackQuery {
val (page, count) = it.data.parsePageAndCount() ?: it.let {
answer(it, "Unsupported data :(")
return@onInlineMessageIdDataCallbackQuery
}
editMessageText(
it.inlineMessageId,
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
) {
regular("This is $page of $count")
}
answer(it)
}
onBaseInlineQuery {
val page = it.query.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
val count = it.query.removePrefix(page.toString()).dropWhile { !it.isDigit() }.takeWhile { it.isDigit() }.toIntOrNull() ?: return@onBaseInlineQuery
answer(
it,
results = listOf(
InlineQueryResultArticle(
it.query,
"Send buttons",
InputTextMessageContent("It is sent via inline mode inline buttons"),
replyMarkup = inlineKeyboard {
includePageButtons(page, count)
}
)
)
)
}
onUnhandledCommand {
reply(
it,
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
row {
simpleButton("/inline")
}
}
) {
+"Use " + botCommand("inline") + " to get pagination inline keyboard"
}
}
setMyCommands(BotCommand("inline", "Creates message with pagination inline keyboard"))
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 {
activateKeyboardsBot(token) {
infoDiv.innerHTML = it.toString()
}
}
}
false
}
}
)
}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Keyboards bot</title>
</head>
<body>
<form id="bot_token_form">
<input type="text" id="bot_token">
<input type="submit" value="Start bot">
</form>
<div id="start_offer">Type your bot token to the input above to start its work</div>
<script type="text/javascript" src="KeyboardsBotLib.js"></script>
<div id="bots_container"></div>
</body>
</html>

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="KeyboardsBotJvmKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(":KeyboardsBot:KeyboardsBotLib")
}

View File

@@ -0,0 +1,10 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
suspend fun main(args: Array<String>) {
withContext(Dispatchers.IO) { // IO for inheriting of it in side of activateKeyboardsBot
activateKeyboardsBot(args.first()) {
println(it)
}
}
}

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,99 @@
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.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,
replyToMessageId = it.messageId
)
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,
replyToMessageId = it.messageId
)
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

@@ -1,2 +1,34 @@
# TelegramBotAPI-examples
This repository contains several examples of simple bots which are using TelegramBotAPI
## 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:
* Example subprojects
* Commits
* Structure
### Example subproject
Each example subproject contains information about how to run this example and what is it
doing. Usually, it is some simple thing like sending "hello" message to the user which
wrote to the bot.
### Commits
Commits can contains some things like migration onto new version (especially it is actual
for major version changes), updates according to the new features in versions and
different other things which usually more important in context of history or changes
between library version
### Structure
Structure of this repository fully representative (it is the reason why this repo
contains multiplatform subprojects) and you can use it as some template (but I am strongly
recommend you to use my
[TelegramBot template](https://github.com/InsanusMokrassar/TelegramBotAPI-bot_template) or
[Multiplatform Project template](https://github.com/InsanusMokrassar/KotlinMultiplatformProjectTemplate))

View File

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -8,17 +8,27 @@ buildscript {
}
}
apply plugin: 'kotlin'
plugins {
id "org.jetbrains.kotlin.multiplatform"
}
apply plugin: 'application'
mainClassName="RandomFileSenderBotKt"
repositories {
jcenter()
kotlin {
jvm()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
}
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
apply from: "$nativePartTemplate"
implementation "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,86 @@
import dev.inmo.micro_utils.common.MPPFile
import dev.inmo.micro_utils.common.filesize
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocument
import dev.inmo.tgbotapi.extensions.api.send.media.sendDocumentsGroup
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.api.send.withUploadDocumentAction
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommandWithArgs
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.Chat
import dev.inmo.tgbotapi.types.media.TelegramMediaDocument
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
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.
* You may send /send_file command to this bot to get random file from the directory OR
* `/send_file $number` when you want to receive required number of files. For example,
* /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
*/
suspend fun doRandomFileSenderBot(token: String, folder: MPPFile) {
val bot = telegramBot(token)
suspend fun TelegramBot.sendFiles(chat: Chat, files: List<MPPFile>) {
when (files.size) {
1 -> sendDocument(
chat.id,
files.first().asMultipartFile(),
protectContent = true
)
else -> sendDocumentsGroup(
chat,
files.map { TelegramMediaDocument(it.asMultipartFile()) },
protectContent = true
)
}
}
bot.buildBehaviourWithLongPolling (defaultExceptionsHandler = { it.printStackTrace() }) {
onCommandWithArgs(command) { message, args ->
withUploadDocumentAction(message.chat) {
val count = args.firstOrNull() ?.toIntOrNull() ?: 1
var sent = false
var left = count
val chosen = mutableListOf<MPPFile>()
while (left > 0) {
val picked = pickFile(folder) ?.takeIf { it.filesize > 0 } ?: continue
chosen.add(picked)
left--
if (chosen.size >= mediaCountInMediaGroup.last) {
sendFiles(message.chat, chosen)
chosen.clear()
sent = true
}
}
if (chosen.isNotEmpty()) {
sendFiles(message.chat, chosen)
sent = true
}
if (!sent) {
reply(message, "Nothing selected :(")
}
}
}
setMyCommands(
BotCommand(command, "Send some random file in picker directory")
)
println(getMe())
}.join()
}

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

@@ -1,57 +0,0 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.bot.getMe
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.bot.setMyCommands
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.send.media.sendDocument
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.send.sendTextMessage
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.safely
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.filterExactCommands
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling
import com.github.insanusmokrassar.TelegramBotAPI.requests.abstracts.toInputFile
import com.github.insanusmokrassar.TelegramBotAPI.types.BotCommand
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.io.File
private const val command = "send_file"
/**
* This bot will send files inside of working directory OR from directory in the second argument
*/
suspend fun main(args: Array<String>) {
val botToken = args.first()
val directoryOrFile = args.getOrNull(1) ?.let { File(it) } ?: File("")
fun pickFile(currentRoot: File = directoryOrFile): File? {
if (currentRoot.isFile) {
return currentRoot
} else {
return pickFile(currentRoot.listFiles() ?.random() ?: return null)
}
}
val bot = telegramBot(botToken)
val scope = CoroutineScope(Dispatchers.Default)
bot.startGettingFlowsUpdatesByLongPolling(scope = scope) {
messageFlow.filterExactCommands(Regex(command)).onEach { message ->
safely {
pickFile() ?.let {
bot.sendDocument(
message.chat.id,
it.toInputFile()
)
} ?: bot.sendTextMessage(message.chat.id, "Nothing selected :(")
}
}.launchIn(scope)
}
safely {
bot.setMyCommands(
BotCommand(command, "Send some random file in picker directory")
)
println(bot.getMe())
}
scope.coroutineContext[Job]!!.join()
}

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

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -9,26 +9,27 @@ buildscript {
}
plugins {
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
id "org.jetbrains.kotlin.multiplatform"
}
repositories {
jcenter()
}
kotlin {
jvm()
// js(LEGACY) {
js(IR) {
browser()
binaries.executable()
}
linuxX64()
mingwX64()
linuxArm64()
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "com.github.insanusmokrassar:TelegramBotAPI:$telegram_bot_api_version"
api "dev.inmo:tgbotapi:$telegram_bot_api_version"
}
}
}

View File

@@ -1,43 +1,44 @@
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.bot.getMe
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.send.media.sendMediaGroup
import com.github.insanusmokrassar.TelegramBotAPI.extensions.api.telegramBot
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.safely
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.shortcuts.*
import com.github.insanusmokrassar.TelegramBotAPI.extensions.utils.updates.retrieving.startGettingFlowsUpdatesByLongPolling
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MediaGroupContent
import com.github.insanusmokrassar.TelegramBotAPI.types.message.content.abstracts.MessageContent
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.send.*
import dev.inmo.tgbotapi.extensions.api.send.media.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.CommonMessageFilterExcludeMediaGroups
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.MessageFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.shortcuts.*
import dev.inmo.tgbotapi.utils.extensions.threadIdOrNull
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
suspend fun activateResenderBot(
token: String,
print: (Any) -> Unit
) {
val bot = telegramBot(token)
telegramBotWithBehaviourAndLongPolling(token, scope = CoroutineScope(currentCoroutineContext() + SupervisorJob())) {
onContentMessage(
subcontextUpdatesFilter = MessageFilterByChat,
) {
val chat = it.chat
print(bot.getMe())
val answer = withTypingAction(chat) {
executeUnsafe(
it.content.createResend(
chat.id,
messageThreadId = it.threadIdOrNull,
replyToMessageId = it.messageId
)
) {
it.forEach(print)
}
}
supervisorScope {
val scope = this
bot.startGettingFlowsUpdatesByLongPolling {
filterContentMessages<MessageContent>(scope).onEach {
it.content.createResends(it.chat.id, replyToMessageId = it.messageId).forEach {
bot.executeUnsafe(it) {
it.forEach(print)
} ?.also {
print(it)
}
}
}.launchIn(scope)
mediaGroupMessages(scope).onEach {
safely({ print(it.stackTraceToString()) }) {
println(it.chat)
bot.execute(it.createResend(it.chat ?: return@safely, replyTo = it.first().messageId)).also {
print(it)
}
}
}.launchIn(scope)
println("Answer info: $answer")
}
}
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
print(bot.getMe())
}.second.join()
}

View File

@@ -1,6 +1,6 @@
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
@@ -13,12 +13,9 @@ apply plugin: 'application'
mainClassName="ResenderBotJvmKt"
repositories {
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation project(":ResenderBot:ResenderBotLib")
}

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.3'
}

View File

@@ -0,0 +1,507 @@
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.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
reply(
replyMessage,
"Manage keyboard:",
replyMarkup = buildCommonKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
)
}
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
reply(
replyMessage,
"Manage keyboard:",
replyMarkup = buildGranularKeyboard(it.chat.id.toChatId(), userInReply) ?: return@onCommand
)
}
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

@@ -0,0 +1,9 @@
# SlotMachineDetectorBot
This bot must reply with information about slot machine answer
## 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="SlotMachineDetectorBotKt"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
}

View File

@@ -0,0 +1,26 @@
import dev.inmo.tgbotapi.bot.ktor.telegramBot
import dev.inmo.tgbotapi.extensions.api.send.reply
import dev.inmo.tgbotapi.extensions.behaviour_builder.buildBehaviourWithLongPolling
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDice
import dev.inmo.tgbotapi.extensions.utils.*
import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType
import kotlinx.coroutines.*
suspend fun main(args: Array<String>) {
val bot = telegramBot(args.first())
bot.buildBehaviourWithLongPolling(scope = CoroutineScope(Dispatchers.IO)) {
onDice {
val content = it.content
val dice = content.dice
val diceType = dice.animationType
if (diceType == SlotMachineDiceAnimationType) {
val result = dice.calculateSlotMachineResult() ?: return@onDice
reply(it, "${result.leftReel}|${result.centerReel}|${result.rightReel}")
} else {
reply(it, "There is no slot machine dice in message")
}
}
}.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,218 @@
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.utils.types.buttons.replyKeyboard
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestBotButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestChatButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestGroupButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestUserButton
import dev.inmo.tgbotapi.extensions.utils.types.buttons.requestUserOrBotButton
import dev.inmo.tgbotapi.types.BotCommand
import dev.inmo.tgbotapi.types.chat.PrivateChat
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 requestIdAnyChat = RequestId(5)
val requestIdChannel = RequestId(6)
val requestIdPublicChannel = RequestId(7)
val requestIdPrivateChannel = RequestId(8)
val requestIdChannelUserOwner = RequestId(9)
val requestIdGroup = RequestId(10)
val requestIdPublicGroup = RequestId(11)
val requestIdPrivateGroup = RequestId(12)
val requestIdGroupUserOwner = RequestId(13)
val requestIdForum = RequestId(14)
val requestIdPublicForum = RequestId(15)
val requestIdPrivateForum = RequestId(16)
val requestIdForumUserOwner = RequestId(17)
val keyboard = replyKeyboard(
resizeKeyboard = true,
) {
row {
requestUserOrBotButton(
"\uD83D\uDC64/\uD83E\uDD16",
requestIdUserOrBot
)
}
row {
requestUserButton(
"\uD83D\uDC64",
requestIdUserNonPremium,
premiumUser = false
)
requestUserButton(
"\uD83D\uDC64",
requestIdUserAny,
premiumUser = null
)
requestUserButton(
"\uD83D\uDC64",
requestIdUserPremium,
premiumUser = true
)
requestBotButton(
"\uD83E\uDD16",
requestIdBot
)
}
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
)
}
onUserShared {
val userId = it.chatEvent.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()
}

17
WebApp/README.md Normal file
View File

@@ -0,0 +1,17 @@
# WebApp
Here you may find simple example of `WebApp`. For work of this example you will need one of two things:
* Your own domain with SSL (letsencrypt is okay)
* Test account in telegram
What is there in this module:
* JVM part of this example is a server with simple static webapp sharing and bot which just gives the webapp button to open webapp
* JS part is the WebApp with one button and reacting to chaged user theme and app viewport
## How to run
```bash
./gradlew run --args="TOKEN WEB_APP_ADDRESS"
```

57
WebApp/build.gradle Normal file
View File

@@ -0,0 +1,57 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply plugin: 'application'
kotlin {
jvm()
js(IR) {
browser()
binaries.executable()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
}
}
jsMain {
dependencies {
implementation "dev.inmo:tgbotapi.webapps:$telegram_bot_api_version"
}
}
jvmMain {
dependencies {
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
implementation "dev.inmo:micro_utils.ktor.server:$micro_utils_version"
implementation "io.ktor:ktor-server-cio:$ktor_version"
}
}
}
}
application {
mainClassName = "WebAppServerKt"
}
tasks.getByName("compileKotlinJvm")
.dependsOn(jsBrowserDistribution)
tasks.getByName("compileKotlinJvm").configure {
mustRunAfter jsBrowserDistribution
}

View File

@@ -0,0 +1,7 @@
import kotlinx.serialization.Serializable
@Serializable
data class WebAppDataWrapper(
val data: String,
val hash: String
)

View File

@@ -0,0 +1,308 @@
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.types.webAppQueryIdField
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.request.*
import io.ktor.client.statement.bodyAsText
import io.ktor.http.*
import io.ktor.http.content.TextContent
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.coroutines.*
import kotlinx.dom.appendElement
import kotlinx.dom.appendText
import kotlinx.dom.clear
import kotlinx.serialization.json.Json
import org.w3c.dom.*
import kotlin.random.Random
import kotlin.random.nextUBytes
fun HTMLElement.log(text: String) {
appendText(text)
appendElement("p", {})
}
fun main() {
console.log("Web app started")
val client = HttpClient()
val baseUrl = window.location.origin.removeSuffix("/")
window.onload = {
val scope = CoroutineScope(Dispatchers.Default)
runCatching {
scope.launchSafelyWithoutExceptions {
val response = client.post("$baseUrl/check") {
setBody(
Json { }.encodeToString(
WebAppDataWrapper.serializer(),
WebAppDataWrapper(webApp.initData, webApp.initDataUnsafe.hash)
)
)
}
val dataIsSafe = response.bodyAsText().toBoolean()
document.body ?.log(
if (dataIsSafe) {
"Data is safe"
} else {
"Data is unsafe"
}
)
document.body ?.log(
webApp.initDataUnsafe.chat.toString()
)
}
document.body ?.appendElement("button") {
addEventListener("click", {
scope.launchSafelyWithoutExceptions {
handleResult({ "Clicked" }) {
client.post("${window.location.origin.removeSuffix("/")}/inline") {
parameter(webAppQueryIdField, it)
setBody(TextContent("Clicked", ContentType.Text.Plain))
document.body ?.log(url.build().toString())
}.coroutineContext.job.join()
}
}
})
appendText("Answer in chat button")
} ?: 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 {
onThemeChanged {
document.body ?.log("Theme changed: ${webApp.themeParams}")
}
onViewportChanged {
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()
document.body ?.appendElement("input", {
(this as HTMLInputElement).value = window.location.href
})
cloudStorageContentDiv.updateCloudStorageContent()
}.onFailure {
window.alert(it.stackTraceToString())
}
}
}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<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>
</head>
<body>
<script type="application/javascript" src="https://telegram.org/js/telegram-web-app.js"></script>
<script type="application/javascript" src="WebApp.js"></script>
</body>
</html>

View File

@@ -0,0 +1,151 @@
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.ktor.server.createKtorServer
import dev.inmo.tgbotapi.extensions.api.answers.answer
import dev.inmo.tgbotapi.extensions.api.answers.answerInlineQuery
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.bot.setMyCommands
import dev.inmo.tgbotapi.extensions.api.send.*
import dev.inmo.tgbotapi.extensions.api.telegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.*
import dev.inmo.tgbotapi.extensions.utils.formatting.makeTelegramStartattach
import dev.inmo.tgbotapi.extensions.utils.types.buttons.*
import dev.inmo.tgbotapi.requests.answers.InlineQueryResultsButton
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.webAppQueryIdField
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.*
import io.ktor.http.*
import io.ktor.server.application.call
import io.ktor.server.http.content.*
import io.ktor.server.request.receiveText
import io.ktor.server.response.respond
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.json.Json
import java.io.File
import java.nio.charset.Charset
/**
* Accepts two parameters:
*
* * Telegram Token
* * URL where will be placed
*
* Will start the server to share the static (index.html and WebApp.js) on 0.0.0.0:8080
*/
@OptIn(PreviewFeature::class)
suspend fun main(vararg args: String) {
val telegramBotAPIUrlsKeeper = TelegramAPIUrlsKeeper(
args.first(),
testServer = args.any { it == "testServer" }
)
val bot = telegramBot(telegramBotAPIUrlsKeeper)
createKtorServer(
"0.0.0.0",
args.getOrNull(2) ?.toIntOrNull() ?: 8080,
additionalEngineEnvironmentConfigurator = {
parentCoroutineContext += Dispatchers.IO
}
) {
routing {
val baseJsFolder = File("WebApp/build/dist/js/")
baseJsFolder.list() ?.forEach {
if (it == "productionExecutable" || it == "developmentExecutable") {
staticFiles("", File(baseJsFolder, it)) {
default("WebApp/build/dist/js/$it/index.html")
}
}
}
post("inline") {
val requestBody = call.receiveText()
val queryId = call.parameters[webAppQueryIdField] ?: error("$webAppQueryIdField should be presented")
bot.answer(queryId, InlineQueryResultArticle(queryId, "Result", InputTextMessageContent(requestBody)))
call.respond(HttpStatusCode.OK)
}
post("check") {
val requestBody = call.receiveText()
val webAppCheckData = Json { }.decodeFromString(WebAppDataWrapper.serializer(), requestBody)
val isSafe = telegramBotAPIUrlsKeeper.checkWebAppData(webAppCheckData.data, webAppCheckData.hash)
call.respond(HttpStatusCode.OK, isSafe.toString())
}
}
}.start(false)
bot.buildBehaviourWithLongPolling(
defaultExceptionsHandler = { it.printStackTrace() }
) {
val me = getMe()
onCommand("reply_markup") {
reply(
it,
"Button",
replyMarkup = replyKeyboard(resizeKeyboard = true, oneTimeKeyboard = true) {
row {
webAppButton("Open WebApp", WebAppInfo(args[1]))
}
}
)
}
onCommand("inline") {
reply(
it,
"Button",
replyMarkup = inlineKeyboard {
row {
webAppButton("Open WebApp", WebAppInfo(args[1]))
}
}
)
}
onCommand("attachment_menu") {
reply(
it,
"Button",
replyMarkup = inlineKeyboard {
row {
webAppButton("Open WebApp", WebAppInfo(args[1]))
}
}
)
}
onBaseInlineQuery {
answerInlineQuery(
it,
button = InlineQueryResultsButton.invoke(
"Open webApp",
WebAppInfo(args[1])
)
)
}
onUnhandledCommand {
reply(
it,
buildEntities {
+"Use " + botCommand("inline") + " to get inline web app button\n"
+"Use " + botCommand("reply_markup") + " to get reply markup web app button\n"
}
)
}
onWriteAccessAllowed(initialFilter = { it.chatEvent.webAppName != null }) {
send(it.chat, "Thanks for adding ${it.chatEvent.webAppName} to the attachment menu")
}
setMyCommands(
BotCommand("reply_markup", "Use to get reply markup keyboard with web app trigger"),
BotCommand("inline", "Use to get inline keyboard with web app trigger"),
)
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
println(it)
}
println(getMe())
}.join()
}

View File

@@ -1,5 +1,31 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
allprojects {
ext {
nativePartTemplate = "${rootProject.projectDir.absolutePath}/native_template.gradle"
}
repositories {
mavenLocal()
mavenCentral()
if (project.hasProperty("GITHUB_USER") && project.hasProperty("GITHUB_TOKEN")) {
maven {
url "https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"
credentials {
username = project.getProperty("GITHUB_USER")
password = project.getProperty("GITHUB_TOKEN")
}
}
}
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" }
}
}
}

View File

@@ -1,5 +1,11 @@
kotlin.code.style=official
org.gradle.parallel=true
# Due to parallel compilation project require next amount of memory on full build
org.gradle.jvmargs=-Xmx2g
kotlin_version=1.4.10
telegram_bot_api_version=0.28.5
kotlin_version=1.9.20
telegram_bot_api_version=9.3.0
micro_utils_version=0.20.12
serialization_version=1.6.0
ktor_version=2.3.5

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-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

@@ -1,7 +1,43 @@
include ":ForwarderBot"
include ":ForwardInfoSenderBot"
include ":RandomFileSenderBot"
include ":HelloBot"
include ":PollsBot"
include ":GetMeBot"
include ":DeepLinksBot"
include ":FilesLoaderBot"
include ":ResenderBot:ResenderBotLib"
include ":ResenderBot:jvm_launcher"
include ":ResenderBot:native_launcher"
include ":KeyboardsBot:KeyboardsBotLib"
include ":KeyboardsBot:jvm_launcher"
include ":StickerInfoBot:StickerInfoBotLib"
include ":StickerInfoBot:jvm_launcher"
include ":SlotMachineDetectorBot"
include ":ChatAvatarSetter"
include ":WebApp"
include ":FSMBot"
include ":TopicsHandling"
include ":UserChatShared"
include ":RightsChangerBot"
include ":LiveLocationsBot"
include ":StickerSetHandler"
include ":InlineQueriesBot"