use std::future::{Future, IntoFuture};
use std::pin::Pin;
use reqwest::multipart::{Form, Part};
use serde::Serialize;
use rustigram_types::file::{InputFile, InputMedia, InputPaidMedia};
use rustigram_types::keyboard::ReplyMarkup;
use rustigram_types::message::{LinkPreviewOptions, Message, ParseMode, ReplyParameters};
use rustigram_types::poll::InputPollOption;
use rustigram_types::suggested_post::SuggestedPostParameters;
use rustigram_types::user::ChatId;
use crate::client::BotClient;
use crate::error::Result;
macro_rules! impl_into_future {
($builder:ident, $return_ty:ty, $method:literal) => {
impl IntoFuture for $builder {
type Output = Result<$return_ty>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move { self.client.post_json($method, &self.params).await })
}
}
};
}
#[derive(Serialize)]
struct SendMessageParams {
chat_id: ChatId,
text: String,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
link_preview_options: Option<LinkPreviewOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
receiver_user_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
callback_query_id: Option<String>,
}
pub struct SendMessage {
client: BotClient,
params: SendMessageParams,
}
impl SendMessage {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
text: impl Into<String>,
) -> Self {
Self {
client,
params: SendMessageParams {
chat_id: chat_id.into(),
text: text.into(),
business_connection_id: None,
message_thread_id: None,
direct_messages_topic_id: None,
parse_mode: None,
entities: None,
link_preview_options: None,
disable_notification: None,
protect_content: None,
allow_paid_broadcast: None,
message_effect_id: None,
reply_parameters: None,
reply_markup: None,
suggested_post_parameters: None,
receiver_user_id: None,
callback_query_id: None,
},
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn parse_mode(mut self, mode: ParseMode) -> Self {
self.params.parse_mode = Some(mode);
self
}
pub fn entities(mut self, entities: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.entities = Some(entities);
self
}
pub fn link_preview_options(mut self, opts: LinkPreviewOptions) -> Self {
self.params.link_preview_options = Some(opts);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self {
self.params.message_effect_id = Some(id.into());
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_to(mut self, message_id: i64) -> Self {
self.params.reply_parameters = Some(ReplyParameters {
message_id: Some(message_id),
ephemeral_message_id: None,
chat_id: None,
allow_sending_without_reply: None,
quote: None,
quote_parse_mode: None,
quote_entities: None,
quote_position: None,
poll_option_id: None,
checklist_task_id: None,
});
self
}
pub fn reply_to_ephemeral(mut self, ephemeral_message_id: i64) -> Self {
self.params.reply_parameters = Some(ReplyParameters {
message_id: None,
ephemeral_message_id: Some(ephemeral_message_id),
chat_id: None,
allow_sending_without_reply: None,
quote: None,
quote_parse_mode: None,
quote_entities: None,
quote_position: None,
poll_option_id: None,
checklist_task_id: None,
});
self
}
pub fn reply_markup(mut self, markup: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(markup.into());
self
}
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(params);
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.params.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.params.callback_query_id = Some(id.into());
self
}
}
impl_into_future!(SendMessage, Message, "sendMessage");
#[derive(Serialize)]
struct ForwardMessageParams {
chat_id: ChatId,
from_chat_id: ChatId,
message_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
video_start_timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct ForwardMessage {
client: BotClient,
params: ForwardMessageParams,
}
impl ForwardMessage {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
from_chat_id: impl Into<ChatId>,
message_id: i64,
) -> Self {
Self {
client,
params: ForwardMessageParams {
chat_id: chat_id.into(),
from_chat_id: from_chat_id.into(),
message_id,
message_thread_id: None,
direct_messages_topic_id: None,
video_start_timestamp: None,
disable_notification: None,
protect_content: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn video_start_timestamp(mut self, ts: i64) -> Self {
self.params.video_start_timestamp = Some(ts);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
}
impl_into_future!(ForwardMessage, Message, "forwardMessage");
#[derive(Serialize)]
struct CopyMessageParams {
chat_id: ChatId,
from_chat_id: ChatId,
message_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
video_start_timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
caption: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
caption_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
show_caption_above_media: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct CopyMessage {
client: BotClient,
params: CopyMessageParams,
}
impl CopyMessage {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
from_chat_id: impl Into<ChatId>,
message_id: i64,
) -> Self {
Self {
client,
params: CopyMessageParams {
chat_id: chat_id.into(),
from_chat_id: from_chat_id.into(),
message_id,
message_thread_id: None,
direct_messages_topic_id: None,
video_start_timestamp: None,
caption: None,
parse_mode: None,
caption_entities: None,
show_caption_above_media: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn video_start_timestamp(mut self, ts: i64) -> Self {
self.params.video_start_timestamp = Some(ts);
self
}
pub fn caption(mut self, c: impl Into<String>) -> Self {
self.params.caption = Some(c.into());
self
}
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.params.parse_mode = Some(m);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
pub fn caption_entities(
mut self,
entities: Vec<rustigram_types::message::MessageEntity>,
) -> Self {
self.params.caption_entities = Some(entities);
self
}
pub fn show_caption_above_media(mut self, v: bool) -> Self {
self.params.show_caption_above_media = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
}
impl_into_future!(
CopyMessage,
rustigram_types::message::MessageId,
"copyMessage"
);
#[derive(Serialize)]
struct SendChatActionParams {
chat_id: ChatId,
action: ChatAction,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
}
#[derive(Serialize, Clone, Copy)]
#[serde(rename_all = "snake_case")]
pub enum ChatAction {
Typing,
UploadPhoto,
RecordVideo,
UploadVideo,
RecordVoice,
UploadVoice,
UploadDocument,
ChooseSticker,
FindLocation,
RecordVideoNote,
UploadVideoNote,
}
pub struct SendChatAction {
client: BotClient,
params: SendChatActionParams,
}
impl SendChatAction {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>, action: ChatAction) -> Self {
Self {
client,
params: SendChatActionParams {
chat_id: chat_id.into(),
action,
business_connection_id: None,
message_thread_id: None,
},
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
}
impl_into_future!(SendChatAction, bool, "sendChatAction");
#[derive(Serialize)]
struct SendDiceParams {
chat_id: ChatId,
#[serde(skip_serializing_if = "Option::is_none")]
emoji: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendDice {
client: BotClient,
params: SendDiceParams,
}
impl SendDice {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>) -> Self {
Self {
client,
params: SendDiceParams {
chat_id: chat_id.into(),
emoji: None,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
business_connection_id: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn emoji(mut self, e: impl Into<String>) -> Self {
self.params.emoji = Some(e.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, v: ReplyParameters) -> Self {
self.params.reply_parameters = Some(v);
self
}
}
impl_into_future!(SendDice, Message, "sendDice");
#[derive(Serialize)]
struct SendLocationParams {
chat_id: ChatId,
latitude: f64,
longitude: f64,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
horizontal_accuracy: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
live_period: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
heading: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
proximity_alert_radius: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
receiver_user_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
callback_query_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendLocation {
client: BotClient,
params: SendLocationParams,
}
impl SendLocation {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
latitude: f64,
longitude: f64,
) -> Self {
Self {
client,
params: SendLocationParams {
chat_id: chat_id.into(),
latitude,
longitude,
message_thread_id: None,
direct_messages_topic_id: None,
horizontal_accuracy: None,
live_period: None,
heading: None,
proximity_alert_radius: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
receiver_user_id: None,
callback_query_id: None,
business_connection_id: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn horizontal_accuracy(mut self, v: f64) -> Self {
self.params.horizontal_accuracy = Some(v);
self
}
pub fn live_period(mut self, v: u32) -> Self {
self.params.live_period = Some(v);
self
}
pub fn heading(mut self, v: u16) -> Self {
self.params.heading = Some(v);
self
}
pub fn proximity_alert_radius(mut self, v: u32) -> Self {
self.params.proximity_alert_radius = Some(v);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.params.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.params.callback_query_id = Some(id.into());
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, v: ReplyParameters) -> Self {
self.params.reply_parameters = Some(v);
self
}
}
impl_into_future!(SendLocation, Message, "sendLocation");
#[derive(Serialize)]
struct SendContactParams {
chat_id: ChatId,
phone_number: String,
first_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
vcard: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
receiver_user_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
callback_query_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendContact {
client: BotClient,
params: SendContactParams,
}
impl SendContact {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
phone_number: impl Into<String>,
first_name: impl Into<String>,
) -> Self {
Self {
client,
params: SendContactParams {
chat_id: chat_id.into(),
phone_number: phone_number.into(),
first_name: first_name.into(),
last_name: None,
vcard: None,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
receiver_user_id: None,
callback_query_id: None,
business_connection_id: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn last_name(mut self, v: impl Into<String>) -> Self {
self.params.last_name = Some(v.into());
self
}
pub fn vcard(mut self, v: impl Into<String>) -> Self {
self.params.vcard = Some(v.into());
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.params.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.params.callback_query_id = Some(id.into());
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, v: ReplyParameters) -> Self {
self.params.reply_parameters = Some(v);
self
}
}
impl_into_future!(SendContact, Message, "sendContact");
#[derive(Serialize)]
struct SendPollParams {
chat_id: ChatId,
question: String,
options: Vec<InputPollOption>,
#[serde(skip_serializing_if = "Option::is_none")]
question_parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
question_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
poll_type: Option<rustigram_types::poll::PollType>,
#[serde(skip_serializing_if = "Option::is_none")]
is_anonymous: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
allows_multiple_answers: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
allows_revoting: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
correct_option_ids: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
explanation: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
explanation_parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
explanation_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
open_period: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
close_date: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
is_closed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
shuffle_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_adding_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
hide_results_until_closes: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
description_parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
description_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
members_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
country_codes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
media: Option<rustigram_types::poll::InputPollMedia>,
#[serde(skip_serializing_if = "Option::is_none")]
explanation_media: Option<rustigram_types::poll::InputPollMedia>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
}
pub struct SendPoll {
client: BotClient,
params: SendPollParams,
}
impl SendPoll {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
question: impl Into<String>,
options: Vec<InputPollOption>,
) -> Self {
Self {
client,
params: SendPollParams {
chat_id: chat_id.into(),
question: question.into(),
options,
question_parse_mode: None,
question_entities: None,
message_thread_id: None,
direct_messages_topic_id: None,
poll_type: None,
is_anonymous: None,
allows_multiple_answers: None,
allows_revoting: None,
correct_option_ids: None,
explanation: None,
explanation_parse_mode: None,
explanation_entities: None,
open_period: None,
close_date: None,
is_closed: None,
shuffle_options: None,
allow_adding_options: None,
hide_results_until_closes: None,
description: None,
description_parse_mode: None,
description_entities: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
suggested_post_parameters: None,
members_only: None,
country_codes: None,
media: None,
explanation_media: None,
business_connection_id: None,
allow_paid_broadcast: None,
message_effect_id: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn is_anonymous(mut self, v: bool) -> Self {
self.params.is_anonymous = Some(v);
self
}
pub fn allows_multiple_answers(mut self, v: bool) -> Self {
self.params.allows_multiple_answers = Some(v);
self
}
pub fn allows_revoting(mut self, v: bool) -> Self {
self.params.allows_revoting = Some(v);
self
}
pub fn quiz(mut self, ids: Vec<u8>) -> Self {
self.params.poll_type = Some(rustigram_types::poll::PollType::Quiz);
self.params.correct_option_ids = Some(ids);
self
}
pub fn quiz_single(self, id: u8) -> Self {
self.quiz(vec![id])
}
pub fn explanation(mut self, text: impl Into<String>) -> Self {
self.params.explanation = Some(text.into());
self
}
pub fn explanation_parse_mode(mut self, mode: ParseMode) -> Self {
self.params.explanation_parse_mode = Some(mode);
self
}
pub fn explanation_entities(mut self, e: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.explanation_entities = Some(e);
self
}
pub fn open_period(mut self, secs: u32) -> Self {
self.params.open_period = Some(secs);
self
}
pub fn close_date(mut self, ts: i64) -> Self {
self.params.close_date = Some(ts);
self
}
pub fn shuffle_options(mut self, v: bool) -> Self {
self.params.shuffle_options = Some(v);
self
}
pub fn allow_adding_options(mut self, v: bool) -> Self {
self.params.allow_adding_options = Some(v);
self
}
pub fn hide_results_until_closes(mut self, v: bool) -> Self {
self.params.hide_results_until_closes = Some(v);
self
}
pub fn description(mut self, d: impl Into<String>) -> Self {
self.params.description = Some(d.into());
self
}
pub fn description_parse_mode(mut self, mode: ParseMode) -> Self {
self.params.description_parse_mode = Some(mode);
self
}
pub fn description_entities(mut self, e: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.description_entities = Some(e);
self
}
pub fn question_parse_mode(mut self, mode: ParseMode) -> Self {
self.params.question_parse_mode = Some(mode);
self
}
pub fn question_entities(mut self, e: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.question_entities = Some(e);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(params);
self
}
pub fn members_only(mut self, v: bool) -> Self {
self.params.members_only = Some(v);
self
}
pub fn country_codes(mut self, codes: Vec<impl Into<String>>) -> Self {
self.params.country_codes = Some(codes.into_iter().map(Into::into).collect());
self
}
pub fn media(mut self, m: rustigram_types::poll::InputPollMedia) -> Self {
self.params.media = Some(m);
self
}
pub fn explanation_media(mut self, m: rustigram_types::poll::InputPollMedia) -> Self {
self.params.explanation_media = Some(m);
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn is_closed(mut self, v: bool) -> Self {
self.params.is_closed = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, v: ReplyParameters) -> Self {
self.params.reply_parameters = Some(v);
self
}
}
impl_into_future!(SendPoll, Message, "sendPoll");
#[derive(Serialize)]
struct SendMessageDraftParams {
chat_id: ChatId,
draft_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
entities: Option<Vec<rustigram_types::message::MessageEntity>>,
}
pub struct SendMessageDraft {
client: BotClient,
params: SendMessageDraftParams,
}
impl SendMessageDraft {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
draft_id: i64,
text: impl Into<String>,
) -> Self {
Self {
client,
params: SendMessageDraftParams {
chat_id: chat_id.into(),
draft_id,
text: Some(text.into()),
message_thread_id: None,
parse_mode: None,
entities: None,
},
}
}
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.params.parse_mode = Some(m);
self
}
pub fn entities(mut self, e: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.entities = Some(e);
self
}
pub fn clear_text(mut self) -> Self {
self.params.text = None;
self
}
pub fn message_thread_id(mut self, v: i64) -> Self {
self.params.message_thread_id = Some(v);
self
}
}
impl_into_future!(SendMessageDraft, bool, "sendMessageDraft");
fn apply_media_opts(mut form: Form, opts: &MediaSendOptions) -> Form {
fn json_text(form: Form, key: &'static str, value: &impl Serialize) -> Form {
match serde_json::to_string(value) {
Ok(json) => form.text(key, json),
Err(_) => form,
}
}
if let Some(v) = &opts.business_connection_id {
form = form.text("business_connection_id", v.clone());
}
if let Some(v) = opts.message_thread_id {
form = form.text("message_thread_id", v.to_string());
}
if let Some(v) = opts.direct_messages_topic_id {
form = form.text("direct_messages_topic_id", v.to_string());
}
if let Some(v) = &opts.caption {
form = form.text("caption", v.clone());
}
if let Some(v) = &opts.parse_mode {
form = form.text("parse_mode", format!("{v:?}"));
}
if let Some(v) = &opts.caption_entities {
form = json_text(form, "caption_entities", v);
}
if let Some(v) = opts.show_caption_above_media {
form = form.text("show_caption_above_media", v.to_string());
}
if let Some(v) = opts.has_spoiler {
form = form.text("has_spoiler", v.to_string());
}
if let Some(v) = opts.disable_notification {
form = form.text("disable_notification", v.to_string());
}
if let Some(v) = opts.protect_content {
form = form.text("protect_content", v.to_string());
}
if let Some(v) = opts.allow_paid_broadcast {
form = form.text("allow_paid_broadcast", v.to_string());
}
if let Some(v) = &opts.message_effect_id {
form = form.text("message_effect_id", v.clone());
}
if let Some(v) = &opts.reply_parameters {
form = json_text(form, "reply_parameters", v);
}
if let Some(v) = &opts.reply_markup {
form = json_text(form, "reply_markup", v);
}
if let Some(v) = &opts.suggested_post_parameters {
form = json_text(form, "suggested_post_parameters", v);
}
if let Some(v) = opts.receiver_user_id {
form = form.text("receiver_user_id", v.to_string());
}
if let Some(v) = &opts.callback_query_id {
form = form.text("callback_query_id", v.clone());
}
form
}
#[derive(Default)]
pub struct MediaSendOptions {
pub business_connection_id: Option<String>,
pub message_thread_id: Option<i64>,
pub direct_messages_topic_id: Option<i64>,
pub caption: Option<String>,
pub parse_mode: Option<ParseMode>,
pub caption_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
pub show_caption_above_media: Option<bool>,
pub has_spoiler: Option<bool>,
pub disable_notification: Option<bool>,
pub protect_content: Option<bool>,
pub allow_paid_broadcast: Option<bool>,
pub message_effect_id: Option<String>,
pub reply_parameters: Option<ReplyParameters>,
pub reply_markup: Option<ReplyMarkup>,
pub suggested_post_parameters: Option<SuggestedPostParameters>,
pub receiver_user_id: Option<i64>,
pub callback_query_id: Option<String>,
}
fn media_json_body(
chat_id: &ChatId,
media_field: &str,
media_value: &str,
opts: &MediaSendOptions,
extra: serde_json::Value,
) -> serde_json::Value {
let mut map = serde_json::json!({
"chat_id": chat_id,
media_field: media_value,
});
let obj = map.as_object_mut().unwrap();
if let Some(v) = &opts.business_connection_id {
obj.insert("business_connection_id".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.message_thread_id {
obj.insert("message_thread_id".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.direct_messages_topic_id {
obj.insert("direct_messages_topic_id".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.caption {
obj.insert("caption".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.parse_mode {
obj.insert("parse_mode".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.caption_entities {
obj.insert("caption_entities".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.show_caption_above_media {
obj.insert("show_caption_above_media".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.has_spoiler {
obj.insert("has_spoiler".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.disable_notification {
obj.insert("disable_notification".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.protect_content {
obj.insert("protect_content".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.allow_paid_broadcast {
obj.insert("allow_paid_broadcast".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.message_effect_id {
obj.insert("message_effect_id".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.reply_parameters {
obj.insert("reply_parameters".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.reply_markup {
obj.insert("reply_markup".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.suggested_post_parameters {
obj.insert("suggested_post_parameters".to_owned(), serde_json::json!(v));
}
if let Some(v) = opts.receiver_user_id {
obj.insert("receiver_user_id".to_owned(), serde_json::json!(v));
}
if let Some(v) = &opts.callback_query_id {
obj.insert("callback_query_id".to_owned(), serde_json::json!(v));
}
if let serde_json::Value::Object(extra_obj) = extra {
for (k, v) in extra_obj {
obj.insert(k, v);
}
}
map
}
pub struct SendPhoto {
client: BotClient,
chat_id: ChatId,
photo: InputFile,
opts: MediaSendOptions,
}
impl SendPhoto {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>, photo: InputFile) -> Self {
Self {
client,
chat_id: chat_id.into(),
photo,
opts: MediaSendOptions::default(),
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.opts.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.opts.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.opts.direct_messages_topic_id = Some(id);
self
}
pub fn caption(mut self, c: impl Into<String>) -> Self {
self.opts.caption = Some(c.into());
self
}
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.opts.parse_mode = Some(m);
self
}
pub fn has_spoiler(mut self, v: bool) -> Self {
self.opts.has_spoiler = Some(v);
self
}
pub fn caption_entities(
mut self,
entities: Vec<rustigram_types::message::MessageEntity>,
) -> Self {
self.opts.caption_entities = Some(entities);
self
}
pub fn show_caption_above_media(mut self, v: bool) -> Self {
self.opts.show_caption_above_media = Some(v);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.opts.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.opts.protect_content = Some(v);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.opts.allow_paid_broadcast = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.opts.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.opts.reply_markup = Some(m.into());
self
}
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self {
self.opts.suggested_post_parameters = Some(params);
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.opts.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.opts.callback_query_id = Some(id.into());
self
}
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self {
self.opts.message_effect_id = Some(id.into());
self
}
}
impl IntoFuture for SendPhoto {
type Output = Result<Message>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
match &self.photo {
InputFile::Bytes {
filename,
data,
mime_type,
} => {
let part = Part::bytes(data.clone())
.file_name(filename.clone())
.mime_str(mime_type)
.map_err(|e| crate::error::Error::Decode(e.to_string()))?;
let mut form = Form::new().part("photo", part);
form = form.text("chat_id", self.chat_id.to_string());
form = apply_media_opts(form, &self.opts);
self.client.post_multipart("sendPhoto", form).await
}
_ => {
let body = media_json_body(
&self.chat_id,
"photo",
self.photo.as_str(),
&self.opts,
serde_json::Value::Null,
);
self.client.post_json("sendPhoto", &body).await
}
}
})
}
}
pub struct SendLivePhoto {
client: BotClient,
chat_id: ChatId,
live_photo: InputFile,
photo: InputFile,
opts: MediaSendOptions,
}
impl SendLivePhoto {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
live_photo: InputFile,
photo: InputFile,
) -> Self {
Self {
client,
chat_id: chat_id.into(),
live_photo,
photo,
opts: MediaSendOptions::default(),
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.opts.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.opts.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.opts.direct_messages_topic_id = Some(id);
self
}
pub fn caption(mut self, c: impl Into<String>) -> Self {
self.opts.caption = Some(c.into());
self
}
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.opts.parse_mode = Some(m);
self
}
pub fn show_caption_above_media(mut self, v: bool) -> Self {
self.opts.show_caption_above_media = Some(v);
self
}
pub fn has_spoiler(mut self, v: bool) -> Self {
self.opts.has_spoiler = Some(v);
self
}
pub fn caption_entities(
mut self,
entities: Vec<rustigram_types::message::MessageEntity>,
) -> Self {
self.opts.caption_entities = Some(entities);
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.opts.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.opts.callback_query_id = Some(id.into());
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.opts.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.opts.protect_content = Some(v);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.opts.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self {
self.opts.message_effect_id = Some(id.into());
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.opts.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.opts.reply_markup = Some(m.into());
self
}
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self {
self.opts.suggested_post_parameters = Some(params);
self
}
}
impl IntoFuture for SendLivePhoto {
type Output = Result<Message>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let lp_bytes = self.live_photo.requires_multipart();
let ph_bytes = self.photo.requires_multipart();
if lp_bytes || ph_bytes {
let mut form = Form::new();
form = form.text("chat_id", self.chat_id.to_string());
if let InputFile::Bytes {
filename,
data,
mime_type,
} = self.live_photo
{
let part = Part::bytes(data)
.file_name(filename)
.mime_str(&mime_type)
.map_err(|e| crate::error::Error::Decode(e.to_string()))?;
form = form.part("live_photo", part);
} else {
form = form.text("live_photo", self.live_photo.as_str().to_owned());
}
if let InputFile::Bytes {
filename,
data,
mime_type,
} = self.photo
{
let part = Part::bytes(data)
.file_name(filename)
.mime_str(&mime_type)
.map_err(|e| crate::error::Error::Decode(e.to_string()))?;
form = form.part("photo", part);
} else {
form = form.text("photo", self.photo.as_str().to_owned());
}
form = apply_media_opts(form, &self.opts);
self.client.post_multipart("sendLivePhoto", form).await
} else {
let mut body = media_json_body(
&self.chat_id,
"live_photo",
self.live_photo.as_str(),
&self.opts,
serde_json::json!({}),
);
body.as_object_mut()
.unwrap()
.insert("photo".to_owned(), serde_json::json!(self.photo.as_str()));
self.client.post_json("sendLivePhoto", &body).await
}
})
}
}
macro_rules! caption_setter {
(caption) => {
pub fn caption(mut self, c: impl Into<String>) -> Self {
self.opts.caption = Some(c.into());
self
}
};
(parse_mode) => {
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.opts.parse_mode = Some(m);
self
}
};
(caption_entities) => {
pub fn caption_entities(
mut self,
entities: Vec<rustigram_types::message::MessageEntity>,
) -> Self {
self.opts.caption_entities = Some(entities);
self
}
};
(show_caption_above_media) => {
pub fn show_caption_above_media(mut self, v: bool) -> Self {
self.opts.show_caption_above_media = Some(v);
self
}
};
(has_spoiler) => {
pub fn has_spoiler(mut self, v: bool) -> Self {
self.opts.has_spoiler = Some(v);
self
}
};
}
macro_rules! media_sender {
($(#[$doc:meta])* $name:ident, $field:literal, $method:literal, $return_ty:ty,
[$($extra_field:ident: $extra_ty:ty),*], [$($caption_opt:ident),*]) => {
$(#[$doc])*
pub struct $name {
/// The API client to use for sending the request.
client: BotClient,
chat_id: ChatId,
file: InputFile,
opts: MediaSendOptions,
$($extra_field: Option<$extra_ty>,)*
}
impl $name {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>, file: InputFile) -> Self {
Self {
client,
chat_id: chat_id.into(),
file,
opts: MediaSendOptions::default(),
$($extra_field: None,)*
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self { self.opts.business_connection_id = Some(id.into()); self }
pub fn message_thread_id(mut self, id: i64) -> Self { self.opts.message_thread_id = Some(id); self }
pub fn direct_messages_topic_id(mut self, id: i64) -> Self { self.opts.direct_messages_topic_id = Some(id); self }
$(caption_setter!($caption_opt);)*
pub fn disable_notification(mut self, v: bool) -> Self { self.opts.disable_notification = Some(v); self }
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self { self.opts.message_effect_id = Some(id.into()); self }
pub fn protect_content(mut self, v: bool) -> Self { self.opts.protect_content = Some(v); self }
pub fn allow_paid_broadcast(mut self, v: bool) -> Self { self.opts.allow_paid_broadcast = Some(v); self }
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self { self.opts.reply_parameters = Some(rp); self }
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self { self.opts.reply_markup = Some(m.into()); self }
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self { self.opts.suggested_post_parameters = Some(params); self }
pub fn receiver_user_id(mut self, id: i64) -> Self { self.opts.receiver_user_id = Some(id); self }
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self { self.opts.callback_query_id = Some(id.into()); self }
$(
#[doc = concat!("Sets the ", stringify!($extra_field), " for the media.")]
pub fn $extra_field(mut self, v: $extra_ty) -> Self {
self.$extra_field = Some(v);
self
}
)*
}
impl IntoFuture for $name {
type Output = Result<$return_ty>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
match &self.file {
InputFile::Bytes { filename, data, mime_type } => {
let part = Part::bytes(data.clone())
.file_name(filename.clone())
.mime_str(mime_type)
.map_err(|e| crate::error::Error::Decode(e.to_string()))?;
let mut form = Form::new().part($field, part);
form = form.text("chat_id", self.chat_id.to_string());
form = apply_media_opts(form, &self.opts);
$(
if let Some(ref v) = self.$extra_field {
form = form.text(stringify!($extra_field), v.to_string());
}
)*
self.client.post_multipart($method, form).await
}
_ => {
let mut extra = serde_json::json!({});
$(
if let Some(ref v) = self.$extra_field {
extra[stringify!($extra_field)] = serde_json::json!(v);
}
)*
let body = media_json_body(&self.chat_id, $field, self.file.as_str(), &self.opts, extra);
self.client.post_json($method, &body).await
}
}
})
}
}
};
}
media_sender!(
SendAudio, "audio", "sendAudio", Message, [duration: u32, performer: String, title: String, thumbnail: String], [caption, parse_mode, caption_entities]);
media_sender!(
SendDocument, "document", "sendDocument", Message, [disable_content_type_detection: bool, thumbnail: String], [caption, parse_mode, caption_entities]);
media_sender!(
SendVideo, "video", "sendVideo", Message, [duration: u32, width: u32, height: u32, supports_streaming: bool, cover: String, start_timestamp: i64, thumbnail: String], [caption, parse_mode, caption_entities, show_caption_above_media, has_spoiler]);
media_sender!(
SendAnimation, "animation", "sendAnimation", Message, [duration: u32, width: u32, height: u32, thumbnail: String], [caption, parse_mode, caption_entities, show_caption_above_media, has_spoiler]);
media_sender!(
SendVoice, "voice", "sendVoice", Message, [duration: u32], [caption, parse_mode, caption_entities]);
media_sender!(
SendVideoNote, "video_note", "sendVideoNote", Message, [duration: u32, length: u32, thumbnail: String], []);
media_sender!(
SendSticker, "sticker", "sendSticker", Message, [emoji: String], []);
#[derive(Serialize)]
struct DeleteMessageParams {
chat_id: ChatId,
message_id: i64,
}
pub struct DeleteMessage {
client: BotClient,
params: DeleteMessageParams,
}
impl DeleteMessage {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>, message_id: i64) -> Self {
Self {
client,
params: DeleteMessageParams {
chat_id: chat_id.into(),
message_id,
},
}
}
}
impl_into_future!(DeleteMessage, bool, "deleteMessage");
#[derive(Serialize)]
struct DeleteMessagesParams {
chat_id: ChatId,
message_ids: Vec<i64>,
}
pub struct DeleteMessages {
client: BotClient,
params: DeleteMessagesParams,
}
impl DeleteMessages {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
message_ids: Vec<i64>,
) -> Self {
Self {
client,
params: DeleteMessagesParams {
chat_id: chat_id.into(),
message_ids,
},
}
}
}
impl_into_future!(DeleteMessages, bool, "deleteMessages");
#[derive(Serialize)]
struct DeleteEphemeralMessageParams {
chat_id: ChatId,
receiver_user_id: i64,
ephemeral_message_id: i64,
}
pub struct DeleteEphemeralMessage {
client: BotClient,
params: DeleteEphemeralMessageParams,
}
impl DeleteEphemeralMessage {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
receiver_user_id: i64,
ephemeral_message_id: i64,
) -> Self {
Self {
client,
params: DeleteEphemeralMessageParams {
chat_id: chat_id.into(),
receiver_user_id,
ephemeral_message_id,
},
}
}
}
impl_into_future!(DeleteEphemeralMessage, bool, "deleteEphemeralMessage");
#[derive(Serialize)]
struct StopPollParams {
chat_id: ChatId,
message_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<rustigram_types::keyboard::InlineKeyboardMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
}
pub struct StopPoll {
client: BotClient,
params: StopPollParams,
}
impl StopPoll {
pub(crate) fn new(client: BotClient, chat_id: impl Into<ChatId>, message_id: i64) -> Self {
Self {
client,
params: StopPollParams {
chat_id: chat_id.into(),
message_id,
reply_markup: None,
business_connection_id: None,
},
}
}
pub fn reply_markup(mut self, m: rustigram_types::keyboard::InlineKeyboardMarkup) -> Self {
self.params.reply_markup = Some(m);
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
}
impl_into_future!(StopPoll, rustigram_types::poll::Poll, "stopPoll");
#[derive(Serialize)]
struct AnswerCallbackQueryParams {
callback_query_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
show_alert: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
cache_time: Option<u32>,
}
pub struct AnswerCallbackQuery {
client: BotClient,
params: AnswerCallbackQueryParams,
}
impl AnswerCallbackQuery {
pub(crate) fn new(client: BotClient, callback_query_id: impl Into<String>) -> Self {
Self {
client,
params: AnswerCallbackQueryParams {
callback_query_id: callback_query_id.into(),
text: None,
show_alert: None,
url: None,
cache_time: None,
},
}
}
pub fn text(mut self, t: impl Into<String>) -> Self {
self.params.text = Some(t.into());
self
}
pub fn show_alert(mut self, v: bool) -> Self {
self.params.show_alert = Some(v);
self
}
pub fn url(mut self, u: impl Into<String>) -> Self {
self.params.url = Some(u.into());
self
}
pub fn cache_time(mut self, secs: u32) -> Self {
self.params.cache_time = Some(secs);
self
}
pub fn alert(self, text: impl Into<String>) -> Self {
self.text(text).show_alert(true)
}
}
impl_into_future!(AnswerCallbackQuery, bool, "answerCallbackQuery");
#[derive(Serialize)]
struct ForwardMessagesParams {
chat_id: ChatId,
from_chat_id: ChatId,
message_ids: Vec<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
}
pub struct ForwardMessages {
client: BotClient,
params: ForwardMessagesParams,
}
impl ForwardMessages {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
from_chat_id: impl Into<ChatId>,
message_ids: Vec<i64>,
) -> Self {
Self {
client,
params: ForwardMessagesParams {
chat_id: chat_id.into(),
from_chat_id: from_chat_id.into(),
message_ids,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
}
impl_into_future!(
ForwardMessages,
Vec<rustigram_types::message::MessageId>,
"forwardMessages"
);
#[derive(Serialize)]
struct CopyMessagesParams {
chat_id: ChatId,
from_chat_id: ChatId,
message_ids: Vec<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
remove_caption: Option<bool>,
}
pub struct CopyMessages {
client: BotClient,
params: CopyMessagesParams,
}
impl CopyMessages {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
from_chat_id: impl Into<ChatId>,
message_ids: Vec<i64>,
) -> Self {
Self {
client,
params: CopyMessagesParams {
chat_id: chat_id.into(),
from_chat_id: from_chat_id.into(),
message_ids,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
remove_caption: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn remove_caption(mut self, v: bool) -> Self {
self.params.remove_caption = Some(v);
self
}
}
impl_into_future!(
CopyMessages,
Vec<rustigram_types::message::MessageId>,
"copyMessages"
);
#[derive(Serialize)]
struct SendVenueParams {
chat_id: ChatId,
latitude: f64,
longitude: f64,
title: String,
address: String,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
foursquare_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
foursquare_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
google_place_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
google_place_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
receiver_user_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
callback_query_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendVenue {
client: BotClient,
params: SendVenueParams,
}
impl SendVenue {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
latitude: f64,
longitude: f64,
title: impl Into<String>,
address: impl Into<String>,
) -> Self {
Self {
client,
params: SendVenueParams {
chat_id: chat_id.into(),
latitude,
longitude,
title: title.into(),
address: address.into(),
message_thread_id: None,
direct_messages_topic_id: None,
foursquare_id: None,
foursquare_type: None,
google_place_id: None,
google_place_type: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
receiver_user_id: None,
callback_query_id: None,
business_connection_id: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn foursquare_id(mut self, id: impl Into<String>) -> Self {
self.params.foursquare_id = Some(id.into());
self
}
pub fn foursquare_type(mut self, t: impl Into<String>) -> Self {
self.params.foursquare_type = Some(t.into());
self
}
pub fn google_place_id(mut self, id: impl Into<String>) -> Self {
self.params.google_place_id = Some(id.into());
self
}
pub fn google_place_type(mut self, t: impl Into<String>) -> Self {
self.params.google_place_type = Some(t.into());
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn receiver_user_id(mut self, id: i64) -> Self {
self.params.receiver_user_id = Some(id);
self
}
pub fn callback_query_id(mut self, id: impl Into<String>) -> Self {
self.params.callback_query_id = Some(id.into());
self
}
pub fn business_connection_id(mut self, v: impl Into<String>) -> Self {
self.params.business_connection_id = Some(v.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
}
impl_into_future!(SendVenue, Message, "sendVenue");
#[derive(Serialize)]
struct SendMediaGroupParams {
chat_id: ChatId,
media: Vec<InputMedia>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
}
pub struct SendMediaGroup {
client: BotClient,
params: SendMediaGroupParams,
}
impl SendMediaGroup {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
media: Vec<InputMedia>,
) -> Self {
Self {
client,
params: SendMediaGroupParams {
chat_id: chat_id.into(),
media,
message_thread_id: None,
direct_messages_topic_id: None,
business_connection_id: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
allow_paid_broadcast: None,
message_effect_id: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
}
impl_into_future!(SendMediaGroup, Vec<Message>, "sendMediaGroup");
#[derive(Serialize)]
struct SendPaidMediaParams {
chat_id: ChatId,
star_count: u32,
media: Vec<InputPaidMedia>,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
caption: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
parse_mode: Option<ParseMode>,
#[serde(skip_serializing_if = "Option::is_none")]
show_caption_above_media: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<ReplyMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
caption_entities: Option<Vec<rustigram_types::message::MessageEntity>>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendPaidMedia {
client: BotClient,
params: SendPaidMediaParams,
}
impl SendPaidMedia {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
star_count: u32,
media: Vec<InputPaidMedia>,
) -> Self {
Self {
client,
params: SendPaidMediaParams {
chat_id: chat_id.into(),
star_count,
media,
business_connection_id: None,
payload: None,
caption: None,
parse_mode: None,
show_caption_above_media: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
allow_paid_broadcast: None,
caption_entities: None,
direct_messages_topic_id: None,
message_thread_id: None,
suggested_post_parameters: None,
},
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn payload(mut self, p: impl Into<String>) -> Self {
self.params.payload = Some(p.into());
self
}
pub fn caption(mut self, c: impl Into<String>) -> Self {
self.params.caption = Some(c.into());
self
}
pub fn parse_mode(mut self, m: ParseMode) -> Self {
self.params.parse_mode = Some(m);
self
}
pub fn show_caption_above_media(mut self, v: bool) -> Self {
self.params.show_caption_above_media = Some(v);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: impl Into<ReplyMarkup>) -> Self {
self.params.reply_markup = Some(m.into());
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn caption_entities(mut self, v: Vec<rustigram_types::message::MessageEntity>) -> Self {
self.params.caption_entities = Some(v);
self
}
pub fn direct_messages_topic_id(mut self, v: i64) -> Self {
self.params.direct_messages_topic_id = Some(v);
self
}
pub fn message_thread_id(mut self, v: i64) -> Self {
self.params.message_thread_id = Some(v);
self
}
pub fn suggested_post_parameters(mut self, v: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(v);
self
}
}
impl_into_future!(SendPaidMedia, Message, "sendPaidMedia");
#[derive(Serialize)]
struct SendGameParams {
chat_id: i64,
game_short_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<rustigram_types::keyboard::InlineKeyboardMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
}
pub struct SendGame {
client: BotClient,
params: SendGameParams,
}
impl SendGame {
pub(crate) fn new(client: BotClient, chat_id: i64, game_short_name: impl Into<String>) -> Self {
Self {
client,
params: SendGameParams {
chat_id,
game_short_name: game_short_name.into(),
business_connection_id: None,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
allow_paid_broadcast: None,
message_effect_id: None,
},
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: rustigram_types::keyboard::InlineKeyboardMarkup) -> Self {
self.params.reply_markup = Some(m);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, v: impl Into<String>) -> Self {
self.params.message_effect_id = Some(v.into());
self
}
}
impl_into_future!(SendGame, Message, "sendGame");
#[derive(Serialize)]
struct SendChecklistParams {
business_connection_id: String,
chat_id: i64,
checklist: rustigram_types::checklist::InputChecklist,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<rustigram_types::keyboard::InlineKeyboardMarkup>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
}
pub struct SendChecklist {
client: BotClient,
params: SendChecklistParams,
}
impl SendChecklist {
pub(crate) fn new(
client: BotClient,
business_connection_id: impl Into<String>,
chat_id: i64,
checklist: rustigram_types::checklist::InputChecklist,
) -> Self {
Self {
client,
params: SendChecklistParams {
business_connection_id: business_connection_id.into(),
chat_id,
checklist,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
message_effect_id: None,
reply_parameters: None,
reply_markup: None,
suggested_post_parameters: None,
},
}
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self {
self.params.message_effect_id = Some(id.into());
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: rustigram_types::keyboard::InlineKeyboardMarkup) -> Self {
self.params.reply_markup = Some(m);
self
}
pub fn suggested_post_parameters(mut self, params: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(params);
self
}
}
impl_into_future!(SendChecklist, Message, "sendChecklist");
#[derive(Serialize)]
struct SendRichMessageParams {
chat_id: ChatId,
rich_message: rustigram_types::rich_message::InputRichMessage,
#[serde(skip_serializing_if = "Option::is_none")]
business_connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
direct_messages_topic_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
disable_notification: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
protect_content: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
allow_paid_broadcast: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
message_effect_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
suggested_post_parameters: Option<SuggestedPostParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_parameters: Option<ReplyParameters>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<rustigram_types::keyboard::ReplyMarkup>,
}
pub struct SendRichMessage {
client: BotClient,
params: SendRichMessageParams,
}
impl SendRichMessage {
pub(crate) fn new(
client: BotClient,
chat_id: impl Into<ChatId>,
rich_message: rustigram_types::rich_message::InputRichMessage,
) -> Self {
Self {
client,
params: SendRichMessageParams {
chat_id: chat_id.into(),
rich_message,
business_connection_id: None,
message_thread_id: None,
direct_messages_topic_id: None,
disable_notification: None,
protect_content: None,
allow_paid_broadcast: None,
message_effect_id: None,
suggested_post_parameters: None,
reply_parameters: None,
reply_markup: None,
},
}
}
pub fn business_connection_id(mut self, id: impl Into<String>) -> Self {
self.params.business_connection_id = Some(id.into());
self
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
pub fn direct_messages_topic_id(mut self, id: i64) -> Self {
self.params.direct_messages_topic_id = Some(id);
self
}
pub fn disable_notification(mut self, v: bool) -> Self {
self.params.disable_notification = Some(v);
self
}
pub fn protect_content(mut self, v: bool) -> Self {
self.params.protect_content = Some(v);
self
}
pub fn allow_paid_broadcast(mut self, v: bool) -> Self {
self.params.allow_paid_broadcast = Some(v);
self
}
pub fn message_effect_id(mut self, id: impl Into<String>) -> Self {
self.params.message_effect_id = Some(id.into());
self
}
pub fn suggested_post_parameters(mut self, p: SuggestedPostParameters) -> Self {
self.params.suggested_post_parameters = Some(p);
self
}
pub fn reply_parameters(mut self, rp: ReplyParameters) -> Self {
self.params.reply_parameters = Some(rp);
self
}
pub fn reply_markup(mut self, m: rustigram_types::keyboard::ReplyMarkup) -> Self {
self.params.reply_markup = Some(m);
self
}
}
impl_into_future!(SendRichMessage, Message, "sendRichMessage");
#[derive(Serialize)]
struct SendRichMessageDraftParams {
chat_id: i64,
draft_id: i64,
rich_message: rustigram_types::rich_message::InputRichMessage,
#[serde(skip_serializing_if = "Option::is_none")]
message_thread_id: Option<i64>,
}
pub struct SendRichMessageDraft {
client: BotClient,
params: SendRichMessageDraftParams,
}
impl SendRichMessageDraft {
pub(crate) fn new(
client: BotClient,
chat_id: i64,
draft_id: i64,
rich_message: rustigram_types::rich_message::InputRichMessage,
) -> Self {
Self {
client,
params: SendRichMessageDraftParams {
chat_id,
draft_id,
rich_message,
message_thread_id: None,
},
}
}
pub fn message_thread_id(mut self, id: i64) -> Self {
self.params.message_thread_id = Some(id);
self
}
}
impl_into_future!(SendRichMessageDraft, bool, "sendRichMessageDraft");
#[cfg(test)]
mod tests {
use super::*;
use crate::client::BotClient;
fn client() -> BotClient {
BotClient::from_token("123456:test-token-for-unit-tests").unwrap()
}
#[test]
fn api_wide_parameters_serialize_on_the_json_path() {
let contact = SendContact::new(client(), 1_i64, "+100", "A")
.business_connection_id("biz")
.allow_paid_broadcast(true)
.message_effect_id("effect")
.suggested_post_parameters(SuggestedPostParameters {
price: None,
send_date: Some(1_700_000_000),
});
let json = serde_json::to_value(&contact.params).unwrap();
assert_eq!(json["business_connection_id"], "biz");
assert_eq!(json["allow_paid_broadcast"], true);
assert_eq!(json["message_effect_id"], "effect");
assert!(json.get("suggested_post_parameters").is_some());
}
#[test]
fn unset_parameters_are_omitted() {
let dice = SendDice::new(client(), 1_i64);
let json = serde_json::to_value(&dice.params).unwrap();
for key in [
"business_connection_id",
"allow_paid_broadcast",
"message_effect_id",
"suggested_post_parameters",
] {
assert!(
json.get(key).is_none(),
"{key} should be omitted when unset"
);
}
}
#[test]
fn message_draft_text_can_be_cleared() {
let draft = SendMessageDraft::new(client(), 1_i64, 7, "hello");
assert_eq!(
serde_json::to_value(&draft.params).unwrap()["text"],
"hello"
);
let empty = SendMessageDraft::new(client(), 1_i64, 7, "hello").clear_text();
assert!(serde_json::to_value(&empty.params)
.unwrap()
.get("text")
.is_none());
}
#[test]
fn multipart_and_json_paths_cover_the_same_options() {
let source = include_str!("sending.rs");
let struct_body = source
.split("pub struct MediaSendOptions {")
.nth(1)
.and_then(|s| s.split("\n}").next())
.expect("MediaSendOptions struct");
let fields: Vec<&str> = struct_body
.lines()
.filter_map(|l| l.trim().strip_prefix("pub "))
.filter_map(|l| l.split(':').next())
.collect();
assert_eq!(
fields.len(),
17,
"field count changed; update both send paths"
);
for (helper, path) in [
("fn apply_media_opts(", "multipart form"),
("fn media_json_body(", "JSON body"),
] {
let body = source
.split(helper)
.nth(1)
.and_then(|s| s.split("\nfn ").next())
.unwrap_or_else(|| panic!("{helper} body"));
for field in &fields {
assert!(
body.contains(&format!("opts.{field}")),
"`{field}` is settable but never written to the {path}"
);
}
}
}
}