use std::future::{Future, IntoFuture};
use std::pin::Pin;
use reqwest::multipart::{Form, Part};
use serde::Serialize;
use rustigram_types::file::InputFile;
use rustigram_types::keyboard::ReplyMarkup;
use rustigram_types::message::{LinkPreviewOptions, Message, ParseMode, ReplyParameters};
use rustigram_types::poll::InputPollOption;
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")]
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>,
}
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,
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,
},
}
}
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 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,
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
}
}
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")]
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>,
}
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,
video_start_timestamp: 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 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!(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")]
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>,
}
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,
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,
},
}
}
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
}
}
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")]
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>,
}
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,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
},
}
}
pub fn emoji(mut self, e: impl Into<String>) -> Self {
self.params.emoji = Some(e.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
}
}
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")]
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>,
}
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,
horizontal_accuracy: None,
live_period: None,
heading: None,
proximity_alert_radius: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
},
}
}
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
}
}
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")]
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>,
}
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,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
},
}
}
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
}
}
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", 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")]
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")]
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>,
}
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,
poll_type: None,
is_anonymous: None,
allows_multiple_answers: None,
allows_revoting: None,
correct_option_ids: None,
explanation: None,
explanation_parse_mode: None,
open_period: None,
close_date: None,
is_closed: None,
disable_notification: None,
protect_content: None,
reply_parameters: None,
reply_markup: None,
},
}
}
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, correct_option_id: u8) -> Self {
self.params.poll_type = Some(rustigram_types::poll::PollType::Quiz);
self.params.correct_option_ids = Some(vec![correct_option_id]);
self
}
pub fn explanation(mut self, text: impl Into<String>) -> Self {
self.params.explanation = Some(text.into());
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 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
}
}
impl_into_future!(SendPoll, Message, "sendPoll");
#[derive(Serialize)]
struct SendMessageDraftParams {
chat_id: ChatId,
draft_id: i64,
text: 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: 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
}
}
impl_into_future!(SendMessageDraft, bool, "sendMessageDraft");
#[derive(Default)]
pub struct MediaSendOptions {
pub business_connection_id: Option<String>,
pub message_thread_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 reply_parameters: Option<ReplyParameters>,
pub reply_markup: Option<ReplyMarkup>,
}
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.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.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 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 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 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
}
}
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());
if let Some(c) = &self.opts.caption {
form = form.text("caption", c.clone());
}
if let Some(m) = &self.opts.parse_mode {
form = form.text("parse_mode", format!("{m:?}"));
}
if let Some(v) = self.opts.disable_notification {
form = form.text("disable_notification", v.to_string());
}
if let Some(v) = self.opts.has_spoiler {
form = form.text("has_spoiler", v.to_string());
}
if let Some(v) = &self.opts.reply_markup {
form = form.text("reply_markup", serde_json::to_string(v).unwrap());
}
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
}
}
})
}
}
macro_rules! media_sender {
($(#[$doc:meta])* $name:ident, $field:literal, $method:literal, $return_ty:ty, [$($extra_field:ident: $extra_ty:ty),*]) => {
$(#[$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 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 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 }
}
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());
if let Some(c) = &self.opts.caption { form = form.text("caption", c.clone()); }
if let Some(v) = self.opts.disable_notification { form = form.text("disable_notification", v.to_string()); }
if let Some(v) = &self.opts.reply_markup { form = form.text("reply_markup", serde_json::to_string(v).unwrap()); }
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]);
media_sender!(
SendDocument, "document", "sendDocument", Message, [disable_content_type_detection: bool]);
media_sender!(
SendVideo, "video", "sendVideo", Message, [duration: u32, width: u32, height: u32, supports_streaming: bool]);
media_sender!(
SendAnimation, "animation", "sendAnimation", Message, [duration: u32, width: u32, height: u32]);
media_sender!(
SendVoice, "voice", "sendVoice", Message, [duration: u32]);
media_sender!(
SendVideoNote, "video_note", "sendVideoNote", Message, [duration: u32, length: u32]);
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 StopPollParams {
chat_id: ChatId,
message_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
reply_markup: Option<rustigram_types::keyboard::InlineKeyboardMarkup>,
}
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,
},
}
}
pub fn reply_markup(mut self, m: rustigram_types::keyboard::InlineKeyboardMarkup) -> Self {
self.params.reply_markup = Some(m);
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");