mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-18 15:20:15 +01:00
Merge branch 'dev' into dispatcher
This commit is contained in:
commit
43992ec378
31 changed files with 1085 additions and 221 deletions
|
@ -5,3 +5,8 @@
|
||||||
<img src="https://travis-ci.com/async-telegram-bot/async-telegram-bot.svg?branch=dev" />
|
<img src="https://travis-ci.com/async-telegram-bot/async-telegram-bot.svg?branch=dev" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Dependency graph
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://github.com/async-telegram-bot/async-telegram-bot/blob/dev/graph.png" />
|
||||||
|
</div>
|
||||||
|
|
BIN
graph.png
Normal file
BIN
graph.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 MiB |
134
src/bot/mod.rs
134
src/bot/mod.rs
|
@ -1,7 +1,24 @@
|
||||||
use reqwest::r#async::Client;
|
use reqwest::r#async::Client;
|
||||||
|
|
||||||
use crate::core::requests::{
|
use crate::core::{
|
||||||
get_me::GetMe, send_message::SendMessage, ChatId, RequestContext,
|
types::{
|
||||||
|
InputFile,
|
||||||
|
InputMedia,
|
||||||
|
},
|
||||||
|
requests::{
|
||||||
|
ChatId,
|
||||||
|
RequestContext,
|
||||||
|
|
||||||
|
get_me::GetMe,
|
||||||
|
send_message::SendMessage,
|
||||||
|
edit_message_live_location::EditMessageLiveLocation,
|
||||||
|
forward_message::ForwardMessage,
|
||||||
|
send_audio::SendAudio,
|
||||||
|
send_location::SendLocation,
|
||||||
|
send_media_group::SendMediaGroup,
|
||||||
|
send_photo::SendPhoto,
|
||||||
|
stop_message_live_location::StopMessageLiveLocation,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Bot {
|
pub struct Bot {
|
||||||
|
@ -23,15 +40,19 @@ impl Bot {
|
||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ctx(&self) -> RequestContext {
|
||||||
|
RequestContext {
|
||||||
|
token: &self.token,
|
||||||
|
client: &self.client,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Telegram functions
|
/// Telegram functions
|
||||||
impl Bot {
|
impl Bot {
|
||||||
pub fn get_me(&self) -> GetMe {
|
pub fn get_me(&self) -> GetMe {
|
||||||
GetMe::new(RequestContext {
|
GetMe::new(self.ctx())
|
||||||
token: &self.token,
|
|
||||||
client: &self.client,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage
|
pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage
|
||||||
|
@ -40,12 +61,105 @@ impl Bot {
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
SendMessage::new(
|
SendMessage::new(
|
||||||
RequestContext {
|
self.ctx(),
|
||||||
token: &self.token,
|
|
||||||
client: &self.client,
|
|
||||||
},
|
|
||||||
chat_id.into(),
|
chat_id.into(),
|
||||||
text.into(),
|
text.into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn edit_message_live_location<Lt, Lg>(
|
||||||
|
&self,
|
||||||
|
latitude: Lt,
|
||||||
|
longitude: Lg,
|
||||||
|
) -> EditMessageLiveLocation
|
||||||
|
where
|
||||||
|
Lt: Into<f64>,
|
||||||
|
Lg: Into<f64>,
|
||||||
|
{
|
||||||
|
EditMessageLiveLocation::new(
|
||||||
|
self.ctx(),
|
||||||
|
latitude.into(),
|
||||||
|
longitude.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forward_message<C, F, M>(
|
||||||
|
&self,
|
||||||
|
chat_id: C,
|
||||||
|
from_chat_id: F,
|
||||||
|
message_id: M
|
||||||
|
) -> ForwardMessage
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
F: Into<ChatId>,
|
||||||
|
M: Into<i64>,
|
||||||
|
{
|
||||||
|
ForwardMessage::new(
|
||||||
|
self.ctx(),
|
||||||
|
chat_id.into(),
|
||||||
|
from_chat_id.into(),
|
||||||
|
message_id.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_audio<C, A>(&self, chat_id: C, audio: A) -> SendAudio
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
A: Into<InputFile>,
|
||||||
|
{
|
||||||
|
SendAudio::new(
|
||||||
|
self.ctx(),
|
||||||
|
chat_id.into(),
|
||||||
|
audio.into()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_location<C, Lt, Lg>(
|
||||||
|
&self,
|
||||||
|
chat_id: C,
|
||||||
|
latitude: Lt,
|
||||||
|
longitude: Lg,
|
||||||
|
) -> SendLocation
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
Lt: Into<f64>,
|
||||||
|
Lg: Into<f64>,
|
||||||
|
{
|
||||||
|
SendLocation::new(
|
||||||
|
self.ctx(),
|
||||||
|
chat_id.into(),
|
||||||
|
latitude.into(),
|
||||||
|
longitude.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_media_group<C, M>(&self, chat_id: C, media: M) -> SendMediaGroup
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
M: Into<Vec<InputMedia>>
|
||||||
|
{
|
||||||
|
SendMediaGroup::new(
|
||||||
|
self.ctx(),
|
||||||
|
chat_id.into(),
|
||||||
|
media.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_photo<C, P>(&self, chat_id: C, photo: P) -> SendPhoto
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
P: Into<InputFile>
|
||||||
|
{
|
||||||
|
SendPhoto::new(
|
||||||
|
self.ctx(),
|
||||||
|
chat_id.into(),
|
||||||
|
photo.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_message_live_location(&self) -> StopMessageLiveLocation {
|
||||||
|
StopMessageLiveLocation::new(
|
||||||
|
self.ctx()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ use reqwest::{
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ pub async fn request_multipart<T: DeserializeOwned>(
|
||||||
TelegramResponse::Err {
|
TelegramResponse::Err {
|
||||||
description,
|
description,
|
||||||
error_code,
|
error_code,
|
||||||
response_parameters,
|
response_parameters: _,
|
||||||
..
|
..
|
||||||
} => Err(RequestError::ApiError {
|
} => Err(RequestError::ApiError {
|
||||||
description,
|
description,
|
||||||
|
@ -95,7 +94,7 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
TelegramResponse::Err {
|
TelegramResponse::Err {
|
||||||
description,
|
description,
|
||||||
error_code,
|
error_code,
|
||||||
response_parameters,
|
response_parameters: _,
|
||||||
..
|
..
|
||||||
} => Err(RequestError::ApiError {
|
} => Err(RequestError::ApiError {
|
||||||
description,
|
description,
|
||||||
|
@ -108,11 +107,17 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum TelegramResponse<R> {
|
enum TelegramResponse<R> {
|
||||||
Ok {
|
Ok {
|
||||||
ok: bool, // true
|
/// Dummy field. Used for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: True type
|
||||||
|
|
||||||
result: R,
|
result: R,
|
||||||
},
|
},
|
||||||
Err {
|
Err {
|
||||||
ok: bool, // false
|
/// Dummy field. Used for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: False type
|
||||||
|
|
||||||
description: String,
|
description: String,
|
||||||
error_code: u16,
|
error_code: u16,
|
||||||
response_parameters: Option<ResponseParameters>,
|
response_parameters: Option<ResponseParameters>,
|
||||||
|
|
83
src/core/requests/answer_pre_checkout_query.rs
Normal file
83
src/core/requests/answer_pre_checkout_query.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::core::{
|
||||||
|
requests::{RequestContext, Request, RequestFuture, ResponseResult},
|
||||||
|
network
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
/// Once the user has confirmed their payment and shipping details, the Bot API
|
||||||
|
/// sends the final confirmation in the form of an [`Update`] with the field
|
||||||
|
/// pre_checkout_query. Use this method to respond to such pre-checkout queries.
|
||||||
|
/// On success, True is returned. Note: The Bot API must receive an answer
|
||||||
|
/// within 10 seconds after the pre-checkout query was sent.
|
||||||
|
pub struct AnswerPreCheckoutQuery<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
|
||||||
|
/// Unique identifier for the query to be answered
|
||||||
|
pub pre_checkout_query_id: String,
|
||||||
|
|
||||||
|
/// Specify True if everything is alright (goods are available, etc.) and
|
||||||
|
/// the bot is ready to proceed with the order. Use False if there are any
|
||||||
|
/// problems.
|
||||||
|
pub ok: bool,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Required if ok is False. Error message in human readable form that
|
||||||
|
/// explains the reason for failure to proceed with the checkout (e.g.
|
||||||
|
/// "Sorry, somebody just bought the last of our amazing black T-shirts
|
||||||
|
/// while you were busy filling out your payment details. Please choose a
|
||||||
|
/// different color or garment!"). Telegram will display this message to
|
||||||
|
/// the user.
|
||||||
|
pub error_message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for AnswerPreCheckoutQuery<'a> {
|
||||||
|
type ReturnValue = bool;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"answerPreCheckoutQuery",
|
||||||
|
&self
|
||||||
|
).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AnswerPreCheckoutQuery<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
pre_checkout_query_id: String,
|
||||||
|
ok: bool
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
pre_checkout_query_id,
|
||||||
|
ok,
|
||||||
|
error_message: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pre_checkout_query_id<T>(mut self, pre_checkout_query_id: T) -> Self
|
||||||
|
where T: Into<String>
|
||||||
|
{
|
||||||
|
self.pre_checkout_query_id = pre_checkout_query_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok<T>(mut self, ok: T) -> Self
|
||||||
|
where T: Into<bool>
|
||||||
|
{
|
||||||
|
self.ok = ok.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_message<T>(mut self, error_message: T) -> Self
|
||||||
|
where T: Into<String>
|
||||||
|
{
|
||||||
|
self.error_message = Some(error_message.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
91
src/core/requests/answer_shipping_query.rs
Normal file
91
src/core/requests/answer_shipping_query.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
use crate::core::types::ShippingOption;
|
||||||
|
use crate::core::requests::{RequestContext, Request, RequestFuture, ResponseResult};
|
||||||
|
use crate::core::network;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
/// If you sent an invoice requesting a shipping address and the parameter
|
||||||
|
/// is_flexible was specified, the Bot API will send an [`Update`] with a
|
||||||
|
/// shipping_query field to the bot. Use this method to reply to shipping
|
||||||
|
/// queries. On success, True is returned.
|
||||||
|
pub struct AnswerShippingQuery<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
|
||||||
|
/// Unique identifier for the query to be answered
|
||||||
|
pub shipping_query_id: String,
|
||||||
|
/// Specify True if delivery to the specified address is possible and False
|
||||||
|
/// if there are any problems (for example, if delivery to the specified
|
||||||
|
/// address is not possible)
|
||||||
|
pub ok: bool,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Required if ok is True. A JSON-serialized array of available shipping
|
||||||
|
/// options.
|
||||||
|
pub shipping_options: Option<Vec<ShippingOption>>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
/// Required if ok is False. Error message in human readable form that
|
||||||
|
/// explains why it is impossible to complete the order (e.g. "Sorry,
|
||||||
|
/// delivery to your desired address is unavailable'). Telegram will
|
||||||
|
/// display this message to the user.
|
||||||
|
pub error_message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for AnswerShippingQuery<'a> {
|
||||||
|
type ReturnValue = bool;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"answerShippingQuery",
|
||||||
|
&self
|
||||||
|
).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AnswerShippingQuery<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
shipping_query_id: String,
|
||||||
|
ok: bool
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
shipping_query_id,
|
||||||
|
ok,
|
||||||
|
shipping_options: None,
|
||||||
|
error_message: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shipping_query_id<T>(mut self, shipping_query_id: T) -> Self
|
||||||
|
where T: Into<String>
|
||||||
|
{
|
||||||
|
self.shipping_query_id = shipping_query_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok<T>(mut self, ok: T) -> Self
|
||||||
|
where T: Into<bool>
|
||||||
|
{
|
||||||
|
self.ok = ok.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shipping_options<T>(mut self, shipping_options: T) -> Self
|
||||||
|
where T: Into<Vec<ShippingOption>>
|
||||||
|
{
|
||||||
|
self.shipping_options = Some(shipping_options.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn error_message<T>(mut self, error_message: T) -> Self
|
||||||
|
where T: Into<String>
|
||||||
|
{
|
||||||
|
self.error_message = Some(error_message.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@ use crate::core::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use reqwest::r#async::multipart::Form;
|
use reqwest::r#async::multipart::Form;
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
/// This is a convenient struct that builds `reqwest::r#async::multipart::Form`
|
/// This is a convenient struct that builds `reqwest::r#async::multipart::Form`
|
||||||
/// from scratch.
|
/// from scratch.
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
ChatId,
|
||||||
RequestFuture, ResponseResult,
|
Request,
|
||||||
|
RequestFuture,
|
||||||
|
RequestContext,
|
||||||
|
ResponseResult,
|
||||||
},
|
},
|
||||||
types::Message,
|
types::Message,
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,6 +89,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod answer_pre_checkout_query;
|
||||||
|
pub mod answer_shipping_query;
|
||||||
pub mod edit_message_live_location;
|
pub mod edit_message_live_location;
|
||||||
pub mod forward_message;
|
pub mod forward_message;
|
||||||
pub mod get_file;
|
pub mod get_file;
|
||||||
|
|
|
@ -1,8 +1,83 @@
|
||||||
use crate::core::requests::RequestContext;
|
use crate::core::network;
|
||||||
//TODO:: need implementation
|
use crate::core::requests::{ChatId, Request, RequestContext, RequestFuture, ResponseResult};
|
||||||
|
use crate::core::types::Message;
|
||||||
|
|
||||||
|
///Use this method when you need to tell the user that something is happening on the bot's side.
|
||||||
|
///The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status).
|
||||||
|
///Returns True on success.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendChatAction<'a> {
|
struct SendChatAction<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
/// Unique identifier for the target chat or
|
||||||
|
/// username of the target channel (in the format @channelusername)
|
||||||
|
pub chat_id: ChatId,
|
||||||
|
/// Type of action to broadcast. Choose one, depending on what the user is
|
||||||
|
/// about to receive: typing for text messages, upload_photo for photos,
|
||||||
|
/// record_video or upload_video for videos, record_audio or upload_audio
|
||||||
|
/// for audio files, upload_document for general files, find_location for
|
||||||
|
/// location data, record_video_note or upload_video_note for video notes.
|
||||||
|
pub action: ChatAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, From, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
enum ChatAction {
|
||||||
|
Typing,
|
||||||
|
UploadPhoto,
|
||||||
|
RecordVideo,
|
||||||
|
UploadVideo,
|
||||||
|
RecordAudio,
|
||||||
|
UploadAudio,
|
||||||
|
UploadDocument,
|
||||||
|
FindLocation,
|
||||||
|
RecordVideoNote,
|
||||||
|
UploadVideoNote,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for SendChatAction<'a> {
|
||||||
|
type ReturnValue = bool;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"sendChatAction",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SendChatAction<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
chat_id: ChatId,
|
||||||
|
action: ChatAction,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
chat_id,
|
||||||
|
action,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
|
{
|
||||||
|
self.chat_id = chat_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn action<T>(mut self, action: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatAction>,
|
||||||
|
{
|
||||||
|
self.action = action.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,142 @@
|
||||||
use crate::core::requests::RequestContext;
|
use crate::core::network;
|
||||||
//TODO:: need implementation
|
use crate::core::requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
};
|
||||||
|
use crate::core::types::{Message, ReplyMarkup};
|
||||||
|
|
||||||
|
/// Use this method to send phone contacts.
|
||||||
|
/// returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendContact<'a> {
|
struct SendContact<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
/// Unique identifier for the target chat or
|
||||||
|
/// username of the target channel (in the format @channelusername)
|
||||||
|
pub chat_id: ChatId,
|
||||||
|
/// Contact's phone number
|
||||||
|
pub phone_number: String,
|
||||||
|
/// Contact's first name
|
||||||
|
pub first_name: String,
|
||||||
|
/// Contact's last name
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last_name: Option<String>,
|
||||||
|
/// Additional data about the contact in the form of a
|
||||||
|
/// vCard, 0-2048 bytes
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub vcard: Option<String>,
|
||||||
|
/// Sends the message silently. Users will receive a
|
||||||
|
/// notification with no sound.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub disable_notification: Option<bool>,
|
||||||
|
/// If the message is a reply, ID of the original
|
||||||
|
/// message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_to_message_id: Option<i32>,
|
||||||
|
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove
|
||||||
|
/// or ForceReply Optional Additional interface options. A JSON-serialized
|
||||||
|
/// object for an inline keyboard, custom reply keyboard, instructions to
|
||||||
|
/// remove keyboard or to force a reply from the user.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for SendContact<'a> {
|
||||||
|
type ReturnValue = Message;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"sendContact",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SendContact<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
chat_id: ChatId,
|
||||||
|
phone_number: String,
|
||||||
|
first_name: String,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
chat_id,
|
||||||
|
phone_number,
|
||||||
|
first_name,
|
||||||
|
last_name: None,
|
||||||
|
vcard: None,
|
||||||
|
disable_notification: None,
|
||||||
|
reply_to_message_id: None,
|
||||||
|
reply_markup: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
|
{
|
||||||
|
self.chat_id = chat_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phone_number<T>(mut self, phone_number: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.phone_number = phone_number.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_name<T>(mut self, first_name: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.first_name = first_name.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_name<T>(mut self, last_name: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.last_name = Some(last_name.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vcard<T>(mut self, vcard: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.vcard = Some(vcard.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_notification<T>(mut self, disable_notification: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<bool>,
|
||||||
|
{
|
||||||
|
self.disable_notification = Some(disable_notification.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_to_message_id<T>(mut self, reply_to_message_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<i32>,
|
||||||
|
{
|
||||||
|
self.reply_to_message_id = Some(reply_to_message_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ReplyMarkup>,
|
||||||
|
{
|
||||||
|
self.reply_markup = Some(reply_markup.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
ChatId,
|
||||||
RequestFuture, ResponseResult,
|
Request,
|
||||||
|
RequestFuture,
|
||||||
|
RequestContext,
|
||||||
|
ResponseResult,
|
||||||
},
|
},
|
||||||
types::{Message, ParseMode, ReplyMarkup},
|
types::{Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
|
|
|
@ -1,8 +1,114 @@
|
||||||
use crate::core::requests::RequestContext;
|
use crate::core::network;
|
||||||
//TODO:: need implementation
|
use crate::core::requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
};
|
||||||
|
use crate::core::types::{Message, ReplyMarkup};
|
||||||
|
|
||||||
|
/// Use this method to send a native poll. A native poll can't be sent to a
|
||||||
|
/// private chat. On success, the sent Message is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendPoll<'a> {
|
struct SendPoll<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
|
/// identifier for the target chat or username of the target channel (in
|
||||||
|
/// the format @channelusername). A native poll can't be sent to a private
|
||||||
|
/// chat.
|
||||||
|
chat_id: ChatId,
|
||||||
|
/// Poll question, 1-255 characters
|
||||||
|
question: String,
|
||||||
|
/// List of answer options, 2-10 strings 1-100 characters each
|
||||||
|
options: Vec<String>,
|
||||||
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
|
/// sound.
|
||||||
|
disable_notification: Option<bool>,
|
||||||
|
/// If the message is a reply, ID of the original message
|
||||||
|
reply_to_message_id: Option<i32>,
|
||||||
|
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove
|
||||||
|
/// or ForceReply Optional Additional interface options. A JSON-serialized
|
||||||
|
/// object for an inline keyboard, custom reply keyboard, instructions to
|
||||||
|
/// remove reply keyboard or to force a reply from the user.
|
||||||
|
reply_markup: Option<ReplyMarkup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for SendPoll<'a> {
|
||||||
|
type ReturnValue = Message;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"sendPoll",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SendPoll<'a> {
|
||||||
|
pub(crate) fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
chat_id: ChatId,
|
||||||
|
question: String,
|
||||||
|
options: Vec<String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
chat_id,
|
||||||
|
question,
|
||||||
|
options,
|
||||||
|
disable_notification: None,
|
||||||
|
reply_to_message_id: None,
|
||||||
|
reply_markup: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
|
{
|
||||||
|
self.chat_id = chat_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn question<T>(mut self, question: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.question = question.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options<T>(mut self, options: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<Vec<String>>,
|
||||||
|
{
|
||||||
|
self.options = options.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_notification<T>(mut self, disable_notification: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<bool>,
|
||||||
|
{
|
||||||
|
self.disable_notification = Some(disable_notification.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_to_message_id<T>(mut self, reply_to_message_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<i32>,
|
||||||
|
{
|
||||||
|
self.reply_to_message_id = Some(reply_to_message_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ReplyMarkup>,
|
||||||
|
{
|
||||||
|
self.reply_markup = Some(reply_markup.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,158 @@
|
||||||
use crate::core::requests::{ChatId, RequestContext};
|
use crate::core::network;
|
||||||
|
use crate::core::requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
};
|
||||||
|
use crate::core::types::{Message, ReplyMarkup};
|
||||||
|
|
||||||
//TODO:: need implementation
|
/// Use this method to send information about a venue.
|
||||||
///Use this method to send information about a venue. On success, the sent
|
|
||||||
/// Message is returned.
|
/// Message is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendVenue<'a> {
|
struct SendVenue<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Integer or String Yes Unique identifier for the target chat or
|
/// Unique identifier for the target chat or
|
||||||
/// username of the target channel (in the format @channelusername)
|
/// username of the target channel (in the format @channelusername)
|
||||||
chat_id: ChatId,
|
pub chat_id: ChatId,
|
||||||
/// Float number Yes Latitude of the venue
|
/// Latitude of the venue
|
||||||
latitude: f64,
|
pub latitude: f64,
|
||||||
///Float number Yes Longitude of the venue
|
/// Longitude of the venue
|
||||||
longitude: f64,
|
pub longitude: f64,
|
||||||
/// Yes Name of the venue
|
/// Name of the venue
|
||||||
title: String,
|
pub title: String,
|
||||||
///String Yes Address of the venue
|
/// Address of the venue
|
||||||
address: String,
|
pub address: String,
|
||||||
/// String Optional Foursquare identifier of the venue
|
/// Foursquare identifier of the venue
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
foursquare_id: Option<String>,
|
pub foursquare_id: Option<String>,
|
||||||
/// String Optional Foursquare type of the venue, if known. (For
|
/// Foursquare type of the venue, if known. (For
|
||||||
/// example, “arts_entertainment/default”, “arts_entertainment/aquarium” or
|
/// example, “arts_entertainment/default”, “arts_entertainment/aquarium” or
|
||||||
/// “food/icecream”.)
|
/// “food/icecream”.)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
foursquare_type: Option<String>,
|
pub foursquare_type: Option<String>,
|
||||||
/// Boolean Optional Sends the message silently. Users will receive a
|
/// Sends the message silently. Users will receive a
|
||||||
/// notification with no sound.
|
/// notification with no sound.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// Integer Optional If the message is a reply, ID of the original
|
/// If the message is a reply, ID of the original
|
||||||
/// message
|
/// message
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
reply_to_message_id: Option<i32>,
|
pub reply_to_message_id: Option<i32>,
|
||||||
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or
|
/// InlineKeyboardMarkup or ReplyKeyboardMarkup or ReplyKeyboardRemove or
|
||||||
/// ForceReply Optional Additional interface options. A JSON-serialized
|
/// ForceReply Optional Additional interface options. A JSON-serialized
|
||||||
/// object for an inline keyboard, custom reply keyboard, instructions to
|
/// object for an inline keyboard, custom reply keyboard, instructions to
|
||||||
/// remove reply keyboard or to force a reply from the user.
|
/// remove reply keyboard or to force a reply from the user.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
reply_markup: Option<()>, //TODO: need concrete type
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for SendVenue<'a> {
|
||||||
|
type ReturnValue = Message;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"sendVenue",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SendVenue<'a> {
|
||||||
|
pub fn new(
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
chat_id: ChatId,
|
||||||
|
latitude: f64,
|
||||||
|
longitude: f64,
|
||||||
|
title: String,
|
||||||
|
address: String,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
chat_id,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
title,
|
||||||
|
address,
|
||||||
|
foursquare_id: None,
|
||||||
|
foursquare_type: None,
|
||||||
|
disable_notification: None,
|
||||||
|
reply_to_message_id: None,
|
||||||
|
reply_markup: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
|
{
|
||||||
|
self.chat_id = chat_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn longitude<T>(mut self, longitude: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<f64>,
|
||||||
|
{
|
||||||
|
self.longitude = longitude.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latitude<T>(mut self, latitude: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<f64>,
|
||||||
|
{
|
||||||
|
self.latitude = latitude.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title<T>(mut self, title: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.title = title.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address<T>(mut self, address: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.address = address.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foursquare_id<T>(mut self, foursquare_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.foursquare_id = Some(foursquare_id.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_notification<T>(mut self, disable_notification: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<bool>,
|
||||||
|
{
|
||||||
|
self.disable_notification = Some(disable_notification.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn foursquare_type<T>(mut self, foursquare_type: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.foursquare_type = Some(foursquare_type.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reply_markup<T>(mut self, reply_markup: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ReplyMarkup>,
|
||||||
|
{
|
||||||
|
self.reply_markup = Some(reply_markup.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
ChatId,
|
||||||
RequestFuture, ResponseResult,
|
Request,
|
||||||
|
RequestFuture,
|
||||||
|
RequestContext,
|
||||||
|
ResponseResult,
|
||||||
},
|
},
|
||||||
types::{InlineKeyboardMarkup, Message, ParseMode},
|
types::{InlineKeyboardMarkup, Message},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Use this method to stop updating a live location message before live_period
|
/// Use this method to stop updating a live location message before live_period
|
||||||
/// expires. On success, if the message was sent by the bot, the sent Message is
|
/// expires. On success, if the message was sent by the bot, the sent Message is
|
||||||
/// returned, otherwise True is returned.
|
/// returned, otherwise True is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct StopMessageLiveLocation<'a> {
|
pub struct StopMessageLiveLocation<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Required if inline_message_id is not specified. Unique identifier for
|
/// Required if inline_message_id is not specified. Unique identifier for
|
||||||
|
@ -52,7 +53,7 @@ impl<'a> Request<'a> for StopMessageLiveLocation<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StopMessageLiveLocation<'a> {
|
impl<'a> StopMessageLiveLocation<'a> {
|
||||||
fn new(ctx: RequestContext<'a>) -> Self {
|
pub(crate) fn new(ctx: RequestContext<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
chat_id: None,
|
chat_id: None,
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use reqwest::r#async::multipart::Part;
|
use reqwest::r#async::multipart::Part;
|
||||||
use std::fs::File;
|
use tokio::{
|
||||||
use std::path::PathBuf;
|
prelude::*,
|
||||||
use tokio::codec::FramedRead;
|
codec::FramedRead,
|
||||||
use tokio::prelude::*;
|
};
|
||||||
|
|
||||||
|
|
||||||
struct FileDecoder;
|
struct FileDecoder;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,63 @@
|
||||||
use crate::core::types::PhotoSize;
|
use crate::core::types::PhotoSize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||||
|
/// This object represents an animation file (GIF or H.264/MPEG-4 AVC video
|
||||||
|
/// without sound).
|
||||||
pub struct Animation {
|
pub struct Animation {
|
||||||
|
/// Identifier for this file
|
||||||
pub file_id: String,
|
pub file_id: String,
|
||||||
|
/// Video width as defined by sender
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
|
/// Video height as defined by sender
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
|
/// Duration of the video in seconds as defined by sender
|
||||||
pub duration: u32,
|
pub duration: u32,
|
||||||
pub thumb: PhotoSize,
|
/// Optional. Animation thumbnail as defined by sender
|
||||||
|
pub thumb: Option<PhotoSize>,
|
||||||
|
/// Optional. Original animation filename as defined by sender
|
||||||
pub file_name: Option<String>,
|
pub file_name: Option<String>,
|
||||||
|
/// Optional. MIME type of the file as defined by sender
|
||||||
pub mime_type: Option<String>,
|
pub mime_type: Option<String>,
|
||||||
|
/// Optional. File size
|
||||||
pub file_size: Option<u32>
|
pub file_size: Option<u32>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let json = r#"{
|
||||||
|
"file_id":"id",
|
||||||
|
"width":320,
|
||||||
|
"height":320,
|
||||||
|
"duration":59,
|
||||||
|
"thumb":{
|
||||||
|
"file_id":"id",
|
||||||
|
"width":320,
|
||||||
|
"height":320,
|
||||||
|
"file_size":3452
|
||||||
|
},
|
||||||
|
"file_name":"some",
|
||||||
|
"mime_type":"gif",
|
||||||
|
"file_size":6500}"#;
|
||||||
|
let expected = Animation {
|
||||||
|
file_id: "id".to_string(),
|
||||||
|
width: 320,
|
||||||
|
height: 320,
|
||||||
|
duration: 59,
|
||||||
|
thumb: Some(PhotoSize {
|
||||||
|
file_id: "id".to_string(),
|
||||||
|
width: 320,
|
||||||
|
height: 320,
|
||||||
|
file_size: Some(3452)
|
||||||
|
}),
|
||||||
|
file_name: Some("some".to_string()),
|
||||||
|
mime_type: Some("gif".to_string()),
|
||||||
|
file_size: Some(6500)
|
||||||
|
};
|
||||||
|
let actual = serde_json::from_str::<Animation>(json).unwrap();
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
|
||||||
pub struct AnswerPreCheckoutQuery {
|
|
||||||
pub pre_checkout_query_id: String,
|
|
||||||
pub ok: bool,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub error_message: Option<String>,
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
use crate::core::types::ShippingOption;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
|
||||||
pub struct AnswerShippingQuery {
|
|
||||||
pub shipping_query_id: String,
|
|
||||||
pub ok: bool,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub shipping_options: Option<Vec<ShippingOption>>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub error_message: Option<String>,
|
|
||||||
}
|
|
|
@ -40,3 +40,54 @@ pub enum InlineKeyboardButtonKind {
|
||||||
/* CallbackGame(CallbackGame), TODO: разобраться, что с этим делать
|
/* CallbackGame(CallbackGame), TODO: разобраться, что с этим делать
|
||||||
* TODO: add LoginUrl, pay */
|
* TODO: add LoginUrl, pay */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build buttons
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```edition2018
|
||||||
|
/// use async_telegram_bot::core::types::InlineKeyboardButton;
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let url_button = InlineKeyboardButton::url(
|
||||||
|
/// "Text".to_string(),
|
||||||
|
/// "http://url.com".to_string(),
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
impl InlineKeyboardButton {
|
||||||
|
pub fn url(text: String, url: String) -> InlineKeyboardButton {
|
||||||
|
InlineKeyboardButton {
|
||||||
|
text,
|
||||||
|
kind: InlineKeyboardButtonKind::Url(url),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn callback(text: String, callback_data: String)
|
||||||
|
-> InlineKeyboardButton {
|
||||||
|
InlineKeyboardButton {
|
||||||
|
text,
|
||||||
|
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_inline_query(text: String, switch_inline_query: String)
|
||||||
|
-> InlineKeyboardButton {
|
||||||
|
InlineKeyboardButton {
|
||||||
|
text,
|
||||||
|
kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn switch_inline_query_current_chat(
|
||||||
|
text: String,
|
||||||
|
switch_inline_query_current_chat: String
|
||||||
|
) -> InlineKeyboardButton {
|
||||||
|
|
||||||
|
InlineKeyboardButton {
|
||||||
|
text,
|
||||||
|
kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
|
||||||
|
switch_inline_query_current_chat
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,3 +11,111 @@ pub struct InlineKeyboardMarkup {
|
||||||
/// [`InlineKeyboardButton`] objects
|
/// [`InlineKeyboardButton`] objects
|
||||||
pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
|
pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build Markup
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```edition2018
|
||||||
|
/// use async_telegram_bot::core::types::{
|
||||||
|
/// InlineKeyboardMarkup,
|
||||||
|
/// InlineKeyboardButton
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let url_button = InlineKeyboardButton::url(
|
||||||
|
/// "text".to_string(),
|
||||||
|
/// "http://url.com".to_string()
|
||||||
|
/// );
|
||||||
|
/// let keyboard = InlineKeyboardMarkup::new()
|
||||||
|
/// .row(vec![url_button]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
impl InlineKeyboardMarkup {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inline_keyboard: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_row(mut self, buttons: Vec<InlineKeyboardButton>) -> Self {
|
||||||
|
self.inline_keyboard.push(buttons);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize)
|
||||||
|
-> Self {
|
||||||
|
match self.inline_keyboard.get_mut(index) {
|
||||||
|
Some(buttons) => buttons.push(button),
|
||||||
|
None => self.inline_keyboard.push(vec![button])
|
||||||
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append_row() {
|
||||||
|
let button1 = InlineKeyboardButton::url(
|
||||||
|
"text 1".to_string(),
|
||||||
|
"url 1".to_string(),
|
||||||
|
);
|
||||||
|
let button2 = InlineKeyboardButton::url(
|
||||||
|
"text 2".to_string(),
|
||||||
|
"url 2".to_string(),
|
||||||
|
);
|
||||||
|
let markup = InlineKeyboardMarkup::new()
|
||||||
|
.append_row(vec![button1.clone(), button2.clone()]);
|
||||||
|
let expected = InlineKeyboardMarkup {
|
||||||
|
inline_keyboard: vec![
|
||||||
|
vec![button1.clone(), button2.clone()]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
assert_eq!(markup, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append_to_row__existent_row() {
|
||||||
|
let button1 = InlineKeyboardButton::url(
|
||||||
|
"text 1".to_string(),
|
||||||
|
"url 1".to_string(),
|
||||||
|
);
|
||||||
|
let button2 = InlineKeyboardButton::url(
|
||||||
|
"text 2".to_string(),
|
||||||
|
"url 2".to_string(),
|
||||||
|
);
|
||||||
|
let markup = InlineKeyboardMarkup::new()
|
||||||
|
.append_row(vec![button1.clone()])
|
||||||
|
.append_to_row(button2.clone(), 0);
|
||||||
|
let expected = InlineKeyboardMarkup {
|
||||||
|
inline_keyboard: vec![
|
||||||
|
vec![button1.clone(), button2.clone()]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
assert_eq!(markup, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append_to_row__nonexistent_row() {
|
||||||
|
let button1 = InlineKeyboardButton::url(
|
||||||
|
"text 1".to_string(),
|
||||||
|
"url 1".to_string(),
|
||||||
|
);
|
||||||
|
let button2 = InlineKeyboardButton::url(
|
||||||
|
"text 2".to_string(),
|
||||||
|
"url 2".to_string(),
|
||||||
|
);
|
||||||
|
let markup = InlineKeyboardMarkup::new()
|
||||||
|
.append_row(vec![button1.clone()])
|
||||||
|
.append_to_row(button2.clone(), 1);
|
||||||
|
let expected = InlineKeyboardMarkup {
|
||||||
|
inline_keyboard: vec![
|
||||||
|
vec![button1.clone()],
|
||||||
|
vec![button2.clone()]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
assert_eq!(markup, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,29 @@
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize)]
|
||||||
|
/// This object represents a portion of the price for goods or services.
|
||||||
pub struct LabeledPrice {
|
pub struct LabeledPrice {
|
||||||
|
/// Portion label
|
||||||
pub label: String,
|
pub label: String,
|
||||||
|
/// Price of the product in the smallest units of the
|
||||||
|
/// [currency](https://core.telegram.org/bots/payments#supported-currencies)
|
||||||
|
/// (integer, not float/double). For example, for a price of US$ 1.45 pass
|
||||||
|
/// amount = 145. See the exp parameter in [`currencies.json`](https://core.telegram.org/bots/payments/currencies.json),
|
||||||
|
/// it shows the number of digits past the decimal point for each currency
|
||||||
|
/// (2 for the majority of currencies).
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
let labeled_price = LabeledPrice {
|
||||||
|
label: "Label".to_string(),
|
||||||
|
amount: 60
|
||||||
|
};
|
||||||
|
let expected = r#"{"label":"Label","amount":60}"#;
|
||||||
|
let actual = serde_json::to_string(&labeled_price).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use self::not_implemented_types::*;
|
use self::not_implemented_types::*;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
answer_pre_checkout_query::AnswerPreCheckoutQuery,
|
|
||||||
answer_shipping_query::AnswerShippingQuery,
|
|
||||||
animation::Animation,
|
animation::Animation,
|
||||||
audio::Audio,
|
audio::Audio,
|
||||||
callback_query::CallbackQuery,
|
callback_query::CallbackQuery,
|
||||||
|
@ -51,8 +49,6 @@ pub use self::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod animation;
|
mod animation;
|
||||||
mod answer_pre_checkout_query;
|
|
||||||
mod answer_shipping_query;
|
|
||||||
mod audio;
|
mod audio;
|
||||||
mod callback_query;
|
mod callback_query;
|
||||||
mod chat;
|
mod chat;
|
||||||
|
|
|
@ -1,7 +1,32 @@
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
||||||
|
/// This object represents one size of a photo or a [`Document`] /
|
||||||
|
/// [`Sticker`] thumbnail.
|
||||||
pub struct PhotoSize {
|
pub struct PhotoSize {
|
||||||
|
/// Identifier for this file
|
||||||
pub file_id: String,
|
pub file_id: String,
|
||||||
|
/// Photo width
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
|
/// Photo height
|
||||||
pub height: i32,
|
pub height: i32,
|
||||||
|
/// Optional. File size
|
||||||
pub file_size: Option<u32>,
|
pub file_size: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let json = r#"{"file_id":"id","width":320,"height":320,
|
||||||
|
"file_size":3452}"#;
|
||||||
|
let expected = PhotoSize {
|
||||||
|
file_id: "id".to_string(),
|
||||||
|
width: 320,
|
||||||
|
height: 320,
|
||||||
|
file_size: Some(3452)
|
||||||
|
};
|
||||||
|
let actual = serde_json::from_str::<PhotoSize>(json).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::types::{InlineKeyboardMarkup, LabeledPrice};
|
use crate::core::types::{InlineKeyboardMarkup, LabeledPrice};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
pub struct SendInvoice {
|
pub struct SendInvoice {
|
||||||
pub chat_id: i64,
|
pub chat_id: i64,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
|
@ -1,8 +1,31 @@
|
||||||
use crate::core::types::LabeledPrice;
|
use crate::core::types::LabeledPrice;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize)]
|
||||||
|
/// This object represents one shipping option.
|
||||||
pub struct ShippingOption {
|
pub struct ShippingOption {
|
||||||
pub id: i64,
|
/// Shipping option identifier
|
||||||
|
pub id: String,
|
||||||
|
/// Option title
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// List of price portions
|
||||||
pub prices: Vec<LabeledPrice>,
|
pub prices: Vec<LabeledPrice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize() {
|
||||||
|
let shipping_option = ShippingOption {
|
||||||
|
id: "0".to_string(),
|
||||||
|
title: "Option".to_string(),
|
||||||
|
prices: vec![
|
||||||
|
LabeledPrice { label: "Label".to_string(), amount: 60 }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let expected = r#"{"id":"0","title":"Option","prices":[{"label":"Label","amount":60}]}"#;
|
||||||
|
let actual = serde_json::to_string(&shipping_option).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
use crate::core::types::{InlineKeyboardButton, InlineKeyboardButtonKind};
|
|
||||||
|
|
||||||
pub struct InlineKeyboardButtonBuilder;
|
|
||||||
|
|
||||||
/// Build buttons
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```edition2018
|
|
||||||
/// use async_telegram_bot::keyboards::InlineKeyboardButtonBuilder;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let url_button = InlineKeyboardButtonBuilder::url(
|
|
||||||
/// "Text".to_string(),
|
|
||||||
/// "http://url.com".to_string(),
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
impl InlineKeyboardButtonBuilder {
|
|
||||||
pub fn url(text: String, url: String) -> InlineKeyboardButton {
|
|
||||||
InlineKeyboardButton {
|
|
||||||
text,
|
|
||||||
kind: InlineKeyboardButtonKind::Url(url),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn callback(text: String, callback_data: String)
|
|
||||||
-> InlineKeyboardButton {
|
|
||||||
InlineKeyboardButton {
|
|
||||||
text,
|
|
||||||
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn switch_inline_query(text: String, switch_inline_query: String)
|
|
||||||
-> InlineKeyboardButton {
|
|
||||||
InlineKeyboardButton {
|
|
||||||
text,
|
|
||||||
kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn switch_inline_query_current_chat(
|
|
||||||
text: String,
|
|
||||||
switch_inline_query_current_chat: String
|
|
||||||
) -> InlineKeyboardButton {
|
|
||||||
|
|
||||||
InlineKeyboardButton {
|
|
||||||
text,
|
|
||||||
kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
|
|
||||||
switch_inline_query_current_chat
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
use crate::core::types::{InlineKeyboardMarkup, InlineKeyboardButton};
|
|
||||||
|
|
||||||
pub struct InlineKeyboardMarkupBuilder {
|
|
||||||
keyboard: InlineKeyboardMarkup,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builder for [`InlineKeyboardMarkup`]
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```edition2018
|
|
||||||
/// use async_telegram_bot::keyboards;
|
|
||||||
///
|
|
||||||
/// fn main() {
|
|
||||||
/// let url_button = keyboards::InlineKeyboardButtonBuilder::url(
|
|
||||||
/// "text".to_string(),
|
|
||||||
/// "http://url.com".to_string()
|
|
||||||
/// );
|
|
||||||
/// let keyboard = keyboards::InlineKeyboardMarkupBuilder::new()
|
|
||||||
/// .row(vec![url_button])
|
|
||||||
/// .build();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
impl InlineKeyboardMarkupBuilder {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
keyboard: InlineKeyboardMarkup {
|
|
||||||
inline_keyboard: vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn row(mut self, buttons: Vec<InlineKeyboardButton>) -> Self {
|
|
||||||
self.keyboard.inline_keyboard.push(buttons);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize)
|
|
||||||
-> Self {
|
|
||||||
match self.keyboard.inline_keyboard.get_mut(index) {
|
|
||||||
Some(buttons) => buttons.push(button),
|
|
||||||
None => self.keyboard.inline_keyboard.push(vec![button])
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> InlineKeyboardMarkup {
|
|
||||||
self.keyboard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::keyboards::InlineKeyboardButtonBuilder;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_row() {
|
|
||||||
let btn = InlineKeyboardButtonBuilder::url(
|
|
||||||
"text".to_string(),
|
|
||||||
"http://url".to_string(),
|
|
||||||
);
|
|
||||||
let kb = InlineKeyboardMarkupBuilder::new()
|
|
||||||
.row(vec![btn.clone()])
|
|
||||||
.build();
|
|
||||||
let expected = InlineKeyboardMarkup {
|
|
||||||
inline_keyboard: vec![vec![btn.clone()]],
|
|
||||||
};
|
|
||||||
assert_eq!(kb, expected);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
mod inline_keyboard_button;
|
|
||||||
mod inline_keyboard_markup;
|
|
||||||
|
|
||||||
pub use self::{
|
|
||||||
inline_keyboard_button::InlineKeyboardButtonBuilder,
|
|
||||||
inline_keyboard_markup::InlineKeyboardMarkupBuilder,
|
|
||||||
};
|
|
|
@ -5,5 +5,4 @@ extern crate serde;
|
||||||
|
|
||||||
pub mod bot;
|
pub mod bot;
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod keyboards;
|
|
||||||
pub mod dispatcher;
|
pub mod dispatcher;
|
||||||
|
|
Loading…
Reference in a new issue