1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-16 03:50:24 +00:00

Compare commits

...

48 Commits

Author SHA1 Message Date
4197e13c54 Update CHANGELOG.md 2022-12-28 09:12:54 +06:00
e09ea9a9b4 upfill changelog 2022-12-28 09:11:49 +06:00
2a32654d57 update miroutils and fill changelog 2022-12-28 09:05:32 +06:00
0fff553ce1 Update KtorRequestsExecutor.kt 2022-12-27 22:31:42 +06:00
33a1701f5b Update ExceptionsOnlyLimiter.kt 2022-12-27 22:30:47 +06:00
f9a9f958ba Update ExceptionsOnlyLimiter.kt 2022-12-27 22:25:26 +06:00
f686be0271 Update ExceptionsOnlyLimiter.kt 2022-12-27 22:22:03 +06:00
91307f3ebf improvements in ExceptionsOnlyLimiter 2022-12-26 21:37:48 +06:00
8e64205f53 several improvements in requests limiters 2022-12-26 20:49:29 +06:00
5434df1f02 Merge pull request #695 from InsanusMokrassar/4.2.2
4.2.2
2022-12-18 10:05:17 +06:00
e56199ac9f Update CHANGELOG.md 2022-12-18 10:01:46 +06:00
3e199c6944 Update libs.versions.toml 2022-12-18 10:01:25 +06:00
b43d9aefb9 fix of #694 2022-12-13 10:52:56 +06:00
fe133bbde0 update microutils 2022-12-13 10:49:18 +06:00
332fe95adf start 4.2.2 2022-12-13 10:48:38 +06:00
1f416d4a28 Merge pull request #693 from InsanusMokrassar/4.2.1
4.2.1
2022-12-08 10:50:07 +06:00
e626d8b5cc fix dependencies and fill changelog 2022-12-08 10:30:26 +06:00
b906d605f4 temporal include of internal microutils build 2022-12-08 09:35:22 +06:00
d3584e793c add makeChatLink 2022-12-08 09:17:58 +06:00
f71ac51461 improve makeLinkToMessage 2022-12-08 09:06:54 +06:00
5fe8cf948a start 4.2.1 2022-12-08 08:57:54 +06:00
85ea101641 Merge pull request #691 from InsanusMokrassar/4.2.0
4.2.0
2022-12-05 11:52:47 +06:00
dc3ecec5c7 Update CHANGELOG.md 2022-12-05 11:51:46 +06:00
ebd5e1a37b Update libs.versions.toml 2022-12-05 05:57:43 +06:00
3da2e4fffb starr 4.2.0 2022-12-05 05:55:15 +06:00
9aea59b44b Update gradle-wrapper.properties 2022-11-28 16:37:50 +06:00
7bab96a9cd Merge pull request #686 from InsanusMokrassar/4.1.3
4.1.3
2022-11-28 16:35:58 +06:00
a0c10d9a9a Update CHANGELOG.md 2022-11-27 17:38:24 +06:00
e2901cb9f9 Update libs.versions.toml 2022-11-27 17:38:16 +06:00
3c60d074ba Update CHANGELOG.md 2022-11-24 19:28:32 +06:00
f10cfdc5f1 Update libs.versions.toml 2022-11-24 19:27:56 +06:00
270f9a60c1 fixes 2022-11-20 13:28:23 +06:00
2b49a75a42 UserId = ChatId 2022-11-20 13:21:20 +06:00
9f7a348000 fill changelog 2022-11-18 23:08:16 +06:00
8cec74aa6a fix in gitea publishing name 2022-11-18 22:22:03 +06:00
d1914c8045 update order of publishing steps 2022-11-18 22:16:47 +06:00
baeb4848cf test version with outs variations of content message generics 2022-11-18 22:11:03 +06:00
dbadcb3f5a Merge pull request #685 from InsanusMokrassar/4.1.2
4.1.2
2022-11-17 12:55:37 +06:00
13dd73dd63 update dependencies 2022-11-17 12:34:33 +06:00
ceba86abcb Fixes in CallbackQuery waiters 2022-11-17 12:33:16 +06:00
f6a0b1a19d start 4.1.2 2022-11-17 12:30:26 +06:00
4ed6e5c7b2 add gitea publication 2022-11-17 11:26:05 +06:00
7f43dc0815 Merge pull request #684 from InsanusMokrassar/4.1.1
4.1.1
2022-11-14 23:24:03 +06:00
c72993017f FullChatIdentifierSerializer 2022-11-11 21:44:37 +06:00
e8991f8f89 Add opportunity to create "IdChatIdentifier" with optional "threadId" 2022-11-11 12:59:42 +06:00
03cd4276a9 start 4.1.1 2022-11-11 12:58:11 +06:00
4427d76cd1 Update FUNDING.yml 2022-11-10 20:50:36 +06:00
00f504a095 Merge pull request #682 from InsanusMokrassar/4.1.0
4.1.0
2022-11-10 20:18:52 +06:00
24 changed files with 206 additions and 103 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,3 +1,3 @@
# These are supported funding model platforms
custom: ['https://www.tinkoff.ru/rm/ovsyannikov.aleksey113/ObMJ712472']
custom: ['https://www.tinkoff.ru/rm/ovsyannikov.aleksey113/ObMJ712472', 'https://boosty.to/insanusmokrassar']

