1
0
mirror of https://github.com/InsanusMokrassar/TelegramBotAPI.git synced 2025-11-27 09:45:46 +00:00

Compare commits

..

56 Commits

Author SHA1 Message Date
a10d766295 slotmachine utils 2020-11-02 20:32:46 +06:00
ea6ab8f024 start 0.29.4 2020-11-02 20:10:38 +06:00
f377d61bf0 Merge pull request #158 from InsanusMokrassar/0.29.3
0.29.3
2020-11-02 14:21:54 +06:00
18c9d4e468 fill changelog 2020-11-02 14:14:08 +06:00
20b931138c update FlowsUpdatesFilter 2020-11-02 14:11:26 +06:00
d7bca15693 deprecations 2020-11-02 13:57:44 +06:00
957649603b fix for media fields in input media 2020-11-02 13:51:20 +06:00
bbc56fe94c hotfix in changelog 2020-11-02 13:26:06 +06:00
d048958423 WrongFileIdentifierException 2020-11-02 13:25:40 +06:00
f0e9267664 fixes in files and filling of changelog 2020-11-02 13:16:46 +06:00
5dd7207e09 update serialization version 2020-11-02 12:56:29 +06:00
d96d47e32c make different interfaces for audio and documents media content 2020-11-02 12:51:09 +06:00
1ee73dd406 sending media groups api updates 2020-11-02 12:39:12 +06:00
e9a05c4930 include audio playlists, documents and slot machine dice 2020-11-02 12:02:12 +06:00
9e771fa04a fixes 2020-11-02 11:13:16 +06:00
06ba21fc1f add changelog for audio and document media items 2020-10-30 20:40:44 +06:00
7d72d72f2c Revert "Revert "Revert "Revert "add slot machine, but currently it is not working""""
This reverts commit b457d11067.
2020-10-30 20:38:04 +06:00
80d4bdfe17 make audio and documents able to be media group items 2020-10-30 20:38:02 +06:00
b457d11067 Revert "Revert "Revert "add slot machine, but currently it is not working"""
This reverts commit 46fe16fd73.
2020-10-30 19:45:55 +06:00
46fe16fd73 Revert "Revert "add slot machine, but currently it is not working""
This reverts commit f317e144e6.
2020-10-30 19:37:26 +06:00
8e1cafb1b6 start 0.29.3 2020-10-30 19:36:48 +06:00
68d971f874 Merge pull request #155 from InsanusMokrassar/0.29.2
0.29.2
2020-10-27 15:55:17 +06:00
37a23c7e79 fixes in aggregation of flows 2020-10-27 15:51:48 +06:00
b053c29ea3 update aggregateFlows 2020-10-27 15:26:32 +06:00
23a1fed7dd Revert "update greetings config"
This reverts commit 87d2537bda.
2020-10-27 15:16:04 +06:00
87d2537bda update greetings config 2020-10-27 15:14:45 +06:00
6d782f28c3 updates after coroutines version change 2020-10-27 15:11:57 +06:00
d8dbbdf549 update coroutines 2020-10-27 15:01:30 +06:00
f317e144e6 Revert "add slot machine, but currently it is not working"
This reverts commit 4f9cb531c0.
2020-10-27 14:53:22 +06:00
05c27804ce update travis config 2020-10-22 19:19:55 +06:00
eb97a8e151 Merge pull request #154 from Megamiun/gm/update-dokka
Gm/update dokka
2020-10-22 19:18:51 +06:00
Gabryel Monteiro
06639e5f3c Update gradle.properties 2020-10-22 09:41:06 -03:00
82b7bf676f Update .travis.yml 2020-10-20 02:05:34 +06:00
4f9cb531c0 add slot machine, but currently it is not working 2020-10-19 20:26:36 +06:00
09bbb1945c start 0.29.2 2020-10-19 20:12:18 +06:00
7bedc4caf0 Merge remote-tracking branch 'original/master' into gm/update-dokka 2020-10-19 19:51:03 +06:00
6a73aa1525 add travis dokka step 2020-10-19 19:50:41 +06:00
e1ee541005 extend max heap size for gradle 2020-10-19 19:45:46 +06:00
Gabryel Monteiro
2d2fe01227 Changing Dokka version 2020-10-19 01:51:09 -03:00
Gabryel Monteiro
af53682b1f Removing kts notation 2020-10-19 00:45:29 -03:00
Gabryel Monteiro
c8a5552c9e Adding projects 2020-10-19 00:40:24 -03:00
Gabryel Monteiro
ffa78ebfe1 Update to 1.4.0 2020-10-18 22:38:16 -03:00
e85e7d02f7 Merge pull request #149 from InsanusMokrassar/0.29.1
0.29.1
2020-10-13 16:08:19 +06:00
37ad279ab4 Merge pull request #148 from leobia/master
Reformat CHANGELOG.md
2020-10-12 15:27:09 +06:00
3b41dc1fbf Merge remote-tracking branch 'original/0.29.1' 2020-10-12 15:26:11 +06:00
5747383ed1 Update README.md 2020-10-12 01:08:56 +06:00
18a6efabb8 post fixes for serialization update 2020-10-09 11:49:24 +06:00
dcb837b155 update serialization version 2020-10-09 11:41:08 +06:00
03755e9eef start 0.29.1 2020-10-09 11:35:33 +06:00
lbia
b9db2d544d Reformat CHANGELOG.md 2020-10-08 22:11:39 +02:00
800d14561e add conversations between AudioFile and VoiceFile 2020-10-04 20:00:43 +06:00
2378237fc5 Merge pull request #146 from InsanusMokrassar/0.29.0
0.29.0
2020-10-04 19:46:22 +06:00
48c6f103b5 Update README.md 2020-10-02 14:27:07 +06:00
e326d289cb Create pull_request_template.md 2020-10-02 13:26:32 +06:00
7fd9c4c897 Create CONTRIBUTING.md 2020-10-02 13:10:01 +06:00
347a6212d4 Merge pull request #141 from InsanusMokrassar/0.28.4
0.28.4
2020-09-28 13:29:22 +06:00
43 changed files with 1117 additions and 697 deletions

