Compare commits

...

229 Commits

Author SHA1 Message Date
renovate[bot]
03871c28d7 Update Gradle to v9 2025-10-21 17:23:03 +00:00
061bac146c Merge pull request #127 from InsanusMokrassar/0.27.0
0.27.0
2025-10-21 23:20:45 +06:00
00fc2a1e0b update dependencies 2025-10-21 23:20:18 +06:00
1e2e45c404 start 0.27.0 2025-10-21 23:17:55 +06:00
17398781a5 Merge pull request #124 from InsanusMokrassar/0.26.8
0.26.8
2025-09-04 17:44:40 +06:00
f86dbcefce update dependencies 2025-09-04 17:38:47 +06:00
e4aba03cdc start 0.26.8 2025-09-04 17:33:39 +06:00
c8f8608a60 Merge pull request #117 from InsanusMokrassar/0.26.7
0.26.7
2025-08-04 13:47:08 +06:00
19fcee9f3e start 0.26.7 and update dependencies 2025-08-04 13:38:57 +06:00
825e6474cc Merge pull request #111 from InsanusMokrassar/0.26.6
0.26.6
2025-06-27 09:50:49 +06:00
ff99bdd45d update publishing way 2025-06-27 09:50:18 +06:00
2ae7507c01 0.26.6 2025-06-27 09:48:39 +06:00
7b30d95f56 Merge pull request #108 from InsanusMokrassar/0.26.5
0.26.5
2025-04-30 13:42:46 +06:00
bbe9367ef3 update gradle wrapper 2025-04-30 13:37:12 +06:00
6403fe6f5d update dependencies 2025-04-30 13:36:50 +06:00
3cad9ec337 start 0.26.5 2025-04-30 13:34:36 +06:00
78e5da9db7 Merge pull request #105 from InsanusMokrassar/0.26.4
0.26.4
2025-03-26 22:40:12 +06:00
74329a8eb2 small improvements 2025-03-26 20:41:46 +06:00
953df71d9e add MessageContentCache.sendAndSave 2025-03-26 18:14:25 +06:00
c25f6d522f start 0.26.4 2025-03-26 18:10:27 +06:00
d569f46e48 Merge pull request #100 from InsanusMokrassar/0.26.3
0.26.3
2025-02-16 09:24:40 +06:00
f2731f6e7d update dependencies 2025-02-16 09:23:30 +06:00
f10c7412f4 start 0.26.3 2025-02-16 09:23:08 +06:00
a113e6e37e Merge pull request #96 from InsanusMokrassar/0.26.2
0.26.2
2025-01-29 14:33:23 +06:00
7c6005bea3 update dependencies 2025-01-29 14:32:39 +06:00
3f6067b34e start 0.26.2 2025-01-29 14:30:21 +06:00
af72bba116 Merge pull request #84 from InsanusMokrassar/0.26.1
0.26.1
2024-11-11 16:17:26 +06:00
84f04fb1f0 Merge pull request #82 from InsanusMokrassar/renovate/configure
Configure Renovate
2024-11-11 16:15:40 +06:00
5524fc9fa0 start 0.26.1 and update dependencies 2024-11-11 16:08:08 +06:00
d0eebc0ace Merge pull request #83 from InsanusMokrassar/0.26.0
0.26.0
2024-11-04 22:23:30 +06:00
891dab1f4b fixes of build 2024-11-04 22:23:16 +06:00
9f6895a530 update dependencies 2024-11-04 22:14:39 +06:00
49084169d2 start 0.26.0 2024-11-04 22:14:09 +06:00
renovate[bot]
684172cc08 Add renovate.json 2024-10-22 10:46:07 +00:00
1457cfc748 Merge pull request #81 from InsanusMokrassar/0.25.0
0.25.0
2024-09-22 19:40:07 +06:00
6784a3c49a update plagubot 2024-09-22 18:22:07 +06:00
8ef89a32c7 Merge pull request #80 from InsanusMokrassar/0.24.0
0.24.0
2024-09-14 21:24:53 +06:00
d16de6428a start 0.24.0 2024-09-14 20:25:49 +06:00
49475a457d Merge pull request #79 from InsanusMokrassar/0.23.0
0.23.0
2024-09-06 00:16:21 +06:00
b8312873b0 start 0.23.0 && update dependencies 2024-09-06 00:14:42 +06:00
3ede07c2a6 Merge pull request #78 from InsanusMokrassar/0.22.0
0.22.0
2024-08-12 16:44:02 +06:00
a0e17a664c fix of build 2024-08-12 16:42:37 +06:00
fc3bf19453 start 0.22.0 and update dependencies 2024-08-12 16:37:18 +06:00
8d1c57700e Merge pull request #77 from InsanusMokrassar/0.21.1
0.21.1
2024-07-24 16:43:37 +06:00
00c0f0fd6f start 0.21.1 and update dependencies 2024-07-24 16:41:36 +06:00
551eb293ab Merge pull request #76 from InsanusMokrassar/0.21.0
0.21.0
2024-07-11 23:10:05 +06:00
f1b58a889f update dependencies 2024-07-11 23:08:41 +06:00
36a0f9b65b start 0.21.0 2024-07-11 23:07:47 +06:00
86abe49bc1 Merge pull request #75 from InsanusMokrassar/0.20.0
0.20.0
2024-06-27 23:27:26 +06:00
5b62bfa7d3 update dependencies 2024-06-27 23:26:52 +06:00
d94d847715 start 0.20.0 2024-06-27 23:18:23 +06:00
91013b914d Merge pull request #74 from InsanusMokrassar/0.19.0
0.19.0
2024-04-23 21:21:02 +06:00
cec4c610f3 fixes in build 2024-04-23 21:20:03 +06:00
b9003388b1 Merge pull request #73 from InsanusMokrassar/0.19.0
0.19.0
2024-04-23 18:53:32 +06:00
6d161c2d78 update dependencies 2024-04-23 18:52:47 +06:00
dc63639fee Merge pull request #72 from InsanusMokrassar/0.18.3
0.18.3
2024-02-21 02:25:50 +06:00
66934c823d revert algorithm of messages resender 2024-02-21 02:22:45 +06:00
90870c225c start 0.18.3 2024-02-21 02:00:10 +06:00
106d01775c Merge pull request #71 from InsanusMokrassar/0.18.2
0.18.2
2024-02-18 21:38:49 +06:00
cea2f7dc6e update dependencies and start 0.18.2 2024-02-18 21:37:38 +06:00
7d461edc9b Merge pull request #70 from InsanusMokrassar/0.18.1
0.18.1
2024-02-10 23:48:47 +06:00
c52c6cb633 update dependencies 2024-02-10 23:48:05 +06:00
6db1755ee7 start 0.18.1 2024-02-10 23:46:47 +06:00
ccf60c95ca Merge pull request #69 from InsanusMokrassar/0.18.0
0.18.0
2024-01-13 15:40:35 +06:00
4057d5167f improvements in MessagesResender 2024-01-13 14:37:21 +06:00
f19664da58 0.18.0 2024-01-12 20:35:48 +06:00
646a551b15 Merge pull request #68 from InsanusMokrassar/0.17.2
0.17.2
2023-12-24 23:33:45 +06:00
a08974e76d update gradle wrapper 2023-12-24 23:33:14 +06:00
30e62041c0 0.17.2 2023-12-24 23:30:19 +06:00
be8fe43e76 Merge pull request #67 from InsanusMokrassar/0.17.1
0.17.1
2023-12-10 15:04:02 +06:00
42f3a064ad update repositories 2023-12-10 14:59:50 +06:00
34f9d8c0ab 0.17.1 2023-12-10 14:56:37 +06:00
6e4fc54a23 Merge pull request #66 from InsanusMokrassar/0.17.0
0.17.0
2023-11-26 21:06:55 +06:00
674ee28991 update dependencies 2023-11-26 21:05:17 +06:00
4950fd4ed0 start 0.17.0 2023-11-26 21:04:41 +06:00
e3f5ae0b24 Merge pull request #65 from InsanusMokrassar/0.16.0
0.16.0
2023-11-06 17:37:49 +06:00
3bf2d858eb update publishing script 2023-11-06 17:35:41 +06:00
b51bc97551 update gradle wrapper version 2023-11-06 17:34:25 +06:00
c0680932ab update jdk version everywhere up to 17 2023-11-06 17:34:07 +06:00
849edadab0 start 0.16.0 and update dependencies 2023-11-06 17:33:02 +06:00
f02933a9d0 Merge pull request #64 from InsanusMokrassar/0.15.3
0.15.3
2023-10-11 15:38:07 +06:00
f018b723cb 0.15.3 2023-10-11 15:34:45 +06:00
65d8fd6be1 0.15.2 2023-10-01 16:01:38 +06:00
d44c13bea5 Merge pull request #63 from InsanusMokrassar/0.15.1
0.15.1
2023-09-29 23:00:14 +06:00
50c1e33b52 update plagubot version 2023-09-29 22:52:37 +06:00
41b9001e1f start 0.15.1 2023-09-29 22:23:54 +06:00
2c44a4e580 Merge pull request #62 from InsanusMokrassar/0.15.0
0.15.0
2023-09-26 01:07:46 +06:00
ffa9525c70 Update gradle.properties 2023-09-26 01:04:09 +06:00
4f2df31e5e Merge pull request #61 from InsanusMokrassar/0.14.0
0.14.0
2023-08-20 14:45:58 +06:00
18f9318b63 0.14.0 2023-08-20 14:40:59 +06:00
c97a48dcd4 Merge pull request #60 from InsanusMokrassar/0.13.0
0.13.0
2023-07-01 16:25:41 +06:00
caaba32f9a Update gradle.properties 2023-07-01 16:22:26 +06:00
2734fc0adc Merge pull request #59 from InsanusMokrassar/0.12.2
0.12.2
2023-06-19 23:31:16 +06:00
9c2e271d57 start 0.12.2 and update dependencies 2023-06-19 23:28:29 +06:00
3edb5b1b11 Merge pull request #58 from InsanusMokrassar/0.12.1
0.12.1
2023-06-07 10:07:22 +06:00
82d859272f 0.12.1 2023-06-07 10:06:27 +06:00
64952fb054 Merge pull request #57 from InsanusMokrassar/0.12.0
0.12.0
2023-05-31 07:16:17 +06:00
bd4e3d2380 Update gradle.properties 2023-05-31 07:13:37 +06:00
d77e08631c make migration 2023-05-28 20:57:43 +06:00
90e063a47a update dependencies 2023-05-28 20:56:54 +06:00
a988117037 start 0.12.0 2023-05-28 20:56:15 +06:00
50d8511db5 Merge pull request #56 from InsanusMokrassar/0.11.3
0.11.3
2023-05-20 12:48:22 +06:00
cf5b02057d 0.11.3 2023-05-20 12:43:01 +06:00
64305c9393 Merge pull request #55 from InsanusMokrassar/0.11.2
0.11.2
2023-05-06 14:01:24 +06:00
b4107cff26 0.11.2 2023-05-06 13:34:00 +06:00
23d68c9aa5 Merge pull request #54 from InsanusMokrassar/0.11.1
0.11.1
2023-05-02 10:19:36 +06:00
735e23cadb Update gradle.properties 2023-05-02 01:53:44 +06:00
59b32f9b9a Merge pull request #53 from InsanusMokrassar/0.11.0
0.11.0
2023-04-22 22:33:58 +06:00
b8601d4c90 add opportunity to be called between resender sends 2023-04-22 22:16:14 +06:00
240ee3de6f start 0.11.0 2023-04-22 21:41:05 +06:00
300ff6514b Merge pull request #52 from InsanusMokrassar/0.10.2
0.10.2
2023-04-20 00:28:11 +06:00
ce85622876 start 0.10.2 2023-04-20 00:23:04 +06:00
bd9e6045a3 Merge pull request #51 from InsanusMokrassar/0.10.1
0.10.1
2023-03-16 20:55:36 +06:00
e8a41d97e8 Update gradle.properties 2023-03-16 19:56:58 +06:00
e2e329a757 Merge pull request #50 from InsanusMokrassar/0.10.0
0.10.0
2023-03-12 00:43:55 +06:00
c69dd61eb9 0.10.0 2023-03-12 00:33:20 +06:00
cf13bfadde Merge pull request #49 from InsanusMokrassar/0.9.4
0.9.4
2023-03-08 22:46:08 +06:00
6e33649e6c 0.9.4 2023-03-08 22:41:16 +06:00
40dd4b166f Merge pull request #48 from InsanusMokrassar/0.9.3
0.9.3
2023-03-02 23:54:49 +06:00
95adaef36f 0.9.3 2023-03-02 22:05:52 +06:00
8386690090 Merge pull request #47 from InsanusMokrassar/0.9.2
0.9.2
2023-03-01 15:48:52 +06:00
0f02910766 0.9.2 2023-03-01 15:37:53 +06:00
46b694c72b Merge pull request #46 from InsanusMokrassar/0.9.1
0.9.1
2023-02-28 21:14:32 +06:00
1bf9a1570f 0.9.1 2023-02-28 20:06:44 +06:00
f24825bcbc Merge pull request #45 from InsanusMokrassar/0.9.0
0.9.0
2023-02-28 14:15:36 +06:00
5b560118c0 start 0.9.0 2023-02-28 00:00:54 +06:00
c27b5647d7 Merge pull request #44 from InsanusMokrassar/0.8.2
0.8.2
2023-02-17 16:04:51 +06:00
6a5cd2f469 update dependencies and commit-publish workflow 2023-02-17 14:11:26 +06:00
69c819162d start 0.8.2 2023-02-17 14:10:14 +06:00
7a603c21b8 Merge pull request #43 from InsanusMokrassar/0.8.1
0.8.1
2023-02-12 01:35:57 +06:00
bfebd1de50 Update gradle.properties 2023-02-12 01:35:42 +06:00
cbbe283305 fixes 2023-02-12 01:13:59 +06:00
a2dab361cf MessageMetaInfo now serialize chatId as is instead of ChatId version 2023-02-11 20:43:22 +06:00
ac89551ac4 start 0.8.1 2023-02-11 20:40:44 +06:00
0b0d1e4ea5 Merge pull request #42 from InsanusMokrassar/0.8.0
0.8.0
2023-02-06 15:33:56 +06:00
9b056656e3 start 0.8.0 && update dependencies 2023-02-06 14:14:52 +06:00
9023cc5acc Update gradle.properties 2023-01-19 00:24:50 +06:00
617b8091db small refactor 2023-01-14 23:42:16 +06:00
88a89ff1e7 add resender module 2023-01-14 22:37:56 +06:00
c211002ac1 start 0.7.1 2023-01-14 22:24:00 +06:00
50f592f52c Merge pull request #40 from InsanusMokrassar/0.7.0
0.7.0
2023-01-01 02:35:50 +06:00
efeb15b971 Update gradle.properties 2023-01-01 02:33:26 +06:00
d33ca67c7c Merge pull request #39 from InsanusMokrassar/0.6.8
0.6.8
2022-12-28 12:46:45 +06:00
f59ff6dd3c Update gradle.properties 2022-12-28 12:43:04 +06:00
8f643a7d5b start 0.6.8 2022-12-28 12:42:28 +06:00
980badd275 Merge pull request #38 from InsanusMokrassar/0.6.7
0.6.7
2022-12-20 09:25:56 +06:00
a2e65f7b24 Update gradle-wrapper.properties 2022-12-20 09:21:14 +06:00
f86cef7118 Update gradle.properties 2022-12-20 09:20:43 +06:00
2278572cb2 Merge pull request #37 from InsanusMokrassar/0.6.6
0.6.6
2022-12-08 11:10:22 +06:00
3ce3b03f02 0.6.6 2022-12-08 10:47:51 +06:00
48b5f88359 Merge pull request #36 from InsanusMokrassar/0.6.5
0.6.5
2022-12-05 16:56:47 +06:00
0bb7257ec3 start 0.6.5 2022-12-05 16:44:41 +06:00
2b503786ca Merge pull request #35 from InsanusMokrassar/0.6.4
0.6.4
2022-11-28 18:21:40 +06:00
4622359592 Update gradle.properties 2022-11-28 16:33:30 +06:00
0fa045f6a9 Merge pull request #34 from InsanusMokrassar/0.6.3
0.6.3
2022-11-17 13:29:32 +06:00
7759d5faa5 add own repo 2022-11-17 13:27:10 +06:00
d6896c63b5 update dependencies 2022-11-17 13:25:31 +06:00
262c496d45 Merge pull request #33 from InsanusMokrassar/0.6.2
0.6.2
2022-11-17 11:42:23 +06:00
17072092ae add gitea publication 2022-11-17 11:35:16 +06:00
78c44a2c61 Update gradle.properties 2022-11-17 11:32:39 +06:00
0263fe0862 Update gradle.properties 2022-11-15 09:55:35 +06:00
3baccc6c12 Update gradle.properties 2022-11-15 00:18:55 +06:00
87bd39544d Merge pull request #32 from InsanusMokrassar/0.6.1
0.6.1
2022-11-10 21:27:46 +06:00
4bb1f54ddb build fixes 2022-11-10 21:25:31 +06:00
a4662b084a Update gradle.properties 2022-11-10 20:30:24 +06:00
53fd94f094 Merge pull request #31 from InsanusMokrassar/0.6.0
0.6.0
2022-11-09 01:48:13 +06:00
2b288c43c0 Update gradle.properties 2022-11-09 00:50:14 +06:00
842dcbf4b6 Merge pull request #30 from InsanusMokrassar/0.5.7
0.5.7
2022-11-03 13:22:28 +06:00
653d17827a Update gradle.properties 2022-11-03 13:03:55 +06:00
e4a21fe293 Update gradle.properties 2022-11-03 13:03:03 +06:00
7b75828b2e Merge pull request #29 from InsanusMokrassar/0.5.6
0.5.6
2022-10-22 22:41:41 +06:00
e8cb9556db cleanup 2022-10-22 22:33:45 +06:00
d118a3d060 remove redundant xmls 2022-10-22 22:29:38 +06:00
62fb8854b9 remove android targets in all modules due to the fact that currently there is no any android-specific code there 2022-10-22 22:28:32 +06:00
7163f7d64a update dependencies 2022-10-22 22:09:39 +06:00
8a6925e95c start 0.5.6 2022-10-22 22:08:50 +06:00
fd275ddacf Merge pull request #28 from InsanusMokrassar/0.5.5
0.5.5
2022-10-02 02:29:59 +06:00
8e281f0edc start 0.5.5 2022-10-02 02:22:18 +06:00
f802aa6a99 Merge pull request #27 from InsanusMokrassar/0.5.4
0.5.4
2022-09-19 23:47:09 +06:00
ee9f524fc6 Update gradle.properties 2022-09-19 23:32:07 +06:00
950eebea06 fixes 2022-09-19 02:42:10 +06:00
7845b7cc5f small fixes 2022-09-19 01:56:41 +06:00
539515da43 update dependencies 2022-09-19 01:54:44 +06:00
3695ab7936 start 0.5.4 2022-09-19 01:53:51 +06:00
ab802df7d1 Merge pull request #26 from InsanusMokrassar/0.5.3
0.5.3
2022-09-10 19:23:07 +06:00
c85d92c7ba remove tryGetChatAdmins 2022-09-10 19:04:48 +06:00
cea8ba47db fixes and improvements 2022-09-10 18:54:25 +06:00
b2719c0760 AdminsCacheAPI with defaults and customizing opportunity 2022-09-09 20:20:39 +06:00
1c87f64a6d fix 2022-09-09 19:32:21 +06:00
53630d4864 rework of plagubot part of admins plugin 2022-09-09 19:16:19 +06:00
bb7fb985e3 update arguments in activateAdminsChangesListening 2022-09-09 18:57:19 +06:00
18fc3fd1dd update dependencies and add opportunity to listen for admins in chats 2022-09-09 18:55:26 +06:00
5f1f512db4 Merge pull request #25 from InsanusMokrassar/0.5.2
0.5.2
2022-08-31 11:12:48 +06:00
961d7d9afd Update gradle.properties 2022-08-31 11:07:31 +06:00
6a84bcb860 start 0.5.2 2022-08-31 11:04:31 +06:00
cd1ba035ac Update gradle.properties 2022-08-18 17:18:20 +06:00
6f443c1617 Merge pull request #24 from InsanusMokrassar/0.5.0
0.5.0
2022-08-06 09:49:24 +06:00
85a1048b13 fixes 2022-08-06 09:31:11 +06:00
8dc1687b8e Update gradle.properties 2022-08-06 00:43:51 +06:00
2518109290 Update gradle.properties 2022-08-05 22:55:00 +06:00
760e51cabb 0.5.0 2022-08-05 22:23:15 +06:00
3a667946da Merge pull request #23 from InsanusMokrassar/0.4.2
0.4.2
2022-08-03 09:40:46 +06:00
f6df96c405 0.4.2 2022-08-03 09:35:06 +06:00
fd33cbfca3 Merge pull request #22 from InsanusMokrassar/0.4.1
0.4.1
2022-07-30 23:26:01 +06:00
62a10b9042 fixes in build 2022-07-30 23:25:38 +06:00
a7814c61b5 update admins api 2022-07-30 23:24:24 +06:00
4eb27b42ad Merge pull request #21 from InsanusMokrassar/0.4.0
0.4.0
2022-07-30 20:55:38 +06:00
5367ff5629 Update gradle-wrapper.properties 2022-07-30 20:42:47 +06:00
50caa740cc start 0.4.0 2022-07-30 20:42:04 +06:00
6c85e977f1 Merge pull request #20 from InsanusMokrassar/0.3.1
0.3.1
2022-07-23 00:17:35 +06:00
156a8dd8be Update gradle.properties 2022-07-23 00:11:24 +06:00
3728983653 start 0.3.1 2022-07-23 00:08:55 +06:00
8e99cb9e18 Merge pull request #19 from InsanusMokrassar/0.3.0
0.3.0
2022-06-26 16:27:19 +06:00
0f569ead82 Update gradle.properties 2022-06-26 16:07:26 +06:00
71f6710397 Update gradle.properties 2022-06-22 12:48:07 +06:00
243926cd17 Merge pull request #18 from InsanusMokrassar/0.2.1
0.2.1
2022-06-13 00:28:11 +06:00
b2d3ded0da Update gradle.properties 2022-06-13 00:07:30 +06:00
fbbd8e640c update build scripts 2022-06-11 20:27:00 +06:00
15c0f9979a fixes related to the new microutils 2022-06-11 19:45:29 +06:00
e6d04de433 start 0.2.1 and update dependencies 2022-06-11 19:35:59 +06:00
a6b837f633 Merge pull request #17 from InsanusMokrassar/0.2.0
0.2.0
2022-05-22 15:35:00 +06:00
866b016e08 fixes 2022-05-22 11:56:30 +06:00
b67212ec0b update dependencies 2022-05-22 11:49:42 +06:00
dc83bbc9aa start 0.2.0 2022-05-22 11:49:20 +06:00
14b56c659b Merge pull request #16 from InsanusMokrassar/0.1.0
0.1.0
2022-05-17 12:10:37 -04:00
99218de932 update telegram 2022-05-17 20:06:29 +06:00
ce14a78e09 fixes 2022-05-17 20:05:53 +06:00
827d9b43f7 update 2022-05-17 15:56:50 +06:00
6aaa801fe0 Update gradle.properties 2022-05-17 00:30:35 +06:00
48 changed files with 1040 additions and 601 deletions

