mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI.git
synced 2025-11-27 09:45:46 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a10d766295 | |||
| ea6ab8f024 | |||
| f377d61bf0 | |||
| 18c9d4e468 | |||
| 20b931138c | |||
| d7bca15693 | |||
| 957649603b | |||
| bbc56fe94c | |||
| d048958423 | |||
| f0e9267664 | |||
| 5dd7207e09 | |||
| d96d47e32c | |||
| 1ee73dd406 | |||
| e9a05c4930 | |||
| 9e771fa04a | |||
| 06ba21fc1f | |||
| 7d72d72f2c | |||
| 80d4bdfe17 | |||
| b457d11067 | |||
| 46fe16fd73 | |||
| 8e1cafb1b6 | |||
| 68d971f874 | |||
| 37a23c7e79 | |||
| b053c29ea3 | |||
| 23a1fed7dd | |||
| 87d2537bda | |||
| 6d782f28c3 | |||
| d8dbbdf549 | |||
| f317e144e6 | |||
| 05c27804ce | |||
| eb97a8e151 | |||
|
|
06639e5f3c | ||
| 82b7bf676f | |||
| 4f9cb531c0 | |||
| 09bbb1945c | |||
| 7bedc4caf0 | |||
| 6a73aa1525 | |||
| e1ee541005 | |||
|
|
2d2fe01227 | ||
|
|
af53682b1f | ||
|
|
c8a5552c9e | ||
|
|
ffa78ebfe1 | ||
| e85e7d02f7 | |||
| 37ad279ab4 | |||
| 3b41dc1fbf | |||
| 5747383ed1 | |||
| 18a6efabb8 | |||
| dcb837b155 | |||
| 03755e9eef | |||
|
|
b9db2d544d | ||
| 800d14561e | |||
| 2378237fc5 | |||
| 48c6f103b5 | |||
| e326d289cb | |||
| 7fd9c4c897 | |||
| 347a6212d4 |
2
.github/pull_request_template.md
vendored
Normal file
2
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
* Please, be sure that you have read [Contributing](https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/CONTRIBUTING.md) file. Of course, this line must be removed during PR preparation :)
|
||||
* Describe your changes, shortly. As example (or format) you can look at the body of any [Project releases](https://github.com/InsanusMokrassar/TelegramBotAPI/releases)
|
||||
@@ -11,3 +11,5 @@ jobs:
|
||||
script: ./gradlew build -s -x jvmTest -x jsIrTest -x jsIrBrowserTest -x jsIrNodeTest -x jsLegacyTest -x jsLegacyBrowserTest -x jsLegacyNodeTest
|
||||
- state: test
|
||||
script: ./gradlew allTests
|
||||
- state: dokka
|
||||
script: ./gradlew dokkaHtml
|
||||
|
||||
1198
CHANGELOG.md
1198
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
10
CONTRIBUTING.md
Normal file
10
CONTRIBUTING.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Contributing
|
||||
|
||||
In case you wish to contribute this project, there are several small things you must remember:
|
||||
|
||||
* Give maintainers opportunity to manage your Pull request. It is required for two reasons
|
||||
* For more efficient Pull Requests handling (it is much easier for me to fix something small directly in your pull request than ask to fix some small things after each review)
|
||||
* Usually, you will set as a target `master` branch, but I prefer to include code into separated version branch firstly. So, if you will give me opporunity to change Pull Request, I will be available to change base branch
|
||||
* Currently in Pull Requests there are several bots. The most important is Travis bot and it must always successfuly build code from your Pull Request
|
||||
|
||||
This project was built on the idea of strongly-typed declaration of TelegramBotAPI. So, do not worry if maintaners will change your pull requests: you are already cool because you have contributed this project:)
|
||||
@@ -1,6 +1,6 @@
|
||||
# TelegramBotAPI
|
||||
|
||||
| Common info | [](https://github.com/KotlinBy/awesome-kotlin) [](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI) [Small survey](https://forms.gle/tnjuExdSKEr32ygKA)|
|
||||
| Common info | [](https://github.com/KotlinBy/awesome-kotlin) [](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI) [Small survey](https://forms.gle/2Hex2ynbHWHhi1KY7)|
|
||||
| -------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Useful links | [](https://t.me/InMoTelegramBotAPI) [](https://tgbotapi.inmo.dev/docs/index.html) [Examples](https://github.com/InsanusMokrassar/TelegramBotAPI-examples/), [Mini tutorial](https://bookstack.inmo.dev/books/telegrambotapi/chapter/introduction-tutorial) |
|
||||
| TelegramBotAPI Core status | [](https://bintray.com/insanusmokrassar/TelegramBotAPI/tgbotapi.core/_latestVersion) [](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.core) |
|
||||
@@ -74,7 +74,7 @@ kotlin {
|
||||
|
||||

|
||||
|
||||
In most cases, the most simple way will be to implement [TelegramBotAPI](TelegramBotAPI/README.md) - it contains
|
||||
In most cases, the most simple way will be to implement [TelegramBotAPI](tgbotapi/README.md) - it contains
|
||||
all necessary tools for comfort usage of this library. If you want to exclude some libraries, you can implement just
|
||||
[TelegramBotAPI API Extensions](tgbotapi.extensions.api/README.md),
|
||||
[TelegramBotAPI Util Extensions](tgbotapi.extensions.utils/README.md) or even
|
||||
|
||||
@@ -46,29 +46,17 @@ kotlin {
|
||||
}
|
||||
}
|
||||
|
||||
private Closure includeSourcesInDokka(String... approximateNames) {
|
||||
return {
|
||||
parent.subprojects.forEach {
|
||||
if (it != project) {
|
||||
File srcDir = new File(it.projectDir.absolutePath, "src")
|
||||
if (srcDir.exists() && srcDir.isDirectory()) {
|
||||
srcDir.eachFile { file ->
|
||||
if (approximateNames.any { file.name.contains(it) } && file.isDirectory()) {
|
||||
String pathToSrc = file.absolutePath
|
||||
sourceRoot {
|
||||
path = pathToSrc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<SourceDirectorySet> findSourcesWithName(String... approximateNames) {
|
||||
return parent.subprojects
|
||||
.findAll { it != project }
|
||||
.collectMany { it.kotlin.sourceSets }
|
||||
.findAll { sourceSet -> approximateNames.any {
|
||||
nameToFilter -> sourceSet.name.contains(nameToFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.collect { it.kotlin }
|
||||
}
|
||||
|
||||
dokka {
|
||||
outputFormat = 'html'
|
||||
|
||||
tasks.dokkaHtml {
|
||||
switch (true) {
|
||||
case project.hasProperty("DOKKA_PATH"):
|
||||
outputDirectory = project.property("DOKKA_PATH").toString()
|
||||
@@ -78,19 +66,27 @@ dokka {
|
||||
break
|
||||
}
|
||||
|
||||
multiplatform {
|
||||
global {
|
||||
skipDeprecated = true
|
||||
dokkaSourceSets {
|
||||
configureEach {
|
||||
skipDeprecated.set(true)
|
||||
|
||||
sourceLink {
|
||||
path = "./"
|
||||
url = "https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/"
|
||||
lineSuffix = "#L"
|
||||
localDirectory.set(file("./"))
|
||||
remoteUrl.set(new URL("https://github.com/InsanusMokrassar/TelegramBotAPI/blob/master/"))
|
||||
remoteLineSuffix.set("#L")
|
||||
}
|
||||
}
|
||||
|
||||
common(includeSourcesInDokka("commonMain"))
|
||||
js(includeSourcesInDokka("jsMain"/*, "commonMain"*/))
|
||||
jvm(includeSourcesInDokka("jvmMain"/*, "commonMain"*/))
|
||||
named("commonMain") {
|
||||
sourceRoots.setFrom(findSourcesWithName("commonMain"))
|
||||
}
|
||||
|
||||
named("jsMain") {
|
||||
sourceRoots.setFrom(findSourcesWithName("jsMain", "commonMain"))
|
||||
}
|
||||
|
||||
named("jvmMain") {
|
||||
sourceRoots.setFrom(findSourcesWithName("jvmMain", "commonMain"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
dokka_version=0.10.1
|
||||
dokka_version=1.4.0
|
||||
|
||||
org.gradle.jvmargs=-Xmx1024m
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1024m
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
kotlin.code.style=official
|
||||
org.gradle.parallel=true
|
||||
kotlin.js.generate.externals=true
|
||||
@@ -6,8 +6,8 @@ kotlin.incremental=true
|
||||
kotlin.incremental.js=true
|
||||
|
||||
kotlin_version=1.4.10
|
||||
kotlin_coroutines_version=1.3.9
|
||||
kotlin_serialisation_runtime_version=1.0.0-RC2
|
||||
kotlin_coroutines_version=1.4.0
|
||||
kotlin_serialisation_runtime_version=1.0.1
|
||||
klock_version=1.12.1
|
||||
uuid_version=0.2.2
|
||||
ktor_version=1.4.1
|
||||
@@ -15,7 +15,7 @@ ktor_version=1.4.1
|
||||
javax_activation_version=1.1.1
|
||||
|
||||
library_group=dev.inmo
|
||||
library_version=0.29.0
|
||||
library_version=0.29.4
|
||||
|
||||
gradle_bintray_plugin_version=1.8.5
|
||||
github_release_plugin_version=2.2.12
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
include ":tgbotapi.core"
|
||||
include ":tgbotapi.extensions.api"
|
||||
include ":tgbotapi.extensions.utils"
|
||||
|
||||
@@ -15,6 +15,7 @@ fun newRequestException(
|
||||
description.contains("Bad Request: message is not modified") -> MessageIsNotModifiedException(response, plainAnswer, message, cause)
|
||||
description == "Unauthorized" -> UnauthorizedException(response, plainAnswer, message, cause)
|
||||
description.contains("PHOTO_INVALID_DIMENSIONS") -> InvalidPhotoDimensionsException(response, plainAnswer, message, cause)
|
||||
description.contains("wrong file identifier") -> WrongFileIdentifierException(response, plainAnswer, message, cause)
|
||||
else -> null
|
||||
}
|
||||
} ?: CommonRequestException(response, plainAnswer, message, cause)
|
||||
@@ -45,3 +46,6 @@ class MessageToEditNotFoundException(response: Response, plainAnswer: String, me
|
||||
|
||||
class InvalidPhotoDimensionsException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) :
|
||||
RequestException(response, plainAnswer, message, cause)
|
||||
|
||||
class WrongFileIdentifierException(response: Response, plainAnswer: String, message: String?, cause: Throwable?) :
|
||||
RequestException(response, plainAnswer, message, cause)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package dev.inmo.tgbotapi.requests.abstracts
|
||||
|
||||
import dev.inmo.tgbotapi.types.InputMedia.toInputMediaFileAttachmentName
|
||||
import dev.inmo.tgbotapi.utils.StorageFile
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
@@ -12,6 +11,14 @@ sealed class InputFile {
|
||||
abstract val fileId: String
|
||||
}
|
||||
|
||||
internal inline val InputFile.attachFileId
|
||||
get() = "attach://$fileId"
|
||||
internal inline val InputFile.fileIdToSend
|
||||
get() = when (this) {
|
||||
is FileId -> fileId
|
||||
is MultipartFile -> attachFileId
|
||||
}
|
||||
|
||||
// TODO:: add checks for file url/file id regex
|
||||
/**
|
||||
* Contains file id or file url
|
||||
@@ -30,12 +37,6 @@ internal object InputFileSerializer : KSerializer<InputFile> {
|
||||
override fun deserialize(decoder: Decoder): FileId = FileId(decoder.decodeString())
|
||||
}
|
||||
|
||||
internal val InputFile.asMediaData: String
|
||||
get() = when (this) {
|
||||
is FileId -> fileId
|
||||
is MultipartFile -> fileId.toInputMediaFileAttachmentName()
|
||||
}
|
||||
|
||||
// TODO:: add checks for files size
|
||||
/**
|
||||
* Contains info about file for sending
|
||||
|
||||
@@ -8,12 +8,16 @@ import dev.inmo.tgbotapi.types.*
|
||||
import dev.inmo.tgbotapi.types.InputMedia.*
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.MediaGroupMessage
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.TelegramBotAPIMessageDeserializeOnlySerializerClass
|
||||
import dev.inmo.tgbotapi.utils.*
|
||||
import dev.inmo.tgbotapi.utils.throwRangeError
|
||||
import dev.inmo.tgbotapi.utils.toJsonWithoutNulls
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
|
||||
const val rawSendingMediaGroupsWarning = "Media groups contains restrictions related to combinations of media" +
|
||||
" types. Currently it is possible to combine photo + video OR audio OR documents"
|
||||
|
||||
@RiskFeature(rawSendingMediaGroupsWarning)
|
||||
fun SendMediaGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<MediaGroupMemberInputMedia>,
|
||||
@@ -52,6 +56,46 @@ fun SendMediaGroup(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to be sure that you are correctly sending playlist with audios
|
||||
*
|
||||
* @see InputMediaAudio
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun SendPlaylist(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<AudioMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId)
|
||||
|
||||
/**
|
||||
* Use this method to be sure that you are correctly sending documents media group
|
||||
*
|
||||
* @see InputMediaDocument
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun SendDocumentsGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<DocumentMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId)
|
||||
|
||||
/**
|
||||
* Use this method to be sure that you are correctly sending visual media group
|
||||
*
|
||||
* @see InputMediaPhoto
|
||||
* @see InputMediaVideo
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun SendVisualMediaGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<VisualMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = SendMediaGroup(chatId, media, disableNotification, replyToMessageId)
|
||||
|
||||
private val messagesListSerializer: KSerializer<List<MediaGroupMessage>>
|
||||
= ListSerializer(TelegramBotAPIMessageDeserializeOnlySerializerClass())
|
||||
|
||||
|
||||
@@ -23,5 +23,4 @@ internal object ChatMemberDeserializationStrategy : DeserializationStrategy<Chat
|
||||
override val descriptor: SerialDescriptor = RawChatMember.serializer().descriptor
|
||||
|
||||
override fun deserialize(decoder: Decoder): ChatMember = RawChatMember.serializer().deserialize(decoder).asChatMember
|
||||
override fun patch(decoder: Decoder, old: ChatMember): ChatMember = error("ChatMember can't be patched")
|
||||
}
|
||||
|
||||
@@ -52,7 +52,11 @@ val inlineQueryAnswerResultsLimit = 0 .. 50
|
||||
|
||||
val customTitleLength = 0 .. 16
|
||||
|
||||
val diceResultLimit = 1 .. 6
|
||||
val commonDiceResultLimit = 1 .. 6
|
||||
@Deprecated("Renamed", ReplaceWith("commonDiceResultLimit", "dev.inmo.tgbotapi.types.commonDiceResultLimit"))
|
||||
val diceResultLimit
|
||||
get() = commonDiceResultLimit
|
||||
val slotMachineDiceResultLimit = 1 .. 64
|
||||
|
||||
val botCommandLengthLimit = 1 .. 32
|
||||
val botCommandLimit = botCommandLengthLimit
|
||||
|
||||
@@ -3,6 +3,7 @@ package dev.inmo.tgbotapi.types.InputMedia
|
||||
import dev.inmo.tgbotapi.requests.abstracts.InputFile
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Deprecated("Will be removed due to redundancy for end-side users")
|
||||
fun String.toInputMediaFileAttachmentName() = "attach://$this"
|
||||
|
||||
@Serializable(InputMediaSerializer::class)
|
||||
|
||||
@@ -23,5 +23,5 @@ data class InputMediaAnimation(
|
||||
|
||||
@SerialName(mediaField)
|
||||
override val media: String
|
||||
init { media = file.fileId } // crutch until js compiling will be fixed
|
||||
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
|
||||
}
|
||||
|
||||
@@ -5,9 +5,13 @@ import dev.inmo.tgbotapi.CommonAbstracts.Performerable
|
||||
import dev.inmo.tgbotapi.requests.abstracts.*
|
||||
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.parseModeField
|
||||
import dev.inmo.tgbotapi.types.files.AudioFile
|
||||
import dev.inmo.tgbotapi.types.files.PhotoSize
|
||||
import dev.inmo.tgbotapi.types.mediaField
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import dev.inmo.tgbotapi.types.message.content.media.AudioContent
|
||||
import kotlinx.serialization.*
|
||||
|
||||
internal const val audioInputMediaType = "audio"
|
||||
|
||||
@Serializable
|
||||
data class InputMediaAudio(
|
||||
@@ -19,10 +23,26 @@ data class InputMediaAudio(
|
||||
override val performer: String? = null,
|
||||
override val title: String? = null,
|
||||
override val thumb: InputFile? = null
|
||||
) : InputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable {
|
||||
override val type: String = "audio"
|
||||
) : InputMedia, AudioMediaGroupMemberInputMedia, DuratedInputMedia, ThumbedInputMedia, TitledInputMedia, CaptionedOutput, Performerable {
|
||||
override val type: String = audioInputMediaType
|
||||
|
||||
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
|
||||
|
||||
@SerialName(mediaField)
|
||||
override val media: String
|
||||
init { media = file.fileId } // crutch until js compiling will be fixed
|
||||
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
|
||||
}
|
||||
|
||||
fun AudioFile.toInputMediaAudio(
|
||||
caption: String? = null,
|
||||
parseMode: ParseMode? = null,
|
||||
title: String? = this.title
|
||||
): InputMediaAudio = InputMediaAudio(
|
||||
fileId,
|
||||
caption,
|
||||
parseMode,
|
||||
duration,
|
||||
performer,
|
||||
title,
|
||||
thumb ?.fileId
|
||||
)
|
||||
|
||||
@@ -4,9 +4,11 @@ import dev.inmo.tgbotapi.CommonAbstracts.CaptionedOutput
|
||||
import dev.inmo.tgbotapi.requests.abstracts.*
|
||||
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.parseModeField
|
||||
import dev.inmo.tgbotapi.types.files.DocumentFile
|
||||
import dev.inmo.tgbotapi.types.mediaField
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.*
|
||||
|
||||
internal const val documentInputMediaType = "document"
|
||||
|
||||
@Serializable
|
||||
data class InputMediaDocument(
|
||||
@@ -15,10 +17,22 @@ data class InputMediaDocument(
|
||||
@SerialName(parseModeField)
|
||||
override val parseMode: ParseMode? = null,
|
||||
override val thumb: InputFile? = null
|
||||
) : InputMedia, ThumbedInputMedia, CaptionedOutput {
|
||||
override val type: String = "document"
|
||||
) : InputMedia, DocumentMediaGroupMemberInputMedia, ThumbedInputMedia, CaptionedOutput {
|
||||
override val type: String = documentInputMediaType
|
||||
|
||||
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
|
||||
|
||||
@SerialName(mediaField)
|
||||
override val media: String
|
||||
init { media = file.fileId } // crutch until js compiling will be fixed
|
||||
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
|
||||
}
|
||||
|
||||
fun DocumentFile.toInputMediaDocument(
|
||||
caption: String? = null,
|
||||
parseMode: ParseMode? = null
|
||||
) = InputMediaDocument(
|
||||
fileId,
|
||||
caption,
|
||||
parseMode,
|
||||
thumb ?.fileId
|
||||
)
|
||||
|
||||
@@ -17,14 +17,14 @@ data class InputMediaPhoto(
|
||||
override val caption: String? = null,
|
||||
@SerialName(parseModeField)
|
||||
override val parseMode: ParseMode? = null
|
||||
) : InputMedia, MediaGroupMemberInputMedia {
|
||||
) : InputMedia, VisualMediaGroupMemberInputMedia {
|
||||
override val type: String = photoInputMediaType
|
||||
|
||||
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
|
||||
|
||||
@SerialName(mediaField)
|
||||
override val media: String
|
||||
init { media = file.fileId } // crutch until js compiling will be fixed
|
||||
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
|
||||
}
|
||||
|
||||
fun PhotoSize.toInputMediaPhoto(
|
||||
|
||||
@@ -19,12 +19,12 @@ data class InputMediaVideo(
|
||||
override val height: Int? = null,
|
||||
override val duration: Long? = null,
|
||||
override val thumb: InputFile? = null
|
||||
) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, MediaGroupMemberInputMedia {
|
||||
) : InputMedia, SizedInputMedia, DuratedInputMedia, ThumbedInputMedia, VisualMediaGroupMemberInputMedia {
|
||||
override val type: String = videoInputMediaType
|
||||
|
||||
override fun serialize(format: StringFormat): String = format.encodeToString(serializer(), this)
|
||||
|
||||
@SerialName(mediaField)
|
||||
override val media: String
|
||||
init { media = file.fileId } // crutch until js compiling will be fixed
|
||||
init { media = file.fileIdToSend } // crutch until js compiling will be fixed
|
||||
}
|
||||
|
||||
@@ -18,3 +18,9 @@ internal fun <T> T.buildArguments(withSerializer: SerializationStrategy<T>) = ar
|
||||
interface MediaGroupMemberInputMedia : InputMedia, CaptionedOutput {
|
||||
fun serialize(format: StringFormat): String
|
||||
}
|
||||
|
||||
interface AudioMediaGroupMemberInputMedia: MediaGroupMemberInputMedia
|
||||
interface DocumentMediaGroupMemberInputMedia: MediaGroupMemberInputMedia
|
||||
|
||||
@Serializable(MediaGroupMemberInputMediaSerializer::class)
|
||||
interface VisualMediaGroupMemberInputMedia : MediaGroupMemberInputMedia
|
||||
|
||||
@@ -16,6 +16,8 @@ internal object MediaGroupMemberInputMediaSerializer : KSerializer<MediaGroupMem
|
||||
when (value) {
|
||||
is InputMediaPhoto -> InputMediaPhoto.serializer().serialize(encoder, value)
|
||||
is InputMediaVideo -> InputMediaVideo.serializer().serialize(encoder, value)
|
||||
is InputMediaAudio -> InputMediaAudio.serializer().serialize(encoder, value)
|
||||
is InputMediaDocument -> InputMediaDocument.serializer().serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +27,8 @@ internal object MediaGroupMemberInputMediaSerializer : KSerializer<MediaGroupMem
|
||||
return when (json[typeField] ?.jsonPrimitive ?.contentOrNull) {
|
||||
photoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaPhoto.serializer(), json)
|
||||
videoInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaVideo.serializer(), json)
|
||||
audioInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaAudio.serializer(), json)
|
||||
documentInputMediaType -> nonstrictJsonFormat.decodeFromJsonElement(InputMediaDocument.serializer(), json)
|
||||
else -> error("Illegal type of incoming MediaGroupMemberInputMedia")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ interface ThumbedInputMedia : InputMedia {
|
||||
val thumb: InputFile?
|
||||
@Serializable
|
||||
@SerialName(thumbField)
|
||||
@Deprecated("Will be removed due to useless state")
|
||||
val thumbMedia: String?
|
||||
get() = thumb ?.let {
|
||||
when (it) {
|
||||
|
||||
@@ -22,6 +22,10 @@ object BasketballDiceAnimationType : DiceAnimationType() {
|
||||
override val emoji: String = "\uD83C\uDFC0"
|
||||
}
|
||||
@Serializable(DiceAnimationTypeSerializer::class)
|
||||
object SlotMachineDiceAnimationType : DiceAnimationType() {
|
||||
override val emoji: String = "\uD83C\uDFB0"
|
||||
}
|
||||
@Serializable(DiceAnimationTypeSerializer::class)
|
||||
data class CustomDiceAnimationType(
|
||||
override val emoji: String
|
||||
) : DiceAnimationType()
|
||||
@@ -34,6 +38,7 @@ internal object DiceAnimationTypeSerializer : KSerializer<DiceAnimationType> {
|
||||
CubeDiceAnimationType.emoji -> CubeDiceAnimationType
|
||||
DartsDiceAnimationType.emoji -> DartsDiceAnimationType
|
||||
BasketballDiceAnimationType.emoji -> BasketballDiceAnimationType
|
||||
SlotMachineDiceAnimationType.emoji -> SlotMachineDiceAnimationType
|
||||
else -> CustomDiceAnimationType(type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,5 @@ data class AudioFile(
|
||||
override val fileSize: Long? = null,
|
||||
override val thumb: PhotoSize? = null
|
||||
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, TitledMediaFile, Performerable
|
||||
|
||||
fun AudioFile.asVoiceFile() = VoiceFile(fileId, fileUniqueId, duration, mimeType, fileSize)
|
||||
|
||||
@@ -22,3 +22,13 @@ data class DocumentFile(
|
||||
@SerialName(fileNameField)
|
||||
override val fileName: String? = null
|
||||
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, CustomNamedMediaFile
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun TelegramMediaFile.asDocumentFile() = DocumentFile(
|
||||
fileId,
|
||||
fileUniqueId,
|
||||
fileSize,
|
||||
(this as? ThumbedMediaFile) ?.thumb,
|
||||
(this as? MimedMediaFile) ?.mimeType,
|
||||
(this as? CustomNamedMediaFile) ?.fileName
|
||||
)
|
||||
|
||||
@@ -2,9 +2,13 @@ package dev.inmo.tgbotapi.types.files
|
||||
|
||||
import dev.inmo.tgbotapi.requests.abstracts.FileId
|
||||
import dev.inmo.tgbotapi.types.FileUniqueId
|
||||
import dev.inmo.tgbotapi.types.InputMedia.InputMediaVideo
|
||||
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.ParseMode
|
||||
import dev.inmo.tgbotapi.types.fileUniqueIdField
|
||||
import dev.inmo.tgbotapi.types.files.abstracts.*
|
||||
import dev.inmo.tgbotapi.utils.MimeType
|
||||
import dev.inmo.tgbotapi.utils.toHtmlCaptions
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -23,3 +27,17 @@ data class VideoFile(
|
||||
@SerialName(fileSizeField)
|
||||
override val fileSize: Long? = null
|
||||
) : TelegramMediaFile, MimedMediaFile, ThumbedMediaFile, PlayableMediaFile, SizedMediaFile
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun VideoFile.toInputMediaVideo(
|
||||
caption: String? = null,
|
||||
parseMode: ParseMode? = null
|
||||
) = InputMediaVideo(
|
||||
fileId,
|
||||
caption,
|
||||
parseMode,
|
||||
width,
|
||||
height,
|
||||
duration,
|
||||
thumb ?.fileId
|
||||
)
|
||||
|
||||
@@ -20,3 +20,8 @@ data class VoiceFile(
|
||||
@SerialName(fileSizeField)
|
||||
override val fileSize: Long? = null
|
||||
) : TelegramMediaFile, MimedMediaFile, PlayableMediaFile
|
||||
|
||||
fun VoiceFile.asAudioFile(
|
||||
performer: String? = null,
|
||||
title: String? = null
|
||||
) = AudioFile(fileId, fileUniqueId, duration, performer, title, mimeType, fileSize)
|
||||
|
||||
@@ -220,17 +220,20 @@ internal data class RawMessage(
|
||||
}
|
||||
} ?: content?.let { content ->
|
||||
media_group_id?.let {
|
||||
val checkedContent = when (content) {
|
||||
is PhotoContent -> content
|
||||
is VideoContent -> content
|
||||
is AudioContent -> content
|
||||
is DocumentContent -> content
|
||||
else -> error("Unsupported content for media group")
|
||||
}
|
||||
when (from) {
|
||||
null -> ChannelMediaGroupMessage(
|
||||
messageId,
|
||||
chat,
|
||||
date.asDate,
|
||||
it,
|
||||
when (content) {
|
||||
is PhotoContent -> content
|
||||
is VideoContent -> content
|
||||
else -> error("Unsupported content for media group")
|
||||
},
|
||||
checkedContent,
|
||||
edit_date?.asDate,
|
||||
forwarded,
|
||||
reply_to_message?.asMessage,
|
||||
@@ -242,11 +245,7 @@ internal data class RawMessage(
|
||||
chat,
|
||||
date.asDate,
|
||||
it,
|
||||
when (content) {
|
||||
is PhotoContent -> content
|
||||
is VideoContent -> content
|
||||
else -> error("Unsupported content for media group")
|
||||
},
|
||||
checkedContent,
|
||||
edit_date?.asDate,
|
||||
forwarded,
|
||||
reply_to_message?.asMessage,
|
||||
|
||||
@@ -26,8 +26,6 @@ internal class TelegramBotAPIMessageDeserializationStrategyClass<T> : Deserializ
|
||||
@InternalSerializationApi
|
||||
override val descriptor: SerialDescriptor = buildSerialDescriptor("TelegramBotAPIMessageSerializer", PolymorphicKind.OPEN)
|
||||
|
||||
override fun patch(decoder: Decoder, old: T): T = error("TelegramBotAPIMessageSerializer")
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun deserialize(decoder: Decoder): T {
|
||||
return RawMessage.serializer().deserialize(decoder).asMessage as T
|
||||
|
||||
@@ -5,4 +5,8 @@ import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
|
||||
|
||||
interface MediaGroupContent : MediaContent, CaptionedInput {
|
||||
fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia
|
||||
}
|
||||
}
|
||||
|
||||
interface VisualMediaGroupContent : MediaGroupContent
|
||||
interface AudioMediaGroupContent : MediaGroupContent
|
||||
interface DocumentMediaGroupContent : MediaGroupContent
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package dev.inmo.tgbotapi.types.message.content.media
|
||||
|
||||
import dev.inmo.tgbotapi.CommonAbstracts.CaptionedInput
|
||||
import dev.inmo.tgbotapi.CommonAbstracts.TextPart
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
import dev.inmo.tgbotapi.requests.send.media.SendAudio
|
||||
import dev.inmo.tgbotapi.types.ChatIdentifier
|
||||
import dev.inmo.tgbotapi.types.InputMedia.InputMediaAudio
|
||||
import dev.inmo.tgbotapi.types.InputMedia.*
|
||||
import dev.inmo.tgbotapi.types.MessageIdentifier
|
||||
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
|
||||
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
|
||||
import dev.inmo.tgbotapi.types.files.AudioFile
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.*
|
||||
import dev.inmo.tgbotapi.utils.toHtmlCaptions
|
||||
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
|
||||
|
||||
@@ -20,7 +19,7 @@ data class AudioContent(
|
||||
override val media: AudioFile,
|
||||
override val caption: String? = null,
|
||||
override val captionEntities: List<TextPart> = emptyList()
|
||||
) : MediaContent, CaptionedInput {
|
||||
) : AudioMediaGroupContent {
|
||||
override fun createResend(
|
||||
chatId: ChatIdentifier,
|
||||
disableNotification: Boolean,
|
||||
@@ -40,13 +39,10 @@ data class AudioContent(
|
||||
replyMarkup
|
||||
)
|
||||
|
||||
override fun asInputMedia(): InputMediaAudio = InputMediaAudio(
|
||||
media.fileId,
|
||||
toMarkdownV2Captions().firstOrNull(),
|
||||
MarkdownV2,
|
||||
media.duration,
|
||||
media.performer,
|
||||
media.title,
|
||||
media.thumb ?.fileId
|
||||
override fun toMediaGroupMemberInputMedia(): InputMediaAudio = asInputMedia()
|
||||
|
||||
override fun asInputMedia(): InputMediaAudio = media.toInputMediaAudio(
|
||||
toHtmlCaptions().firstOrNull(),
|
||||
HTMLParseMode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,14 +5,15 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
import dev.inmo.tgbotapi.requests.send.media.SendDocument
|
||||
import dev.inmo.tgbotapi.types.ChatIdentifier
|
||||
import dev.inmo.tgbotapi.types.InputMedia.InputMediaDocument
|
||||
import dev.inmo.tgbotapi.types.InputMedia.*
|
||||
import dev.inmo.tgbotapi.types.MessageIdentifier
|
||||
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
|
||||
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
|
||||
import dev.inmo.tgbotapi.types.files.DocumentFile
|
||||
import dev.inmo.tgbotapi.types.files.asDocumentFile
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.*
|
||||
import dev.inmo.tgbotapi.utils.toHtmlCaptions
|
||||
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
|
||||
|
||||
@@ -20,7 +21,7 @@ data class DocumentContent(
|
||||
override val media: DocumentFile,
|
||||
override val caption: String? = null,
|
||||
override val captionEntities: List<TextPart> = emptyList()
|
||||
) : MediaContent, CaptionedInput {
|
||||
) : DocumentMediaGroupContent {
|
||||
override fun createResend(
|
||||
chatId: ChatIdentifier,
|
||||
disableNotification: Boolean,
|
||||
@@ -37,10 +38,22 @@ data class DocumentContent(
|
||||
replyMarkup
|
||||
)
|
||||
|
||||
override fun asInputMedia(): InputMediaDocument = InputMediaDocument(
|
||||
media.fileId,
|
||||
toMarkdownV2Captions().firstOrNull(),
|
||||
MarkdownV2,
|
||||
media.thumb ?.fileId
|
||||
override fun toMediaGroupMemberInputMedia(): InputMediaDocument = asInputMedia()
|
||||
|
||||
override fun asInputMedia(): InputMediaDocument = media.toInputMediaDocument(
|
||||
toHtmlCaptions().firstOrNull(),
|
||||
HTMLParseMode
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun MediaContent.asDocumentContent() = when (this) {
|
||||
is CaptionedInput -> DocumentContent(
|
||||
media.asDocumentFile(),
|
||||
caption,
|
||||
captionEntities
|
||||
)
|
||||
else -> DocumentContent(
|
||||
media.asDocumentFile()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,14 @@ import dev.inmo.tgbotapi.CommonAbstracts.TextPart
|
||||
import dev.inmo.tgbotapi.requests.abstracts.Request
|
||||
import dev.inmo.tgbotapi.requests.send.media.SendPhoto
|
||||
import dev.inmo.tgbotapi.types.ChatIdentifier
|
||||
import dev.inmo.tgbotapi.types.InputMedia.InputMediaPhoto
|
||||
import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
|
||||
import dev.inmo.tgbotapi.types.InputMedia.*
|
||||
import dev.inmo.tgbotapi.types.MessageIdentifier
|
||||
import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
|
||||
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
|
||||
import dev.inmo.tgbotapi.types.files.*
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaCollectionContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.*
|
||||
import dev.inmo.tgbotapi.utils.toHtmlCaptions
|
||||
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
|
||||
|
||||
@@ -21,7 +19,7 @@ data class PhotoContent(
|
||||
override val mediaCollection: Photo,
|
||||
override val caption: String? = null,
|
||||
override val captionEntities: List<TextPart> = emptyList()
|
||||
) : MediaCollectionContent<PhotoSize>, MediaGroupContent {
|
||||
) : MediaCollectionContent<PhotoSize>, VisualMediaGroupContent {
|
||||
override val media: PhotoSize = mediaCollection.biggest() ?: throw IllegalStateException("Can't locate any photo size for this content")
|
||||
|
||||
override fun createResend(
|
||||
@@ -39,15 +37,10 @@ data class PhotoContent(
|
||||
replyMarkup
|
||||
)
|
||||
|
||||
override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaPhoto(
|
||||
media.fileId,
|
||||
override fun toMediaGroupMemberInputMedia(): InputMediaPhoto = asInputMedia()
|
||||
|
||||
override fun asInputMedia(): InputMediaPhoto = media.toInputMediaPhoto(
|
||||
toHtmlCaptions().firstOrNull(),
|
||||
HTMLParseMode
|
||||
)
|
||||
|
||||
override fun asInputMedia(): InputMediaPhoto = InputMediaPhoto(
|
||||
media.fileId,
|
||||
toMarkdownV2Captions().firstOrNull(),
|
||||
MarkdownV2
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ import dev.inmo.tgbotapi.types.ParseMode.HTMLParseMode
|
||||
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
|
||||
import dev.inmo.tgbotapi.types.buttons.KeyboardMarkup
|
||||
import dev.inmo.tgbotapi.types.files.VideoFile
|
||||
import dev.inmo.tgbotapi.types.files.toInputMediaVideo
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.VisualMediaGroupContent
|
||||
import dev.inmo.tgbotapi.utils.toHtmlCaptions
|
||||
import dev.inmo.tgbotapi.utils.toMarkdownV2Captions
|
||||
|
||||
@@ -20,7 +22,7 @@ data class VideoContent(
|
||||
override val media: VideoFile,
|
||||
override val caption: String? = null,
|
||||
override val captionEntities: List<TextPart> = emptyList()
|
||||
) : MediaGroupContent {
|
||||
) : VisualMediaGroupContent {
|
||||
override fun createResend(
|
||||
chatId: ChatIdentifier,
|
||||
disableNotification: Boolean,
|
||||
@@ -41,23 +43,10 @@ data class VideoContent(
|
||||
replyMarkup
|
||||
)
|
||||
|
||||
override fun toMediaGroupMemberInputMedia(): MediaGroupMemberInputMedia = InputMediaVideo(
|
||||
media.fileId,
|
||||
toHtmlCaptions().firstOrNull(),
|
||||
HTMLParseMode,
|
||||
media.width,
|
||||
media.height,
|
||||
media.duration,
|
||||
media.thumb ?.fileId
|
||||
)
|
||||
override fun toMediaGroupMemberInputMedia(): InputMediaVideo = asInputMedia()
|
||||
|
||||
override fun asInputMedia(): InputMediaVideo = InputMediaVideo(
|
||||
media.fileId,
|
||||
toMarkdownV2Captions().firstOrNull(),
|
||||
MarkdownV2,
|
||||
media.width,
|
||||
media.height,
|
||||
media.duration,
|
||||
media.thumb ?.fileId
|
||||
override fun asInputMedia(): InputMediaVideo = media.toInputMediaVideo(
|
||||
toHtmlCaptions().firstOrNull(),
|
||||
HTMLParseMode
|
||||
)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,4 @@ object UpdateDeserializationStrategy : DeserializationStrategy<Update> {
|
||||
asJson
|
||||
)
|
||||
}
|
||||
|
||||
override fun patch(decoder: Decoder, old: Update): Update = error("Unsupported operation")
|
||||
}
|
||||
|
||||
@@ -5,21 +5,20 @@ import dev.inmo.tgbotapi.types.update.*
|
||||
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.*
|
||||
import dev.inmo.tgbotapi.types.update.abstracts.UnknownUpdate
|
||||
import dev.inmo.tgbotapi.types.update.abstracts.Update
|
||||
import kotlinx.coroutines.channels.BroadcastChannel
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE", "unused")
|
||||
class FlowsUpdatesFilter(
|
||||
broadcastChannelsSize: Int = 100
|
||||
): UpdatesFilter {
|
||||
private val updatesReceivingChannel = BroadcastChannel<Update>(broadcastChannelsSize)
|
||||
private val updatesSharedFlow = MutableSharedFlow<Update>(extraBufferCapacity = broadcastChannelsSize)
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val allUpdatesFlow: Flow<Update> = updatesReceivingChannel.asFlow()
|
||||
val allUpdatesFlow: Flow<Update> = updatesSharedFlow.asSharedFlow()
|
||||
|
||||
override val allowedUpdates: List<String>
|
||||
get() = ALL_UPDATES_LIST
|
||||
override val asUpdateReceiver: UpdateReceiver<Update> = {
|
||||
updatesReceivingChannel.send(it)
|
||||
updatesSharedFlow.emit(it)
|
||||
}
|
||||
|
||||
val messageFlow: Flow<MessageUpdate> = allUpdatesFlow.filterIsInstance()
|
||||
|
||||
@@ -17,3 +17,21 @@ package dev.inmo.tgbotapi.utils
|
||||
AnnotationTarget.TYPE_PARAMETER
|
||||
)
|
||||
annotation class PreviewFeature
|
||||
|
||||
@RequiresOptIn(
|
||||
"This feature can work unstable and may have some restrictions in Telegram System",
|
||||
RequiresOptIn.Level.WARNING
|
||||
)
|
||||
@Target(
|
||||
AnnotationTarget.CLASS,
|
||||
AnnotationTarget.CONSTRUCTOR,
|
||||
AnnotationTarget.FIELD,
|
||||
AnnotationTarget.PROPERTY,
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
AnnotationTarget.PROPERTY_SETTER,
|
||||
AnnotationTarget.FUNCTION,
|
||||
AnnotationTarget.TYPE,
|
||||
AnnotationTarget.TYPEALIAS,
|
||||
AnnotationTarget.TYPE_PARAMETER
|
||||
)
|
||||
annotation class RiskFeature(val message: String)
|
||||
|
||||
@@ -11,6 +11,7 @@ private sealed class DebounceAction<T> {
|
||||
private data class AddValue<T>(override val value: T) : DebounceAction<T>()
|
||||
private data class RemoveJob<T>(override val value: T, val job: Job) : DebounceAction<T>()
|
||||
|
||||
@Deprecated("Unused and will be removed in next major release")
|
||||
fun <T> ReceiveChannel<T>.debounceByValue(
|
||||
delayMillis: Long,
|
||||
scope: CoroutineScope = CoroutineScope(Dispatchers.Default),
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package dev.inmo.tgbotapi.extensions.api.send.media
|
||||
|
||||
import dev.inmo.tgbotapi.bot.TelegramBot
|
||||
import dev.inmo.tgbotapi.requests.send.media.SendMediaGroup
|
||||
import dev.inmo.tgbotapi.requests.send.media.*
|
||||
import dev.inmo.tgbotapi.types.ChatIdentifier
|
||||
import dev.inmo.tgbotapi.types.InputMedia.MediaGroupMemberInputMedia
|
||||
import dev.inmo.tgbotapi.types.InputMedia.*
|
||||
import dev.inmo.tgbotapi.types.MessageIdentifier
|
||||
import dev.inmo.tgbotapi.types.chat.abstracts.Chat
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.Message
|
||||
import dev.inmo.tgbotapi.utils.RiskFeature
|
||||
|
||||
/**
|
||||
* @see SendMediaGroup
|
||||
*/
|
||||
@RiskFeature(rawSendingMediaGroupsWarning)
|
||||
suspend fun TelegramBot.sendMediaGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<MediaGroupMemberInputMedia>,
|
||||
@@ -19,6 +24,10 @@ suspend fun TelegramBot.sendMediaGroup(
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendMediaGroup
|
||||
*/
|
||||
@RiskFeature(rawSendingMediaGroupsWarning)
|
||||
suspend fun TelegramBot.sendMediaGroup(
|
||||
chat: Chat,
|
||||
media: List<MediaGroupMemberInputMedia>,
|
||||
@@ -28,12 +37,108 @@ suspend fun TelegramBot.sendMediaGroup(
|
||||
chat.id, media, disableNotification, replyToMessageId
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendPlaylist
|
||||
*/
|
||||
suspend fun TelegramBot.sendPlaylist(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<AudioMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = execute(
|
||||
SendPlaylist(
|
||||
chatId, media, disableNotification, replyToMessageId
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendPlaylist
|
||||
*/
|
||||
suspend fun TelegramBot.sendPlaylist(
|
||||
chat: Chat,
|
||||
media: List<AudioMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = sendPlaylist(
|
||||
chat.id, media, disableNotification, replyToMessageId
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendDocumentsGroup
|
||||
*/
|
||||
suspend fun TelegramBot.sendDocumentsGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<DocumentMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = execute(
|
||||
SendDocumentsGroup(
|
||||
chatId, media, disableNotification, replyToMessageId
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendDocumentsGroup
|
||||
*/
|
||||
suspend fun TelegramBot.sendDocumentsGroup(
|
||||
chat: Chat,
|
||||
media: List<DocumentMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = sendDocumentsGroup(
|
||||
chat.id, media, disableNotification, replyToMessageId
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendVisualMediaGroup
|
||||
*/
|
||||
suspend fun TelegramBot.sendVisualMediaGroup(
|
||||
chatId: ChatIdentifier,
|
||||
media: List<VisualMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = execute(
|
||||
SendVisualMediaGroup(
|
||||
chatId, media, disableNotification, replyToMessageId
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* @see SendVisualMediaGroup
|
||||
*/
|
||||
suspend fun TelegramBot.sendVisualMediaGroup(
|
||||
chat: Chat,
|
||||
media: List<VisualMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false,
|
||||
replyToMessageId: MessageIdentifier? = null
|
||||
) = sendVisualMediaGroup(
|
||||
chat.id, media, disableNotification, replyToMessageId
|
||||
)
|
||||
|
||||
suspend inline fun TelegramBot.replyWithMediaGroup(
|
||||
to: Message,
|
||||
media: List<MediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false
|
||||
) = sendMediaGroup(to.chat, media, disableNotification, to.messageId)
|
||||
|
||||
suspend inline fun TelegramBot.replyWithPlaylist(
|
||||
to: Message,
|
||||
media: List<AudioMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false
|
||||
) = sendPlaylist(to.chat, media, disableNotification, to.messageId)
|
||||
|
||||
suspend inline fun TelegramBot.replyWithDocumentsGroup(
|
||||
to: Message,
|
||||
media: List<DocumentMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false
|
||||
) = sendDocumentsGroup(to.chat, media, disableNotification, to.messageId)
|
||||
|
||||
suspend inline fun TelegramBot.replyWithVisualMediaGroup(
|
||||
to: Message,
|
||||
media: List<VisualMediaGroupMemberInputMedia>,
|
||||
disableNotification: Boolean = false
|
||||
) = sendVisualMediaGroup(to.chat, media, disableNotification, to.messageId)
|
||||
|
||||
suspend inline fun TelegramBot.reply(
|
||||
to: Message,
|
||||
media: List<MediaGroupMemberInputMedia>,
|
||||
|
||||
@@ -2,7 +2,6 @@ package dev.inmo.tgbotapi.extensions.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.BroadcastChannel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
/**
|
||||
@@ -11,15 +10,15 @@ import kotlinx.coroutines.flow.*
|
||||
fun <T> aggregateFlows(
|
||||
withScope: CoroutineScope,
|
||||
vararg flows: Flow<T>,
|
||||
internalBufferSize: Int = Channel.BUFFERED
|
||||
internalBufferSize: Int = 64
|
||||
): Flow<T> {
|
||||
val bc = BroadcastChannel<T>(internalBufferSize)
|
||||
val sharedFlow = MutableSharedFlow<T>(extraBufferCapacity = internalBufferSize)
|
||||
flows.forEach {
|
||||
it.onEach {
|
||||
safely { bc.send(it) }
|
||||
safely { sharedFlow.emit(it) }
|
||||
}.launchIn(withScope)
|
||||
}
|
||||
return bc.asFlow()
|
||||
return sharedFlow
|
||||
}
|
||||
|
||||
fun <T> Flow<Iterable<T>>.flatMap(): Flow<T> = flow {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.inmo.tgbotapi.extensions.utils
|
||||
|
||||
import dev.inmo.tgbotapi.types.DiceResult
|
||||
import dev.inmo.tgbotapi.types.dice.Dice
|
||||
import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
enum class SlotMachineReelImages {
|
||||
BAR, BERRIES, LEMON, SEVEN
|
||||
}
|
||||
val Int.asSlotMachineReelImage
|
||||
get() = when (this) {
|
||||
0 -> SlotMachineReelImages.BAR
|
||||
1 -> SlotMachineReelImages.BERRIES
|
||||
2 -> SlotMachineReelImages.LEMON
|
||||
else -> SlotMachineReelImages.SEVEN
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SlotMachineResult(
|
||||
val rawValue: DiceResult
|
||||
) {
|
||||
@Transient
|
||||
val left = rawValue and 3
|
||||
@Transient
|
||||
val center = rawValue shr 2 and 3
|
||||
@Transient
|
||||
val right = rawValue shr 4
|
||||
|
||||
@Transient
|
||||
val leftReel = left.asSlotMachineReelImage
|
||||
@Transient
|
||||
val centerReel = center.asSlotMachineReelImage
|
||||
@Transient
|
||||
val rightReel = right.asSlotMachineReelImage
|
||||
}
|
||||
|
||||
fun Dice.calculateSlotMachineResult() = if (animationType == SlotMachineDiceAnimationType) {
|
||||
SlotMachineResult(value - 1)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
@@ -6,8 +6,7 @@ import dev.inmo.tgbotapi.extensions.utils.updates.asContentMessagesFlow
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.CommonMessage
|
||||
import dev.inmo.tgbotapi.types.message.abstracts.ContentMessage
|
||||
import dev.inmo.tgbotapi.types.message.content.*
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MediaGroupContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.MessageContent
|
||||
import dev.inmo.tgbotapi.types.message.content.abstracts.*
|
||||
import dev.inmo.tgbotapi.types.message.content.media.*
|
||||
import dev.inmo.tgbotapi.types.message.payments.InvoiceContent
|
||||
import dev.inmo.tgbotapi.types.update.MediaGroupUpdates.SentMediaGroupUpdate
|
||||
@@ -101,6 +100,12 @@ fun Flow<BaseSentMessageUpdate>.audioMessages() = filterContentMessages<AudioCon
|
||||
fun FlowsUpdatesFilter.audioMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterContentMessages<AudioContent>(scopeToIncludeChannels)
|
||||
fun FlowsUpdatesFilter.audioMessagesWithMediaGroups(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = merge(
|
||||
filterContentMessages<AudioContent>(scopeToIncludeChannels),
|
||||
mediaGroupAudioMessages(scopeToIncludeChannels).flatMap()
|
||||
)
|
||||
|
||||
fun Flow<BaseSentMessageUpdate>.contactMessages() = filterContentMessages<ContactContent>()
|
||||
fun FlowsUpdatesFilter.contactMessages(
|
||||
@@ -116,6 +121,12 @@ fun Flow<BaseSentMessageUpdate>.documentMessages() = filterContentMessages<Docum
|
||||
fun FlowsUpdatesFilter.documentMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterContentMessages<DocumentContent>(scopeToIncludeChannels)
|
||||
fun FlowsUpdatesFilter.documentMessagesWithMediaGroups(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = merge(
|
||||
filterContentMessages<DocumentContent>(scopeToIncludeChannels),
|
||||
mediaGroupDocumentMessages(scopeToIncludeChannels).flatMap()
|
||||
)
|
||||
|
||||
fun Flow<BaseSentMessageUpdate>.gameMessages() = filterContentMessages<GameContent>()
|
||||
fun FlowsUpdatesFilter.gameMessages(
|
||||
@@ -210,3 +221,18 @@ fun Flow<SentMediaGroupUpdate>.mediaGroupVideosMessages() = filterMediaGroupMess
|
||||
fun FlowsUpdatesFilter.mediaGroupVideosMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterMediaGroupMessages<VideoContent>(scopeToIncludeChannels)
|
||||
|
||||
fun Flow<SentMediaGroupUpdate>.mediaGroupVisualMessages() = filterMediaGroupMessages<VisualMediaGroupContent>()
|
||||
fun FlowsUpdatesFilter.mediaGroupVisualMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterMediaGroupMessages<VisualMediaGroupContent>(scopeToIncludeChannels)
|
||||
|
||||
fun Flow<SentMediaGroupUpdate>.mediaGroupAudioMessages() = filterMediaGroupMessages<AudioContent>()
|
||||
fun FlowsUpdatesFilter.mediaGroupAudioMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterMediaGroupMessages<AudioContent>(scopeToIncludeChannels)
|
||||
|
||||
fun Flow<SentMediaGroupUpdate>.mediaGroupDocumentMessages() = filterMediaGroupMessages<DocumentContent>()
|
||||
fun FlowsUpdatesFilter.mediaGroupDocumentMessages(
|
||||
scopeToIncludeChannels: CoroutineScope? = null
|
||||
) = filterMediaGroupMessages<DocumentContent>(scopeToIncludeChannels)
|
||||
|
||||
Reference in New Issue
Block a user