2
.github/pull_request_template.md vendored Normal file
View 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)

View File

@@ -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

File diff suppressed because it is too large Load Diff

10
CONTRIBUTING.md Normal file
View 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:)

View File

@@ -1,6 +1,6 @@
# TelegramBotAPI
| Common info | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Build Status](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI.svg?branch=master)](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI) [Small survey](https://forms.gle/tnjuExdSKEr32ygKA)|
| Common info | [![Awesome Kotlin Badge](https://kotlin.link/awesome-kotlin.svg)](https://github.com/KotlinBy/awesome-kotlin) [![Build Status](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI.svg?branch=master)](https://travis-ci.com/InsanusMokrassar/TelegramBotAPI) [Small survey](https://forms.gle/2Hex2ynbHWHhi1KY7)|
| -------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Useful links | [![Chat in Telegram](badges/chat.svg)](https://t.me/InMoTelegramBotAPI) [![KDocs](badges/kdocs.svg)](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 | [![Download](https://api.bintray.com/packages/insanusmokrassar/TelegramBotAPI/tgbotapi.core/images/download.svg)](https://bintray.com/insanusmokrassar/TelegramBotAPI/tgbotapi.core/_latestVersion) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/tgbotapi.core) |
@@ -74,7 +74,7 @@ kotlin {
![Libraries hierarchy](resources/TelegramBotAPI-libraries-hierarchy.svg)
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

View File

@@ -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"))
}
}
}

View File

@@ -1,3 +1,3 @@
dokka_version=0.10.1
dokka_version=1.4.0
org.gradle.jvmargs=-Xmx1024m

View File

@@ -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

View File

@@ -1,3 +1,10 @@
pluginManagement {
repositories {
gradlePluginPortal()
jcenter()
}
}
include ":tgbotapi.core"
include ":tgbotapi.extensions.api"
include ":tgbotapi.extensions.utils"

View File

@@ -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)

View File

@@ -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

View File

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

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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(

View File

@@ -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
}

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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) {

View File

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

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

@@ -47,6 +47,4 @@ object UpdateDeserializationStrategy : DeserializationStrategy<Update> {
asJson
)
}
override fun patch(decoder: Decoder, old: Update): Update = error("Unsupported operation")
}

View File

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

View File

@@ -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)

View File

@@ -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),

View File

@@ -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>,

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)