Skip to content

Commit

Permalink
Merge pull request #3 from simple-robot/dev/api/sendPhoto
Browse files Browse the repository at this point in the history
支持图片消息元素的发送与接收
  • Loading branch information
ForteScarlet authored Apr 12, 2024
2 parents f1bd9c8 + 793a6b7 commit 017a1c2
Show file tree
Hide file tree
Showing 12 changed files with 535 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* Copyright (c) 2024. ForteScarlet.
*
* This file is part of simbot-component-telegram.
*
* simbot-component-telegram is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* simbot-component-telegram is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with simbot-component-telegram.
* If not, see <https://www.gnu.org/licenses/>.
*/

package love.forte.simbot.telegram.api.message

import io.ktor.client.request.forms.*
import io.ktor.http.*
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Required
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import love.forte.simbot.common.ktor.inputfile.InputFile
import love.forte.simbot.telegram.api.FormBodyTelegramApi
import love.forte.simbot.telegram.api.Telegram
import love.forte.simbot.telegram.api.TelegramApiResult
import love.forte.simbot.telegram.api.utils.FormParamHeaderHandler
import love.forte.simbot.telegram.api.utils.KtorFormSerializer
import love.forte.simbot.telegram.api.utils.StringValueInputFile
import love.forte.simbot.telegram.type.ChatId
import love.forte.simbot.telegram.type.Message
import love.forte.simbot.telegram.type.MessageEntity
import love.forte.simbot.telegram.type.ReplyParameters
import kotlin.jvm.JvmStatic


/**
* [sendPhoto](https://core.telegram.org/bots/api#sendphoto)
*
* @author ForteScarlet
*/
public class SendPhotoApi private constructor(body: Body) : FormBodyTelegramApi<Message>() {
public companion object Factory {
private const val NAME = "sendPhoto"
private val RES = TelegramApiResult.serializer(Message.serializer())
private val formSerializer = KtorFormSerializer(
Body.serializer(),
Telegram.DefaultJson,
FormParamHeaderHandler
.isInputFileAndNotStringValueInputFileWithoutParams {
headers {
// TODO type?
append(HttpHeaders.ContentType, "image/png")
append(HttpHeaders.ContentDisposition, "filename=\"file\"")
}
}
)

/**
* Create [SendPhotoApi].
*/
@JvmStatic
public fun create(body: Body): SendPhotoApi =
SendPhotoApi(body)

/**
* Create [SendPhotoApi] with only required params.
*
* @param chatId The chat id.
* @param photo The photo you want to send.
*/
@JvmStatic
public fun create(chatId: ChatId, photo: InputFile): SendPhotoApi =
SendPhotoApi(
Body().also {
it.chatId = chatId
it.photo = photo
}
)

}

override val name: String
get() = NAME

override val body: MultiPartFormDataContent =
MultiPartFormDataContent(
formData(values = formSerializer.serialize(body).toTypedArray())
)

override val responseDeserializer: DeserializationStrategy<Message>
get() = Message.serializer()

override val resultDeserializer: DeserializationStrategy<TelegramApiResult<Message>>
get() = RES

/**
* The request body for [SendPhotoApi]
*/
@Serializable
public class Body {
/**
* Optional.
* Unique identifier of the business connection on behalf of which the message will be sent
*/
@SerialName("business_connection_id")
public var businessConnectionId: String? = null

/**
* Required unique identifier for the target chat or username of the target channel (in the format `@channelusername`)
*/
@SerialName("chat_id")
@Required
public lateinit var chatId: ChatId

/**
* Optional.
* Unique identifier for the target message thread (topic) of the forum; for forum supergroups only
*/
@SerialName("message_thread_id")
public var messageThreadId: Int? = null

/**
* Required.
* Photo to send.
* (See [the documentation](https://core.telegram.org/bots/api#sendphoto)).
*
* See also [StringValueInputFile] for send a photo with type of [String].
*
* @see StringValueInputFile
*/
@Required
public lateinit var photo: InputFile

/**
* Optional.
* Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing
*/
public var caption: String? = null

/**
* Optional.
* Mode for parsing entities in the photo caption. See formatting options for more details.
*/
@SerialName("parse_mode")
public var parseMode: String? = null

/**
* Optional.
* A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
*/
@SerialName("caption_entities")
public var captionEntities: MutableCollection<MessageEntity>? = null

/**
* Add a [captionEntity] into [captionEntities].
*/
public fun addCaptionEntity(captionEntity: MessageEntity) {
captionEntities?.add(captionEntity)
?: mutableListOf(captionEntity).also { captionEntities = it }
}

/**
* Optional.
* Pass True if the photo needs to be covered with a spoiler animation
*/
@SerialName("has_spoiler")
public var hasSpoiler: Boolean? = null

/**
* Optional.
* Sends the message silently. Users will receive a notification with no sound.
*/
@SerialName("disable_notification")
public var disableNotification: Boolean? = null

/**
* Optional.
* Protects the contents of the sent message from forwarding and saving
*/
@SerialName("protect_content")
public var protectContent: Boolean? = null

/**
* Optional.
* Description of the message to reply to
*/
@SerialName("reply_parameters")
public var replyParameters: ReplyParameters? = null

/**
* Additional interface options.
* A JSON-serialized object for an inline keyboard,
* custom reply keyboard,
* instructions to remove a reply keyboard or to force a reply from the user.
* Not supported for messages sent on behalf of a business account
*/
@SerialName("reply_markup")
public var replyMarkup: ReplyMarkupWrapper? = null
}

}

