mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 09:49:07 +01:00
Merge branch 'dev' into dispatcher
This commit is contained in:
commit
78988205e9
120 changed files with 1818 additions and 783 deletions
|
@ -15,4 +15,4 @@ derive_more = "0.15.0"
|
||||||
tokio = "0.2.0-alpha.4"
|
tokio = "0.2.0-alpha.4"
|
||||||
bytes = "0.4.12"
|
bytes = "0.4.12"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
futures-util-preview = "0.3.0-alpha.18"
|
futures-preview = "0.3.0-alpha.18"
|
||||||
|
|
116
src/bot/api.rs
Normal file
116
src/bot/api.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use crate::{
|
||||||
|
bot::Bot,
|
||||||
|
requests::{
|
||||||
|
edit_message_live_location::EditMessageLiveLocation,
|
||||||
|
forward_message::ForwardMessage, get_file::GetFile, get_me::GetMe,
|
||||||
|
send_audio::SendAudio, send_location::SendLocation,
|
||||||
|
send_media_group::SendMediaGroup, send_message::SendMessage,
|
||||||
|
send_photo::SendPhoto,
|
||||||
|
stop_message_live_location::StopMessageLiveLocation, ChatId,
|
||||||
|
},
|
||||||
|
types::{InputFile, InputMedia},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Telegram functions
|
||||||
|
impl Bot {
|
||||||
|
pub fn get_me(&self) -> GetMe {
|
||||||
|
GetMe::new(self.ctx())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage
|
||||||
|
where
|
||||||
|
C: Into<ChatId>,
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
SendMessage::new(self.ctx(), chat_id.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<i32>,
|
||||||
|
{
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_file<F>(&self, file_id: F) -> GetFile
|
||||||
|
where
|
||||||
|
F: Into<String>,
|
||||||
|
{
|
||||||
|
GetFile::new(self.ctx(), file_id.into())
|
||||||
|
}
|
||||||
|
}
|
65
src/bot/download.rs
Normal file
65
src/bot/download.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use reqwest::r#async::Chunk;
|
||||||
|
use tokio::{io::AsyncWrite, stream::Stream};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bot::Bot,
|
||||||
|
network::{download_file, download_file_stream},
|
||||||
|
DownloadError,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Bot {
|
||||||
|
/// Download file from telegram into `destination`.
|
||||||
|
/// `path` can be obtained from [`get_file`] method.
|
||||||
|
///
|
||||||
|
/// For downloading as Stream of Chunks see [`download_file_stream`].
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use async_telegram_bot::{
|
||||||
|
/// bot::Bot, requests::Request, types::File as TgFile,
|
||||||
|
/// };
|
||||||
|
/// use tokio::fs::File;
|
||||||
|
/// # use async_telegram_bot::RequestError;
|
||||||
|
///
|
||||||
|
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let bot = Bot::new("TOKEN");
|
||||||
|
/// let mut file = File::create("/home/waffle/Pictures/test.png").await?;
|
||||||
|
///
|
||||||
|
/// let TgFile { file_path, .. } = bot.get_file("*file_id*").send().await?;
|
||||||
|
/// bot.download_file(&file_path, &mut file).await?;
|
||||||
|
/// # Ok(()) }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`get_file`]: crate::bot::Bot::get_file
|
||||||
|
/// [`download_file_stream`]: crate::bot::Bot::download_file_stream
|
||||||
|
pub async fn download_file<D>(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
destination: &mut D,
|
||||||
|
) -> Result<(), DownloadError>
|
||||||
|
where
|
||||||
|
D: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
download_file(&self.client, &self.token, path, destination).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Download file from telegram.
|
||||||
|
///
|
||||||
|
/// `path` can be obtained from [`get_file`] method.
|
||||||
|
///
|
||||||
|
/// For downloading into [`AsyncWrite`] (e.g. [`tokio::fs::File`])
|
||||||
|
/// see [`download_file`].
|
||||||
|
///
|
||||||
|
/// [`get_file`]: crate::bot::Bot::get_file
|
||||||
|
/// [`AsyncWrite`]: tokio::io::AsyncWrite
|
||||||
|
/// [`tokio::fs::File`]: tokio::fs::File
|
||||||
|
/// [`download_file`]: crate::bot::Bot::download_file
|
||||||
|
pub async fn download_file_stream(
|
||||||
|
&self,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<impl Stream<Item = Result<Chunk, reqwest::Error>>, reqwest::Error>
|
||||||
|
{
|
||||||
|
download_file_stream(&self.client, &self.token, path).await
|
||||||
|
}
|
||||||
|
}
|
142
src/bot/mod.rs
142
src/bot/mod.rs
|
@ -1,31 +1,16 @@
|
||||||
|
mod api;
|
||||||
|
mod download;
|
||||||
|
|
||||||
use reqwest::r#async::Client;
|
use reqwest::r#async::Client;
|
||||||
|
|
||||||
use crate::core::{
|
use crate::requests::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 {
|
||||||
token: String,
|
token: String,
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructors
|
||||||
impl Bot {
|
impl Bot {
|
||||||
pub fn new(token: &str) -> Self {
|
pub fn new(token: &str) -> Self {
|
||||||
Bot {
|
Bot {
|
||||||
|
@ -40,7 +25,9 @@ impl Bot {
|
||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bot {
|
||||||
fn ctx(&self) -> RequestContext {
|
fn ctx(&self) -> RequestContext {
|
||||||
RequestContext {
|
RequestContext {
|
||||||
token: &self.token,
|
token: &self.token,
|
||||||
|
@ -48,118 +35,3 @@ impl Bot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Telegram functions
|
|
||||||
impl Bot {
|
|
||||||
pub fn get_me(&self) -> GetMe {
|
|
||||||
GetMe::new(self.ctx())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_message<C, T>(&self, chat_id: C, text: T) -> SendMessage
|
|
||||||
where
|
|
||||||
C: Into<ChatId>,
|
|
||||||
T: Into<String>,
|
|
||||||
{
|
|
||||||
SendMessage::new(
|
|
||||||
self.ctx(),
|
|
||||||
chat_id.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()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
mod network;
|
|
||||||
pub mod requests;
|
|
||||||
pub mod types;
|
|
|
@ -1,158 +0,0 @@
|
||||||
use crate::core::{
|
|
||||||
requests::{RequestError, ResponseResult},
|
|
||||||
types::ResponseParameters,
|
|
||||||
};
|
|
||||||
|
|
||||||
use apply::Apply;
|
|
||||||
use reqwest::{
|
|
||||||
r#async::{multipart::Form, Client},
|
|
||||||
StatusCode,
|
|
||||||
};
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
|
||||||
|
|
||||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
|
||||||
|
|
||||||
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
|
|
||||||
///
|
|
||||||
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
|
|
||||||
fn method_url(base: &str, token: &str, method_name: &str) -> String {
|
|
||||||
format!(
|
|
||||||
"{url}/bot{token}/{method}",
|
|
||||||
url = base,
|
|
||||||
token = token,
|
|
||||||
method = method_name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates URL for downloading a file. See the [Telegram documentation].
|
|
||||||
///
|
|
||||||
/// [Telegram documentation] (https://core.telegram.org/bots/api#file)
|
|
||||||
fn file_url(base: &str, token: &str, file_path: &str) -> String {
|
|
||||||
format!(
|
|
||||||
"{url}/file/bot{token}/{file}",
|
|
||||||
url = base,
|
|
||||||
token = token,
|
|
||||||
file = file_path,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn request_multipart<T: DeserializeOwned>(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
method_name: &str,
|
|
||||||
params: Option<Form>,
|
|
||||||
) -> ResponseResult<T> {
|
|
||||||
let mut response = client
|
|
||||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
|
||||||
.apply(|request_builder| match params {
|
|
||||||
Some(params) => request_builder.multipart(params),
|
|
||||||
None => request_builder,
|
|
||||||
})
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?;
|
|
||||||
|
|
||||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
|
||||||
&response.text().await.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
|
||||||
.map_err(RequestError::InvalidJson)?;
|
|
||||||
|
|
||||||
match response {
|
|
||||||
TelegramResponse::Ok { result, .. } => Ok(result),
|
|
||||||
TelegramResponse::Err {
|
|
||||||
description,
|
|
||||||
error_code,
|
|
||||||
response_parameters: _,
|
|
||||||
..
|
|
||||||
} => Err(RequestError::ApiError {
|
|
||||||
description,
|
|
||||||
status_code: StatusCode::from_u16(error_code).unwrap(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
|
||||||
client: &Client,
|
|
||||||
token: &str,
|
|
||||||
method_name: &str,
|
|
||||||
params: &P,
|
|
||||||
) -> ResponseResult<T> {
|
|
||||||
let mut response = client
|
|
||||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
|
||||||
.json(params)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map_err(RequestError::NetworkError)?;
|
|
||||||
|
|
||||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
|
||||||
&response.text().await.map_err(RequestError::NetworkError)?,
|
|
||||||
)
|
|
||||||
.map_err(RequestError::InvalidJson)?;
|
|
||||||
|
|
||||||
match response {
|
|
||||||
TelegramResponse::Ok { result, .. } => Ok(result),
|
|
||||||
TelegramResponse::Err {
|
|
||||||
description,
|
|
||||||
error_code,
|
|
||||||
response_parameters: _,
|
|
||||||
..
|
|
||||||
} => Err(RequestError::ApiError {
|
|
||||||
description,
|
|
||||||
status_code: StatusCode::from_u16(error_code).unwrap(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum TelegramResponse<R> {
|
|
||||||
Ok {
|
|
||||||
/// Dummy field. Used for deserialization.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
ok: bool, // TODO: True type
|
|
||||||
|
|
||||||
result: R,
|
|
||||||
},
|
|
||||||
Err {
|
|
||||||
/// Dummy field. Used for deserialization.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
ok: bool, // TODO: False type
|
|
||||||
|
|
||||||
description: String,
|
|
||||||
error_code: u16,
|
|
||||||
response_parameters: Option<ResponseParameters>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn method_url_test() {
|
|
||||||
let url = method_url(
|
|
||||||
TELEGRAM_API_URL,
|
|
||||||
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
|
|
||||||
"methodName",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
url,
|
|
||||||
"https://api.telegram.org/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/methodName"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn file_url_test() {
|
|
||||||
let url = file_url(
|
|
||||||
TELEGRAM_API_URL,
|
|
||||||
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
|
|
||||||
"AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
url,
|
|
||||||
"https://api.telegram.org/file/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
use crate::core::requests::RequestContext;
|
|
||||||
//TODO:: need implementation
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
struct GetFile<'a> {
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
ctx: RequestContext<'a>,
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
use crate::core::requests::RequestContext;
|
|
||||||
//TODO:: need implementation
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
struct GetUserProfilePhotos<'a> {
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
ctx: RequestContext<'a>,
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
use crate::core::requests::RequestContext;
|
|
||||||
//TODO:: need implementation
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
struct KickChatMember<'a> {
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
ctx: RequestContext<'a>,
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
use crate::core::types::PhotoSize;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct Audio {
|
|
||||||
pub file_id: String,
|
|
||||||
pub duration: u32,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub performer: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub title: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub mime_type: Option<String>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub file_size: Option<u32>,
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub thumb: Option<PhotoSize>,
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
use crate::core::types::{User, Message};
|
|
||||||
|
|
||||||
/// This object represents an incoming callback query from a callback button in an inline keyboard.
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
|
||||||
pub struct CallbackQuery {
|
|
||||||
/// Unique identifier for this query
|
|
||||||
pub id: String,
|
|
||||||
/// Sender
|
|
||||||
pub from: User,
|
|
||||||
/// Message with the callback button that originated the query.
|
|
||||||
/// Note that message content and message date will not be available if the message is too old
|
|
||||||
pub message: Message,
|
|
||||||
/// Global identifier, uniquely corresponding to the chat to which the message
|
|
||||||
/// with the callback button was sent. Useful for high scores in games.
|
|
||||||
pub chat_instance: String,
|
|
||||||
/// Data associated with the callback button. Be aware that a bad client can
|
|
||||||
/// send arbitrary data in this field.
|
|
||||||
pub data: String,
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
use self::not_implemented_types::*;
|
|
||||||
pub use self::{
|
|
||||||
animation::Animation,
|
|
||||||
audio::Audio,
|
|
||||||
callback_query::CallbackQuery,
|
|
||||||
chat::{Chat, ChatKind, NonPrivateChatKind},
|
|
||||||
chat_member::ChatMember,
|
|
||||||
chat_permissions::ChatPermissions,
|
|
||||||
chat_photo::ChatPhoto,
|
|
||||||
chosen_inline_result::ChosenInlineResult,
|
|
||||||
contact::Contact,
|
|
||||||
document::Document,
|
|
||||||
force_reply::ForceReply,
|
|
||||||
game::Game,
|
|
||||||
inline_keyboard_button::{InlineKeyboardButton, InlineKeyboardButtonKind},
|
|
||||||
inline_keyboard_markup::InlineKeyboardMarkup,
|
|
||||||
input_file::InputFile,
|
|
||||||
input_media::InputMedia,
|
|
||||||
invoice::Invoice,
|
|
||||||
keyboard_button::KeyboardButton,
|
|
||||||
label_price::LabeledPrice,
|
|
||||||
location::Location,
|
|
||||||
mask_position::MaskPosition,
|
|
||||||
message::{
|
|
||||||
ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind, Sender,
|
|
||||||
},
|
|
||||||
message_entity::MessageEntity,
|
|
||||||
order_info::OrderInfo,
|
|
||||||
parse_mode::ParseMode,
|
|
||||||
photo_size::PhotoSize,
|
|
||||||
poll::{Poll, PollOption},
|
|
||||||
pre_checkout_query::PreCheckoutQuery,
|
|
||||||
reply_keyboard_markup::ReplyKeyboardMarkup,
|
|
||||||
reply_keyboard_remove::ReplyKeyboardRemove,
|
|
||||||
reply_markup::ReplyMarkup,
|
|
||||||
response_parameters::ResponseParameters,
|
|
||||||
send_invoice::SendInvoice,
|
|
||||||
shipping_address::ShippingAddress,
|
|
||||||
shipping_option::ShippingOption,
|
|
||||||
shipping_query::ShippingQuery,
|
|
||||||
sticker::Sticker,
|
|
||||||
successful_payment::SuccessfulPayment,
|
|
||||||
update::{Update, UpdateKind},
|
|
||||||
user::User,
|
|
||||||
venue::Venue,
|
|
||||||
video::Video,
|
|
||||||
video_note::VideoNote,
|
|
||||||
voice::Voice,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod animation;
|
|
||||||
mod audio;
|
|
||||||
mod callback_query;
|
|
||||||
mod chat;
|
|
||||||
mod chat_member;
|
|
||||||
mod chat_permissions;
|
|
||||||
mod chat_photo;
|
|
||||||
mod chosen_inline_result;
|
|
||||||
mod contact;
|
|
||||||
mod document;
|
|
||||||
mod force_reply;
|
|
||||||
mod game;
|
|
||||||
mod inline_keyboard_button;
|
|
||||||
mod inline_keyboard_markup;
|
|
||||||
mod input_file;
|
|
||||||
mod input_media;
|
|
||||||
mod invoice;
|
|
||||||
mod keyboard_button;
|
|
||||||
mod label_price;
|
|
||||||
mod location;
|
|
||||||
mod mask_position;
|
|
||||||
mod message;
|
|
||||||
mod message_entity;
|
|
||||||
mod not_implemented_types;
|
|
||||||
mod order_info;
|
|
||||||
mod parse_mode;
|
|
||||||
mod photo_size;
|
|
||||||
mod poll;
|
|
||||||
mod pre_checkout_query;
|
|
||||||
mod reply_keyboard_markup;
|
|
||||||
mod reply_keyboard_remove;
|
|
||||||
mod reply_markup;
|
|
||||||
mod response_parameters;
|
|
||||||
mod send_invoice;
|
|
||||||
mod shipping_address;
|
|
||||||
mod shipping_option;
|
|
||||||
mod shipping_query;
|
|
||||||
mod sticker;
|
|
||||||
mod successful_payment;
|
|
||||||
mod update;
|
|
||||||
mod user;
|
|
||||||
mod venue;
|
|
||||||
mod video;
|
|
||||||
mod video_note;
|
|
||||||
mod voice;
|
|
|
@ -1,5 +0,0 @@
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct PassportData;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
|
||||||
pub struct ChatMemberStatus;
|
|
|
@ -1,9 +0,0 @@
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone, Serialize)]
|
|
||||||
pub struct User {
|
|
||||||
pub id: i64,
|
|
||||||
pub is_bot: bool,
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: Option<String>,
|
|
||||||
pub username: Option<String>,
|
|
||||||
pub language_code: Option<String>,
|
|
||||||
}
|
|
60
src/errors.rs
Normal file
60
src/errors.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
//<editor-fold desc="download">
|
||||||
|
#[derive(Debug, Display, From)]
|
||||||
|
pub enum DownloadError {
|
||||||
|
#[display(fmt = "Network error: {err}", err = _0)]
|
||||||
|
NetworkError(reqwest::Error),
|
||||||
|
|
||||||
|
#[display(fmt = "IO Error: {err}", err = _0)]
|
||||||
|
Io(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for DownloadError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
DownloadError::NetworkError(err) => Some(err),
|
||||||
|
DownloadError::Io(err) => Some(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//</editor-fold>
|
||||||
|
|
||||||
|
//<editor-fold desc="request">
|
||||||
|
#[derive(Debug, Display)]
|
||||||
|
pub enum RequestError {
|
||||||
|
#[display(fmt = "Telegram error #{}: {}", status_code, description)]
|
||||||
|
ApiError {
|
||||||
|
status_code: StatusCode,
|
||||||
|
description: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The group has been migrated to a supergroup with the specified
|
||||||
|
/// identifier.
|
||||||
|
#[display(fmt = "The group has been migrated to a supergroup with id {id}", id = _0)]
|
||||||
|
MigrateToChatId(i64),
|
||||||
|
|
||||||
|
/// In case of exceeding flood control, the number of seconds left to wait
|
||||||
|
/// before the request can be repeated
|
||||||
|
#[display(fmt = "Retry after {secs} seconds", secs = _0)]
|
||||||
|
RetryAfter(i32),
|
||||||
|
|
||||||
|
#[display(fmt = "Network error: {err}", err = _0)]
|
||||||
|
NetworkError(reqwest::Error),
|
||||||
|
|
||||||
|
#[display(fmt = "InvalidJson error caused by: {err}", err = _0)]
|
||||||
|
InvalidJson(serde_json::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for RequestError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
RequestError::ApiError { .. } => None,
|
||||||
|
RequestError::MigrateToChatId(_) => None,
|
||||||
|
RequestError::RetryAfter(_) => None,
|
||||||
|
RequestError::NetworkError(err) => Some(err),
|
||||||
|
RequestError::InvalidJson(err) => Some(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//</editor-fold>
|
|
@ -3,6 +3,12 @@ extern crate derive_more;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
mod network;
|
||||||
|
|
||||||
pub mod bot;
|
pub mod bot;
|
||||||
pub mod core;
|
|
||||||
pub mod dispatcher;
|
pub mod dispatcher;
|
||||||
|
pub mod errors;
|
||||||
|
pub mod requests;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
pub use errors::{DownloadError, RequestError};
|
||||||
|
|
40
src/network/download.rs
Normal file
40
src/network/download.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use futures::StreamExt;
|
||||||
|
use reqwest::r#async::{Chunk, Client};
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncWrite, AsyncWriteExt},
|
||||||
|
stream::Stream,
|
||||||
|
};
|
||||||
|
use bytes::Buf;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
network::{file_url, TELEGRAM_API_URL},
|
||||||
|
DownloadError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn download_file<D>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
path: &str,
|
||||||
|
destination: &mut D,
|
||||||
|
) -> Result<(), DownloadError>
|
||||||
|
where
|
||||||
|
D: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
let mut stream = download_file_stream(client, token, path).await?;
|
||||||
|
|
||||||
|
while let Some(chunk) = stream.next().await {
|
||||||
|
destination.write_all(chunk?.bytes()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn download_file_stream(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<impl Stream<Item = Result<Chunk, reqwest::Error>>, reqwest::Error> {
|
||||||
|
let url = file_url(TELEGRAM_API_URL, token, path);
|
||||||
|
let resp = client.get(&url).send().await?.error_for_status()?;
|
||||||
|
Ok(resp.into_body())
|
||||||
|
}
|
66
src/network/mod.rs
Normal file
66
src/network/mod.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
mod download;
|
||||||
|
mod request;
|
||||||
|
mod telegram_response;
|
||||||
|
|
||||||
|
pub use download::{download_file, download_file_stream};
|
||||||
|
pub use request::{request_json, request_multipart};
|
||||||
|
pub use telegram_response::TelegramResponse;
|
||||||
|
|
||||||
|
pub const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||||
|
|
||||||
|
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
|
||||||
|
///
|
||||||
|
/// [Telegram documentation]: https://core.telegram.org/bots/api#making-requests
|
||||||
|
fn method_url(base: &str, token: &str, method_name: &str) -> String {
|
||||||
|
format!(
|
||||||
|
"{url}/bot{token}/{method}",
|
||||||
|
url = base,
|
||||||
|
token = token,
|
||||||
|
method = method_name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates URL for downloading a file. See the [Telegram documentation].
|
||||||
|
///
|
||||||
|
/// [Telegram documentation] (https://core.telegram.org/bots/api#file)
|
||||||
|
fn file_url(base: &str, token: &str, file_path: &str) -> String {
|
||||||
|
format!(
|
||||||
|
"{url}/file/bot{token}/{file}",
|
||||||
|
url = base,
|
||||||
|
token = token,
|
||||||
|
file = file_path,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn method_url_test() {
|
||||||
|
let url = method_url(
|
||||||
|
TELEGRAM_API_URL,
|
||||||
|
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
|
||||||
|
"methodName",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
url,
|
||||||
|
"https://api.telegram.org/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/methodName"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn file_url_test() {
|
||||||
|
let url = file_url(
|
||||||
|
TELEGRAM_API_URL,
|
||||||
|
"535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao",
|
||||||
|
"AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
url,
|
||||||
|
"https://api.telegram.org/file/bot535362388:AAF7-g0gYncWnm5IyfZlpPRqRRv6kNAGlao/AgADAgADyqoxG2g8aEsu_KjjVsGF4-zetw8ABAEAAwIAA20AA_8QAwABFgQ"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
60
src/network/request.rs
Normal file
60
src/network/request.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use apply::Apply;
|
||||||
|
use reqwest::r#async::{multipart::Form, Client, Response};
|
||||||
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
network::{method_url, TELEGRAM_API_URL, TelegramResponse},
|
||||||
|
requests::ResponseResult,
|
||||||
|
RequestError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn request_multipart<T>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
method_name: &str,
|
||||||
|
params: Option<Form>,
|
||||||
|
) -> ResponseResult<T>
|
||||||
|
where
|
||||||
|
T: DeserializeOwned,
|
||||||
|
{
|
||||||
|
process_response(
|
||||||
|
client
|
||||||
|
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||||
|
.apply(|request_builder| match params {
|
||||||
|
Some(params) => request_builder.multipart(params),
|
||||||
|
None => request_builder,
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
||||||
|
client: &Client,
|
||||||
|
token: &str,
|
||||||
|
method_name: &str,
|
||||||
|
params: &P,
|
||||||
|
) -> ResponseResult<T> {
|
||||||
|
process_response(
|
||||||
|
client
|
||||||
|
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||||
|
.json(params)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_response<T: DeserializeOwned>(
|
||||||
|
mut response: Response,
|
||||||
|
) -> ResponseResult<T> {
|
||||||
|
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||||
|
&response.text().await.map_err(RequestError::NetworkError)?,
|
||||||
|
)
|
||||||
|
.map_err(RequestError::InvalidJson)?;
|
||||||
|
|
||||||
|
response.into()
|
||||||
|
}
|
56
src/network/telegram_response.rs
Normal file
56
src/network/telegram_response.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
requests::ResponseResult, types::ResponseParameters, RequestError,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum TelegramResponse<R> {
|
||||||
|
Ok {
|
||||||
|
/// A dummy field. Used only for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: True type
|
||||||
|
|
||||||
|
result: R,
|
||||||
|
},
|
||||||
|
Err {
|
||||||
|
/// A dummy field. Used only for deserialization.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ok: bool, // TODO: False type
|
||||||
|
|
||||||
|
description: String,
|
||||||
|
error_code: u16,
|
||||||
|
response_parameters: Option<ResponseParameters>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> Into<ResponseResult<R>> for TelegramResponse<R> {
|
||||||
|
fn into(self) -> Result<R, RequestError> {
|
||||||
|
match self {
|
||||||
|
TelegramResponse::Ok { result, .. } => Ok(result),
|
||||||
|
TelegramResponse::Err {
|
||||||
|
description,
|
||||||
|
error_code,
|
||||||
|
response_parameters,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
if let Some(params) = response_parameters {
|
||||||
|
match params {
|
||||||
|
ResponseParameters::RetryAfter(i) => {
|
||||||
|
Err(RequestError::RetryAfter(i))
|
||||||
|
}
|
||||||
|
ResponseParameters::MigrateToChatId(to) => {
|
||||||
|
Err(RequestError::MigrateToChatId(to))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(RequestError::ApiError {
|
||||||
|
description,
|
||||||
|
status_code: StatusCode::from_u16(error_code).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
requests::{RequestContext, Request, RequestFuture, ResponseResult},
|
network,
|
||||||
network
|
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone)]
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
@ -40,8 +40,9 @@ impl<'a> Request<'a> for AnswerPreCheckoutQuery<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"answerPreCheckoutQuery",
|
"answerPreCheckoutQuery",
|
||||||
&self
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,32 +51,35 @@ impl<'a> AnswerPreCheckoutQuery<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
pre_checkout_query_id: String,
|
pre_checkout_query_id: String,
|
||||||
ok: bool
|
ok: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
pre_checkout_query_id,
|
pre_checkout_query_id,
|
||||||
ok,
|
ok,
|
||||||
error_message: None
|
error_message: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pre_checkout_query_id<T>(mut self, pre_checkout_query_id: T) -> Self
|
pub fn pre_checkout_query_id<T>(mut self, pre_checkout_query_id: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.pre_checkout_query_id = pre_checkout_query_id.into();
|
self.pre_checkout_query_id = pre_checkout_query_id.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ok<T>(mut self, ok: T) -> Self
|
pub fn ok<T>(mut self, ok: T) -> Self
|
||||||
where T: Into<bool>
|
where
|
||||||
|
T: Into<bool>,
|
||||||
{
|
{
|
||||||
self.ok = ok.into();
|
self.ok = ok.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error_message<T>(mut self, error_message: T) -> Self
|
pub fn error_message<T>(mut self, error_message: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.error_message = Some(error_message.into());
|
self.error_message = Some(error_message.into());
|
||||||
self
|
self
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::core::types::ShippingOption;
|
use crate::{
|
||||||
use crate::core::requests::{RequestContext, Request, RequestFuture, ResponseResult};
|
network,
|
||||||
use crate::core::network;
|
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
||||||
|
types::ShippingOption,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
/// If you sent an invoice requesting a shipping address and the parameter
|
/// If you sent an invoice requesting a shipping address and the parameter
|
||||||
|
@ -40,8 +42,9 @@ impl<'a> Request<'a> for AnswerShippingQuery<'a> {
|
||||||
&self.ctx.client,
|
&self.ctx.client,
|
||||||
&self.ctx.token,
|
&self.ctx.token,
|
||||||
"answerShippingQuery",
|
"answerShippingQuery",
|
||||||
&self
|
&self,
|
||||||
).await
|
)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,40 +53,44 @@ impl<'a> AnswerShippingQuery<'a> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
shipping_query_id: String,
|
shipping_query_id: String,
|
||||||
ok: bool
|
ok: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
shipping_query_id,
|
shipping_query_id,
|
||||||
ok,
|
ok,
|
||||||
shipping_options: None,
|
shipping_options: None,
|
||||||
error_message: None
|
error_message: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shipping_query_id<T>(mut self, shipping_query_id: T) -> Self
|
pub fn shipping_query_id<T>(mut self, shipping_query_id: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.shipping_query_id = shipping_query_id.into();
|
self.shipping_query_id = shipping_query_id.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ok<T>(mut self, ok: T) -> Self
|
pub fn ok<T>(mut self, ok: T) -> Self
|
||||||
where T: Into<bool>
|
where
|
||||||
|
T: Into<bool>,
|
||||||
{
|
{
|
||||||
self.ok = ok.into();
|
self.ok = ok.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shipping_options<T>(mut self, shipping_options: T) -> Self
|
pub fn shipping_options<T>(mut self, shipping_options: T) -> Self
|
||||||
where T: Into<Vec<ShippingOption>>
|
where
|
||||||
|
T: Into<Vec<ShippingOption>>,
|
||||||
{
|
{
|
||||||
self.shipping_options = Some(shipping_options.into());
|
self.shipping_options = Some(shipping_options.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error_message<T>(mut self, error_message: T) -> Self
|
pub fn error_message<T>(mut self, error_message: T) -> Self
|
||||||
where T: Into<String>
|
where
|
||||||
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
self.error_message = Some(error_message.into());
|
self.error_message = Some(error_message.into());
|
||||||
self
|
self
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::core::network;
|
use crate::{
|
||||||
use crate::core::requests::{
|
network,
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{Message, ReplyMarkup},
|
||||||
};
|
};
|
||||||
use crate::core::types::{Message, ReplyMarkup};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
/// Use this method to edit live location messages. A location can be edited
|
/// Use this method to edit live location messages. A location can be edited
|
||||||
|
@ -23,7 +24,7 @@ pub struct EditMessageLiveLocation<'a> {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Required if inline_message_id is not specified. Identifier of the
|
/// Required if inline_message_id is not specified. Identifier of the
|
||||||
/// message to edit
|
/// message to edit
|
||||||
message_id: Option<i64>,
|
message_id: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// Required if chat_id and message_id are not specified. Identifier of
|
/// Required if chat_id and message_id are not specified. Identifier of
|
||||||
/// the inline message
|
/// the inline message
|
||||||
|
@ -75,7 +76,7 @@ impl<'a> EditMessageLiveLocation<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_id<T: Into<i64>>(mut self, message_id: T) -> Self {
|
pub fn message_id<T: Into<i32>>(mut self, message_id: T) -> Self {
|
||||||
self.message_id = Some(message_id.into());
|
self.message_id = Some(message_id.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::core::{
|
use reqwest::r#async::multipart::Form;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
requests::{utils, ChatId},
|
requests::{utils, ChatId},
|
||||||
types::{InputMedia, ParseMode},
|
types::{InputMedia, ParseMode},
|
||||||
};
|
};
|
||||||
|
|
||||||
use reqwest::r#async::multipart::Form;
|
|
||||||
|
|
||||||
/// 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.
|
||||||
pub struct FormBuilder {
|
pub struct FormBuilder {
|
|
@ -1,11 +1,7 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
Request,
|
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
},
|
},
|
||||||
types::Message,
|
types::Message,
|
||||||
};
|
};
|
||||||
|
@ -24,7 +20,7 @@ pub struct ForwardMessage<'a> {
|
||||||
/// (in the format @channelusername)
|
/// (in the format @channelusername)
|
||||||
pub from_chat_id: ChatId,
|
pub from_chat_id: ChatId,
|
||||||
/// Message identifier in the chat specified in from_chat_id
|
/// Message identifier in the chat specified in from_chat_id
|
||||||
pub message_id: i64,
|
pub message_id: i32,
|
||||||
|
|
||||||
/// Sends the message silently. Users will receive a notification with no
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
/// sound.
|
/// sound.
|
||||||
|
@ -53,7 +49,7 @@ impl<'a> ForwardMessage<'a> {
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
from_chat_id: ChatId,
|
from_chat_id: ChatId,
|
||||||
message_id: i64,
|
message_id: i32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -74,7 +70,7 @@ impl<'a> ForwardMessage<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message_id<T: Into<i64>>(mut self, val: T) -> Self {
|
pub fn message_id<T: Into<i32>>(mut self, val: T) -> Self {
|
||||||
self.message_id = val.into();
|
self.message_id = val.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
46
src/requests/get_chat.rs
Normal file
46
src/requests/get_chat.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
use crate::{
|
||||||
|
network,
|
||||||
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::Chat,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Use this method to get up to date information about the chat
|
||||||
|
/// (current name of the user for one-on-one conversations,
|
||||||
|
/// current username of a user, group or channel, etc.).
|
||||||
|
/// Returns a Chat object on success.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct GetChat<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
/// Unique identifier for the target chat or username
|
||||||
|
/// of the target supergroup or channel (in the format @channelusername)
|
||||||
|
chat_id: ChatId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for GetChat<'a> {
|
||||||
|
type ReturnValue = Chat;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"getChat",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GetChat<'a> {
|
||||||
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<ChatId>,
|
||||||
|
{
|
||||||
|
self.chat_id = chat_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
50
src/requests/get_file.rs
Normal file
50
src/requests/get_file.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use crate::{
|
||||||
|
network,
|
||||||
|
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
||||||
|
types::File,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Use this method to get basic info about a file and prepare it for
|
||||||
|
/// downloading. For the moment, bots can download files of up to 20MB in size.
|
||||||
|
/// On success, a File object is returned.
|
||||||
|
/// The file can then be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>,
|
||||||
|
/// where <file_path> is taken from the response.
|
||||||
|
/// It is guaranteed that the link will be valid for at least 1 hour.
|
||||||
|
/// When the link expires, a new one can be requested by calling getFile again.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct GetFile<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
/// File identifier to get info about
|
||||||
|
pub file_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Request<'a> for GetFile<'a> {
|
||||||
|
type ReturnValue = File;
|
||||||
|
|
||||||
|
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
network::request_json(
|
||||||
|
&self.ctx.client,
|
||||||
|
&self.ctx.token,
|
||||||
|
"getFile",
|
||||||
|
&self,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GetFile<'a> {
|
||||||
|
pub(crate) fn new(ctx: RequestContext<'a>, file_id: String) -> Self {
|
||||||
|
Self { ctx, file_id }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_id<T>(mut self, file_id: T) -> Self
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
self.file_id = file_id.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
requests::{Request, RequestContext, RequestFuture, ResponseResult},
|
||||||
types::User,
|
types::User,
|
19
src/requests/get_user_profile_photos.rs
Normal file
19
src/requests/get_user_profile_photos.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::requests::RequestContext;
|
||||||
|
|
||||||
|
//TODO: complete implementation after user_profile_fotos will be added to
|
||||||
|
// types/mod.rs
|
||||||
|
///Use this method to get a list of profile pictures for a user. Returns a
|
||||||
|
/// UserProfilePhotos object.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct GetUserProfilePhotos<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
/// Unique identifier of the target user
|
||||||
|
user_id: i32,
|
||||||
|
/// Sequential number of the first photo to be returned. By default, all
|
||||||
|
/// photos are returned.
|
||||||
|
offset: Option<i64>,
|
||||||
|
///Limits the number of photos to be retrieved. Values between 1—100 are
|
||||||
|
/// accepted. Defaults to 100.
|
||||||
|
limit: Option<i64>,
|
||||||
|
}
|
12
src/requests/kick_chat_member.rs
Normal file
12
src/requests/kick_chat_member.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use crate::requests::RequestContext;
|
||||||
|
//TODO:: need implementation
|
||||||
|
/// Use this method to kick a user from a group, a supergroup or a channel. In
|
||||||
|
/// the case of supergroups and channels, the user will not be able to return to
|
||||||
|
/// the group on their own using invite links, etc., unless unbanned first. The
|
||||||
|
/// bot must be an administrator in the chat for this to work and must have the
|
||||||
|
/// appropriate admin rights. Returns True on success.
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
struct KickChatMember<'a> {
|
||||||
|
#[serde(skip_serializing)]
|
||||||
|
ctx: RequestContext<'a>,
|
||||||
|
}
|
|
@ -1,37 +1,11 @@
|
||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use reqwest::{r#async::Client, StatusCode};
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
|
|
||||||
mod form_builder;
|
mod form_builder;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[derive(Debug, Display)]
|
use reqwest::r#async::Client;
|
||||||
pub enum RequestError {
|
use serde::de::DeserializeOwned;
|
||||||
#[display(fmt = "Telegram error #{}: {}", status_code, description)]
|
use std::{future::Future, pin::Pin};
|
||||||
ApiError {
|
|
||||||
// TODO: add response parameters
|
|
||||||
status_code: StatusCode,
|
|
||||||
description: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[display(fmt = "Network error: {err}", err = _0)]
|
use crate::RequestError;
|
||||||
NetworkError(reqwest::Error),
|
|
||||||
|
|
||||||
#[display(fmt = "InvalidJson error caused by: {err}", err = _0)]
|
|
||||||
InvalidJson(serde_json::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for RequestError {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
match self {
|
|
||||||
RequestError::ApiError { .. } => None,
|
|
||||||
RequestError::NetworkError(err) => Some(err),
|
|
||||||
RequestError::InvalidJson(err) => Some(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ResponseResult<T> = Result<T, RequestError>;
|
pub type ResponseResult<T> = Result<T, RequestError>;
|
||||||
|
|
||||||
|
@ -93,6 +67,7 @@ pub mod answer_pre_checkout_query;
|
||||||
pub mod answer_shipping_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_chat;
|
||||||
pub mod get_file;
|
pub mod get_file;
|
||||||
pub mod get_me;
|
pub mod get_me;
|
||||||
pub mod get_user_profile_photos;
|
pub mod get_user_profile_photos;
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::requests::RequestContext;
|
use crate::requests::RequestContext;
|
||||||
//TODO:: need implementation
|
//TODO:: need implementation
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::form_builder::FormBuilder,
|
|
||||||
requests::{
|
requests::{
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
|
RequestFuture, ResponseResult,
|
||||||
},
|
},
|
||||||
types::{InputFile, Message, ParseMode, ReplyMarkup},
|
types::{InputFile, Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
@ -32,10 +32,10 @@ pub struct SendAudio<'a> {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
pub parse_mode: Option<ParseMode>,
|
pub parse_mode: Option<ParseMode>,
|
||||||
/// Duration of the audio in seconds
|
/// Duration of the audio in seconds
|
||||||
pub duration: Option<i32>,
|
pub duration: Option<i32>,
|
||||||
|
@ -55,7 +55,7 @@ pub struct SendAudio<'a> {
|
||||||
/// sound.
|
/// sound.
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i32>,
|
||||||
pub reply_markup: Option<ReplyMarkup>,
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ impl<'a> SendAudio<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(
|
pub fn reply_to_message_id<T: Into<i32>>(
|
||||||
mut self,
|
mut self,
|
||||||
reply_to_message_id: T,
|
reply_to_message_id: T,
|
||||||
) -> Self {
|
) -> Self {
|
|
@ -1,12 +1,16 @@
|
||||||
use crate::core::network;
|
use crate::{
|
||||||
use crate::core::requests::{ChatId, Request, RequestContext, RequestFuture, ResponseResult};
|
network,
|
||||||
use crate::core::types::Message;
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
///Use this method when you need to tell the user that something is happening on the bot's side.
|
///Use this method when you need to tell the user that something is happening
|
||||||
///The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status).
|
/// on the bot's side. The status is set for 5 seconds or less (when a message
|
||||||
///Returns True on success.
|
/// 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> {
|
pub struct SendChatAction<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Unique identifier for the target chat or
|
/// Unique identifier for the target chat or
|
||||||
|
@ -22,7 +26,7 @@ struct SendChatAction<'a> {
|
||||||
|
|
||||||
#[derive(Debug, Serialize, From, Clone)]
|
#[derive(Debug, Serialize, From, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
enum ChatAction {
|
pub enum ChatAction {
|
||||||
Typing,
|
Typing,
|
||||||
UploadPhoto,
|
UploadPhoto,
|
||||||
RecordVideo,
|
RecordVideo,
|
||||||
|
@ -46,7 +50,7 @@ impl<'a> Request<'a> for SendChatAction<'a> {
|
||||||
"sendChatAction",
|
"sendChatAction",
|
||||||
&self,
|
&self,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,19 +69,18 @@ impl<'a> SendChatAction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<ChatId>,
|
T: Into<ChatId>,
|
||||||
{
|
{
|
||||||
self.chat_id = chat_id.into();
|
self.chat_id = chat_id.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn action<T>(mut self, action: T) -> Self
|
pub fn action<T>(mut self, action: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<ChatAction>,
|
T: Into<ChatAction>,
|
||||||
{
|
{
|
||||||
self.action = action.into();
|
self.action = action.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
use crate::core::network;
|
use crate::{
|
||||||
use crate::core::requests::{
|
network,
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{Message, ReplyMarkup},
|
||||||
};
|
};
|
||||||
use crate::core::types::{Message, ReplyMarkup};
|
|
||||||
|
|
||||||
/// Use this method to send phone contacts.
|
/// Use this method to send phone contacts.
|
||||||
/// returned.
|
/// returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendContact<'a> {
|
pub struct SendContact<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Unique identifier for the target chat or
|
/// Unique identifier for the target chat or
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
@ -33,7 +33,7 @@ pub struct SendLocation<'a> {
|
||||||
disable_notification: Option<bool>,
|
disable_notification: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
reply_to_message_id: Option<i64>,
|
reply_to_message_id: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
reply_markup: Option<ReplyMarkup>,
|
reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ impl<'a> SendLocation<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(mut self, val: T) -> Self {
|
pub fn reply_to_message_id<T: Into<i32>>(mut self, val: T) -> Self {
|
||||||
self.reply_to_message_id = Some(val.into());
|
self.reply_to_message_id = Some(val.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::core::{
|
use apply::Apply;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
network::request_multipart,
|
network::request_multipart,
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
|
@ -6,7 +8,6 @@ use crate::core::{
|
||||||
},
|
},
|
||||||
types::{InputFile, InputMedia, Message},
|
types::{InputFile, InputMedia, Message},
|
||||||
};
|
};
|
||||||
use apply::Apply;
|
|
||||||
|
|
||||||
/// Use this method to send a group of photos or videos as an album.
|
/// Use this method to send a group of photos or videos as an album.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -17,7 +18,7 @@ pub struct SendMediaGroup<'a> {
|
||||||
pub media: Vec<InputMedia>,
|
pub media: Vec<InputMedia>,
|
||||||
|
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> for SendMediaGroup<'a> {
|
impl<'a> Request<'a> for SendMediaGroup<'a> {
|
||||||
|
@ -96,7 +97,7 @@ impl<'a> SendMediaGroup<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(mut self, val: T) -> Self {
|
pub fn reply_to_message_id<T: Into<i32>>(mut self, val: T) -> Self {
|
||||||
self.reply_to_message_id = Some(val.into());
|
self.reply_to_message_id = Some(val.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
|
@ -1,11 +1,7 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
Request,
|
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
},
|
},
|
||||||
types::{Message, ParseMode, ReplyMarkup},
|
types::{Message, ParseMode, ReplyMarkup},
|
||||||
};
|
};
|
||||||
|
@ -27,10 +23,10 @@ pub struct SendMessage<'a> {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub parse_mode: Option<ParseMode>,
|
pub parse_mode: Option<ParseMode>,
|
||||||
/// Disables link previews for links in this message
|
/// Disables link previews for links in this message
|
||||||
|
@ -42,7 +38,7 @@ pub struct SendMessage<'a> {
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub reply_markup: Option<ReplyMarkup>,
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
@ -106,7 +102,7 @@ impl<'a> SendMessage<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(mut self, val: T) -> Self {
|
pub fn reply_to_message_id<T: Into<i32>>(mut self, val: T) -> Self {
|
||||||
self.reply_to_message_id = Some(val.into());
|
self.reply_to_message_id = Some(val.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
form_builder::FormBuilder, ChatId, Request, RequestContext,
|
||||||
|
@ -30,16 +30,16 @@ pub struct SendPhoto<'a> {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
pub parse_mode: Option<ParseMode>,
|
pub parse_mode: Option<ParseMode>,
|
||||||
/// Sends the message silently. Users will receive a notification with no
|
/// Sends the message silently. Users will receive a notification with no
|
||||||
/// sound.
|
/// sound.
|
||||||
pub disable_notification: Option<bool>,
|
pub disable_notification: Option<bool>,
|
||||||
/// If the message is a reply, ID of the original message
|
/// If the message is a reply, ID of the original message
|
||||||
pub reply_to_message_id: Option<i64>,
|
pub reply_to_message_id: Option<i32>,
|
||||||
pub reply_markup: Option<ReplyMarkup>,
|
pub reply_markup: Option<ReplyMarkup>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ impl<'a> SendPhoto<'a> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply_to_message_id<T: Into<i64>>(
|
pub fn reply_to_message_id<T: Into<i32>>(
|
||||||
mut self,
|
mut self,
|
||||||
reply_to_message_id: T,
|
reply_to_message_id: T,
|
||||||
) -> Self {
|
) -> Self {
|
|
@ -1,13 +1,15 @@
|
||||||
use crate::core::network;
|
use crate::{
|
||||||
use crate::core::requests::{
|
network,
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{Message, ReplyMarkup},
|
||||||
};
|
};
|
||||||
use crate::core::types::{Message, ReplyMarkup};
|
|
||||||
|
|
||||||
/// Use this method to send a native poll. A native poll can't be sent to a
|
/// 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.
|
/// private chat. On success, the sent Message is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendPoll<'a> {
|
pub 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
|
/// identifier for the target chat or username of the target channel (in
|
|
@ -1,13 +1,15 @@
|
||||||
use crate::core::network;
|
use crate::{
|
||||||
use crate::core::requests::{
|
network,
|
||||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
requests::{
|
||||||
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
|
},
|
||||||
|
types::{Message, ReplyMarkup},
|
||||||
};
|
};
|
||||||
use crate::core::types::{Message, ReplyMarkup};
|
|
||||||
|
|
||||||
/// Use this method to send information about a venue.
|
/// Use this method to send information about a venue.
|
||||||
/// Message is returned.
|
/// Message is returned.
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct SendVenue<'a> {
|
pub struct SendVenue<'a> {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
/// Unique identifier for the target chat or
|
/// Unique identifier for the target chat or
|
||||||
|
@ -62,7 +64,7 @@ impl<'a> Request<'a> for SendVenue<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SendVenue<'a> {
|
impl<'a> SendVenue<'a> {
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
ctx: RequestContext<'a>,
|
ctx: RequestContext<'a>,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
latitude: f64,
|
latitude: f64,
|
|
@ -1,11 +1,7 @@
|
||||||
use crate::core::{
|
use crate::{
|
||||||
network,
|
network,
|
||||||
requests::{
|
requests::{
|
||||||
ChatId,
|
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||||
Request,
|
|
||||||
RequestFuture,
|
|
||||||
RequestContext,
|
|
||||||
ResponseResult,
|
|
||||||
},
|
},
|
||||||
types::{InlineKeyboardMarkup, Message},
|
types::{InlineKeyboardMarkup, Message},
|
||||||
};
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::requests::RequestContext;
|
use crate::requests::RequestContext;
|
||||||
//TODO:: need implementation
|
//TODO:: need implementation
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
|
@ -2,11 +2,7 @@ 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 tokio::{
|
use tokio::{codec::FramedRead, prelude::*};
|
||||||
prelude::*,
|
|
||||||
codec::FramedRead,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FileDecoder;
|
struct FileDecoder;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::PhotoSize;
|
use crate::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
|
/// This object represents an animation file (GIF or H.264/MPEG-4 AVC video
|
||||||
|
@ -19,7 +19,7 @@ pub struct Animation {
|
||||||
/// Optional. MIME type of the file as defined by sender
|
/// Optional. MIME type of the file as defined by sender
|
||||||
pub mime_type: Option<String>,
|
pub mime_type: Option<String>,
|
||||||
/// Optional. File size
|
/// Optional. File size
|
||||||
pub file_size: Option<u32>
|
pub file_size: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -51,11 +51,11 @@ mod tests {
|
||||||
file_id: "id".to_string(),
|
file_id: "id".to_string(),
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
file_size: Some(3452)
|
file_size: Some(3452),
|
||||||
}),
|
}),
|
||||||
file_name: Some("some".to_string()),
|
file_name: Some("some".to_string()),
|
||||||
mime_type: Some("gif".to_string()),
|
mime_type: Some("gif".to_string()),
|
||||||
file_size: Some(6500)
|
file_size: Some(6500),
|
||||||
};
|
};
|
||||||
let actual = serde_json::from_str::<Animation>(json).unwrap();
|
let actual = serde_json::from_str::<Animation>(json).unwrap();
|
||||||
assert_eq!(actual, expected)
|
assert_eq!(actual, expected)
|
51
src/types/audio.rs
Normal file
51
src/types/audio.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use crate::types::PhotoSize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone)]
|
||||||
|
pub struct Audio {
|
||||||
|
pub file_id: String,
|
||||||
|
pub duration: u32,
|
||||||
|
pub performer: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub mime_type: Option<String>,
|
||||||
|
pub file_size: Option<u32>,
|
||||||
|
pub thumb: Option<PhotoSize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let json = r#"{
|
||||||
|
"file_id":"id",
|
||||||
|
"duration":60,
|
||||||
|
"performer":"Performer",
|
||||||
|
"title":"Title",
|
||||||
|
"mime_type":"MimeType",
|
||||||
|
"file_size":123456,
|
||||||
|
"thumb":{
|
||||||
|
"file_id":"id",
|
||||||
|
"width":320,
|
||||||
|
"height":320,
|
||||||
|
"file_size":3452
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
let expected = Audio {
|
||||||
|
file_id: "id".to_string(),
|
||||||
|
duration: 60,
|
||||||
|
performer: Some("Performer".to_string()),
|
||||||
|
title: Some("Title".to_string()),
|
||||||
|
mime_type: Some("MimeType".to_string()),
|
||||||
|
file_size: Some(123456),
|
||||||
|
thumb: Some(PhotoSize {
|
||||||
|
file_id: "id".to_string(),
|
||||||
|
width: 320,
|
||||||
|
height: 320,
|
||||||
|
file_size: Some(3452),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let actual = serde_json::from_str::<Audio>(&json).unwrap();
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,2 @@
|
||||||
/// A placeholder, currently holds no information. Use [BotFather](https://t.me/botfather) to set up your game.
|
/// A placeholder, currently holds no information. Use [BotFather](https://t.me/botfather) to set up your game.
|
||||||
pub struct CallbackGame;
|
pub struct CallbackGame;
|
51
src/types/callback_query.rs
Normal file
51
src/types/callback_query.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use crate::types::{Message, User};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
|
pub struct CallbackQuery {
|
||||||
|
pub id: String,
|
||||||
|
pub from: User,
|
||||||
|
pub chat_instance: String,
|
||||||
|
pub message: Option<Message>,
|
||||||
|
pub inline_message_id: Option<String>,
|
||||||
|
pub data: Option<String>,
|
||||||
|
pub game_short_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let json = r#"{
|
||||||
|
"id":"id",
|
||||||
|
"from":{
|
||||||
|
"id":12345,
|
||||||
|
"is_bot":false,
|
||||||
|
"first_name":"firstName"
|
||||||
|
},
|
||||||
|
"inline_message_id":"i_m_id",
|
||||||
|
"chat_instance":"123456",
|
||||||
|
"data":"some_data",
|
||||||
|
"game_short_name":"game_name"
|
||||||
|
}"#;
|
||||||
|
let expected = CallbackQuery {
|
||||||
|
id: "id".to_string(),
|
||||||
|
from: User {
|
||||||
|
id: 12345,
|
||||||
|
is_bot: false,
|
||||||
|
first_name: "firstName".to_string(),
|
||||||
|
last_name: None,
|
||||||
|
username: None,
|
||||||
|
language_code: None,
|
||||||
|
},
|
||||||
|
chat_instance: "123456".to_string(),
|
||||||
|
message: None,
|
||||||
|
inline_message_id: Some("i_m_id".to_string()),
|
||||||
|
data: Some("some_data".to_string()),
|
||||||
|
game_short_name: Some("game_name".to_string()),
|
||||||
|
};
|
||||||
|
let actual = serde_json::from_str::<CallbackQuery>(json).unwrap();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::core::types::{ChatPermissions, ChatPhoto, Message};
|
use crate::types::{ChatPermissions, ChatPhoto, Message};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Chat {
|
pub struct Chat {
|
||||||
|
@ -9,7 +8,6 @@ pub struct Chat {
|
||||||
pub photo: Option<ChatPhoto>,
|
pub photo: Option<ChatPhoto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum ChatKind {
|
pub enum ChatKind {
|
||||||
|
@ -82,7 +80,7 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::core::types::*;
|
use crate::types::*;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::{ChatMemberStatus, User};
|
use crate::types::User;
|
||||||
|
|
||||||
/// This object contains information about one member of the chat.
|
/// This object contains information about one member of the chat.
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone)]
|
||||||
|
@ -54,3 +54,74 @@ pub struct ChatMember {
|
||||||
/// his messages, implies can_send_media_messages
|
/// his messages, implies can_send_media_messages
|
||||||
pub can_add_web_page_previews: Option<bool>,
|
pub can_add_web_page_previews: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ChatMemberStatus {
|
||||||
|
Creator,
|
||||||
|
Administrator,
|
||||||
|
Member,
|
||||||
|
Restricted,
|
||||||
|
Left,
|
||||||
|
Kicked,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize() {
|
||||||
|
let json = r#"{
|
||||||
|
"user":{
|
||||||
|
"id":12345,
|
||||||
|
"is_bot":false,
|
||||||
|
"first_name":"firstName"
|
||||||
|
},
|
||||||
|
"status":"creator",
|
||||||
|
"until_date":123456,
|
||||||
|
"can_be_edited":true,
|
||||||
|
"can_post_messages":true,
|
||||||
|
"can_edit_messages":true,
|
||||||
|
"can_delete_messages":true,
|
||||||
|
"can_restrict_members":true,
|
||||||
|
"can_promote_members":true,
|
||||||
|
"can_change_info":true,
|
||||||
|
"can_invite_users":true,
|
||||||
|
"can_pin_messages":true,
|
||||||
|
"is_member":true,
|
||||||
|
"can_send_messages":true,
|
||||||
|
"can_send_media_messages":true,
|
||||||
|
"can_send_polls":true,
|
||||||
|
"can_send_other_messages":true,
|
||||||
|
"can_add_web_page_previews":true
|
||||||
|
}"#;
|
||||||
|
let expected = ChatMember {
|
||||||
|
user: User {
|
||||||
|
id: 12345,
|
||||||
|
is_bot: false,
|
||||||
|
first_name: "firstName".to_string(),
|
||||||
|
last_name: None,
|
||||||
|
username: None,
|
||||||
|
language_code: None,
|
||||||
|
},
|
||||||
|
status: ChatMemberStatus::Creator,
|
||||||
|
until_date: Some(123456),
|
||||||
|
can_be_edited: Some(true),
|
||||||
|
can_change_info: Some(true),
|
||||||
|
can_post_messages: Some(true),
|
||||||
|
can_edit_messages: Some(true),
|
||||||
|
can_delete_messages: Some(true),
|
||||||
|
can_invite_users: Some(true),
|
||||||
|
can_restrict_members: Some(true),
|
||||||
|
can_pin_messages: Some(true),
|
||||||
|
can_promote_members: Some(true),
|
||||||
|
can_send_messages: Some(true),
|
||||||
|
can_send_media_messages: Some(true),
|
||||||
|
can_send_other_messages: Some(true),
|
||||||
|
can_add_web_page_previews: Some(true),
|
||||||
|
};
|
||||||
|
let actual = serde_json::from_str::<ChatMember>(&json).unwrap();
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::{User, Location};
|
use crate::types::{Location, User};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
/// Represents a result of an inline query that was chosen by the user and
|
/// Represents a result of an inline query that was chosen by the user and
|
||||||
|
@ -11,9 +11,9 @@ pub struct ChosenInlineResult {
|
||||||
pub from: User,
|
pub from: User,
|
||||||
/// Optional. Sender location, only for bots that require user location
|
/// Optional. Sender location, only for bots that require user location
|
||||||
pub location: Option<Location>,
|
pub location: Option<Location>,
|
||||||
/// Optional. Identifier of the sent inline message. Available only if there is an inline
|
/// Optional. Identifier of the sent inline message. Available only if
|
||||||
/// keyboard attached to the message. Will be also received in callback queries and can
|
/// there is an inline keyboard attached to the message. Will be also
|
||||||
/// be used to edit the message.
|
/// received in callback queries and can be used to edit the message.
|
||||||
pub inline_message_id: Option<String>,
|
pub inline_message_id: Option<String>,
|
||||||
/// The query that was used to obtain the result
|
/// The query that was used to obtain the result
|
||||||
pub query: String,
|
pub query: String,
|
|
@ -8,8 +8,8 @@ pub struct Contact {
|
||||||
/// Optional. Contact's last name
|
/// Optional. Contact's last name
|
||||||
pub last_name: Option<String>,
|
pub last_name: Option<String>,
|
||||||
/// Optional. Contact's user identifier in Telegram
|
/// Optional. Contact's user identifier in Telegram
|
||||||
pub user_id: Option<i64>,
|
pub user_id: Option<i32>,
|
||||||
/// Optional. Additional data about the contact in the form of a
|
/// Optional. Additional data about the contact in the form of a
|
||||||
/// [vCard](https://en.wikipedia.org/wiki/VCard)
|
/// [vCard](https://en.wikipedia.org/wiki/VCard)
|
||||||
pub vcard: Option<String>,
|
pub vcard: Option<String>,
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::PhotoSize;
|
use crate::types::PhotoSize;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
||||||
pub struct Document {
|
pub struct Document {
|
|
@ -2,5 +2,5 @@
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub file_id: String,
|
pub file_id: String,
|
||||||
pub file_size: u32,
|
pub file_size: u32,
|
||||||
pub file_path: String
|
pub file_path: String,
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::core::types::{MessageEntity, PhotoSize, Animation};
|
use crate::types::{Animation, MessageEntity, PhotoSize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||||
/// This object represents a game. Use BotFather to create and edit games, their short names
|
/// This object represents a game. Use BotFather to create and edit games, their
|
||||||
/// will act as unique identifiers.
|
/// short names will act as unique identifiers.
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
/// Title of the game
|
/// Title of the game
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
@ -12,13 +12,15 @@ pub struct Game {
|
||||||
pub description: String,
|
pub description: String,
|
||||||
/// Photo that will be displayed in the game message in chats.
|
/// Photo that will be displayed in the game message in chats.
|
||||||
pub photo: Vec<PhotoSize>,
|
pub photo: Vec<PhotoSize>,
|
||||||
/// Optional. Brief description of the game or high scores included in the game message.
|
/// Optional. Brief description of the game or high scores included in the
|
||||||
/// Can be automatically edited to include current high scores for the game when
|
/// game message. Can be automatically edited to include current high
|
||||||
/// the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters.
|
/// scores for the game when the bot calls setGameScore, or manually
|
||||||
|
/// edited using editMessageText. 0-4096 characters.
|
||||||
pub text: Option<String>,
|
pub text: Option<String>,
|
||||||
/// Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc.
|
/// Optional. Special entities that appear in text, such as usernames,
|
||||||
|
/// URLs, bot commands, etc.
|
||||||
pub text_entities: Option<Vec<MessageEntity>>,
|
pub text_entities: Option<Vec<MessageEntity>>,
|
||||||
/// Optional. Animation that will be displayed in the game message in chats.
|
/// Optional. Animation that will be displayed in the game message in
|
||||||
/// Upload via BotFather
|
/// chats. Upload via BotFather
|
||||||
pub animation: Option<Animation>,
|
pub animation: Option<Animation>,
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::core::types::user::User;
|
use crate::types::user::User;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
/// This object represents one row of the high scores table for a game.
|
/// This object represents one row of the high scores table for a game.
|
||||||
|
@ -11,4 +11,4 @@ pub struct GameHighScore {
|
||||||
pub user: User,
|
pub user: User,
|
||||||
/// Score
|
/// Score
|
||||||
pub score: u32,
|
pub score: u32,
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ pub enum InlineKeyboardButtonKind {
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```edition2018
|
/// ```edition2018
|
||||||
/// use async_telegram_bot::core::types::InlineKeyboardButton;
|
/// use async_telegram_bot::types::InlineKeyboardButton;
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let url_button = InlineKeyboardButton::url(
|
/// let url_button = InlineKeyboardButton::url(
|
||||||
|
@ -62,32 +62,37 @@ impl InlineKeyboardButton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn callback(text: String, callback_data: String)
|
pub fn callback(
|
||||||
-> InlineKeyboardButton {
|
text: String,
|
||||||
|
callback_data: String,
|
||||||
|
) -> InlineKeyboardButton {
|
||||||
InlineKeyboardButton {
|
InlineKeyboardButton {
|
||||||
text,
|
text,
|
||||||
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
|
kind: InlineKeyboardButtonKind::CallbackData(callback_data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_inline_query(text: String, switch_inline_query: String)
|
pub fn switch_inline_query(
|
||||||
-> InlineKeyboardButton {
|
text: String,
|
||||||
|
switch_inline_query: String,
|
||||||
|
) -> InlineKeyboardButton {
|
||||||
InlineKeyboardButton {
|
InlineKeyboardButton {
|
||||||
text,
|
text,
|
||||||
kind: InlineKeyboardButtonKind::SwitchInlineQuery(switch_inline_query)
|
kind: InlineKeyboardButtonKind::SwitchInlineQuery(
|
||||||
|
switch_inline_query,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_inline_query_current_chat(
|
pub fn switch_inline_query_current_chat(
|
||||||
text: String,
|
text: String,
|
||||||
switch_inline_query_current_chat: String
|
switch_inline_query_current_chat: String,
|
||||||
) -> InlineKeyboardButton {
|
) -> InlineKeyboardButton {
|
||||||
|
|
||||||
InlineKeyboardButton {
|
InlineKeyboardButton {
|
||||||
text,
|
text,
|
||||||
kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
|
kind: InlineKeyboardButtonKind::SwitchInlineQueryCurrentChat(
|
||||||
switch_inline_query_current_chat
|
switch_inline_query_current_chat,
|
||||||
)
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::InlineKeyboardButton;
|
use crate::types::InlineKeyboardButton;
|
||||||
|
|
||||||
/// This object represents an inline keyboard that appears right next to the
|
/// This object represents an inline keyboard that appears right next to the
|
||||||
/// message it belongs to.
|
/// message it belongs to.
|
||||||
|
@ -15,25 +15,21 @@ pub struct InlineKeyboardMarkup {
|
||||||
/// Build Markup
|
/// Build Markup
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
/// ```edition2018
|
/// ```
|
||||||
/// use async_telegram_bot::core::types::{
|
/// use async_telegram_bot::types::{
|
||||||
/// InlineKeyboardMarkup,
|
/// InlineKeyboardButton, InlineKeyboardMarkup,
|
||||||
/// InlineKeyboardButton
|
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// fn main() {
|
/// let url_button = InlineKeyboardButton::url(
|
||||||
/// let url_button = InlineKeyboardButton::url(
|
/// "text".to_string(),
|
||||||
/// "text".to_string(),
|
/// "http://url.com".to_string(),
|
||||||
/// "http://url.com".to_string()
|
/// );
|
||||||
/// );
|
/// let keyboard = InlineKeyboardMarkup::new().append_row(vec![url_button]);
|
||||||
/// let keyboard = InlineKeyboardMarkup::new()
|
|
||||||
/// .row(vec![url_button]);
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
impl InlineKeyboardMarkup {
|
impl InlineKeyboardMarkup {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inline_keyboard: vec![]
|
inline_keyboard: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,11 +38,14 @@ impl InlineKeyboardMarkup {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append_to_row(mut self, button: InlineKeyboardButton, index: usize)
|
pub fn append_to_row(
|
||||||
-> Self {
|
mut self,
|
||||||
|
button: InlineKeyboardButton,
|
||||||
|
index: usize,
|
||||||
|
) -> Self {
|
||||||
match self.inline_keyboard.get_mut(index) {
|
match self.inline_keyboard.get_mut(index) {
|
||||||
Some(buttons) => buttons.push(button),
|
Some(buttons) => buttons.push(button),
|
||||||
None => self.inline_keyboard.push(vec![button])
|
None => self.inline_keyboard.push(vec![button]),
|
||||||
};
|
};
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -69,15 +68,13 @@ mod tests {
|
||||||
let markup = InlineKeyboardMarkup::new()
|
let markup = InlineKeyboardMarkup::new()
|
||||||
.append_row(vec![button1.clone(), button2.clone()]);
|
.append_row(vec![button1.clone(), button2.clone()]);
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup {
|
||||||
inline_keyboard: vec![
|
inline_keyboard: vec![vec![button1.clone(), button2.clone()]],
|
||||||
vec![button1.clone(), button2.clone()]
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn append_to_row__existent_row() {
|
fn append_to_row_existent_row() {
|
||||||
let button1 = InlineKeyboardButton::url(
|
let button1 = InlineKeyboardButton::url(
|
||||||
"text 1".to_string(),
|
"text 1".to_string(),
|
||||||
"url 1".to_string(),
|
"url 1".to_string(),
|
||||||
|
@ -90,15 +87,13 @@ mod tests {
|
||||||
.append_row(vec![button1.clone()])
|
.append_row(vec![button1.clone()])
|
||||||
.append_to_row(button2.clone(), 0);
|
.append_to_row(button2.clone(), 0);
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup {
|
||||||
inline_keyboard: vec![
|
inline_keyboard: vec![vec![button1.clone(), button2.clone()]],
|
||||||
vec![button1.clone(), button2.clone()]
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn append_to_row__nonexistent_row() {
|
fn append_to_row_nonexistent_row() {
|
||||||
let button1 = InlineKeyboardButton::url(
|
let button1 = InlineKeyboardButton::url(
|
||||||
"text 1".to_string(),
|
"text 1".to_string(),
|
||||||
"url 1".to_string(),
|
"url 1".to_string(),
|
||||||
|
@ -111,10 +106,7 @@ mod tests {
|
||||||
.append_row(vec![button1.clone()])
|
.append_row(vec![button1.clone()])
|
||||||
.append_to_row(button2.clone(), 1);
|
.append_to_row(button2.clone(), 1);
|
||||||
let expected = InlineKeyboardMarkup {
|
let expected = InlineKeyboardMarkup {
|
||||||
inline_keyboard: vec![
|
inline_keyboard: vec![vec![button1.clone()], vec![button2.clone()]],
|
||||||
vec![button1.clone()],
|
|
||||||
vec![button2.clone()]
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
assert_eq!(markup, expected);
|
assert_eq!(markup, expected);
|
||||||
}
|
}
|
15
src/types/inline_query.rs
Normal file
15
src/types/inline_query.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use crate::types::{Location, User};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQuery {
|
||||||
|
/// Unique identifier for this query
|
||||||
|
pub id: String,
|
||||||
|
/// Sender
|
||||||
|
pub from: User,
|
||||||
|
/// Optional. Sender location, only for bots that request user location
|
||||||
|
pub location: Option<Location>,
|
||||||
|
/// Text of the query (up to 512 characters)
|
||||||
|
pub query: String,
|
||||||
|
/// Offset of the results to be returned, can be controlled by the bot
|
||||||
|
pub offset: String,
|
||||||
|
}
|
115
src/types/inline_query_result.rs
Normal file
115
src/types/inline_query_result.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use crate::types::{
|
||||||
|
InlineQueryResultArticle, InlineQueryResultAudio,
|
||||||
|
InlineQueryResultCachedAudio, InlineQueryResultCachedDocument,
|
||||||
|
InlineQueryResultCachedGif, InlineQueryResultCachedMpeg4Gif,
|
||||||
|
InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker,
|
||||||
|
InlineQueryResultCachedVideo, InlineQueryResultCachedVoice,
|
||||||
|
InlineQueryResultContact, InlineQueryResultDocument, InlineQueryResultGame,
|
||||||
|
InlineQueryResultGif, InlineQueryResultLocation, InlineQueryResultMpeg4Gif,
|
||||||
|
InlineQueryResultPhoto, InlineQueryResultVenue, InlineQueryResultVideo,
|
||||||
|
InlineQueryResultVoice,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This object represents one result of an inline query.
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone, From)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum InlineQueryResult {
|
||||||
|
#[serde(rename = "audio")]
|
||||||
|
CachedAudio(InlineQueryResultCachedAudio),
|
||||||
|
#[serde(rename = "document")]
|
||||||
|
CachedDocument(InlineQueryResultCachedDocument),
|
||||||
|
#[serde(rename = "gif")]
|
||||||
|
CachedGif(InlineQueryResultCachedGif),
|
||||||
|
#[serde(rename = "mpeg4_gif")]
|
||||||
|
CachedMpeg4Gif(InlineQueryResultCachedMpeg4Gif),
|
||||||
|
#[serde(rename = "photo")]
|
||||||
|
CachedPhoto(InlineQueryResultCachedPhoto),
|
||||||
|
#[serde(rename = "sticker")]
|
||||||
|
CachedSticker(InlineQueryResultCachedSticker),
|
||||||
|
#[serde(rename = "video")]
|
||||||
|
CachedVideo(InlineQueryResultCachedVideo),
|
||||||
|
#[serde(rename = "voice")]
|
||||||
|
CachedVoice(InlineQueryResultCachedVoice),
|
||||||
|
|
||||||
|
Article(InlineQueryResultArticle),
|
||||||
|
Audio(InlineQueryResultAudio),
|
||||||
|
Contact(InlineQueryResultContact),
|
||||||
|
Game(InlineQueryResultGame),
|
||||||
|
Document(InlineQueryResultDocument),
|
||||||
|
Gif(InlineQueryResultGif),
|
||||||
|
Location(InlineQueryResultLocation),
|
||||||
|
#[serde(rename = "mpeg4_gif")]
|
||||||
|
Mpeg4Gif(InlineQueryResultMpeg4Gif),
|
||||||
|
Photo(InlineQueryResultPhoto),
|
||||||
|
Venue(InlineQueryResultVenue),
|
||||||
|
Video(InlineQueryResultVideo),
|
||||||
|
Voice(InlineQueryResultVoice),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::types::inline_keyboard_markup::InlineKeyboardMarkup;
|
||||||
|
use crate::types::parse_mode::ParseMode;
|
||||||
|
use crate::types::{
|
||||||
|
InlineQueryResult, InlineQueryResultCachedAudio, InputMessageContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn into() {
|
||||||
|
let structure =
|
||||||
|
InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio {
|
||||||
|
id: String::from("id"),
|
||||||
|
audio_file_id: String::from("audio_file_id"),
|
||||||
|
caption: None,
|
||||||
|
parse_mode: None,
|
||||||
|
reply_markup: None,
|
||||||
|
input_message_content: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let _: InlineQueryResult = structure.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cached_audio_min_serialize() {
|
||||||
|
let structure =
|
||||||
|
InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio {
|
||||||
|
id: String::from("id"),
|
||||||
|
audio_file_id: String::from("audio_file_id"),
|
||||||
|
caption: None,
|
||||||
|
parse_mode: None,
|
||||||
|
reply_markup: None,
|
||||||
|
input_message_content: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let expected_json =
|
||||||
|
r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id"}"#;
|
||||||
|
let actual_json = serde_json::to_string(&structure).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expected_json, actual_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cached_audio_full_serialize() {
|
||||||
|
let structure =
|
||||||
|
InlineQueryResult::CachedAudio(InlineQueryResultCachedAudio {
|
||||||
|
id: String::from("id"),
|
||||||
|
audio_file_id: String::from("audio_file_id"),
|
||||||
|
caption: Some(String::from("caption")),
|
||||||
|
parse_mode: Some(ParseMode::HTML),
|
||||||
|
reply_markup: Some(InlineKeyboardMarkup::new()),
|
||||||
|
input_message_content: Some(InputMessageContent::Text {
|
||||||
|
message_text: String::from("message_text"),
|
||||||
|
parse_mode: Some(ParseMode::Markdown),
|
||||||
|
disable_web_page_preview: Some(true),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
let expected_json = r#"{"type":"audio","id":"id","audio_file_id":"audio_file_id","caption":"caption","parse_mode":"HTML","reply_markup":{"inline_keyboard":[]},"input_message_content":{"message_text":"message_text","parse_mode":"Markdown","disable_web_page_preview":true}}"#;
|
||||||
|
let actual_json = serde_json::to_string(&structure).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(expected_json, actual_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add more tests
|
||||||
|
}
|
33
src/types/inline_query_result_article.rs
Normal file
33
src/types/inline_query_result_article.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultArticle {
|
||||||
|
/// Unique identifier for this result, 1-64 Bytes
|
||||||
|
pub id: String,
|
||||||
|
/// Title of the result
|
||||||
|
pub title: String,
|
||||||
|
/// Content of the message to be sent
|
||||||
|
pub input_message_content: InputMessageContent,
|
||||||
|
/// Optional. Inline keyboard attached to the message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
/// Optional. URL of the result
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub url: Option<String>,
|
||||||
|
/// Optional. Pass True, if you don't want the URL to be shown in the
|
||||||
|
/// message
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub hide_url: Option<bool>,
|
||||||
|
/// Optional. Short description of the result
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// Optional. Url of the thumbnail for the result
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_url: Option<String>,
|
||||||
|
/// Optional. Thumbnail width
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_width: Option<i32>,
|
||||||
|
/// Optional. Thumbnail height
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_height: Option<i32>,
|
||||||
|
}
|
20
src/types/inline_query_result_audio.rs
Normal file
20
src/types/inline_query_result_audio.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultAudio {
|
||||||
|
pub id: String,
|
||||||
|
pub audio_url: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub performer: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub audio_duration: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
15
src/types/inline_query_result_cached_audio.rs
Normal file
15
src/types/inline_query_result_cached_audio.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedAudio {
|
||||||
|
pub id: String,
|
||||||
|
pub audio_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
18
src/types/inline_query_result_cached_document.rs
Normal file
18
src/types/inline_query_result_cached_document.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedDocument {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub document_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
17
src/types/inline_query_result_cached_gif.rs
Normal file
17
src/types/inline_query_result_cached_gif.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedGif {
|
||||||
|
pub id: String,
|
||||||
|
pub gif_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
17
src/types/inline_query_result_cached_mpeg4_gif.rs
Normal file
17
src/types/inline_query_result_cached_mpeg4_gif.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedMpeg4Gif {
|
||||||
|
pub id: String,
|
||||||
|
pub mpeg4_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
19
src/types/inline_query_result_cached_photo.rs
Normal file
19
src/types/inline_query_result_cached_photo.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedPhoto {
|
||||||
|
pub id: String,
|
||||||
|
pub photo_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
11
src/types/inline_query_result_cached_sticker.rs
Normal file
11
src/types/inline_query_result_cached_sticker.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedSticker {
|
||||||
|
pub id: String,
|
||||||
|
pub sticker_file_id: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
18
src/types/inline_query_result_cached_video.rs
Normal file
18
src/types/inline_query_result_cached_video.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedVideo {
|
||||||
|
pub id: String,
|
||||||
|
pub video_file_id: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
16
src/types/inline_query_result_cached_voice.rs
Normal file
16
src/types/inline_query_result_cached_voice.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultCachedVoice {
|
||||||
|
pub id: String,
|
||||||
|
pub voice_file_id: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
22
src/types/inline_query_result_contact.rs
Normal file
22
src/types/inline_query_result_contact.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultContact {
|
||||||
|
pub id: String,
|
||||||
|
pub phone_number: String,
|
||||||
|
pub first_name: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub last_name: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub vcard: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_height: Option<i32>,
|
||||||
|
}
|
25
src/types/inline_query_result_document.rs
Normal file
25
src/types/inline_query_result_document.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultDocument {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
pub document_url: String,
|
||||||
|
pub mime_type: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_height: Option<i32>,
|
||||||
|
}
|
9
src/types/inline_query_result_game.rs
Normal file
9
src/types/inline_query_result_game.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::types::InlineKeyboardMarkup;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub struct InlineQueryResultGame {
|
||||||
|
pub id: String,
|
||||||
|
pub game_short_name: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
}
|
24
src/types/inline_query_result_gif.rs
Normal file
24
src/types/inline_query_result_gif.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultGif {
|
||||||
|
pub id: String,
|
||||||
|
pub gif_url: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gif_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gif_height: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub gif_duration: Option<i32>,
|
||||||
|
pub thumb_url: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
21
src/types/inline_query_result_location.rs
Normal file
21
src/types/inline_query_result_location.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultLocation {
|
||||||
|
pub id: String,
|
||||||
|
pub latitude: f64,
|
||||||
|
pub longitude: f64,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub live_period: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_height: Option<i32>,
|
||||||
|
}
|
24
src/types/inline_query_result_mpeg4_gif.rs
Normal file
24
src/types/inline_query_result_mpeg4_gif.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultMpeg4Gif {
|
||||||
|
pub id: String,
|
||||||
|
pub mpeg4_url: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mpeg4_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mpeg4_height: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mpeg4_duration: Option<i32>,
|
||||||
|
pub thumb_url: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
27
src/types/inline_query_result_photo.rs
Normal file
27
src/types/inline_query_result_photo.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
/// Represents a link to a photo. By default, this photo will be sent by the
|
||||||
|
/// user with optional caption. Alternatively, you can use input_message_content
|
||||||
|
/// to send a message with the specified content instead of the photo.
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultPhoto {
|
||||||
|
pub id: String,
|
||||||
|
pub photo_url: String,
|
||||||
|
pub thumb_url: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub photo_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub photo_height: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
24
src/types/inline_query_result_venue.rs
Normal file
24
src/types/inline_query_result_venue.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultVenue {
|
||||||
|
pub id: String,
|
||||||
|
pub latitude: f64,
|
||||||
|
pub longitude: f64,
|
||||||
|
pub title: String,
|
||||||
|
pub address: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub foursquare_id: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub foursquare_type: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_url: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub thumb_height: Option<i32>,
|
||||||
|
}
|
26
src/types/inline_query_result_video.rs
Normal file
26
src/types/inline_query_result_video.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultVideo {
|
||||||
|
pub id: String,
|
||||||
|
pub video_url: String,
|
||||||
|
pub mime_type: String,
|
||||||
|
pub thumb_url: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub video_width: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub video_height: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub video_duration: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
18
src/types/inline_query_result_voice.rs
Normal file
18
src/types/inline_query_result_voice.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use crate::types::{InlineKeyboardMarkup, InputMessageContent, ParseMode};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, PartialEq, Clone)]
|
||||||
|
pub struct InlineQueryResultVoice {
|
||||||
|
pub id: String,
|
||||||
|
pub voice_url: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub caption: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub parse_mode: Option<ParseMode>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub voice_duration: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub input_message_content: Option<InputMessageContent>,
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::{InputFile, ParseMode};
|
use crate::types::{InputFile, ParseMode};
|
||||||
|
|
||||||
// TODO: should variants use new-type?
|
// TODO: should variants use new-type?
|
||||||
#[derive(Debug, Serialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, PartialEq, Eq, Clone)]
|
||||||
|
@ -18,10 +18,10 @@ pub enum InputMedia {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<ParseMode>,
|
parse_mode: Option<ParseMode>,
|
||||||
},
|
},
|
||||||
|
@ -35,7 +35,7 @@ pub enum InputMedia {
|
||||||
/// size. A thumbnail‘s width and height should not exceed 320.
|
/// size. A thumbnail‘s width and height should not exceed 320.
|
||||||
/// Ignored if the file is not uploaded using [InputFile::File].
|
/// Ignored if the file is not uploaded using [InputFile::File].
|
||||||
///
|
///
|
||||||
/// [InputFile::File]: crate::core::types::InputFile::File
|
/// [InputFile::File]: crate::types::InputFile::File
|
||||||
thumb: Option<InputFile>,
|
thumb: Option<InputFile>,
|
||||||
/// Caption of the video to be sent, 0-1024 characters.
|
/// Caption of the video to be sent, 0-1024 characters.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -44,10 +44,10 @@ pub enum InputMedia {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<ParseMode>,
|
parse_mode: Option<ParseMode>,
|
||||||
/// Video width
|
/// Video width
|
||||||
|
@ -74,7 +74,7 @@ pub enum InputMedia {
|
||||||
/// size. A thumbnail‘s width and height should not exceed 320.
|
/// size. A thumbnail‘s width and height should not exceed 320.
|
||||||
/// Ignored if the file is not uploaded using [InputFile::File].
|
/// Ignored if the file is not uploaded using [InputFile::File].
|
||||||
///
|
///
|
||||||
/// [InputFile::File]: crate::core::types::InputFile::File
|
/// [InputFile::File]: crate::types::InputFile::File
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
thumb: Option<InputFile>,
|
thumb: Option<InputFile>,
|
||||||
/// Caption of the animation to be sent, 0-1024 characters
|
/// Caption of the animation to be sent, 0-1024 characters
|
||||||
|
@ -84,10 +84,10 @@ pub enum InputMedia {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<ParseMode>,
|
parse_mode: Option<ParseMode>,
|
||||||
/// Animation width
|
/// Animation width
|
||||||
|
@ -110,7 +110,7 @@ pub enum InputMedia {
|
||||||
/// size. A thumbnail‘s width and height should not exceed 320.
|
/// size. A thumbnail‘s width and height should not exceed 320.
|
||||||
/// Ignored if the file is not uploaded using [InputFile::File].
|
/// Ignored if the file is not uploaded using [InputFile::File].
|
||||||
///
|
///
|
||||||
/// [InputFile::File]: crate::core::types::InputFile::File
|
/// [InputFile::File]: crate::types::InputFile::File
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
thumb: Option<InputFile>,
|
thumb: Option<InputFile>,
|
||||||
/// Caption of the audio to be sent, 0-1024 characters
|
/// Caption of the audio to be sent, 0-1024 characters
|
||||||
|
@ -120,10 +120,10 @@ pub enum InputMedia {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<String>,
|
parse_mode: Option<String>,
|
||||||
/// Duration of the audio in seconds
|
/// Duration of the audio in seconds
|
||||||
|
@ -146,7 +146,7 @@ pub enum InputMedia {
|
||||||
/// size. A thumbnail‘s width and height should not exceed 320.
|
/// size. A thumbnail‘s width and height should not exceed 320.
|
||||||
/// Ignored if the file is not uploaded using [InputFile::File].
|
/// Ignored if the file is not uploaded using [InputFile::File].
|
||||||
///
|
///
|
||||||
/// [InputFile::File]: crate::core::types::InputFile::File
|
/// [InputFile::File]: crate::types::InputFile::File
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
thumb: Option<InputFile>,
|
thumb: Option<InputFile>,
|
||||||
/// Caption of the document to be sent, 0-1024 characters
|
/// Caption of the document to be sent, 0-1024 characters
|
||||||
|
@ -156,10 +156,10 @@ pub enum InputMedia {
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// or inline URLs] in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]:
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
/// crate::core::types::ParseMode
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<ParseMode>,
|
parse_mode: Option<ParseMode>,
|
||||||
},
|
},
|
|
@ -1,25 +1,27 @@
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::core::types::ParseMode;
|
use crate::types::ParseMode;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone)]
|
#[derive(Debug, Serialize, Clone, PartialEq)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
/// This object represents the content of a message to be sent as
|
/// This object represents the content of a message to be sent as
|
||||||
/// a result of an inline query.
|
/// a result of an inline query.
|
||||||
/// [More](https://core.telegram.org/bots/api#inputmessagecontent)
|
/// [More](https://core.telegram.org/bots/api#inputmessagecontent)
|
||||||
pub enum InputMessageContent {
|
pub enum InputMessageContent {
|
||||||
/// Represents the content of a text message to be sent as the result of an inline query.
|
/// Represents the content of a text message to be sent as the result of an
|
||||||
|
/// inline query.
|
||||||
Text {
|
Text {
|
||||||
/// Text of the message to be sent, 1-4096 characters
|
/// Text of the message to be sent, 1-4096 characters
|
||||||
message_text: String,
|
message_text: String,
|
||||||
|
|
||||||
/// Send [Markdown] or [HTML],
|
/// Send [Markdown] or [HTML],
|
||||||
/// if you want Telegram apps to show [bold, italic, fixed-width text or inline URLs]
|
/// if you want Telegram apps to show [bold, italic, fixed-width text
|
||||||
/// in the media caption.
|
/// or inline URLs] in the media caption.
|
||||||
///
|
///
|
||||||
/// [Markdown]: crate::core::types::ParseMode::Markdown
|
/// [Markdown]: crate::types::ParseMode::Markdown
|
||||||
/// [Html]: crate::core::types::ParseMode::Html
|
/// [Html]: crate::types::ParseMode::Html
|
||||||
/// [bold, italic, fixed-width text or inline URLs]: crate::core::types::ParseMode
|
/// [bold, italic, fixed-width text or inline URLs]:
|
||||||
|
/// crate::types::ParseMode
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
parse_mode: Option<ParseMode>,
|
parse_mode: Option<ParseMode>,
|
||||||
|
|
||||||
|
@ -27,18 +29,21 @@ pub enum InputMessageContent {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
disable_web_page_preview: Option<bool>,
|
disable_web_page_preview: Option<bool>,
|
||||||
},
|
},
|
||||||
/// Represents the content of a location message to be sent as the result of an inline query.
|
/// Represents the content of a location message to be sent as the result
|
||||||
|
/// of an inline query.
|
||||||
Location {
|
Location {
|
||||||
/// Latitude of the location in degrees
|
/// Latitude of the location in degrees
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
/// Longitude of the location in degrees
|
/// Longitude of the location in degrees
|
||||||
longitude: f64,
|
longitude: f64,
|
||||||
|
|
||||||
/// Period in seconds for which the location can be updated, should be between 60 and 86400.
|
/// Period in seconds for which the location can be updated, should be
|
||||||
|
/// between 60 and 86400.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
live_period: Option<u32>,
|
live_period: Option<u32>,
|
||||||
},
|
},
|
||||||
/// Represents the content of a venue message to be sent as the result of an inline query.
|
/// Represents the content of a venue message to be sent as the result of
|
||||||
|
/// an inline query.
|
||||||
Venue {
|
Venue {
|
||||||
/// Latitude of the venue in degrees
|
/// Latitude of the venue in degrees
|
||||||
latitude: f64,
|
latitude: f64,
|
||||||
|
@ -53,12 +58,14 @@ pub enum InputMessageContent {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
foursquare_id: Option<String>,
|
foursquare_id: Option<String>,
|
||||||
|
|
||||||
/// Foursquare type of the venue, if known. (For example, “arts_entertainment/default”,
|
/// Foursquare type of the venue, if known. (For example,
|
||||||
/// “arts_entertainment/aquarium” or “food/icecream”.)
|
/// “arts_entertainment/default”, “arts_entertainment/aquarium”
|
||||||
|
/// or “food/icecream”.)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
foursquare_type: Option<String>,
|
foursquare_type: Option<String>,
|
||||||
},
|
},
|
||||||
/// Represents the content of a contact message to be sent as the result of an inline query.
|
/// Represents the content of a contact message to be sent as the result of
|
||||||
|
/// an inline query.
|
||||||
Contact {
|
Contact {
|
||||||
/// Contact's phone number
|
/// Contact's phone number
|
||||||
phone_number: String,
|
phone_number: String,
|
||||||
|
@ -108,8 +115,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn venue_serialize() {
|
fn venue_serialize() {
|
||||||
let expected_json = r#"{"latitude":59.08,"longitude":38.4326,"title":"some title",
|
let expected_json = r#"{"latitude":59.08,"longitude":38.4326,"title":"some title","address":"some address"}"#;
|
||||||
"address":"some address"}"#;
|
|
||||||
let venue_content = InputMessageContent::Venue {
|
let venue_content = InputMessageContent::Venue {
|
||||||
latitude: 59.08,
|
latitude: 59.08,
|
||||||
longitude: 38.4326,
|
longitude: 38.4326,
|
||||||
|
@ -125,7 +131,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn contact_serialize() {
|
fn contact_serialize() {
|
||||||
let expected_json = r#"{"phone_number":"+3800000000","first_name":"jhon"}"#;
|
let expected_json =
|
||||||
|
r#"{"phone_number":"+3800000000","first_name":"jhon"}"#;
|
||||||
let contact_content = InputMessageContent::Contact {
|
let contact_content = InputMessageContent::Contact {
|
||||||
phone_number: String::from("+3800000000"),
|
phone_number: String::from("+3800000000"),
|
||||||
first_name: String::from("jhon"),
|
first_name: String::from("jhon"),
|
||||||
|
@ -136,4 +143,4 @@ mod tests {
|
||||||
let actual_json = serde_json::to_string(&contact_content).unwrap();
|
let actual_json = serde_json::to_string(&contact_content).unwrap();
|
||||||
assert_eq!(expected_json, actual_json);
|
assert_eq!(expected_json, actual_json);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ pub struct Invoice {
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub start_parameter: String,
|
pub start_parameter: String,
|
||||||
pub currency: String,
|
pub currency: String,
|
||||||
pub total_amount: i64,
|
pub total_amount: i32,
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ pub struct LabeledPrice {
|
||||||
/// amount = 145. See the exp parameter in [`currencies.json`](https://core.telegram.org/bots/payments/currencies.json),
|
/// 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
|
/// it shows the number of digits past the decimal point for each currency
|
||||||
/// (2 for the majority of currencies).
|
/// (2 for the majority of currencies).
|
||||||
pub amount: i64,
|
pub amount: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -20,7 +20,7 @@ mod tests {
|
||||||
fn serialize() {
|
fn serialize() {
|
||||||
let labeled_price = LabeledPrice {
|
let labeled_price = LabeledPrice {
|
||||||
label: "Label".to_string(),
|
label: "Label".to_string(),
|
||||||
amount: 60
|
amount: 60,
|
||||||
};
|
};
|
||||||
let expected = r#"{"label":"Label","amount":60}"#;
|
let expected = r#"{"label":"Label","amount":60}"#;
|
||||||
let actual = serde_json::to_string(&labeled_price).unwrap();
|
let actual = serde_json::to_string(&labeled_price).unwrap();
|
|
@ -5,4 +5,4 @@ pub struct Location {
|
||||||
pub longitude: f64,
|
pub longitude: f64,
|
||||||
/// Latitude as defined by sender
|
/// Latitude as defined by sender
|
||||||
pub latitude: f64,
|
pub latitude: f64,
|
||||||
}
|
}
|
|
@ -7,4 +7,4 @@ pub struct LoginUrl {
|
||||||
pub bot_username: Option<String>,
|
pub bot_username: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub request_write_access: Option<bool>,
|
pub request_write_access: Option<bool>,
|
||||||
}
|
}
|
|
@ -5,4 +5,3 @@ pub struct MaskPosition {
|
||||||
pub y_shift: f64,
|
pub y_shift: f64,
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::{
|
use crate::types::{
|
||||||
Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup,
|
Animation, Audio, Chat, Contact, Document, Game, InlineKeyboardMarkup,
|
||||||
Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker,
|
Invoice, Location, MessageEntity, PassportData, PhotoSize, Poll, Sticker,
|
||||||
SuccessfulPayment, User, Venue, Video, VideoNote, Voice,
|
SuccessfulPayment, User, Venue, Video, VideoNote, Voice,
|
||||||
|
@ -7,17 +7,31 @@ use crate::core::types::{
|
||||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
#[serde(rename = "message_id")]
|
#[serde(rename = "message_id")]
|
||||||
pub id: i64,
|
pub id: i32,
|
||||||
pub date: i32,
|
pub date: i32,
|
||||||
pub chat: Chat,
|
pub chat: Chat,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub message_kind: MessageKind,
|
pub kind: MessageKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
fn text(&self) -> Option<&str> {
|
||||||
|
if let MessageKind::Common {
|
||||||
|
media_kind: MediaKind::Text { ref text, .. },
|
||||||
|
..
|
||||||
|
} = self.kind
|
||||||
|
{
|
||||||
|
Some(text)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
#[derive(Debug, Deserialize, PartialEq, Clone)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum MessageKind {
|
pub enum MessageKind {
|
||||||
IncomingMessage {
|
Common {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
from: Sender,
|
from: Sender,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -55,8 +69,8 @@ pub enum MessageKind {
|
||||||
migrate_to_chat_id: i64,
|
migrate_to_chat_id: i64,
|
||||||
migrate_from_chat_id: i64,
|
migrate_from_chat_id: i64,
|
||||||
},
|
},
|
||||||
PinnedMessage {
|
Pinned {
|
||||||
pinned_message: Box<Message>,
|
pinned: Box<Message>,
|
||||||
},
|
},
|
||||||
Invoice {
|
Invoice {
|
||||||
invoice: Invoice,
|
invoice: Invoice,
|
||||||
|
@ -91,7 +105,7 @@ pub enum ForwardKind {
|
||||||
#[serde(rename = "forward_from_chat")]
|
#[serde(rename = "forward_from_chat")]
|
||||||
chat: Chat,
|
chat: Chat,
|
||||||
#[serde(rename = "forward_from_message_id")]
|
#[serde(rename = "forward_from_message_id")]
|
||||||
message_id: i64,
|
message_id: i32,
|
||||||
#[serde(rename = "forward_signature")]
|
#[serde(rename = "forward_signature")]
|
||||||
signature: Option<String>,
|
signature: Option<String>,
|
||||||
},
|
},
|
||||||
|
@ -189,7 +203,7 @@ pub enum MediaKind {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::core::types::*;
|
use crate::types::*;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::User;
|
use crate::types::User;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Clone, Serialize)]
|
||||||
pub struct MessageEntity {
|
pub struct MessageEntity {
|
156
src/types/mod.rs
Normal file
156
src/types/mod.rs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
use self::not_implemented_types::*;
|
||||||
|
pub use self::{
|
||||||
|
animation::Animation,
|
||||||
|
audio::Audio,
|
||||||
|
callback_game::CallbackGame,
|
||||||
|
callback_query::CallbackQuery,
|
||||||
|
chat::{Chat, ChatKind, NonPrivateChatKind},
|
||||||
|
chat_member::{ChatMember, ChatMemberStatus},
|
||||||
|
chat_permissions::ChatPermissions,
|
||||||
|
chat_photo::ChatPhoto,
|
||||||
|
chosen_inline_result::ChosenInlineResult,
|
||||||
|
contact::Contact,
|
||||||
|
document::Document,
|
||||||
|
file::File,
|
||||||
|
force_reply::ForceReply,
|
||||||
|
game::Game,
|
||||||
|
game_high_score::GameHighScore,
|
||||||
|
inline_keyboard_button::{InlineKeyboardButton, InlineKeyboardButtonKind},
|
||||||
|
inline_keyboard_markup::InlineKeyboardMarkup,
|
||||||
|
inline_query::InlineQuery,
|
||||||
|
inline_query_result::InlineQueryResult,
|
||||||
|
inline_query_result_article::InlineQueryResultArticle,
|
||||||
|
inline_query_result_audio::InlineQueryResultAudio,
|
||||||
|
inline_query_result_cached_audio::InlineQueryResultCachedAudio,
|
||||||
|
inline_query_result_cached_document::InlineQueryResultCachedDocument,
|
||||||
|
inline_query_result_cached_gif::InlineQueryResultCachedGif,
|
||||||
|
inline_query_result_cached_mpeg4_gif::InlineQueryResultCachedMpeg4Gif,
|
||||||
|
inline_query_result_cached_photo::InlineQueryResultCachedPhoto,
|
||||||
|
inline_query_result_cached_sticker::InlineQueryResultCachedSticker,
|
||||||
|
inline_query_result_cached_video::InlineQueryResultCachedVideo,
|
||||||
|
inline_query_result_cached_voice::InlineQueryResultCachedVoice,
|
||||||
|
inline_query_result_contact::InlineQueryResultContact,
|
||||||
|
inline_query_result_document::InlineQueryResultDocument,
|
||||||
|
inline_query_result_game::InlineQueryResultGame,
|
||||||
|
inline_query_result_gif::InlineQueryResultGif,
|
||||||
|
inline_query_result_location::InlineQueryResultLocation,
|
||||||
|
inline_query_result_mpeg4_gif::InlineQueryResultMpeg4Gif,
|
||||||
|
inline_query_result_photo::InlineQueryResultPhoto,
|
||||||
|
inline_query_result_venue::InlineQueryResultVenue,
|
||||||
|
inline_query_result_video::InlineQueryResultVideo,
|
||||||
|
inline_query_result_voice::InlineQueryResultVoice,
|
||||||
|
input_file::InputFile,
|
||||||
|
input_media::InputMedia,
|
||||||
|
input_message_content::InputMessageContent,
|
||||||
|
invoice::Invoice,
|
||||||
|
keyboard_button::KeyboardButton,
|
||||||
|
label_price::LabeledPrice,
|
||||||
|
location::Location,
|
||||||
|
login_url::LoginUrl,
|
||||||
|
mask_position::MaskPosition,
|
||||||
|
message::{
|
||||||
|
ForwardKind, ForwardedFrom, MediaKind, Message, MessageKind, Sender,
|
||||||
|
},
|
||||||
|
message_entity::MessageEntity,
|
||||||
|
order_info::OrderInfo,
|
||||||
|
parse_mode::ParseMode,
|
||||||
|
photo_size::PhotoSize,
|
||||||
|
poll::{Poll, PollOption},
|
||||||
|
pre_checkout_query::PreCheckoutQuery,
|
||||||
|
reply_keyboard_markup::ReplyKeyboardMarkup,
|
||||||
|
reply_keyboard_remove::ReplyKeyboardRemove,
|
||||||
|
reply_markup::ReplyMarkup,
|
||||||
|
response_parameters::ResponseParameters,
|
||||||
|
send_invoice::SendInvoice,
|
||||||
|
shipping_address::ShippingAddress,
|
||||||
|
shipping_option::ShippingOption,
|
||||||
|
shipping_query::ShippingQuery,
|
||||||
|
sticker::Sticker,
|
||||||
|
sticker_set::StickerSet,
|
||||||
|
successful_payment::SuccessfulPayment,
|
||||||
|
update::{Update, UpdateKind},
|
||||||
|
user::User,
|
||||||
|
user_profile_photos::UserProfilePhotos,
|
||||||
|
venue::Venue,
|
||||||
|
video::Video,
|
||||||
|
video_note::VideoNote,
|
||||||
|
voice::Voice,
|
||||||
|
webhook_info::WebhookInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod animation;
|
||||||
|
mod audio;
|
||||||
|
mod callback_game;
|
||||||
|
mod callback_query;
|
||||||
|
mod chat;
|
||||||
|
mod chat_member;
|
||||||
|
mod chat_permissions;
|
||||||
|
mod chat_photo;
|
||||||
|
mod chosen_inline_result;
|
||||||
|
mod contact;
|
||||||
|
mod document;
|
||||||
|
mod file;
|
||||||
|
mod force_reply;
|
||||||
|
mod game;
|
||||||
|
mod game_high_score;
|
||||||
|
mod inline_keyboard_button;
|
||||||
|
mod inline_keyboard_markup;
|
||||||
|
mod input_file;
|
||||||
|
mod input_media;
|
||||||
|
mod input_message_content;
|
||||||
|
mod invoice;
|
||||||
|
mod keyboard_button;
|
||||||
|
mod label_price;
|
||||||
|
mod location;
|
||||||
|
mod login_url;
|
||||||
|
mod mask_position;
|
||||||
|
mod message;
|
||||||
|
mod message_entity;
|
||||||
|
mod not_implemented_types;
|
||||||
|
mod order_info;
|
||||||
|
mod parse_mode;
|
||||||
|
mod photo_size;
|
||||||
|
mod poll;
|
||||||
|
mod pre_checkout_query;
|
||||||
|
mod reply_keyboard_markup;
|
||||||
|
mod reply_keyboard_remove;
|
||||||
|
mod reply_markup;
|
||||||
|
mod response_parameters;
|
||||||
|
mod send_invoice;
|
||||||
|
mod shipping_address;
|
||||||
|
mod shipping_option;
|
||||||
|
mod shipping_query;
|
||||||
|
mod sticker;
|
||||||
|
mod sticker_set;
|
||||||
|
mod successful_payment;
|
||||||
|
mod update;
|
||||||
|
mod user;
|
||||||
|
mod user_profile_photos;
|
||||||
|
mod venue;
|
||||||
|
mod video;
|
||||||
|
mod video_note;
|
||||||
|
mod voice;
|
||||||
|
mod webhook_info;
|
||||||
|
|
||||||
|
mod inline_query;
|
||||||
|
mod inline_query_result;
|
||||||
|
mod inline_query_result_article;
|
||||||
|
mod inline_query_result_audio;
|
||||||
|
mod inline_query_result_cached_audio;
|
||||||
|
mod inline_query_result_cached_document;
|
||||||
|
mod inline_query_result_cached_gif;
|
||||||
|
mod inline_query_result_cached_mpeg4_gif;
|
||||||
|
mod inline_query_result_cached_photo;
|
||||||
|
mod inline_query_result_cached_sticker;
|
||||||
|
mod inline_query_result_cached_video;
|
||||||
|
mod inline_query_result_cached_voice;
|
||||||
|
mod inline_query_result_contact;
|
||||||
|
mod inline_query_result_document;
|
||||||
|
mod inline_query_result_game;
|
||||||
|
mod inline_query_result_gif;
|
||||||
|
mod inline_query_result_location;
|
||||||
|
mod inline_query_result_mpeg4_gif;
|
||||||
|
mod inline_query_result_photo;
|
||||||
|
mod inline_query_result_venue;
|
||||||
|
mod inline_query_result_video;
|
||||||
|
mod inline_query_result_voice;
|
2
src/types/not_implemented_types.rs
Normal file
2
src/types/not_implemented_types.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Serialize, Clone)]
|
||||||
|
pub struct PassportData;
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::core::types::ShippingAddress;
|
use crate::types::ShippingAddress;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Hash, PartialEq, Eq, Clone, Serialize)]
|
||||||
pub struct OrderInfo {
|
pub struct OrderInfo {
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)]
|
||||||
/// ## Formatting options
|
/// ## Formatting options
|
||||||
/// The Bot API supports basic formatting for messages.
|
/// The Bot API supports basic formatting for messages.
|
||||||
/// You can use **bold** and *italic* text, as well as [inline links](https://example.com) and `pre-formatted code` in
|
/// You can use **bold** and *italic* text, as well as [inline links](https://example.com) and `pre-formatted code` in
|
|
@ -24,7 +24,7 @@ mod tests {
|
||||||
file_id: "id".to_string(),
|
file_id: "id".to_string(),
|
||||||
width: 320,
|
width: 320,
|
||||||
height: 320,
|
height: 320,
|
||||||
file_size: Some(3452)
|
file_size: Some(3452),
|
||||||
};
|
};
|
||||||
let actual = serde_json::from_str::<PhotoSize>(json).unwrap();
|
let actual = serde_json::from_str::<PhotoSize>(json).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue