Merge pull request #2 from teloxide/unstrict_get_updates

add `GetUpdatesNonStrict` - fail proof version of `GetUpdates`
This commit is contained in:
Waffle Lapkin 2020-08-16 20:36:53 +03:00 committed by GitHub
commit 0a397d8900
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 13 deletions

View file

@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `client_from_env` was moved from `teloxide::utils` to crate root of `teloxide-core`
- To simplify `GetUpdates` request it was changed to simply return `Vec<Update>`
(instead of `Vec<Result<Update, (Value, serde_json::Error)>>`)
- `GetUpdatesNonStrict` 'telegram' method, that behaves just like `GetUpdates` but doesn't
fail if one of updates fails to be deserialized
### Changed

View file

@ -7,15 +7,15 @@ use crate::{
EditInlineMessageText, EditMessageCaption, EditMessageLiveLocation, EditMessageMedia,
EditMessageReplyMarkup, EditMessageText, ExportChatInviteLink, ForwardMessage, GetChat,
GetChatAdministrators, GetChatMember, GetChatMembersCount, GetFile, GetGameHighScores,
GetMe, GetMyCommands, GetStickerSet, GetUpdates, GetUserProfilePhotos, GetWebhookInfo,
KickChatMember, LeaveChat, PinChatMessage, PromoteChatMember, RestrictChatMember,
SendAnimation, SendAudio, SendChatAction, SendChatActionKind, SendContact, SendDice,
SendDocument, SendGame, SendInvoice, SendLocation, SendMediaGroup, SendMessage, SendPhoto,
SendPoll, SendSticker, SendVenue, SendVideo, SendVideoNote, SendVoice,
SetChatAdministratorCustomTitle, SetChatDescription, SetChatPermissions, SetChatPhoto,
SetChatStickerSet, SetChatTitle, SetGameScore, SetMyCommands, SetStickerPositionInSet,
SetStickerSetThumb, SetWebhook, StopInlineMessageLiveLocation, StopMessageLiveLocation,
StopPoll, UnbanChatMember, UnpinChatMessage, UploadStickerFile,
GetMe, GetMyCommands, GetStickerSet, GetUpdates, GetUpdatesNonStrict, GetUserProfilePhotos,
GetWebhookInfo, KickChatMember, LeaveChat, PinChatMessage, PromoteChatMember,
RestrictChatMember, SendAnimation, SendAudio, SendChatAction, SendChatActionKind,
SendContact, SendDice, SendDocument, SendGame, SendInvoice, SendLocation, SendMediaGroup,
SendMessage, SendPhoto, SendPoll, SendSticker, SendVenue, SendVideo, SendVideoNote,
SendVoice, SetChatAdministratorCustomTitle, SetChatDescription, SetChatPermissions,
SetChatPhoto, SetChatStickerSet, SetChatTitle, SetGameScore, SetMyCommands,
SetStickerPositionInSet, SetStickerSetThumb, SetWebhook, StopInlineMessageLiveLocation,
StopMessageLiveLocation, StopPoll, UnbanChatMember, UnpinChatMessage, UploadStickerFile,
},
types::{
BotCommand, ChatId, ChatPermissions, InlineQueryResult, InputFile, InputMedia,
@ -39,6 +39,18 @@ impl Bot {
GetUpdates::new(self.clone())
}
/// This is non strict version of [`get_updates`], this means that if it
/// will fail to deserialize some updates, it won't fail entirely, but
/// will just return some errors.
///
/// Note: this is not a 'real' telegram method, this is simply
/// [`get_updates`] with changed return type.
///
/// [`get_updates`]: crate::Bot::get_updates
pub fn get_updates_non_strict(&self) -> GetUpdatesNonStrict {
GetUpdatesNonStrict::new(self.clone())
}
/// Use this method to specify a url and receive incoming updates via an
/// outgoing webhook.
///

View file

@ -21,15 +21,13 @@ use crate::{
#[derive(Debug, Clone, Serialize)]
pub struct GetUpdates {
#[serde(skip_serializing)]
bot: Bot,
pub(crate) bot: Bot,
pub(crate) offset: Option<i32>,
pub(crate) limit: Option<u8>,
pub(crate) timeout: Option<u32>,
pub(crate) allowed_updates: Option<Vec<AllowedUpdate>>,
}
// TODO: Add version of this method that will ignore unparsed updates,
// to be bullet proof
#[async_trait::async_trait]
impl Request for GetUpdates {
type Output = Vec<Update>;

View file

@ -0,0 +1,97 @@
use serde::Serialize;
use crate::{
net,
requests::{GetUpdates, Request, ResponseResult},
types::{AllowedUpdate, NonStrictVec, Update},
Bot,
};
/// This is non strict version of [`GetUpdates`], this means that if it will
/// fail to deserialize some updates, it won't fail entirely, but will just
/// return some errors.
///
/// Note: this is not a 'real' telegram method, this is simply [`GetUpdates`]
/// with changed return type.
///
/// [`GetUpdates`]: crate::requests::GetUpdates
#[derive(Debug, Clone, Serialize)]
#[serde(transparent)]
pub struct GetUpdatesNonStrict(pub GetUpdates);
#[async_trait::async_trait]
impl Request for GetUpdatesNonStrict {
type Output = NonStrictVec<Update>;
async fn send(&self) -> ResponseResult<Self::Output> {
net::request_json(self.0.bot.client(), self.0.bot.token(), "getUpdates", &self).await
}
}
impl GetUpdatesNonStrict {
pub(crate) fn new(bot: Bot) -> Self {
Self(GetUpdates::new(bot))
}
/// Identifier of the first update to be returned.
///
/// Must be greater by one than the highest among the identifiers of
/// previously received updates. By default, updates starting with the
/// earliest unconfirmed update are returned. An update is considered
/// confirmed as soon as [`GetUpdates`] is called with an [`offset`]
/// higher than its [`id`]. The negative offset can be specified to
/// retrieve updates starting from `-offset` update from the end of the
/// updates queue. All previous updates will forgotten.
///
/// [`GetUpdates`]: self::GetUpdates
/// [`offset`]: self::GetUpdates::offset
/// [`id`]: crate::types::Update::id
pub fn offset(mut self, value: i32) -> Self {
self.0.offset = Some(value);
self
}
/// Limits the number of updates to be retrieved.
///
/// Values between `1`—`100` are accepted. Defaults to `100`.
pub fn limit(mut self, value: u8) -> Self {
self.0.limit = Some(value);
self
}
/// Timeout in seconds for long polling.
///
/// Defaults to `0`, i.e. usual short polling. Should be positive, short
/// polling should be used for testing purposes only.
pub fn timeout(mut self, value: u32) -> Self {
self.0.timeout = Some(value);
self
}
/// List the types of updates you want your bot to receive.
///
/// For example, specify [[`Message`], [`EditedChannelPost`],
/// [`CallbackQuery`]] to only receive updates of these types.
/// See [`AllowedUpdate`] for a complete list of available update types.
///
/// Specify an empty list to receive all updates regardless of type
/// (default). If not specified, the previous setting will be used.
///
/// **Note:**
/// This parameter doesn't affect updates created before the call to the
/// [`Bot::get_updates`], so unwanted updates may be received for a short
/// period of time.
///
/// [`Message`]: self::AllowedUpdate::Message
/// [`EditedChannelPost`]: self::AllowedUpdate::EditedChannelPost
/// [`CallbackQuery`]: self::AllowedUpdate::CallbackQuery
/// [`AllowedUpdate`]: self::AllowedUpdate
/// [`Bot::get_updates`]: crate::Bot::get_updates
pub fn allowed_updates<T>(mut self, value: T) -> Self
where
T: Into<Vec<AllowedUpdate>>,
{
self.0.allowed_updates = Some(value.into());
self
}
}

View file

@ -31,6 +31,7 @@ mod get_me;
mod get_my_commands;
mod get_sticker_set;
mod get_updates;
mod get_updates_non_strict;
mod get_user_profile_photos;
mod get_webhook_info;
mod kick_chat_member;
@ -107,6 +108,7 @@ pub use get_me::*;
pub use get_my_commands::*;
pub use get_sticker_set::*;
pub use get_updates::*;
pub use get_updates_non_strict::*;
pub use get_user_profile_photos::*;
pub use get_webhook_info::*;
pub use kick_chat_member::*;

View file

@ -266,7 +266,7 @@ impl<'de> serde::de::Visitor<'de> for PrivateChatKindVisitor {
write!(f, r#"field equal to "private""#)
}
fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
match v {
"private" => Ok(()),
_ => Err(E::invalid_value(serde::de::Unexpected::Str(v), &r#""private""#)),

View file

@ -1,7 +1,9 @@
pub use country_code::*;
pub use currency::*;
pub use mime_wrapper::*;
pub use non_strict_vec::*;
mod country_code;
mod currency;
mod mime_wrapper;
mod non_strict_vec;

View file

@ -0,0 +1,49 @@
use serde::de::DeserializeOwned;
use serde_json::{from_value, Value};
/// Similar to `Vec<T>` but if it fails to deserialize element, it just saves
/// `Err((value, err))`
#[derive(Debug, serde::Deserialize)]
#[serde(from = "Vec<serde_json::Value>")]
#[serde(bound = "T: DeserializeOwned")]
pub struct NonStrictVec<T>(pub Vec<Result<T, (serde_json::Value, serde_json::Error)>>);
impl<T: DeserializeOwned> From<Vec<serde_json::Value>> for NonStrictVec<T> {
fn from(vec: Vec<Value>) -> Self {
Self(vec.into_iter().map(|val| from_value(val.clone()).map_err(|e| (val, e))).collect())
}
}
#[test]
fn test() {
use crate::types::Update;
let x: NonStrictVec<Update> = serde_json::from_str(
r#"[{
"update_id": 923808447,
"message": {
"message_id": 361678,
"from": {
"id": 218485655,
"is_bot": false,
"first_name": "вафель",
"last_name": "🧇",
"username": "WaffleLapkin",
"language_code": "en"
},
"chat": {
"id": 218485655,
"first_name": "вафель",
"last_name": "🧇",
"username": "WaffleLapkin",
"type": "private"
},
"date": 1595860067,
"text": "s"
}
}]"#,
)
.unwrap();
assert!(x.0.first().unwrap().is_ok())
}