Compare commits

...

167 Commits

Author SHA1 Message Date
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
40 changed files with 628 additions and 293 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,10 +7,8 @@ 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"
} }
} }
@@ -20,6 +18,7 @@ allprojects {
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

@@ -7,8 +7,8 @@ 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,19 +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.chat.ExtendedBot import dev.inmo.tgbotapi.types.chat.ExtendedBot
import dev.inmo.tgbotapi.types.chat.member.AdministratorChatMember 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
@@ -29,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
@@ -63,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.chat.member.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

@@ -5,75 +5,79 @@ 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.chat.member.* import dev.inmo.tgbotapi.types.chat.member.*
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,
DefaultAdminsCacheAPIRepo(
ExposedKeyValuesRepo( ExposedKeyValuesRepo(
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,18 +1,27 @@
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 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.Koin
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.core.qualifier.named
import org.koin.core.scope.Scope import org.koin.core.scope.Scope
import org.koin.dsl.binds
val Scope.adminsPlugin: AdminsPlugin? val Scope.adminsPlugin: AdminsPlugin?
get() = getOrNull() get() = getOrNull()
@@ -28,6 +37,7 @@ class AdminsPlugin : Plugin {
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 {
val flow = mutex.withLock { val flow = mutex.withLock {
databaseToAdminsCacheAPI.getOrPut(database){ MutableStateFlow(null) } databaseToAdminsCacheAPI.getOrPut(database){ MutableStateFlow(null) }
@@ -35,18 +45,63 @@ class AdminsPlugin : Plugin {
return flow.filterNotNull().first() return flow.filterNotNull().first()
} }
override fun Module.setupDI(database: Database, params: JsonObject) { override fun Module.setupDI(config: JsonObject) {
single { this@AdminsPlugin } single { this@AdminsPlugin }
val scopeQualifier = named("admins plugin scope")
single(scopeQualifier) { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
single<DefaultAdminsCacheAPIRepo> {
DefaultAdminsCacheAPIRepoImpl(
ExposedKeyValuesRepo(
database,
{ long("chatId") },
{ text("member") },
"AdminsTable"
).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) { override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) {
with(koin) { with(koin) {
mutex.withLock { activateAdminsChangesListening(
val flow = databaseToAdminsCacheAPI.getOrPut(koin.get()){ MutableStateFlow(null) } get()
if (flow.value == null) { )
flow.value = AdminsCacheAPI(koin.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

@@ -4,7 +4,7 @@ import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.requests.DownloadFileStream import dev.inmo.tgbotapi.requests.DownloadFileStream
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.media.* import dev.inmo.tgbotapi.types.media.*
import dev.inmo.tgbotapi.types.message.content.MediaContent import dev.inmo.tgbotapi.types.message.content.MediaContent
import dev.inmo.tgbotapi.types.message.content.MessageContent import dev.inmo.tgbotapi.types.message.content.MessageContent
@@ -13,7 +13,7 @@ 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)
@@ -111,7 +111,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,11 +1,12 @@
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.MediaContent import dev.inmo.tgbotapi.types.message.content.MediaContent
@@ -14,7 +15,7 @@ fun interface MediaFileActualityChecker {
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 +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

@@ -4,7 +4,7 @@ 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.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
@@ -47,7 +47,7 @@ class SimpleKeyValueMessageContentCache<K>(
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)
@@ -56,9 +56,9 @@ inline fun <K> KeyValueRepo<K, MessageContent>.asMessageContentCache() = SimpleK
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) },
@@ -70,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) },
@@ -84,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,26 @@
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.21
kotlin_serialisation_core_version=1.3.3
github_release_plugin_version=2.4.1 kotlin_version=2.0.20
kotlin_serialisation_core_version=1.7.2
tgbotapi_version=2.2.2 github_release_plugin_version=2.5.2
micro_utils_version=0.11.13
exposed_version=0.38.2
plagubot_version=1.4.1
# ANDROID tgbotapi_version=18.2.0
micro_utils_version=0.22.2
android_minSdkVersion=21 exposed_version=0.54.0
android_compileSdkVersion=32 plagubot_version=10.0.0
android_buildToolsVersion=32.0.0
dexcount_version=3.1.0
junit_version=4.12
test_ext_junit_version=1.1.2
espresso_core=3.3.0
# Dokka # Dokka
dokka_version=1.6.21 dokka_version=1.9.20
# Project data # Project data
group=dev.inmo group=dev.inmo
version=0.4.2 version=0.25.0
android_code_version=26

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

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

@@ -7,15 +7,20 @@ kotlin {
jvm { jvm {
compilations.main { compilations.main {
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "17"
} }
} }
} }
js (IR) {
browser()
nodejs()
}
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
implementation kotlin('stdlib') implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_core_version"
} }
} }
commonTest { commonTest {
@@ -24,16 +29,20 @@ kotlin {
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
} }
} }
jvmTest { jvmTest {
dependencies { dependencies {
implementation kotlin('test-junit') implementation kotlin('test-junit')
} }
} }
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
} }
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }

View File

@@ -1,61 +0,0 @@
project.version = "$version"
project.group = "$group"
apply from: "$publishGradlePath"
kotlin {
jvm {
compilations.main {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
js (IR) {
browser()
nodejs()
}
android {
publishAllLibraryVariants()
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib')
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_core_version"
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmTest {
dependencies {
implementation kotlin('test-junit')
}
}
jsTest {
dependencies {
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 {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
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"}}}

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(
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(
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(
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(
targetChatId,
it.chat.id,
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",
] ]