/**
* Create [SendPhotoApi].
*/
public inline fun buildSendPhotoApi(block: SendPhotoApi.Body.() -> Unit): SendPhotoApi =
SendPhotoApi.create(SendPhotoApi.Body().apply(block))
1 change: 1 addition & 0 deletions simbot-component-telegram-api/supports.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- [x] [ForwardMessageApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/message/ForwardMessageApi.kt)
- [x] [ForwardMessagesApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/message/ForwardMessagesApi.kt)
- [x] [SendMessageApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/message/SendMessageApi.kt)
- [x] [SendPhotoApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/message/SendPhotoApi.kt)
- [x] [TelegramApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/TelegramApi.kt)
- [update](src/commonMain/kotlin/love/forte/simbot/telegram/api/update)
- [x] [DeleteWebhookApi](src/commonMain/kotlin/love/forte/simbot/telegram/api/update/DeleteWebhookApi.kt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package love.forte.simbot.component.telegram.core.message

import love.forte.simbot.component.telegram.core.bot.internal.TelegramBotImpl
import love.forte.simbot.component.telegram.core.bot.requestDataBy
import love.forte.simbot.component.telegram.core.message.internal.ImageSendingResolver
import love.forte.simbot.component.telegram.core.message.internal.TelegramAggregatedMessageIdReceiptImpl
import love.forte.simbot.component.telegram.core.message.internal.TextSendingResolver
import love.forte.simbot.component.telegram.core.message.internal.toTelegramMessageReceipt
Expand Down Expand Up @@ -80,10 +81,11 @@ internal suspend fun TelegramBotImpl.send(
chatId: Long,
builderFactory: BuilderFactory = DefaultBuilderFactory
): TelegramMessageReceipt {
val funcList = message.resolve {
val cid = ChatId(chatId)
val funcList = message.resolve(cid) {
builderFactory().also {
if (it.chatId == null) {
it.chatId = ChatId(chatId)
it.chatId = cid
}
}
}
Expand Down Expand Up @@ -159,13 +161,14 @@ private const val MESSAGE_ID_MARK = 0b10
private const val MESSAGE_ALL_MARK = MESSAGE_MARK or MESSAGE_ID_MARK

internal suspend fun Message.resolve(
chatId: ChatId,
builderFactory: BuilderFactory = DefaultBuilderFactory
): List<SendingMessageResolvedFunction> {
return when (val m = this) {
is Message.Element -> {
val context = SendingMessageResolverContext(builderFactory)
for (resolver in sendingResolvers) {
resolver.resolve(0, m, this, context)
resolver.resolve(chatId, 0, m, this, context)
}

context.end()
Expand All @@ -179,7 +182,7 @@ internal suspend fun Message.resolve(
val context = SendingMessageResolverContext(builderFactory)
m.forEachIndexed { index, element ->
for (resolver in sendingResolvers) {
resolver.resolve(index, element, this, context)
resolver.resolve(chatId, index, element, this, context)
}
}

Expand All @@ -190,6 +193,7 @@ internal suspend fun Message.resolve(

internal fun interface SendingMessageResolver {
suspend fun resolve(
chatId: ChatId,
index: Int,
element: Message.Element,
source: Message,
Expand All @@ -199,6 +203,7 @@ internal fun interface SendingMessageResolver {

private val sendingResolvers = listOf(
TextSendingResolver,
ImageSendingResolver,
TelegramMessageResultApiElementSendingResolver,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import love.forte.simbot.telegram.api.TelegramApi
import love.forte.simbot.telegram.api.message.CopyMessageApi
import love.forte.simbot.telegram.api.message.ForwardMessageApi
import love.forte.simbot.telegram.api.message.SendMessageApi
import love.forte.simbot.telegram.type.ChatId
import love.forte.simbot.telegram.type.Message
import love.forte.simbot.telegram.type.MessageId
import kotlin.jvm.JvmStatic
Expand Down Expand Up @@ -67,6 +68,7 @@ internal data class MessageIdResult(override val api: TelegramApi<MessageId>) :

internal object TelegramMessageResultApiElementSendingResolver : SendingMessageResolver {
override suspend fun resolve(
chatId: ChatId,
index: Int,
element: love.forte.simbot.message.Message.Element,
source: love.forte.simbot.message.Message,
Expand Down
Loading

0 comments on commit 017a1c2

Please sign in to comment.