View File

@@ -16,7 +16,12 @@ jobs:
mv gradle.properties.tmp gradle.properties
- name: Build
run: ./gradlew build
- name: Publish
- name: Publish to Gitea
continue-on-error: true
run: ./gradlew publishAllPublicationsToGiteaRepository
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
- name: Publish to GithubPackages
continue-on-error: true
run: ./gradlew publishAllPublicationsToGithubPackagesRepository --no-parallel
env:

View File

@@ -1,5 +1,55 @@
# TelegramBotAPI changelog
## 4.2.3
* `Versions`:
* `MicroUtils`: `0.16.2` -> `0.16.4`
* `Core`:
* Simplify default `RequestsLimiter` (`ExceptionsOnlyLimiter`) (thanks to [@y9san9](https://github.com/y9san9) for help)
## 4.2.2
* `Versions`:
* `MicroUtils`: `0.16.0` -> `0.16.2`
* `Core`:
* Fix of [#694](https://github.com/InsanusMokrassar/TelegramBotAPI/issues/694): add opportunity to create `ChatId` and `ChatIdWithThreadId` from `IdChatIdentifier`
## 4.2.1
* `Versions`:
* `MicroUtils`: `0.15.0` -> `0.16.0`
* `Ktor`: `2.1.3` -> `2.2.1`
* `Utils`:
* Improve support of `makeLinkToMessage` extensions
## 4.2.0
* `Versions`:
* `Kotlin`: `1.7.21` -> `1.7.22`
* `MicroUtils`: `0.14.4` -> `0.15.0`
## 4.1.3
* `Versions`:
* `MicroUtils`: `0.14.2` -> `0.14.4`
* `Core`:
* `ContentMessage`, `CommonMessage`, `PossiblyMediaGroupMessage` and `PossiblySentViaBotCommonMessage` got `out`
variance
* `UserId` now is `ChatId` instead of `IdChatIdentififer`
## 4.1.2
* `Versions`:
* `MicroUtils`: `0.14.1` -> `0.14.2`
* `BehaviourBuilder`:
* Fixes in `CallbackQuery` waiters
## 4.1.1
* `Core`:
* Add opportunity to create `IdChatIdentifier` with optional `threadId`
* New serializer `FullChatIdentifierSerializer` with serialization of `ChatIdWithThreadId`
## 4.1.0
* `Versions`:

View File

@@ -20,6 +20,7 @@ allprojects {
mavenLocal()
mavenCentral()
google()
maven { url "https://git.inmo.dev/api/packages/InsanusMokrassar/maven" }
}
if (it != rootProject.findProject("docs")) {
tasks.whenTaskAdded { task ->

View File

@@ -6,4 +6,4 @@ kotlin.incremental=true
kotlin.incremental.js=true
library_group=dev.inmo
library_version=4.1.0
library_version=4.2.3

View File

@@ -1,6 +1,6 @@
[versions]
kotlin = "1.7.21"
kotlin = "1.7.22"
kotlin-serialization = "1.4.1"
kotlin-coroutines = "1.6.4"
@@ -8,12 +8,12 @@ javax-activation = "1.1.1"
korlibs = "3.4.0"
uuid = "0.6.0"
ktor = "2.1.3"
ktor = "2.2.1"
ksp = "1.7.21-1.0.8"
ksp = "1.7.22-1.0.8"
kotlin-poet = "1.12.0"
microutils = "0.14.1"
microutils = "0.16.4"
github-release-plugin = "2.4.1"
dokka = "1.7.20"

View File

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

View File

@@ -42,20 +42,40 @@ publishing {
maven {
name = "GithubPackages"
url = uri("https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI")
credentials {
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')
}
}
}
if (project.hasProperty('GITEA_TOKEN') || System.getenv('GITEA_TOKEN') != null) {
maven {
name = "Gitea"
url = uri("https://git.inmo.dev/api/packages/InsanusMokrassar/maven")
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = project.hasProperty('GITEA_TOKEN') ? project.property('GITEA_TOKEN') : System.getenv('GITEA_TOKEN')
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) {
maven {
name = "sonatype"
url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
credentials {
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')
}
}
}
}

View File

@@ -1 +1 @@
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"${project.description}","url":"https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}
{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/LICENSE"}],"mavenConfig":{"name":"${project.name}","description":"${project.description}","url":"https://insanusmokrassar.github.io/TelegramBotAPI/TelegramBotAPI","vcsUrl":"https://github.com/insanusmokrassar/TelegramBotAPI.git","developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"GithubPackages","url":"https://maven.pkg.github.com/InsanusMokrassar/TelegramBotAPI"},{"name":"Gitea","url":"https://git.inmo.dev/api/packages/InsanusMokrassar/maven","credsType":{"type":"dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository.CredentialsType.HttpHeaderCredentials","headerName":"Authorization","headerValueProperty":"GITEA_TOKEN"}},{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}],"gpgSigning":{"type":"dev.inmo.kmppscriptbuilder.core.models.GpgSigning.Optional"}}}

View File

@@ -20,7 +20,7 @@ suspend inline fun <reified O> BehaviourContext.waitCallbackQueries(
initRequest,
errorFactory
) {
(it.callbackQueryUpdateOrNull() ?.data as O).let(::listOfNotNull)
(it.callbackQueryUpdateOrNull() ?.data as? O).let(::listOfNotNull)
}

View File

@@ -30,7 +30,7 @@ class KtorRequestsExecutor(
client: HttpClient = HttpClient(),
callsFactories: List<KtorCallFactory> = emptyList(),
excludeDefaultFactories: Boolean = false,
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter(),
private val requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter,
private val jsonFormatter: Json = nonstrictJsonFormat,
private val pipelineStepsHolder: KtorPipelineStepsHolder = KtorPipelineStepsHolder
) : BaseRequestsExecutor(telegramAPIUrlsKeeper) {
@@ -51,7 +51,7 @@ class KtorRequestsExecutor(
override suspend fun <T : Any> execute(request: Request<T>): T {
return runCatchingSafely {
pipelineStepsHolder.onBeforeSearchCallFactory(request, callsFactories)
requestsLimiter.limit {
requestsLimiter.limit(request) {
var result: T? = null
lateinit var factoryHandledRequest: KtorCallFactory
for (potentialFactory in callsFactories) {
@@ -111,7 +111,7 @@ class KtorRequestsExecutorBuilder(
var client: HttpClient = HttpClient()
var callsFactories: List<KtorCallFactory> = emptyList()
var excludeDefaultFactories: Boolean = false
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter()
var requestsLimiter: RequestLimiter = ExceptionsOnlyLimiter
var jsonFormatter: Json = nonstrictJsonFormat
fun build() = KtorRequestsExecutor(telegramAPIUrlsKeeper, client, callsFactories, excludeDefaultFactories, requestsLimiter, jsonFormatter)

View File

@@ -1,66 +1,18 @@
package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.tgbotapi.bot.exceptions.TooMuchRequestsException
import dev.inmo.tgbotapi.types.MilliSeconds
import dev.inmo.tgbotapi.types.RetryAfterError
import io.ktor.client.plugins.ClientRequestException
import io.ktor.http.HttpStatusCode
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
/**
* This limiter will limit requests only after getting a [RetryAfterError] or [ClientRequestException] with
* [HttpStatusCode.TooManyRequests] status code. Important thing is that in case if some of block has been blocked, all
* the others will wait until it will be possible to be called
*
* @param defaultTooManyRequestsDelay This parameter will be used in case of getting [ClientRequestException] with
* [HttpStatusCode.TooManyRequests] as a parameter for delay like it would be [TooMuchRequestsException]. The reason of
* it is that in [ClientRequestException] there is no information about required delay between requests
* Simple limiter which will lock any request when [TooMuchRequestsExceptions] is thrown and rerun request after lock time
*/
class ExceptionsOnlyLimiter(
private val defaultTooManyRequestsDelay: MilliSeconds = 1000L
) : RequestLimiter {
private val lockState = MutableStateFlow(false)
private suspend fun lock(timeMillis: MilliSeconds) {
try {
safely {
lockState.emit(true)
delay(timeMillis)
}
} finally {
lockState.emit(false)
}
}
object ExceptionsOnlyLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T {
while (true) {
lockState.first { !it }
var throwable: Throwable? = null
val result = safely({
throwable = when (it) {
is TooMuchRequestsException -> {
lock(it.retryAfter.leftToRetry)
it
}
is ClientRequestException -> {
if (it.response.status == HttpStatusCode.TooManyRequests) {
lock(defaultTooManyRequestsDelay)
} else {
throw it
}
it
}
else -> throw it
}
null
}) {
block()
}
if (throwable == null) {
return result!!
}
return try {
block()
} catch (e: TooMuchRequestsException) {
delay(e.retryAfter.leftToRetry)
limit(block)
}
}
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.tgbotapi.bot.settings.limiters
object NoLimitsLimiter : RequestLimiter {
override suspend fun <T> limit(block: suspend () -> T): T = block()
}

View File

@@ -1,8 +1,12 @@
package dev.inmo.tgbotapi.bot.settings.limiters
import dev.inmo.tgbotapi.requests.abstracts.Request
interface RequestLimiter {
/**
* Use limit for working of block (like delay between or after, for example)
*/
suspend fun <T> limit(block: suspend () -> T): T
suspend fun <T : Any> limit(request: Request<T>, block: suspend () -> T) = limit(block)
}

View File

@@ -30,6 +30,9 @@ sealed interface IdChatIdentifier : ChatIdentifier {
companion object {
operator fun invoke(chatId: Identifier) = ChatId(chatId)
operator fun invoke(chatId: Identifier, threadId: MessageThreadId?) = threadId ?.let {
ChatIdWithThreadId(chatId, threadId)
} ?: ChatId(chatId)
}
}
@@ -51,6 +54,13 @@ value class ChatIdWithThreadId(val chatIdWithThreadId: Pair<Identifier, MessageT
val ChatIdentifier.threadId: MessageThreadId?
get() = (this as? IdChatIdentifier) ?.threadId
fun IdChatIdentifier.toChatId() = when (this) {
is ChatId -> this
is ChatIdWithThreadId -> ChatId(chatId)
}
fun IdChatIdentifier.toChatWithThreadId(threadId: MessageThreadId) = IdChatIdentifier(chatId, threadId)
/**
* https://core.telegram.org/bots/api#formatting-options
*/
@@ -66,9 +76,9 @@ val UserId.userLink: String
val User.link: String
get() = id.userLink
typealias UserId = IdChatIdentifier
typealias UserId = ChatId
fun Identifier.toChatId(): IdChatIdentifier = ChatId(this)
fun Identifier.toChatId(): ChatId = ChatId(this)
fun Int.toChatId(): IdChatIdentifier = toLong().toChatId()
fun Byte.toChatId(): IdChatIdentifier = toLong().toChatId()
@@ -94,6 +104,7 @@ object ChatIdentifierSerializer : KSerializer<ChatIdentifier> {
override val descriptor: SerialDescriptor = internalSerializer.descriptor
override fun deserialize(decoder: Decoder): ChatIdentifier {
val id = internalSerializer.deserialize(decoder)
return id.longOrNull ?.let {
ChatId(it)
} ?: id.content.let {
@@ -112,3 +123,41 @@ object ChatIdentifierSerializer : KSerializer<ChatIdentifier> {
}
}
}
@RiskFeature
object FullChatIdentifierSerializer : KSerializer<ChatIdentifier> {
private val internalSerializer = JsonPrimitive.serializer()
override val descriptor: SerialDescriptor = internalSerializer.descriptor
override fun deserialize(decoder: Decoder): ChatIdentifier {
val id = internalSerializer.deserialize(decoder)
return id.longOrNull ?.let {
ChatId(it)
} ?:let {
val splitted = id.content.split("/")
if (splitted.size == 2) {
val (chatId, threadId) = splitted
ChatIdWithThreadId(
chatId.toLongOrNull() ?: return@let null,
threadId.toLongOrNull() ?: return@let null
)
} else {
null
}
} ?: id.content.let {
if (!it.startsWith("@")) {
Username("@$it")
} else {
Username(it)
}
}
}
override fun serialize(encoder: Encoder, value: ChatIdentifier) {
when (value) {
is ChatId -> encoder.encodeLong(value.chatId)
is ChatIdWithThreadId -> encoder.encodeString("${value.chatId}/${value.threadId}")
is Username -> encoder.encodeString(value.username)
}
}
}

View File

@@ -52,7 +52,7 @@ data class ExtendedGroupChatImpl(
@Serializable
data class ExtendedPrivateChatImpl(
@SerialName(idField)
override val id: IdChatIdentifier,
override val id: UserId,
@SerialName(photoField)
override val chatPhoto: ChatPhoto? = null,
@SerialName(usernameField)

View File

@@ -18,7 +18,7 @@ data class GroupChatImpl(
@Serializable
data class PrivateChatImpl(
@SerialName(idField)
override val id: IdChatIdentifier,
override val id: UserId,
@SerialName(usernameField)
override val username: Username? = null,
@SerialName(firstNameField)

View File

@@ -42,6 +42,7 @@ sealed interface Sticker : TelegramMediaFile, SizedMediaFile, ThumbedMediaFile {
get() = false
}
@OptIn(RiskFeature::class)
object StickerSerializer : KSerializer<Sticker> {
override val descriptor: SerialDescriptor = StickerSurrogate.serializer().descriptor

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.message.content.MessageContent
sealed interface CommonMessage<T: MessageContent> : Message,
sealed interface CommonMessage<out T: MessageContent> : Message,
PossiblyForwardedMessage,
PossiblyEditedMessage,
PossiblyReplyMessage,

View File

@@ -2,7 +2,7 @@ package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.message.content.MessageContent
interface ContentMessage<T: MessageContent>: Message {
interface ContentMessage<out T: MessageContent>: Message {
val hasProtectedContent: Boolean
val content: T

View File

@@ -3,6 +3,6 @@ package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.MediaGroupIdentifier
import dev.inmo.tgbotapi.types.message.content.MessageContent
interface PossiblyMediaGroupMessage<T : MessageContent> : ContentMessage<T> {
interface PossiblyMediaGroupMessage<out T : MessageContent> : ContentMessage<T> {
val mediaGroupId: MediaGroupIdentifier?
}

View File

@@ -2,4 +2,4 @@ package dev.inmo.tgbotapi.types.message.abstracts
import dev.inmo.tgbotapi.types.message.content.MessageContent
sealed interface PossiblySentViaBotCommonMessage<T: MessageContent> : CommonMessage<T>, PossiblySentViaBot
sealed interface PossiblySentViaBotCommonMessage<out T: MessageContent> : CommonMessage<T>, PossiblySentViaBot

View File

@@ -7,39 +7,61 @@ import dev.inmo.tgbotapi.types.message.textsources.link
import io.ktor.http.encodeURLQueryComponent
fun makeUsernameLink(username: String) = "$internalLinkBeginning/$username"
fun makeUsernameLink(username: String, threadId: MessageThreadId? = null) = "$internalLinkBeginning/$username${threadId ?.let { "/$it" } ?: ""}"
fun makeChatLink(identifier: Identifier, threadId: MessageThreadId? = null) = identifier.toString().replace(
linkIdRedundantPartRegex,
""
).let { bareId ->
"$internalLinkBeginning/c/$bareId${threadId ?.let { "/$it" } ?: ""}"
}
fun makeUsernameDeepLinkPrefix(username: String) = "${makeUsernameLink(username)}?start="
fun makeUsernameStartattachPrefix(username: String) = "$internalLinkBeginning/$username?startattach"
fun makeUsernameStartattachLink(username: String, data: String? = null) = "${makeUsernameStartattachPrefix(username)}${data?.let { "=$it" } ?: ""}"
inline val Username.link
get() = makeUsernameLink(usernameWithoutAt)
val IdChatIdentifier.link: String
get() = makeChatLink(chatId, threadId)
fun ChatId.link(threadId: MessageThreadId?) = makeChatLink(chatId, threadId)
inline fun Username.link(threadId: MessageThreadId?) = makeUsernameLink(usernameWithoutAt, threadId)
inline val Username.deepLinkPrefix
get() = makeUsernameDeepLinkPrefix(usernameWithoutAt)
inline val Username.startattachPrefix
get() = makeUsernameStartattachPrefix(usernameWithoutAt)
inline fun makeLink(username: Username) = username.link
inline fun makeLink(username: Username, threadId: MessageThreadId? = null) = username.link(threadId)
inline fun makeTelegramDeepLink(username: String, startParameter: String) = "${makeUsernameDeepLinkPrefix(username)}$startParameter".encodeURLQueryComponent()
inline fun makeTelegramStartattach(username: String, data: String? = null) = makeUsernameStartattachLink(username, data)
inline fun makeDeepLink(username: Username, startParameter: String) = makeTelegramDeepLink(username.usernameWithoutAt, startParameter)
inline fun makeTelegramDeepLink(username: Username, startParameter: String) = makeDeepLink(username, startParameter)
inline fun makeTelegramStartattach(username: Username, data: String? = null) = makeTelegramStartattach(username.usernameWithoutAt, data)
fun makeLinkToMessage(
username: String,
messageId: MessageId
): String = "$internalLinkBeginning/$username/$messageId"
fun makeLinkToMessage(
username: Username,
messageId: MessageId
): String = makeLinkToMessage(username.username, messageId)
fun makeLinkToMessage(
chat: UsernameChat,
messageId: MessageId
): String? = chat.username ?.let { makeLinkToMessage(it, messageId) }
private val linkIdRedundantPartRegex = Regex("^-100")
private val usernameBeginSymbolRegex = Regex("^@")
fun makeLinkToMessage(
username: String,
messageId: MessageId,
threadId: MessageThreadId? = null
): String = "${makeUsernameLink(username, threadId)}/$messageId"
fun makeLinkToMessage(
username: Username,
messageId: MessageId,
threadId: MessageThreadId? = null
): String = makeLinkToMessage(username.username, messageId, threadId)
fun makeLinkToMessage(
chatId: Identifier,
messageId: MessageId,
threadId: MessageThreadId? = null
): String = chatId.toString().replace(
linkIdRedundantPartRegex,
""
).let { bareId ->
"$internalLinkBeginning/c/$bareId/${threadId ?.let { "$it/" } ?: ""}$messageId"
}
fun makeLinkToMessage(
chatId: IdChatIdentifier,
messageId: MessageId,
): String = makeLinkToMessage(chatId.chatId, messageId, chatId.threadId)
/**
* Link which can be used as by any user to get access to [Message]. Returns null in case when there are no
* known way to build link (for [PrivateChat]s, for example)
@@ -49,16 +71,10 @@ fun makeLinkToMessage(
messageId: MessageId
): String? {
return when {
chat is UsernameChat && chat.username != null -> {
"$internalLinkBeginning/${chat.username ?.username ?.replace(
usernameBeginSymbolRegex, "")}/$messageId"
}
chat !is PrivateChat -> chat.id.chatId.toString().replace(
linkIdRedundantPartRegex,
""
).let { bareId ->
"$internalLinkBeginning/c/$bareId/$messageId"
}
chat is UsernameChat && chat.username != null -> chat.username ?.let {
makeLinkToMessage(it, messageId, chat.id.threadId)
} ?: makeLinkToMessage(chat.id, messageId)
chat !is PrivateChat -> makeLinkToMessage(chat.id, messageId)
else -> return null
}
}
@@ -79,7 +95,7 @@ val Message.link: String?
val Chat.link: String?
get() {
if (this is UsernameChat) {
username ?.link
username ?.link ?: id.link
}
if (this is ExtendedPublicChat) {
inviteLink ?.let { return it }

View File

@@ -200,7 +200,7 @@ infix fun String.mention(parseMode: ParseMode): String = when (parseMode) {
is MarkdownV2 -> mentionMarkdownV2()
}
infix fun Pair<String, IdChatIdentifier>.mention(parseMode: ParseMode): String = when (parseMode) {
infix fun Pair<String, UserId>.mention(parseMode: ParseMode): String = when (parseMode) {
is HTML -> first.textMentionHTML(second)
is Markdown -> first.textMentionMarkdown(second)
is MarkdownV2 -> first.textMentionMarkdownV2(second)