View File

@@ -7,10 +7,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-java@v1 - uses: actions/setup-java@v1
with: with:
java-version: 11 java-version: 17
- name: Fix android 32.0.0 dx
continue-on-error: true
run: cd /usr/local/lib/android/sdk/build-tools/32.0.0/ && mv d8 dx && cd lib && mv d8.jar dx.jar
- name: Update version - name: Update version
run: | run: |
branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`" branch="`echo "${{ github.ref }}" | grep -o "[^/]*$"`"

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
.idea .idea
.kotlin
out/* out/*
*.iml *.iml
target target

View File

@@ -7,19 +7,35 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
} }
} }
plugins {
id "com.gradleup.nmcp.aggregation" version "$nmcp_version"
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
nmcpAggregation {
centralPortal {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
validationTimeout = Duration.ofHours(4)
publishingType = System.getenv('PUBLISHING_TYPE') != "" ? System.getenv('PUBLISHING_TYPE') : "USER_MANAGED"
}
publishAllProjectsProbablyBreakingProjectIsolation()
}
}
allprojects { allprojects {
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
google() google()
maven { url "https://nexus.inmo.dev/repository/maven-releases/" }
} }
} }

View File

@@ -1,10 +1,9 @@
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppJavaWithJsProjectPath"
kotlin { kotlin {
sourceSets { sourceSets {

View File

@@ -2,13 +2,13 @@ package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.tgbotapi.extensions.utils.asGroupContentMessage import dev.inmo.tgbotapi.extensions.utils.asGroupContentMessage
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.ChatMember.abstracts.AdministratorChatMember import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import dev.inmo.tgbotapi.types.message.abstracts.GroupContentMessage import dev.inmo.tgbotapi.types.message.abstracts.GroupContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message import dev.inmo.tgbotapi.types.message.abstracts.Message
interface AdminsCacheAPI { interface AdminsCacheAPI {
suspend fun getChatAdmins(chatId: ChatId): List<AdministratorChatMember>? suspend fun getChatAdmins(chatId: IdChatIdentifier): List<AdministratorChatMember>?
suspend fun isAdmin(chatId: ChatId, userId: UserId): Boolean = getChatAdmins(chatId) ?.any { suspend fun isAdmin(chatId: IdChatIdentifier, userId: UserId): Boolean = getChatAdmins(chatId) ?.any {
it.user.id == userId it.user.id == userId
} == true } == true
suspend fun sentByAdmin(groupContentMessage: GroupContentMessage<*>): Boolean suspend fun sentByAdmin(groupContentMessage: GroupContentMessage<*>): Boolean

View File

@@ -1,8 +1,9 @@
package dev.inmo.tgbotapi.libraries.cache.admins package dev.inmo.tgbotapi.libraries.cache.admins
import com.soywiz.klock.minutes import korlibs.time.minutes
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.Seconds import dev.inmo.tgbotapi.types.Seconds
import korlibs.time.seconds
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -15,27 +16,30 @@ data class AdminsCacheSettings(
*/ */
val disableRequestsRefreshMode: Boolean = false val disableRequestsRefreshMode: Boolean = false
) { ) {
val refreshOnRequests: Boolean val refreshOnCacheCalls: Boolean
get() = !disableRequestsRefreshMode get() = !disableRequestsRefreshMode
@Deprecated("Renamed", ReplaceWith("refreshOnCacheCalls"))
val refreshOnRequests: Boolean
get() = refreshOnCacheCalls
} }
interface AdminsCacheSettingsAPI { interface AdminsCacheSettingsAPI {
suspend fun getChatSettings(chatId: ChatId): AdminsCacheSettings? suspend fun getChatSettings(chatId: IdChatIdentifier): AdminsCacheSettings?
} }
interface MutableAdminsCacheSettingsAPI : AdminsCacheSettingsAPI { interface MutableAdminsCacheSettingsAPI : AdminsCacheSettingsAPI {
val chatSettingsUpdatedFlow: SharedFlow<Pair<ChatId, AdminsCacheSettings>> val chatSettingsUpdatedFlow: SharedFlow<Pair<IdChatIdentifier, AdminsCacheSettings>>
suspend fun setChatSettings(chatId: ChatId, settings: AdminsCacheSettings) suspend fun setChatSettings(chatId: IdChatIdentifier, settings: AdminsCacheSettings)
} }
fun AdminsCacheSettingsAPI.asMutable(): MutableAdminsCacheSettingsAPI? = this as? MutableAdminsCacheSettingsAPI fun AdminsCacheSettingsAPI.asMutable(): MutableAdminsCacheSettingsAPI? = this as? MutableAdminsCacheSettingsAPI
@Serializable @Serializable
class StaticAdminsCacheSettingsAPI( class StaticAdminsCacheSettingsAPI(
private val settings: Map<ChatId, AdminsCacheSettings> private val settings: Map<IdChatIdentifier, AdminsCacheSettings>
) : AdminsCacheSettingsAPI { ) : AdminsCacheSettingsAPI {
override suspend fun getChatSettings(chatId: ChatId): AdminsCacheSettings? = settings[chatId] override suspend fun getChatSettings(chatId: IdChatIdentifier): AdminsCacheSettings? = settings[chatId]
} }

View File

@@ -0,0 +1,44 @@
package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.behaviour_builder.*
import dev.inmo.tgbotapi.extensions.behaviour_builder.filters.ChatMemberUpdatedFilterByChat
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onChatMemberUpdated
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.ByChatChatMemberUpdatedMarkerFactory
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.marker_factories.MarkerFactory
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import dev.inmo.tgbotapi.types.chat.member.ChatMemberUpdated
import dev.inmo.tgbotapi.types.update.abstracts.Update
import kotlinx.coroutines.Job
suspend fun BehaviourContext.activateAdminsChangesListening(
repo: DefaultAdminsCacheAPIRepo,
initialFilter: SimpleFilter<ChatMemberUpdated>? = null,
markerFactory: MarkerFactory<ChatMemberUpdated, Any> = ByChatChatMemberUpdatedMarkerFactory
): Job {
val me = getMe()
return onChatMemberUpdated(initialFilter, markerFactory = markerFactory) {
when {
it.oldChatMemberState is AdministratorChatMember && it.newChatMemberState !is AdministratorChatMember ||
it.newChatMemberState is AdministratorChatMember && it.oldChatMemberState !is AdministratorChatMember -> {
updateAdmins(
it.chat.id,
repo,
me
)
}
}
}
}
suspend fun BehaviourContext.activateAdminsChangesListening(
repo: DefaultAdminsCacheAPIRepo,
allowedChats: List<IdChatIdentifier>
) = activateAdminsChangesListening(
repo,
{
it.chat.id in allowedChats
}
)

View File

@@ -0,0 +1,37 @@
package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.tgbotapi.abstracts.FromUser
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.message.abstracts.Message
fun AdminsChecker(
adminsCacheAPI: AdminsCacheAPI
): SimpleFilter<Pair<IdChatIdentifier, UserId>> = SimpleFilter {
adminsCacheAPI.isAdmin(it.first, it.second)
}
fun <T> AdminsChecker(
adminsCacheAPI: AdminsCacheAPI,
mapper: (T) -> Pair<IdChatIdentifier, UserId>
): SimpleFilter<T> {
val baseChecker = AdminsChecker(adminsCacheAPI)
return SimpleFilter<T> {
baseChecker(mapper(it))
}
}
fun MessageAdminsChecker(
adminsCacheAPI: AdminsCacheAPI
) = SimpleFilter<Message> {
adminsCacheAPI.isAdmin(it)
}
fun AdminsChecker(
adminsCacheAPI: AdminsCacheAPI,
chatId: IdChatIdentifier
) = SimpleFilter<FromUser> {
adminsCacheAPI.isAdmin(chatId, it.from.id)
}

View File

@@ -1,18 +1,20 @@
package dev.inmo.tgbotapi.libraries.cache.admins package dev.inmo.tgbotapi.libraries.cache.admins
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChatAdministrators import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.ChatMember.abstracts.AdministratorChatMember import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import dev.inmo.tgbotapi.types.message.abstracts.* import dev.inmo.tgbotapi.types.message.abstracts.*
import korlibs.time.seconds
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
interface DefaultAdminsCacheAPIRepo { interface DefaultAdminsCacheAPIRepo {
suspend fun getChatAdmins(chatId: ChatId): List<AdministratorChatMember>? suspend fun getChatAdmins(chatId: IdChatIdentifier): List<AdministratorChatMember>?
suspend fun setChatAdmins(chatId: ChatId, chatMembers: List<AdministratorChatMember>) suspend fun setChatAdmins(chatId: IdChatIdentifier, chatMembers: List<AdministratorChatMember>)
suspend fun lastUpdate(chatId: ChatId): DateTime? suspend fun lastUpdate(chatId: IdChatIdentifier): DateTime?
} }
@Serializable @Serializable
@@ -28,28 +30,37 @@ class DefaultAdminsCacheAPI(
bot.getMe().also { botInfo = it } bot.getMe().also { botInfo = it }
} }
private suspend fun triggerUpdate(chatId: ChatId): List<AdministratorChatMember> { override suspend fun getChatAdmins(chatId: IdChatIdentifier): List<AdministratorChatMember>? {
val botInfo = getBotInfo()
val admins = bot.getChatAdministrators(chatId).filter {
botInfo.id != it.user.id
}
repo.setChatAdmins(chatId, admins)
return admins
}
override suspend fun getChatAdmins(chatId: ChatId): List<AdministratorChatMember>? {
val settings = settingsAPI.getChatSettings(chatId) val settings = settingsAPI.getChatSettings(chatId)
val lastUpdate = repo.lastUpdate(chatId) val lastUpdate = repo.lastUpdate(chatId)
return when { return when {
settings == null -> null settings == null -> null
settings.refreshOnRequests && settings.refreshOnCacheCalls &&
(lastUpdate == null || (DateTime.now() - lastUpdate).seconds > settings.refreshSeconds) -> { (lastUpdate == null || (DateTime.now() - lastUpdate).seconds > settings.refreshSeconds) -> {
triggerUpdate(chatId) bot.updateAdmins(chatId, repo, getBotInfo())
} }
else -> repo.getChatAdmins(chatId) ?: triggerUpdate(chatId) else -> repo.getChatAdmins(chatId) ?: bot.updateAdmins(chatId, repo, getBotInfo())
} }
} }
override suspend fun isAdmin(chatId: IdChatIdentifier, userId: UserId): Boolean {
val settings = settingsAPI.getChatSettings(chatId)
val lastUpdate = repo.lastUpdate(chatId)
return when {
settings == null -> return false
settings.refreshOnCacheCalls && (lastUpdate == null || (DateTime.now() - lastUpdate).seconds > settings.refreshSeconds) -> {
bot.updateAdmins(chatId, repo, getBotInfo())
}
else -> {
val chatAdmins = repo.getChatAdmins(chatId)
if (chatAdmins == null) {
return bot.getChatMember(chatId, userId) is AdministratorChatMember
}
chatAdmins
}
}.any { it.user.id == userId }
}
override suspend fun sentByAdmin(groupContentMessage: GroupContentMessage<*>): Boolean { override suspend fun sentByAdmin(groupContentMessage: GroupContentMessage<*>): Boolean {
return when (groupContentMessage) { return when (groupContentMessage) {
is AnonymousGroupContentMessage -> true is AnonymousGroupContentMessage -> true
@@ -62,5 +73,4 @@ class DefaultAdminsCacheAPI(
} }
override suspend fun settings(): AdminsCacheSettingsAPI = settingsAPI override suspend fun settings(): AdminsCacheSettingsAPI = settingsAPI
} }

View File

@@ -1,20 +1,43 @@
package dev.inmo.tgbotapi.libraries.cache.admins package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.UserId import dev.inmo.tgbotapi.types.UserId
import dev.inmo.tgbotapi.types.message.abstracts.* import dev.inmo.tgbotapi.types.message.abstracts.*
suspend fun AdminsCacheAPI.verifyMessageFromAdmin(message: ContentMessage<*>) = when (message) { suspend inline fun AdminsCacheAPI.isAdmin(message: Message) = when (message) {
is CommonGroupContentMessage<*> -> isAdmin(message.chat.id, message.user.id) is CommonGroupContentMessage<*> -> isAdmin(message.chat.id, message.user.id)
is AnonymousGroupContentMessage<*> -> true is AnonymousGroupContentMessage<*> -> true
else -> false else -> false
} }
suspend fun <R> ContentMessage<*>.doAfterVerification(adminsCacheAPI: AdminsCacheAPI, block: suspend () -> R): R? { suspend inline fun AdminsCacheAPI.verifyMessageFromAdmin(message: Message) = isAdmin(message)
val verified = adminsCacheAPI.verifyMessageFromAdmin(this)
suspend inline fun <R : Any> AdminsCacheAPI.doIfAdmin(
chatId: IdChatIdentifier,
userId: UserId,
block: () -> R
) = if(isAdmin(chatId, userId)) {
block()
} else {
null
}
suspend inline fun <R : Any> AdminsCacheAPI.doIfAdmin(
message: Message,
block: () -> R
) = if(isAdmin(message)) {
block()
} else {
null
}
suspend inline fun <R> ContentMessage<*>.doIfAdmin(adminsCacheAPI: AdminsCacheAPI, block: () -> R): R? {
val verified = adminsCacheAPI.isAdmin(this)
return if (verified) { return if (verified) {
block() block()
} else { } else {
null null
} }
} }
suspend inline fun <R> ContentMessage<*>.doAfterVerification(adminsCacheAPI: AdminsCacheAPI, block: () -> R) = doIfAdmin(adminsCacheAPI, block)

View File

@@ -0,0 +1,21 @@
package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.api.bot.getMe
import dev.inmo.tgbotapi.extensions.api.chat.get.getChatAdministrators
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
internal suspend fun TelegramBot.updateAdmins(
chatId: IdChatIdentifier,
repo: DefaultAdminsCacheAPIRepo,
botInfo: ExtendedBot? = null
): List<AdministratorChatMember> {
val botInfo = botInfo ?: getMe()
val admins = getChatAdministrators(chatId).filter {
botInfo.id != it.user.id
}
repo.setChatAdmins(chatId, admins)
return admins
}

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.tgbotapi.libraries.cache.admins"/>

View File

@@ -1,10 +1,9 @@
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppJavaWithJsProjectPath"
kotlin { kotlin {
sourceSets { sourceSets {

View File

@@ -1,62 +1,91 @@
package dev.inmo.tgbotapi.libraries.cache.admins.micro_utils package dev.inmo.tgbotapi.libraries.cache.admins.micro_utils
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import dev.inmo.micro_utils.coroutines.actor import dev.inmo.micro_utils.coroutines.*
import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.tgbotapi.libraries.cache.admins.DefaultAdminsCacheAPIRepo import dev.inmo.tgbotapi.libraries.cache.admins.DefaultAdminsCacheAPIRepo
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.ChatMember.abstracts.AdministratorChatMember import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.*
import kotlin.coroutines.* import kotlin.coroutines.*
private sealed class RepoActions<T> { private sealed class RepoActions<T> {
abstract val toReturn: Continuation<T> abstract val deferred: CompletableDeferred<T>
} }
private class GetUpdateDateTimeRepoAction( private class GetUpdateDateTimeRepoAction(
val chatId: ChatId, val chatId: IdChatIdentifier,
override val toReturn: Continuation<DateTime?> override val deferred: CompletableDeferred<DateTime?>
) : RepoActions<DateTime?>() ) : RepoActions<DateTime?>()
private class GetChatAdminsRepoAction( private class GetChatAdminsRepoAction(
val chatId: ChatId, val chatId: IdChatIdentifier,
override val toReturn: Continuation<List<AdministratorChatMember>?> override val deferred: CompletableDeferred<List<AdministratorChatMember>?>
) : RepoActions<List<AdministratorChatMember>?>() ) : RepoActions<List<AdministratorChatMember>?>()
private class SetChatAdminsRepoAction( private class SetChatAdminsRepoAction(
val chatId: ChatId, val chatId: IdChatIdentifier,
val newValue: List<AdministratorChatMember>, val newValue: List<AdministratorChatMember>,
override val toReturn: Continuation<Unit> override val deferred: CompletableDeferred<Unit>
) : RepoActions<Unit>() ) : RepoActions<Unit>()
class DefaultAdminsCacheAPIRepo( class DefaultAdminsCacheAPIRepoImpl(
private val adminsRepo: KeyValuesRepo<ChatId, AdministratorChatMember>, private val adminsRepo: KeyValuesRepo<IdChatIdentifier, AdministratorChatMember>,
private val updatesRepo: KeyValueRepo<ChatId, MilliSeconds>, private val updatesRepo: KeyValueRepo<IdChatIdentifier, MilliSeconds>,
private val scope: CoroutineScope private val scope: CoroutineScope
) : DefaultAdminsCacheAPIRepo { ) : DefaultAdminsCacheAPIRepo {
private val actor = scope.actor<RepoActions<*>>(Channel.UNLIMITED) { private val actor = scope.actorAsync<RepoActions<*>>(Channel.UNLIMITED) {
safelyWithoutExceptions { safelyWithoutExceptions(
{ e ->
it.deferred.completeExceptionally(e)
}
) {
when (it) { when (it) {
is GetUpdateDateTimeRepoAction -> it.toReturn.resume( is GetUpdateDateTimeRepoAction -> it.deferred.complete(
updatesRepo.get(it.chatId) ?.let { DateTime(it.toDouble()) } updatesRepo.get(it.chatId) ?.let { DateTime(it.toDouble()) }
) )
is GetChatAdminsRepoAction -> it.toReturn.resume(adminsRepo.getAll(it.chatId)) is GetChatAdminsRepoAction -> it.deferred.complete(adminsRepo.getAll(it.chatId))
is SetChatAdminsRepoAction -> { is SetChatAdminsRepoAction -> {
adminsRepo.clear(it.chatId) adminsRepo.clear(it.chatId)
adminsRepo.set(it.chatId, it.newValue) adminsRepo.set(it.chatId, it.newValue)
updatesRepo.set(it.chatId, DateTime.now().unixMillisLong) updatesRepo.set(it.chatId, DateTime.now().unixMillisLong)
it.toReturn.resume(Unit) it.deferred.complete(Unit)
} }
} }
} }
} }
override suspend fun getChatAdmins(chatId: ChatId): List<AdministratorChatMember>? = suspendCoroutine { override suspend fun getChatAdmins(chatId: IdChatIdentifier): List<AdministratorChatMember>? {
actor.trySend(GetChatAdminsRepoAction(chatId, it)) val deferred = CompletableDeferred<List<AdministratorChatMember>?>()
actor.trySend(
GetChatAdminsRepoAction(chatId, deferred)
).onFailure {
deferred.completeExceptionally(it ?: IllegalStateException("Something went wrong when tried to add getChatAdmins action"))
}
return deferred.await()
} }
override suspend fun setChatAdmins(chatId: ChatId, chatMembers: List<AdministratorChatMember>) = suspendCoroutine<Unit> {
actor.trySend(SetChatAdminsRepoAction(chatId, chatMembers, it)) override suspend fun setChatAdmins(chatId: IdChatIdentifier, chatMembers: List<AdministratorChatMember>) {
val deferred = CompletableDeferred<Unit>()
actor.trySend(
SetChatAdminsRepoAction(chatId, chatMembers, deferred)
).onFailure {
deferred.completeExceptionally(it ?: IllegalStateException("Something went wrong when tried to add setChatAdmins action"))
}
return deferred.await()
} }
override suspend fun lastUpdate(chatId: ChatId): DateTime? = suspendCoroutine { override suspend fun lastUpdate(chatId: IdChatIdentifier): DateTime? {
actor.trySend(GetUpdateDateTimeRepoAction(chatId, it)) val deferred = CompletableDeferred<DateTime?>()
actor.trySend(
GetUpdateDateTimeRepoAction(chatId, deferred)
).onFailure {
deferred.completeExceptionally(it ?: IllegalStateException("Something went wrong when tried to add lastUpdate action"))
}
return deferred.await()
} }
} }
fun DefaultAdminsCacheAPIRepo(
adminsRepo: KeyValuesRepo<IdChatIdentifier, AdministratorChatMember>,
updatesRepo: KeyValueRepo<IdChatIdentifier, MilliSeconds>,
scope: CoroutineScope
) = DefaultAdminsCacheAPIRepoImpl(adminsRepo, updatesRepo, scope)

View File

@@ -3,22 +3,22 @@ package dev.inmo.tgbotapi.libraries.cache.admins.micro_utils
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.tgbotapi.libraries.cache.admins.* import dev.inmo.tgbotapi.libraries.cache.admins.*
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.IdChatIdentifier
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
class DynamicAdminsCacheSettingsAPI( class DynamicAdminsCacheSettingsAPI(
private val repo: KeyValueRepo<ChatId, AdminsCacheSettings>, private val repo: KeyValueRepo<IdChatIdentifier, AdminsCacheSettings>,
private val scope: CoroutineScope private val scope: CoroutineScope
) : AdminsCacheSettingsAPI, MutableAdminsCacheSettingsAPI { ) : AdminsCacheSettingsAPI, MutableAdminsCacheSettingsAPI {
override val chatSettingsUpdatedFlow: SharedFlow<Pair<ChatId, AdminsCacheSettings>> override val chatSettingsUpdatedFlow: SharedFlow<Pair<IdChatIdentifier, AdminsCacheSettings>>
get() = repo.onNewValue.shareIn(scope, SharingStarted.Eagerly) get() = repo.onNewValue.shareIn(scope, SharingStarted.Eagerly)
override suspend fun setChatSettings(chatId: ChatId, settings: AdminsCacheSettings) { override suspend fun setChatSettings(chatId: IdChatIdentifier, settings: AdminsCacheSettings) {
repo.set(chatId, settings) repo.set(chatId, settings)
} }
override suspend fun getChatSettings(chatId: ChatId): AdminsCacheSettings { override suspend fun getChatSettings(chatId: IdChatIdentifier): AdminsCacheSettings {
val settings = repo.get(chatId) val settings = repo.get(chatId)
return if (settings == null) { return if (settings == null) {
val newSettings = AdminsCacheSettings() val newSettings = AdminsCacheSettings()
@@ -28,4 +28,4 @@ class DynamicAdminsCacheSettingsAPI(
settings settings
} }
} }
} }

View File

@@ -1,81 +1,83 @@
package dev.inmo.tgbotapi.libraries.cache.admins package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedOneToManyKeyValueRepo import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedKeyValuesRepo
import dev.inmo.micro_utils.repos.mappers.withMapper import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DefaultAdminsCacheAPIRepo import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DefaultAdminsCacheAPIRepoImpl
import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DynamicAdminsCacheSettingsAPI import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DynamicAdminsCacheSettingsAPI
import dev.inmo.tgbotapi.types.* import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.ChatMember.AdministratorChatMemberImpl import dev.inmo.tgbotapi.types.chat.member.*
import dev.inmo.tgbotapi.types.ChatMember.CreatorChatMember
import dev.inmo.tgbotapi.types.ChatMember.abstracts.AdministratorChatMember
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.polymorphic
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
private val serializationFormat = Json { val telegramAdminsSerializationFormat = Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
serializersModule = SerializersModule { serializersModule = SerializersModule {
polymorphic(AdministratorChatMember::class) { polymorphic(AdministratorChatMember::class) {
subclass(AdministratorChatMemberImpl::class, AdministratorChatMemberImpl.serializer()) subclass(AdministratorChatMemberImpl::class, AdministratorChatMemberImpl.serializer())
subclass(CreatorChatMember::class, CreatorChatMember.serializer()) subclass(OwnerChatMember::class, OwnerChatMember.serializer())
} }
contextual(AdministratorChatMember::class, PolymorphicSerializer(AdministratorChatMember::class)) contextual(AdministratorChatMember::class, PolymorphicSerializer(AdministratorChatMember::class))
} }
} }
fun AdminsCacheAPI( fun BehaviourContext.createAdminsCacheAPI(database: Database) = AdminsCacheAPI(this, database, this)
bot: TelegramBot,
fun TelegramBot.createAdminsCacheAPI(
database: Database, database: Database,
scope: CoroutineScope scope: CoroutineScope,
) : AdminsCacheAPI = DefaultAdminsCacheAPI( defaultAdminsCacheAPIRepo: DefaultAdminsCacheAPIRepo = DefaultAdminsCacheAPIRepoImpl(
bot, ExposedKeyValuesRepo(
DefaultAdminsCacheAPIRepo(
ExposedOneToManyKeyValueRepo(
database, database,
{ long("chatId") }, { long("chatId") },
{ text("member") }, { text("member") },
"AdminsTable" "AdminsTable"
).withMapper<ChatId, AdministratorChatMember, Identifier, String>( ).withMapper<IdChatIdentifier, AdministratorChatMember, Long, String>(
keyFromToTo = { chatId }, keyFromToTo = { chatId.long },
valueFromToTo = { serializationFormat.encodeToString(this) }, valueFromToTo = { telegramAdminsSerializationFormat.encodeToString(AdministratorChatMember.serializer(), this) },
keyToToFrom = { toChatId() }, keyToToFrom = { toChatId() },
valueToToFrom = { serializationFormat.decodeFromString(this) } valueToToFrom = { telegramAdminsSerializationFormat.decodeFromString(AdministratorChatMember.serializer(), this) }
), ),
ExposedKeyValueRepo( ExposedKeyValueRepo(
database, database,
{ long("chatId") }, { long("chatId") },
{ long("datetime") }, { long("datetime") },
"AdminsUpdatesTimesTable" "AdminsUpdatesTimesTable"
).withMapper<ChatId, Long, Identifier, Long>( ).withMapper<IdChatIdentifier, Long, Long, Long>(
keyFromToTo = { chatId }, keyFromToTo = { chatId.long },
valueFromToTo = { this }, valueFromToTo = { this },
keyToToFrom = { toChatId() }, keyToToFrom = { toChatId() },
valueToToFrom = { this } valueToToFrom = { this }
), ),
scope scope
), ),
DynamicAdminsCacheSettingsAPI( adminsCacheSettingsAPI: AdminsCacheSettingsAPI = DynamicAdminsCacheSettingsAPI(
ExposedKeyValueRepo( ExposedKeyValueRepo(
database, database,
{ long("chatId") }, { long("chatId") },
{ text("settings") }, { text("settings") },
"DynamicAdminsCacheSettingsAPI" "DynamicAdminsCacheSettingsAPI"
).withMapper<ChatId, AdminsCacheSettings, Identifier, String>( ).withMapper<IdChatIdentifier, AdminsCacheSettings, Long, String>(
keyFromToTo = { chatId }, keyFromToTo = { chatId.long },
valueFromToTo = { serializationFormat.encodeToString(this) }, valueFromToTo = { telegramAdminsSerializationFormat.encodeToString(AdminsCacheSettings.serializer() , this) },
keyToToFrom = { toChatId() }, keyToToFrom = { toChatId() },
valueToToFrom = { serializationFormat.decodeFromString(this) } valueToToFrom = { telegramAdminsSerializationFormat.decodeFromString(AdminsCacheSettings.serializer() , this) }
), ),
scope scope
) )
) ) = DefaultAdminsCacheAPI(this, defaultAdminsCacheAPIRepo, adminsCacheSettingsAPI)
fun BehaviourContext.AdminsCacheAPI(database: Database) = AdminsCacheAPI(this, database, this) fun AdminsCacheAPI(
bot: TelegramBot,
database: Database,
scope: CoroutineScope
) : AdminsCacheAPI = bot.createAdminsCacheAPI(
database,
scope
)

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.tgbotapi.libraries.cache.admins.micro_utils"/>

View File

@@ -1,10 +1,9 @@
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppJavaWithJsProjectPath"
kotlin { kotlin {
sourceSets { sourceSets {

View File

@@ -1,53 +1,107 @@
package dev.inmo.tgbotapi.libraries.cache.admins package dev.inmo.tgbotapi.libraries.cache.admins
import dev.inmo.micro_utils.repos.exposed.keyvalue.ExposedKeyValueRepo
import dev.inmo.micro_utils.repos.exposed.onetomany.ExposedKeyValuesRepo
import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.plagubot.Plugin import dev.inmo.plagubot.Plugin
import dev.inmo.plagubot.database
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DefaultAdminsCacheAPIRepoImpl
import dev.inmo.tgbotapi.libraries.cache.admins.micro_utils.DynamicAdminsCacheSettingsAPI
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.Serializable import kotlinx.serialization.*
import kotlinx.serialization.Transient import kotlinx.serialization.json.JsonObject
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.koin.core.Koin
import org.koin.core.module.Module
import org.koin.core.qualifier.named
import org.koin.core.scope.Scope
import org.koin.dsl.binds
val Map<String, Any>.adminsPlugin: AdminsPlugin? val Scope.adminsPlugin: AdminsPlugin?
get() = get("admins") as? AdminsPlugin get() = getOrNull()
val Koin.adminsPlugin: AdminsPlugin?
get() = getOrNull()
@Serializable @Serializable
class AdminsPlugin( class AdminsPlugin : Plugin {
private val chatsSettings: Map<ChatId, AdminsCacheSettings>? = null
) : Plugin {
@Transient @Transient
private val globalAdminsCacheAPI = MutableStateFlow<AdminsCacheAPI?>(null) private val globalAdminsCacheAPI = MutableStateFlow<AdminsCacheAPI?>(null)
@Transient @Transient
private val databaseToAdminsCacheAPI = mutableMapOf<Database, MutableStateFlow<AdminsCacheAPI?>>() private val databaseToAdminsCacheAPI = mutableMapOf<Database, MutableStateFlow<AdminsCacheAPI?>>()
private val mutex = Mutex() private val mutex = Mutex()
@Deprecated("Will be removed soon due to its redundancy")
suspend fun adminsAPI(database: Database): AdminsCacheAPI { suspend fun adminsAPI(database: Database): AdminsCacheAPI {
return when (chatsSettings) { val flow = mutex.withLock {
null -> { databaseToAdminsCacheAPI.getOrPut(database){ MutableStateFlow(null) }
val flow = mutex.withLock {
databaseToAdminsCacheAPI.getOrPut(database){ MutableStateFlow(null) }
}
flow.first { it != null }!!
}
else -> globalAdminsCacheAPI.first { it != null }!!
} }
return flow.filterNotNull().first()
} }
override suspend fun BehaviourContext.invoke(database: Database, params: Map<String, Any>) { override fun Module.setupDI(config: JsonObject) {
when (chatsSettings) { single { this@AdminsPlugin }
null -> { val scopeQualifier = named("admins plugin scope")
mutex.withLock { single(scopeQualifier) { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
val flow = databaseToAdminsCacheAPI.getOrPut(database){ MutableStateFlow(null) } single<DefaultAdminsCacheAPIRepo> {
if (flow.value == null) { DefaultAdminsCacheAPIRepoImpl(
flow.value = AdminsCacheAPI(database) ExposedKeyValuesRepo(
} database,
} { long("chatId") },
} { text("member") },
else -> mutex.withLock { "AdminsTable"
globalAdminsCacheAPI.value = AdminsCacheAPI(database) ).withMapper<IdChatIdentifier, AdministratorChatMember, Long, String>(
} keyFromToTo = { chatId.long },
valueFromToTo = { telegramAdminsSerializationFormat.encodeToString(this) },
keyToToFrom = { toChatId() },
valueToToFrom = { telegramAdminsSerializationFormat.decodeFromString(this) }
),
ExposedKeyValueRepo(
database,
{ long("chatId") },
{ long("datetime") },
"AdminsUpdatesTimesTable"
).withMapper<IdChatIdentifier, Long, Long, Long>(
keyFromToTo = { chatId.long },
valueFromToTo = { this },
keyToToFrom = { toChatId() },
valueToToFrom = { this }
),
get(scopeQualifier)
)
}
single<AdminsCacheSettingsAPI> {
DynamicAdminsCacheSettingsAPI(
ExposedKeyValueRepo(
database,
{ long("chatId") },
{ text("settings") },
"DynamicAdminsCacheSettingsAPI"
).withMapper<IdChatIdentifier, AdminsCacheSettings, Long, String>(
keyFromToTo = { chatId.long },
valueFromToTo = { telegramAdminsSerializationFormat.encodeToString(this) },
keyToToFrom = { toChatId() },
valueToToFrom = { telegramAdminsSerializationFormat.decodeFromString(this) }
),
get(scopeQualifier)
)
}
single { DefaultAdminsCacheAPI(get(), get(), get()) } binds arrayOf(
AdminsCacheAPI::class
)
}
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
with(koin) {
activateAdminsChangesListening(
get()
)
} }
} }
} }

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.tgbotapi.libraries.cache.admins.plagubot"/>

View File

@@ -1,10 +1,9 @@
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppJavaWithJsProjectPath"
kotlin { kotlin {
sourceSets { sourceSets {

View File

@@ -6,24 +6,26 @@ import dev.inmo.tgbotapi.requests.DownloadFileStream
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.get.GetFile import dev.inmo.tgbotapi.requests.get.GetFile
import dev.inmo.tgbotapi.requests.send.media.* import dev.inmo.tgbotapi.requests.send.media.*
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.InputMedia.* import dev.inmo.tgbotapi.types.media.*
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaContent
import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.utils.asInput import dev.inmo.tgbotapi.utils.asInput
import io.ktor.utils.io.core.Input import io.ktor.utils.io.core.Input
class DefaultMessageContentCache<K>( class DefaultMessageContentCache<K>(
private val bot: TelegramBot, private val bot: TelegramBot,
private val filesRefreshingChatId: ChatId, private val filesRefreshingChatId: IdChatIdentifier,
private val simpleMessageContentCache: MessagesSimpleCache<K>, private val simpleMessageContentCache: MessagesSimpleCache<K>,
private val mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay( private val mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay(
MediaFileActualityChecker.Default(filesRefreshingChatId) MediaFileActualityChecker.Default(filesRefreshingChatId)
), ),
private val messagesFilesCache: MessagesFilesCache<K> = InMemoryMessagesFilesCache() private val messagesFilesCache: MessagesFilesCache<K> = InMemoryMessagesFilesCache()
) : MessageContentCache<K> { ) : MessageContentCache<K> {
override suspend fun save(content: MessageContent): K { override suspend fun save(k: K, content: MessageContent) {
return when (content) { when (content) {
is MediaContent -> { is MediaContent -> {
val extendedInfo = bot.execute( val extendedInfo = bot.execute(
GetFile(content.media.fileId) GetFile(content.media.fileId)
@@ -34,31 +36,54 @@ class DefaultMessageContentCache<K>(
) )
) )
save(content, extendedInfo.fileName) { save(k, content, extendedInfo.fileName) {
allocator.invoke().asInput() allocator.invoke().asInput()
} }
} }
else -> simpleMessageContentCache.add(content) else -> simpleMessageContentCache.set(k, content)
} }
} }
override suspend fun save( override suspend fun save(
k: K,
content: MediaContent, content: MediaContent,
filename: String, filename: String,
inputAllocator: suspend () -> Input inputAllocator: suspend () -> Input
): K { ) {
val key = simpleMessageContentCache.add(content) simpleMessageContentCache.set(k, content)
runCatching { runCatching {
messagesFilesCache.set(key, filename, inputAllocator) messagesFilesCache.set(k, filename, inputAllocator)
}.onFailure { }.onFailure {
simpleMessageContentCache.remove(key) simpleMessageContentCache.remove(k)
}.onSuccess { }.onSuccess {
with(mediaFileActualityChecker) { with(mediaFileActualityChecker) {
bot.saved(content) bot.saved(content)
} }
} }
}
return key override suspend fun sendAndSave(
k: K,
filename: String,
inputAllocator: () -> Input
): DocumentContent {
val sentDocument = bot.execute(
SendDocument(
filesRefreshingChatId,
MultipartFile(filename, inputAllocator),
)
)
save(k, sentDocument.content, filename, inputAllocator)
runCatching {
bot.execute(
DeleteMessage(
sentDocument.chat.id,
sentDocument.messageId
)
)
}
return sentDocument.content
} }
override suspend fun get(k: K): MessageContent? { override suspend fun get(k: K): MessageContent? {
@@ -67,40 +92,30 @@ class DefaultMessageContentCache<K>(
if (savedSimpleContent is MediaContent && !with(mediaFileActualityChecker) { bot.isActual(savedSimpleContent) }) { if (savedSimpleContent is MediaContent && !with(mediaFileActualityChecker) { bot.isActual(savedSimpleContent) }) {
val savedFileContentAllocator = messagesFilesCache.get(k) ?: error("Unexpected absence of $k file for content ($simpleMessageContentCache)") val savedFileContentAllocator = messagesFilesCache.get(k) ?: error("Unexpected absence of $k file for content ($simpleMessageContentCache)")
val newContent = bot.execute( val newContent = bot.execute(
when (savedSimpleContent.asInputMedia()) { when (savedSimpleContent.asTelegramMedia()) {
is InputMediaAnimation -> SendAnimation( is TelegramMediaAnimation -> SendAnimation(
filesRefreshingChatId, filesRefreshingChatId,
MultipartFile( savedFileContentAllocator,
savedFileContentAllocator
),
disableNotification = true disableNotification = true
) )
is InputMediaAudio -> SendAudio( is TelegramMediaAudio -> SendAudio(
filesRefreshingChatId, filesRefreshingChatId,
MultipartFile( savedFileContentAllocator,
savedFileContentAllocator
),
disableNotification = true disableNotification = true
) )
is InputMediaVideo -> SendVideo( is TelegramMediaVideo -> SendVideo(
filesRefreshingChatId, filesRefreshingChatId,
MultipartFile( savedFileContentAllocator,
savedFileContentAllocator
),
disableNotification = true disableNotification = true
) )
is InputMediaDocument -> SendDocument( is TelegramMediaDocument -> SendDocument(
filesRefreshingChatId, filesRefreshingChatId,
MultipartFile( savedFileContentAllocator,
savedFileContentAllocator
),
disableNotification = true disableNotification = true
) )
is InputMediaPhoto -> SendPhoto( is TelegramMediaPhoto -> SendPhoto(
filesRefreshingChatId, filesRefreshingChatId,
MultipartFile( savedFileContentAllocator,
savedFileContentAllocator
),
disableNotification = true disableNotification = true
) )
} }
@@ -124,7 +139,7 @@ class DefaultMessageContentCache<K>(
companion object { companion object {
operator fun invoke( operator fun invoke(
bot: TelegramBot, bot: TelegramBot,
filesRefreshingChatId: ChatId, filesRefreshingChatId: IdChatIdentifier,
simpleMessageContentCache: MessagesSimpleCache<String> = InMemoryMessagesSimpleCache(), simpleMessageContentCache: MessagesSimpleCache<String> = InMemoryMessagesSimpleCache(),
mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay( mediaFileActualityChecker: MediaFileActualityChecker = MediaFileActualityChecker.WithDelay(
MediaFileActualityChecker.Default(filesRefreshingChatId) MediaFileActualityChecker.Default(filesRefreshingChatId)

View File

@@ -1,20 +1,21 @@
package dev.inmo.tgbotapi.libraries.cache.media.common package dev.inmo.tgbotapi.libraries.cache.media.common
import com.soywiz.klock.DateTime import korlibs.time.DateTime
import com.soywiz.klock.milliseconds import korlibs.time.milliseconds
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.DeleteMessage import dev.inmo.tgbotapi.requests.DeleteMessage
import dev.inmo.tgbotapi.requests.abstracts.FileId import dev.inmo.tgbotapi.requests.abstracts.FileId
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MilliSeconds import dev.inmo.tgbotapi.types.MilliSeconds
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.types.message.content.MediaContent
fun interface MediaFileActualityChecker { fun interface MediaFileActualityChecker {
suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean
suspend fun TelegramBot.saved(mediaContent: MediaContent) {} suspend fun TelegramBot.saved(mediaContent: MediaContent) {}
class Default( class Default(
private val checkingChatId: ChatId private val checkingChatId: IdChatIdentifier
) : MediaFileActualityChecker { ) : MediaFileActualityChecker {
override suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean { override suspend fun TelegramBot.isActual(mediaContent: MediaContent): Boolean {
return runCatching { return runCatching {

View File

@@ -1,16 +1,25 @@
package dev.inmo.tgbotapi.libraries.cache.media.common package dev.inmo.tgbotapi.libraries.cache.media.common
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent import dev.inmo.tgbotapi.types.message.content.DocumentContent
import dev.inmo.tgbotapi.types.message.content.MediaContent
import dev.inmo.tgbotapi.types.message.content.MessageContent
import io.ktor.utils.io.core.Input import io.ktor.utils.io.core.Input
interface MessageContentCache<K> { interface MessageContentCache<K> {
suspend fun save(content: MessageContent): K suspend fun save(k: K, content: MessageContent)
suspend fun save( suspend fun save(
k: K,
content: MediaContent, content: MediaContent,
filename: String, filename: String,
inputAllocator: suspend () -> Input inputAllocator: suspend () -> Input
): K )
suspend fun sendAndSave(
k: K,
filename: String,
inputAllocator: () -> Input
): DocumentContent
suspend fun get(k: K): MessageContent? suspend fun get(k: K): MessageContent?
suspend fun contains(k: K): Boolean suspend fun contains(k: K): Boolean
suspend fun remove(k: K) suspend fun remove(k: K)

View File

@@ -1,13 +1,11 @@
package dev.inmo.tgbotapi.libraries.cache.media.common package dev.inmo.tgbotapi.libraries.cache.media.common
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.types.MessageIdentifier
import dev.inmo.tgbotapi.utils.StorageFile
import io.ktor.utils.io.core.* import io.ktor.utils.io.core.*
interface MessagesFilesCache<K> { interface MessagesFilesCache<K> {
suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input)
suspend fun get(k: K): StorageFile? suspend fun get(k: K): MultipartFile?
suspend fun remove(k: K) suspend fun remove(k: K)
suspend fun contains(k: K): Boolean suspend fun contains(k: K): Boolean
} }
@@ -18,16 +16,18 @@ interface MessagesFilesCache<K> {
* disks-oriented one * disks-oriented one
*/ */
class InMemoryMessagesFilesCache<K> : MessagesFilesCache<K> { class InMemoryMessagesFilesCache<K> : MessagesFilesCache<K> {
private val map = mutableMapOf<K, StorageFile>() private val map = mutableMapOf<K, MultipartFile>()
override suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) { override suspend fun set(k: K, filename: String, inputAllocator: suspend () -> Input) {
map[k] = StorageFile( val input = inputAllocator()
filename, map[k] = MultipartFile(
inputAllocator().readBytes() filename
) ) {
input
}
} }
override suspend fun get(k: K): StorageFile? { override suspend fun get(k: K): MultipartFile? {
return map[k] return map[k]
} }

View File

@@ -1,10 +1,9 @@
package dev.inmo.tgbotapi.libraries.cache.media.common package dev.inmo.tgbotapi.libraries.cache.media.common
import com.benasher44.uuid.uuid4 import dev.inmo.tgbotapi.types.message.content.MessageContent
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
interface MessagesSimpleCache<K> { interface MessagesSimpleCache<K> {
suspend fun add(content: MessageContent): K suspend fun set(k: K, content: MessageContent)
suspend fun update(k: K, content: MessageContent): Boolean suspend fun update(k: K, content: MessageContent): Boolean
suspend fun get(k: K): MessageContent? suspend fun get(k: K): MessageContent?
suspend fun remove(k: K) suspend fun remove(k: K)
@@ -16,17 +15,14 @@ interface MessagesSimpleCache<K> {
* start of application creation with usage of [MessageContentCache] with aim to replace this realization by some * start of application creation with usage of [MessageContentCache] with aim to replace this realization by some
* disks-oriented one * disks-oriented one
*/ */
class InMemoryMessagesSimpleCache<K>( class InMemoryMessagesSimpleCache<K> : MessagesSimpleCache<K> {
private val keyGenerator: () -> K
) : MessagesSimpleCache<K> {
private val map = mutableMapOf<K, MessageContent>() private val map = mutableMapOf<K, MessageContent>()
override suspend fun add( override suspend fun set(
k: K,
content: MessageContent content: MessageContent
): K { ) {
val key = keyGenerator() map[k] = content
map[key] = content
return key
} }
override suspend fun update( override suspend fun update(
@@ -54,10 +50,4 @@ class InMemoryMessagesSimpleCache<K>(
override suspend fun contains(k: K): Boolean { override suspend fun contains(k: K): Boolean {
return map.contains(k) return map.contains(k)
} }
companion object {
operator fun invoke() = InMemoryMessagesSimpleCache {
uuid4().toString()
}
}
} }

View File

@@ -1,26 +1,23 @@
package dev.inmo.tgbotapi.libraries.cache.media.common package dev.inmo.tgbotapi.libraries.cache.media.common
import dev.inmo.tgbotapi.utils.* import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import io.ktor.utils.io.core.Input import io.ktor.utils.io.core.Input
import io.ktor.utils.io.core.copyTo import io.ktor.utils.io.core.copyTo
import io.ktor.utils.io.streams.asInput import io.ktor.utils.io.streams.*
import io.ktor.utils.io.streams.asOutput
import java.io.File import java.io.File
class InFilesMessagesFilesCache<K>( class InFilesMessagesFilesCache<K>(
private val folderFile: File, private val folderFile: File,
private val filePrefixBuilder: (K) -> String private val filePrefixBuilder: (K) -> String
) : MessagesFilesCache<K> { ) : MessagesFilesCache<K> {
private val K.storageFile: StorageFile? private val K.multipartFile: MultipartFile?
get() { get() {
val prefix = filePrefix(this) val prefix = filePrefix(this)
val filename = folderFile.list() ?.firstOrNull { it.startsWith(prefix) } ?: return null val filename = folderFile.list() ?.firstOrNull { it.startsWith(prefix) } ?: return null
val file = File(folderFile, filename) val file = File(folderFile, filename)
val storageFileFilename = file.name.removePrefix("$prefix ") val storageFileFilename = file.name.removePrefix("$prefix ")
return StorageFile( return MultipartFile(storageFileFilename) {
StorageFileInfo(storageFileFilename)
) {
file.inputStream().asInput() file.inputStream().asInput()
} }
} }
@@ -41,15 +38,15 @@ class InFilesMessagesFilesCache<K>(
val file = File(folderFile, fullFileName).apply { val file = File(folderFile, fullFileName).apply {
delete() delete()
} }
inputAllocator().use { input -> inputAllocator().inputStream().use { input ->
file.outputStream().asOutput().use { output -> file.outputStream().use { output ->
input.copyTo(output) input.copyTo(output)
} }
} }
} }
override suspend fun get(k: K): StorageFile? { override suspend fun get(k: K): MultipartFile? {
return k.storageFile return k.multipartFile
} }
override suspend fun remove(k: K) { override suspend fun remove(k: K) {

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.tgbotapi.libraries.cache.content.common"/>

View File

@@ -1,10 +1,9 @@
plugins { plugins {
id "org.jetbrains.kotlin.multiplatform" id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization" id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
} }
apply from: "$mppProjectWithSerializationPresetPath" apply from: "$mppJavaWithJsProjectPath"
kotlin { kotlin {
sourceSets { sourceSets {

View File

@@ -1,12 +1,11 @@
package dev.inmo.tgbotapi.libraries.cache.media.micro_utils package dev.inmo.tgbotapi.libraries.cache.media.micro_utils
import com.benasher44.uuid.uuid4
import dev.inmo.micro_utils.repos.* import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.mappers.withMapper import dev.inmo.micro_utils.repos.mappers.withMapper
import dev.inmo.tgbotapi.libraries.cache.media.common.MessagesSimpleCache import dev.inmo.tgbotapi.libraries.cache.media.common.MessagesSimpleCache
import dev.inmo.tgbotapi.types.ChatId import dev.inmo.tgbotapi.types.ChatId
import dev.inmo.tgbotapi.types.MessageIdentifier import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.PairSerializer import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
@@ -16,14 +15,10 @@ import kotlin.js.JsName
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
class SimpleKeyValueMessageContentCache<K>( class SimpleKeyValueMessageContentCache<K>(
private val keyValueRepo: KeyValueRepo<K, MessageContent>, private val keyValueRepo: KeyValueRepo<K, MessageContent>
private val keyGenerator: () -> K
) : MessagesSimpleCache<K> { ) : MessagesSimpleCache<K> {
override suspend fun add(content: MessageContent): K { override suspend fun set(k: K, content: MessageContent) {
val key = keyGenerator() keyValueRepo.set(k, content)
keyValueRepo.set(key, content)
return key
} }
override suspend fun update(k: K, content: MessageContent): Boolean { override suspend fun update(k: K, content: MessageContent): Boolean {
@@ -48,26 +43,22 @@ class SimpleKeyValueMessageContentCache<K>(
override suspend fun remove(k: K) { override suspend fun remove(k: K) {
keyValueRepo.unset(k) keyValueRepo.unset(k)
} }
companion object {
operator fun invoke(
keyValueRepo: KeyValueRepo<String, MessageContent>
) = SimpleKeyValueMessageContentCache(keyValueRepo) { uuid4().toString() }
}
} }
val chatIdToMessageIdentifierSerializer = PairSerializer( val chatIdToMessageIdentifierSerializer = PairSerializer(
ChatId.serializer(), ChatId.serializer(),
MessageIdentifier.serializer() MessageId.serializer()
) )
val messageContentSerializer = PolymorphicSerializer<MessageContent>(MessageContent::class) val messageContentSerializer = PolymorphicSerializer<MessageContent>(MessageContent::class)
inline fun <K> KeyValueRepo<K, MessageContent>.asMessageContentCache() = SimpleKeyValueMessageContentCache(this)
inline fun KeyValueRepo<String, String>.asMessageContentCache( inline fun KeyValueRepo<String, String>.asMessageContentCache(
serialFormatCreator: (SerializersModule) -> StringFormat = { Json { serializersModule = it } } serialFormatCreator: (SerializersModule) -> StringFormat = { Json { serializersModule = it } }
): StandardKeyValueRepo<Pair<ChatId, MessageIdentifier>, MessageContent> { ): StandardKeyValueRepo<Pair<ChatId, MessageId>, MessageContent> {
val serialFormat = serialFormatCreator(MessageContent.serializationModule()) val serialFormat = serialFormatCreator(MessageContent.serializationModule())
return withMapper<Pair<ChatId, MessageIdentifier>, MessageContent, String, String>( return withMapper<Pair<ChatId, MessageId>, MessageContent, String, String>(
{ serialFormat.encodeToString(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.encodeToString(chatIdToMessageIdentifierSerializer, this) },
{ serialFormat.encodeToString(messageContentSerializer, this) }, { serialFormat.encodeToString(messageContentSerializer, this) },
{ serialFormat.decodeFromString(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.decodeFromString(chatIdToMessageIdentifierSerializer, this) },
@@ -79,9 +70,9 @@ inline fun KeyValueRepo<String, String>.asMessageContentCache(
@JsName("stringsKeyValueAsHexMessageContentCache") @JsName("stringsKeyValueAsHexMessageContentCache")
inline fun KeyValueRepo<String, String>.asMessageContentCache( inline fun KeyValueRepo<String, String>.asMessageContentCache(
serialFormatCreator: (SerializersModule) -> BinaryFormat serialFormatCreator: (SerializersModule) -> BinaryFormat
): StandardKeyValueRepo<Pair<ChatId, MessageIdentifier>, MessageContent> { ): StandardKeyValueRepo<Pair<ChatId, MessageId>, MessageContent> {
val serialFormat = serialFormatCreator(MessageContent.serializationModule()) val serialFormat = serialFormatCreator(MessageContent.serializationModule())
return withMapper<Pair<ChatId, MessageIdentifier>, MessageContent, String, String>( return withMapper<Pair<ChatId, MessageId>, MessageContent, String, String>(
{ serialFormat.encodeToHexString(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.encodeToHexString(chatIdToMessageIdentifierSerializer, this) },
{ serialFormat.encodeToHexString(messageContentSerializer, this) }, { serialFormat.encodeToHexString(messageContentSerializer, this) },
{ serialFormat.decodeFromHexString(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.decodeFromHexString(chatIdToMessageIdentifierSerializer, this) },
@@ -93,9 +84,9 @@ inline fun KeyValueRepo<String, String>.asMessageContentCache(
@JsName("bytesKeyValueAsMessageContentCache") @JsName("bytesKeyValueAsMessageContentCache")
inline fun KeyValueRepo<ByteArray, ByteArray>.asMessageContentCache( inline fun KeyValueRepo<ByteArray, ByteArray>.asMessageContentCache(
serialFormatCreator: (SerializersModule) -> BinaryFormat serialFormatCreator: (SerializersModule) -> BinaryFormat
): StandardKeyValueRepo<Pair<ChatId, MessageIdentifier>, MessageContent> { ): StandardKeyValueRepo<Pair<ChatId, MessageId>, MessageContent> {
val serialFormat = serialFormatCreator(MessageContent.serializationModule()) val serialFormat = serialFormatCreator(MessageContent.serializationModule())
return withMapper<Pair<ChatId, MessageIdentifier>, MessageContent, ByteArray, ByteArray>( return withMapper<Pair<ChatId, MessageId>, MessageContent, ByteArray, ByteArray>(
{ serialFormat.encodeToByteArray(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.encodeToByteArray(chatIdToMessageIdentifierSerializer, this) },
{ serialFormat.encodeToByteArray(messageContentSerializer, this) }, { serialFormat.encodeToByteArray(messageContentSerializer, this) },
{ serialFormat.decodeFromByteArray(chatIdToMessageIdentifierSerializer, this) }, { serialFormat.decodeFromByteArray(chatIdToMessageIdentifierSerializer, this) },

View File

@@ -1 +0,0 @@
<manifest package="dev.inmo.tgbotapi.libraries.cache.content.micro_utils"/>

View File

@@ -1,40 +0,0 @@
apply plugin: 'com.getkeepsafe.dexcount'
android {
compileSdkVersion "$android_compileSdkVersion".toInteger()
buildToolsVersion "$android_buildToolsVersion"
defaultConfig {
minSdkVersion "$android_minSdkVersion".toInteger()
targetSdkVersion "$android_compileSdkVersion".toInteger()
versionCode "${android_code_version}".toInteger()
versionName "$version"
}
buildTypes {
release {
minifyEnabled false
}
debug {
debuggable true
}
}
packagingOptions {
exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module'
exclude 'META-INF/kotlinx-serialization-cbor.kotlin_module'
exclude 'META-INF/kotlinx-serialization-properties.kotlin_module'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}

View File

@@ -13,11 +13,7 @@ allprojects {
projectByName(name) projectByName(name)
} }
mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle" mppJavaWithJsProjectPath = "${rootProject.projectDir.absolutePath}/mppJavaWithJsProject.gradle"
mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle"
mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle"
defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle"
publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle"
} }

View File

@@ -1,37 +1,28 @@
org.gradle.jvmargs=-Xmx512m
kotlin.code.style=official kotlin.code.style=official
org.gradle.parallel=true org.gradle.parallel=true
kotlin.js.generate.externals=true kotlin.js.generate.externals=true
kotlin.incremental=true kotlin.incremental=true
kotlin.incremental.js=true kotlin.incremental.js=true
android.useAndroidX=true
android.enableJetifier=true
kotlin_version=1.6.10
kotlin_serialisation_core_version=1.3.2
github_release_plugin_version=2.2.12 kotlin_version=2.2.20
kotlin_serialisation_core_version=1.9.0
tgbotapi_version=0.38.13 github_release_plugin_version=2.5.2
micro_utils_version=0.9.22
exposed_version=0.37.3
plagubot_version=0.5.1
# ANDROID tgbotapi_version=30.0.0
micro_utils_version=0.26.6
exposed_version=0.61.0
plagubot_version=10.9.0
android_minSdkVersion=21 nmcp_version=1.2.0
android_compileSdkVersion=32
android_buildToolsVersion=32.0.0
dexcount_version=3.0.1
junit_version=4.12
test_ext_junit_version=1.1.2
espresso_core=3.3.0
# Dokka # Dokka
dokka_version=1.6.10 dokka_version=2.1.0
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.0.18 version=0.27.0
android_code_version=18

Binary file not shown.

View File

@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

295
gradlew vendored
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,81 +15,114 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" # This is normally unused
APP_BASE_NAME=`basename "$0"` # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
MAX_FD="$MAX_FD_LIMIT" # shellcheck disable=SC2039,SC3045
fi MAX_FD=$( ulimit -H -n ) ||
ulimit -n $MAX_FD warn "Could not query maximum file descriptor limit"
if [ $? -ne 0 ] ; then esac
warn "Could not set maximum file descriptor limit: $MAX_FD" case $MAX_FD in #(
fi '' | soft) :;; #(
else *)
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
fi # shellcheck disable=SC2039,SC3045
fi ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=`save "$@"` # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

182
gradlew.bat vendored
View File

@@ -1,89 +1,93 @@
@rem @rem
@rem Copyright 2015 the original author or authors. @rem Copyright 2015 the original author or authors.
@rem @rem
@rem Licensed under the Apache License, Version 2.0 (the "License"); @rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License. @rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at @rem You may obtain a copy of the License at
@rem @rem
@rem https://www.apache.org/licenses/LICENSE-2.0 @rem https://www.apache.org/licenses/LICENSE-2.0
@rem @rem
@rem Unless required by applicable law or agreed to in writing, software @rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS, @rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@if "%DEBUG%" == "" @echo off @rem
@rem ##########################################################################
@rem @if "%DEBUG%"=="" @echo off
@rem Gradle startup script for Windows @rem ##########################################################################
@rem @rem
@rem ########################################################################## @rem Gradle startup script for Windows
@rem
@rem Set local scope for the variables with windows NT shell @rem ##########################################################################
if "%OS%"=="Windows_NT" setlocal
@rem Set local scope for the variables with windows NT shell
set DIRNAME=%~dp0 if "%OS%"=="Windows_NT" setlocal
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set DIRNAME=%~dp0
set APP_HOME=%DIRNAME% if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
@rem Resolve any "." and ".." in APP_HOME to make it shorter. set APP_BASE_NAME=%~n0
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Resolve any "." and ".." in APP_HOME to make it shorter.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Find java.exe @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
if defined JAVA_HOME goto findJavaFromJavaHome set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
set JAVA_EXE=java.exe @rem Find java.exe
%JAVA_EXE% -version >NUL 2>&1 if defined JAVA_HOME goto findJavaFromJavaHome
if "%ERRORLEVEL%" == "0" goto execute
set JAVA_EXE=java.exe
echo. %JAVA_EXE% -version >NUL 2>&1
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if %ERRORLEVEL% equ 0 goto execute
echo.
echo Please set the JAVA_HOME variable in your environment to match the echo. 1>&2
echo location of your Java installation. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
goto fail echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% goto fail
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
:findJavaFromJavaHome
if exist "%JAVA_EXE%" goto execute set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% if exist "%JAVA_EXE%" goto execute
echo.
echo Please set the JAVA_HOME variable in your environment to match the echo. 1>&2
echo location of your Java installation. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
goto fail echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
:execute
@rem Setup the command line goto fail
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :execute
@rem Setup the command line
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
@rem Execute Gradle
:end "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd :end
@rem End local scope for the variables with windows NT shell
:fail if %ERRORLEVEL% equ 0 goto mainEnd
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! :fail
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
exit /b 1 rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
:mainEnd if %EXIT_CODE% equ 0 set EXIT_CODE=1
if "%OS%"=="Windows_NT" endlocal if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:omega
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,26 +0,0 @@
project.version = "$version"
project.group = "$group"
apply from: "$publishGradlePath"
kotlin {
android {
publishAllLibraryVariants()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
}
}
apply from: "$defaultAndroidSettingsPresetPath"

View File

@@ -1,35 +0,0 @@
project.version = "$version"
project.group = "$group"
apply from: "$publishGradlePath"
kotlin {
jvm {
compilations.main.kotlinOptions.useIR = true
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
}
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

View File

@@ -4,14 +4,17 @@ project.group = "$group"
apply from: "$publishGradlePath" apply from: "$publishGradlePath"
kotlin { kotlin {
jvm() jvm {
compilations.main {
kotlinOptions {
jvmTarget = "17"
}
}
}
js (IR) { js (IR) {
browser() browser()
nodejs() nodejs()
} }
android {
publishAllLibraryVariants()
}
sourceSets { sourceSets {
commonMain { commonMain {
@@ -34,22 +37,12 @@ kotlin {
jsTest { jsTest {
dependencies { dependencies {
implementation kotlin('test-js') implementation kotlin('test-js')
implementation kotlin('test-junit')
}
}
androidTest {
dependencies {
implementation kotlin('test-junit')
implementation "androidx.test.ext:junit:$test_ext_junit_version"
implementation "androidx.test.espresso:espresso-core:$espresso_core"
} }
} }
} }
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }
apply from: "$defaultAndroidSettingsPresetPath"

View File

@@ -1,7 +1,7 @@
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
task javadocsJar(type: Jar) { task javadocsJar(type: Jar) {
classifier = 'javadoc' archiveClassifier = 'javadoc'
} }
publishing { publishing {
@@ -42,20 +42,36 @@ publishing {
maven { maven {
name = "GithubPackages" name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotApiLibraries") url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotApiLibraries")
credentials { credentials {
username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER') username = project.hasProperty('GITHUBPACKAGES_USER') ? project.property('GITHUBPACKAGES_USER') : System.getenv('GITHUBPACKAGES_USER')
password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD') password = project.hasProperty('GITHUBPACKAGES_PASSWORD') ? project.property('GITHUBPACKAGES_PASSWORD') : System.getenv('GITHUBPACKAGES_PASSWORD')
} }
}
}
if ((project.hasProperty('INMONEXUS_USER') || System.getenv('INMONEXUS_USER') != null) && (project.hasProperty('INMONEXUS_PASSWORD') || System.getenv('INMONEXUS_PASSWORD') != null)) {
maven {
name = "InmoNexus"
url = uri("https://nexus.inmo.dev/repository/maven-releases/")
credentials {
username = project.hasProperty('INMONEXUS_USER') ? project.property('INMONEXUS_USER') : System.getenv('INMONEXUS_USER')
password = project.hasProperty('INMONEXUS_PASSWORD') ? project.property('INMONEXUS_PASSWORD') : System.getenv('INMONEXUS_PASSWORD')
}
} }
} }
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven { maven {
name = "sonatype" name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials { credentials {
username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')
password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')
} }
} }
} }
} }
@@ -76,4 +92,27 @@ if (project.hasProperty("signing.gnupg.keyName")) {
dependsOn(it) dependsOn(it)
} }
} }
// Workaround to make android sign operations depend on signing tasks
project.getTasks().withType(AbstractPublishToMaven.class).configureEach {
def signingTasks = project.getTasks().withType(Sign.class)
mustRunAfter(signingTasks)
}
// Workaround to make test tasks use sign
project.getTasks().withType(Sign.class).configureEach { signTask ->
def withoutSign = (signTask.name.startsWith("sign") ? signTask.name.minus("sign") : signTask.name)
def pubName = withoutSign.endsWith("Publication") ? withoutSign.substring(0, withoutSign.length() - "Publication".length()) : withoutSign
// These tasks only exist for native targets, hence findByName() to avoid trying to find them for other targets
// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def debugTestTask = tasks.findByName("linkDebugTest$pubName")
if (debugTestTask != null) {
signTask.mustRunAfter(debugTestTask)
}
// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
def testTask = tasks.findByName("compileTestKotlin$pubName")
if (testTask != null) {
signTask.mustRunAfter(testTask)
}
}
} }

View File

@@ -1 +1 @@
{"licenses":[{"id":"MIT","title":"MIT License","url":"https://opensource.org/licenses/MIT"}],"mavenConfig":{"name":"${project.name}","description":"${project.name}","url":"https://github.com/InsanusMokrassar/TelegramBotApiLibraries","vcsUrl":"https://github.com/InsanusMokrassar/TelegramBotApiLibraries.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotApiLibraries"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}} {"licenses":[{"id":"MIT","title":"MIT License","url":"https://opensource.org/licenses/MIT"}],"mavenConfig":{"name":"${project.name}","description":"${project.name}","url":"https://github.com/InsanusMokrassar/TelegramBotApiLibraries","vcsUrl":"https://github.com/InsanusMokrassar/TelegramBotApiLibraries.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotApiLibraries"},{"name":"InmoNexus","url":"https://nexus.inmo.dev/repository/maven-releases/"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

6
renovate.json Normal file
View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}

17
resender/build.gradle Normal file
View File

@@ -0,0 +1,17 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJavaWithJsProjectPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api "dev.inmo:tgbotapi.core:$tgbotapi_version"
}
}
}
}

View File

@@ -0,0 +1,36 @@
package dev.inmo.tgbotapi.libraries.resender
import dev.inmo.tgbotapi.types.FullChatIdentifierSerializer
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.MediaGroupId
import dev.inmo.tgbotapi.types.MessageId
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.abstracts.Message
import dev.inmo.tgbotapi.types.message.abstracts.PossiblyMediaGroupMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupContent
import kotlinx.serialization.Serializable
@Serializable
data class MessageMetaInfo(
@Serializable(FullChatIdentifierSerializer::class)
val chatId: IdChatIdentifier,
val messageId: MessageId,
val group: MediaGroupId? = null
) {
val metaInfo: Message.MetaInfo
get() = Message.MetaInfo(chatId, messageId)
}
fun Message.asMessageMetaInfos(): List<MessageMetaInfo> {
return if (this is ContentMessage<*>) {
(content as? MediaGroupContent<*>) ?.group ?.map {
MessageMetaInfo(it.sourceMessage.chat.id, it.sourceMessage.messageId, it.sourceMessage.mediaGroupId)
}
} else {
null
} ?: listOf(MessageMetaInfo(chat.id, messageId, (this as? PossiblyMediaGroupMessage<*>) ?.mediaGroupId))
}
operator fun MessageMetaInfo.Companion.invoke(
message: Message
) = MessageMetaInfo(message.chat.id, message.messageId, (message as? PossiblyMediaGroupMessage<*>) ?.mediaGroupId)

View File

@@ -0,0 +1,160 @@
package dev.inmo.tgbotapi.libraries.resender
import dev.inmo.micro_utils.common.applyDiff
import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.ForwardMessage
import dev.inmo.tgbotapi.requests.ForwardMessages
import dev.inmo.tgbotapi.requests.send.CopyMessage
import dev.inmo.tgbotapi.requests.send.CopyMessages
import dev.inmo.tgbotapi.requests.send.media.SendMediaGroup
import dev.inmo.tgbotapi.types.ChatIdentifier
import dev.inmo.tgbotapi.types.IdChatIdentifier
import dev.inmo.tgbotapi.types.mediaCountInMediaGroup
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
import dev.inmo.tgbotapi.types.message.content.MediaGroupPartContent
class MessagesResender(
private val bot: TelegramBot,
private val cacheChatId: ChatIdentifier
) {
suspend fun resend(
targetChatId: IdChatIdentifier,
messagesInfo: List<MessageMetaInfo>,
onBetweenMessages: suspend (sent: List<MessageMetaInfo>, toBeSent: List<MessageMetaInfo>) -> Unit
): List<Pair<MessageMetaInfo, MessageMetaInfo>> {
val messagesWithOrders = messagesInfo.mapIndexed { i, messageInfo -> messageInfo to i }.toMap()
val ordersWithMessagesGroups = messagesInfo.groupBy { it.group }.flatMap { (group, list) ->
if (group == null) {
list.map {
messagesWithOrders.getValue(it) to listOf(it)
}
} else {
listOf(messagesWithOrders.getValue(list.first()) to list)
}
}.sortedBy { it.first }
val sent = mutableListOf<MessageMetaInfo>()
val leftToSend = ordersWithMessagesGroups.map { it.second }.toMutableList()
return ordersWithMessagesGroups.flatMap { (_, contents) ->
val sourceMessagesToSentMessages = mutableListOf<Pair<MessageMetaInfo, MessageMetaInfo>>()
onBetweenMessages(sent.toList(), leftToSend.flatten())
when {
contents.size == 1 -> {
val messageInfo = contents.first()
runCatching {
MessageMetaInfo(
targetChatId,
bot.execute(
CopyMessage(
toChatId = targetChatId,
fromChatId = messageInfo.chatId,
messageId = messageInfo.messageId,
)
)
)
}.onFailure { _ ->
runCatching {
bot.execute(
ForwardMessage(
toChatId = targetChatId,
fromChatId = messageInfo.chatId,
messageId = messageInfo.messageId
)
)
}.onSuccess {
MessageMetaInfo(
targetChatId,
bot.execute(
CopyMessage(
toChatId = targetChatId,
fromChatId = it.chat.id,
messageId = it.messageId
)
)
)
}
}.getOrNull() ?.let {
sourceMessagesToSentMessages.add(messageInfo to it)
}
}
else -> {
val resultContents = contents.mapNotNull {
it to (
bot.execute(
ForwardMessage(
toChatId = cacheChatId,
fromChatId = it.chatId,
messageId = it.messageId
)
) as? ContentMessage<*> ?: return@mapNotNull null)
}.mapNotNull { (src, forwardedMessage) ->
val forwardedMessageAsMediaPartMessage = forwardedMessage.takeIf {
it.content is MediaGroupPartContent
} ?.let {
it as ContentMessage<MediaGroupPartContent>
}
src to (forwardedMessageAsMediaPartMessage ?: null.also { _ ->
sourceMessagesToSentMessages.add(
src to MessageMetaInfo(
targetChatId,
bot.execute(
CopyMessage(
toChatId = targetChatId,
fromChatId = forwardedMessage.chat.id,
messageId = forwardedMessage.messageId
)
)
)
)
} ?: return@mapNotNull null)
}
resultContents.singleOrNull() ?.also { (src, it) ->
sourceMessagesToSentMessages.add(
src to MessageMetaInfo(
targetChatId,
bot.execute(
CopyMessage(
toChatId = targetChatId,
fromChatId = it.chat.id,
messageId = it.messageId
)
)
)
)
} ?: resultContents.chunked(mediaCountInMediaGroup.last).forEach {
bot.execute(
SendMediaGroup<MediaGroupPartContent>(
targetChatId,
it.map { it.second.content.toMediaGroupMemberTelegramMedia() }
)
).content.group.mapIndexed { i, partWrapper ->
it.getOrNull(i) ?.let {
sourceMessagesToSentMessages.add(
it.first to MessageMetaInfo(
partWrapper.sourceMessage.chat.id,
partWrapper.sourceMessage.messageId,
partWrapper.sourceMessage.mediaGroupId
)
)
}
}
}
}
}
leftToSend.takeIf { it.isNotEmpty() } ?.removeAt(0) ?.also {
sent.addAll(it)
}
sourceMessagesToSentMessages.toList()
}
}
suspend fun resend(
targetChatId: IdChatIdentifier,
messagesInfo: List<MessageMetaInfo>
): List<Pair<MessageMetaInfo, MessageMetaInfo>> = resend(targetChatId, messagesInfo) { _, _ -> }
}

View File

@@ -7,6 +7,8 @@ String[] includes = [
":cache:content:common", ":cache:content:common",
":cache:content:micro_utils", ":cache:content:micro_utils",
":resender",
] ]