mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-08 19:33:53 +01:00
Merge branch 'dev' into unbox_send_future
This commit is contained in:
commit
816d2bad2f
22 changed files with 206 additions and 76 deletions
|
@ -1,10 +1,10 @@
|
|||
mod api;
|
||||
mod download;
|
||||
|
||||
use reqwest::r#async::Client;
|
||||
|
||||
use crate::requests::RequestContext;
|
||||
|
||||
mod api;
|
||||
mod download;
|
||||
|
||||
pub struct Bot {
|
||||
token: String,
|
||||
client: Client,
|
||||
|
|
|
@ -5,11 +5,11 @@ extern crate derive_more;
|
|||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
mod network;
|
||||
pub use errors::{DownloadError, RequestError};
|
||||
|
||||
mod errors;
|
||||
mod network;
|
||||
|
||||
pub mod bot;
|
||||
pub mod requests;
|
||||
pub mod types;
|
||||
|
||||
pub use errors::{DownloadError, RequestError};
|
||||
|
|
|
@ -6,10 +6,9 @@ use tokio::{
|
|||
stream::Stream,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
network::{file_url, TELEGRAM_API_URL},
|
||||
DownloadError,
|
||||
};
|
||||
use crate::DownloadError;
|
||||
|
||||
use super::TELEGRAM_API_URL;
|
||||
|
||||
pub async fn download_file<D>(
|
||||
client: &Client,
|
||||
|
@ -35,7 +34,10 @@ pub async fn download_file_stream(
|
|||
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())
|
||||
Ok(client
|
||||
.get(&super::file_url(TELEGRAM_API_URL, token, path))
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.into_body())
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
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";
|
||||
mod download;
|
||||
mod request;
|
||||
mod telegram_response;
|
||||
|
||||
const TELEGRAM_API_URL: &str = "https://api.telegram.org";
|
||||
|
||||
/// Creates URL for making HTTPS requests. See the [Telegram documentation].
|
||||
///
|
||||
|
|
|
@ -2,11 +2,9 @@ use apply::Apply;
|
|||
use reqwest::r#async::{multipart::Form, Client, Response};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{
|
||||
network::{method_url, TelegramResponse, TELEGRAM_API_URL},
|
||||
requests::ResponseResult,
|
||||
RequestError,
|
||||
};
|
||||
use crate::{requests::ResponseResult, RequestError};
|
||||
|
||||
use super::{TelegramResponse, TELEGRAM_API_URL};
|
||||
|
||||
pub async fn request_multipart<T>(
|
||||
client: &Client,
|
||||
|
@ -19,7 +17,7 @@ where
|
|||
{
|
||||
process_response(
|
||||
client
|
||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||
.post(&super::method_url(TELEGRAM_API_URL, token, method_name))
|
||||
.apply(|request_builder| match params {
|
||||
Some(params) => request_builder.multipart(params),
|
||||
None => request_builder,
|
||||
|
@ -39,7 +37,7 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
|||
) -> ResponseResult<T> {
|
||||
process_response(
|
||||
client
|
||||
.post(&method_url(TELEGRAM_API_URL, token, method_name))
|
||||
.post(&super::method_url(TELEGRAM_API_URL, token, method_name))
|
||||
.json(params)
|
||||
.send()
|
||||
.await
|
||||
|
@ -51,10 +49,9 @@ pub async fn request_json<T: DeserializeOwned, P: Serialize>(
|
|||
async fn process_response<T: DeserializeOwned>(
|
||||
mut response: Response,
|
||||
) -> ResponseResult<T> {
|
||||
let response = serde_json::from_str::<TelegramResponse<T>>(
|
||||
serde_json::from_str::<TelegramResponse<T>>(
|
||||
&response.text().await.map_err(RequestError::NetworkError)?,
|
||||
)
|
||||
.map_err(RequestError::InvalidJson)?;
|
||||
|
||||
response.into()
|
||||
.map_err(RequestError::InvalidJson)?
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -72,11 +72,10 @@ impl<'a> GetUserProfilePhotos<'a> {
|
|||
}
|
||||
|
||||
pub fn limit<T>(mut self, limit: T) -> Self
|
||||
where
|
||||
T: Into<i64>,
|
||||
where
|
||||
T: Into<i64>,
|
||||
{
|
||||
self.limit = Some(limit.into());
|
||||
self
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
mod form_builder;
|
||||
mod utils;
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use reqwest::r#async::Client;
|
||||
|
@ -15,15 +14,21 @@ pub use self::{
|
|||
get_me::GetMe, get_updates::GetUpdates,
|
||||
get_user_profile_photos::GetUserProfilePhotos,
|
||||
kick_chat_member::KickChatMember, pin_chat_message::PinChatMessage,
|
||||
restrict_chat_member::RestrictChatMember,
|
||||
promote_chat_member::PromoteChatMember,
|
||||
restrict_chat_member::RestrictChatMember, send_animation::SendAnimation,
|
||||
send_audio::SendAudio, send_chat_action::SendChatAction,
|
||||
send_contact::SendContact, send_location::SendLocation,
|
||||
send_media_group::SendMediaGroup, send_message::SendMessage,
|
||||
send_photo::SendPhoto, send_poll::SendPoll, send_venue::SendVenue,
|
||||
send_contact::SendContact, send_document::SendDocument,
|
||||
send_location::SendLocation, send_media_group::SendMediaGroup,
|
||||
send_message::SendMessage, send_photo::SendPhoto, send_poll::SendPoll,
|
||||
send_venue::SendVenue, send_video::SendVideo,
|
||||
send_video_note::SendVideoNote, send_voice::SendVoice,
|
||||
stop_message_live_location::StopMessageLiveLocation,
|
||||
unban_chat_member::UnbanChatMember,
|
||||
};
|
||||
|
||||
mod form_builder;
|
||||
mod utils;
|
||||
|
||||
pub type ResponseResult<T> = Result<T, RequestError>;
|
||||
|
||||
/// Request that can be sent to telegram.
|
||||
|
@ -90,15 +95,21 @@ mod get_updates;
|
|||
mod get_user_profile_photos;
|
||||
mod kick_chat_member;
|
||||
mod pin_chat_message;
|
||||
mod promote_chat_member;
|
||||
mod restrict_chat_member;
|
||||
mod send_animation;
|
||||
mod send_audio;
|
||||
mod send_chat_action;
|
||||
mod send_contact;
|
||||
mod send_document;
|
||||
mod send_location;
|
||||
mod send_media_group;
|
||||
mod send_message;
|
||||
mod send_photo;
|
||||
mod send_poll;
|
||||
mod send_venue;
|
||||
mod send_video;
|
||||
mod send_video_note;
|
||||
mod send_voice;
|
||||
mod stop_message_live_location;
|
||||
mod unban_chat_member;
|
||||
|
|
|
@ -6,30 +6,38 @@ use crate::{
|
|||
types::True
|
||||
};
|
||||
|
||||
/// 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.).
|
||||
/// 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 PinChatMessage<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
/// Unique identifier for the target chat or username
|
||||
/// Unique identifier for the target chat or username
|
||||
/// of the target supergroup or channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
pub message_id: i32,
|
||||
pub disable_notification: Option<bool>
|
||||
pub disable_notification: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'a> PinChatMessage<'a> {
|
||||
pub(crate) fn new(
|
||||
ctx: RequestContext<'a>, chat_id: ChatId, message_id: i32
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: ChatId,
|
||||
message_id: i32,
|
||||
) -> Self {
|
||||
Self { ctx, chat_id, message_id, disable_notification: None }
|
||||
Self {
|
||||
ctx,
|
||||
chat_id,
|
||||
message_id,
|
||||
disable_notification: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_notification<T>(mut self, val: T) -> Self
|
||||
where T: Into<bool>
|
||||
where
|
||||
T: Into<bool>,
|
||||
{
|
||||
self.disable_notification = Some(val.into());
|
||||
self
|
||||
|
@ -39,7 +47,6 @@ impl<'a> PinChatMessage<'a> {
|
|||
#[async_trait]
|
||||
impl<'a> Request for PinChatMessage<'a> {
|
||||
type ReturnValue = True;
|
||||
|
||||
async fn send_boxed(self) -> ResponseResult<Self::ReturnValue> {
|
||||
self.send().await
|
||||
}
|
||||
|
|
8
src/requests/promote_chat_member.rs
Normal file
8
src/requests/promote_chat_member.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PromoteChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
8
src/requests/send_animation.rs
Normal file
8
src/requests/send_animation.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendAnimation<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
8
src/requests/send_document.rs
Normal file
8
src/requests/send_document.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendDocument<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
network,
|
||||
|
@ -6,8 +7,6 @@ use crate::{
|
|||
types::{Message, ReplyMarkup},
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
/// Use this method to send point on the map. On success, the sent [`Message`]
|
||||
/// is returned.
|
||||
|
|
8
src/requests/send_video.rs
Normal file
8
src/requests/send_video.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVideo<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
8
src/requests/send_video_note.rs
Normal file
8
src/requests/send_video_note.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVideoNote<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
8
src/requests/send_voice.rs
Normal file
8
src/requests/send_voice.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::requests::RequestContext;
|
||||
|
||||
///TODO: add implementation
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SendVoice<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
}
|
|
@ -1,8 +1,65 @@
|
|||
use crate::requests::RequestContext;
|
||||
//TODO:: need implementation
|
||||
use crate::network;
|
||||
use crate::requests::{
|
||||
ChatId, Request, RequestContext, RequestFuture, ResponseResult,
|
||||
};
|
||||
|
||||
/// Use this method to unban a previously kicked user in a supergroup or
|
||||
/// channel. The user will not return to the group or channel automatically, but
|
||||
/// will be able to join via link, etc. The bot must be an administrator for
|
||||
/// this to work. Returns True on success.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct UnbanChatMember<'a> {
|
||||
#[serde(skip_serializing)]
|
||||
ctx: RequestContext<'a>,
|
||||
///Unique identifier for the target group or username of the target
|
||||
/// supergroup or channel (in the format @channelusername)
|
||||
pub chat_id: ChatId,
|
||||
/// Unique identifier of the target user
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
impl<'a> Request<'a> for UnbanChatMember<'a> {
|
||||
type ReturnValue = bool;
|
||||
|
||||
fn send(self) -> RequestFuture<'a, ResponseResult<Self::ReturnValue>> {
|
||||
Box::pin(async move {
|
||||
network::request_json(
|
||||
&self.ctx.client,
|
||||
&self.ctx.token,
|
||||
"unbanChatMember",
|
||||
&self,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UnbanChatMember<'a> {
|
||||
pub(crate) fn new(
|
||||
ctx: RequestContext<'a>,
|
||||
chat_id: ChatId,
|
||||
user_id: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
ctx,
|
||||
chat_id,
|
||||
user_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_id<T>(mut self, chat_id: T) -> Self
|
||||
where
|
||||
T: Into<ChatId>,
|
||||
{
|
||||
self.chat_id = chat_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_id<T>(mut self, user_id: T) -> Self
|
||||
where
|
||||
T: Into<i32>,
|
||||
{
|
||||
self.user_id = user_id.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,9 +80,10 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::*;
|
||||
use serde_json::from_str;
|
||||
|
||||
use crate::types::*;
|
||||
|
||||
#[test]
|
||||
fn channel_de() {
|
||||
let expected = Chat {
|
||||
|
@ -113,9 +114,9 @@ mod tests {
|
|||
type_: (),
|
||||
username: Some("username".into()),
|
||||
first_name: Some("Anon".into()),
|
||||
last_name: None
|
||||
last_name: None,
|
||||
},
|
||||
photo: None
|
||||
photo: None,
|
||||
},
|
||||
from_str(
|
||||
r#"{"id":0,"type":"private","username":"username","first_name":"Anon"}"#
|
||||
|
|
|
@ -4,64 +4,68 @@ use super::passport_file::PassportFile;
|
|||
pub struct EncryptedPassportElement {
|
||||
pub hash: String,
|
||||
#[serde(flatten)]
|
||||
pub kind: EncryptedPassportElementKind
|
||||
pub kind: EncryptedPassportElementKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum EncryptedPassportElementKind {
|
||||
PersonalDetails {
|
||||
data: String
|
||||
data: String,
|
||||
},
|
||||
Passport {
|
||||
data: String,
|
||||
front_side: PassportFile,
|
||||
selfie: PassportFile,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
DriverLicense {
|
||||
data: String,
|
||||
front_side: PassportFile,
|
||||
reverse_side: PassportFile,
|
||||
selfie: PassportFile,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
IdentityCard {
|
||||
data: String,
|
||||
front_side: PassportFile,
|
||||
reverse_side: PassportFile,
|
||||
selfie: PassportFile,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
InternalPassport {
|
||||
data: String,
|
||||
front_side: PassportFile,
|
||||
selfie: PassportFile,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
Address {
|
||||
data: String
|
||||
data: String,
|
||||
},
|
||||
UtilityBill {
|
||||
files: Vec<PassportFile>,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
BankStatement {
|
||||
files: Vec<PassportFile>,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
RentalAgreement {
|
||||
files: Vec<PassportFile>,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
PassportRegistration {
|
||||
files: Vec<PassportFile>,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
TemporaryRegistration {
|
||||
files: Vec<PassportFile>,
|
||||
translation: Option<Vec<PassportFile>>
|
||||
translation: Option<Vec<PassportFile>>,
|
||||
},
|
||||
PhoneNumber {
|
||||
phone_number: String,
|
||||
},
|
||||
Email {
|
||||
email: String,
|
||||
},
|
||||
PhoneNumber { phone_number: String },
|
||||
Email { email: String }
|
||||
}
|
||||
|
|
|
@ -203,9 +203,10 @@ pub enum MediaKind {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::types::*;
|
||||
use serde_json::from_str;
|
||||
|
||||
use crate::types::*;
|
||||
|
||||
#[test]
|
||||
fn de_media_forwarded() {
|
||||
let json = r#"{
|
||||
|
|
|
@ -12,7 +12,9 @@ pub use self::{
|
|||
contact::Contact,
|
||||
document::Document,
|
||||
encrypted_credintials::EncryptedCredentials,
|
||||
encrypted_passport_element::{EncryptedPassportElement, EncryptedPassportElementKind},
|
||||
encrypted_passport_element::{
|
||||
EncryptedPassportElement, EncryptedPassportElementKind,
|
||||
},
|
||||
file::File,
|
||||
force_reply::ForceReply,
|
||||
game::Game,
|
||||
|
|
|
@ -16,7 +16,7 @@ impl std::convert::TryFrom<bool> for True {
|
|||
fn try_from(value: bool) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
true => Ok(True),
|
||||
false => Err(())
|
||||
false => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ impl std::convert::TryFrom<bool> for True {
|
|||
impl<'de> Deserialize<'de> for True {
|
||||
fn deserialize<D>(des: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
des.deserialize_bool(TrueVisitor)
|
||||
}
|
||||
|
@ -41,11 +41,11 @@ impl<'de> Visitor<'de> for TrueVisitor {
|
|||
|
||||
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error
|
||||
E: de::Error,
|
||||
{
|
||||
match value {
|
||||
true => Ok(True),
|
||||
false => Err(E::custom("expected `true`, found `false`"))
|
||||
false => Err(E::custom("expected `true`, found `false`")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,10 @@ impl Serialize for True {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::True;
|
||||
use serde_json::{from_str, to_string};
|
||||
|
||||
use super::True;
|
||||
|
||||
#[test]
|
||||
fn unit_true_de() {
|
||||
let json = "true";
|
||||
|
|
|
@ -16,7 +16,8 @@ pub enum UpdateKind {
|
|||
EditedMessage(Message),
|
||||
ChannelPost(Message),
|
||||
EditedChannelPost(Message),
|
||||
InlineQuery(()), // TODO
|
||||
InlineQuery(()),
|
||||
// TODO
|
||||
ChosenInlineResult(ChosenInlineResult),
|
||||
CallbackQuery(CallbackQuery),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue