mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
Merge pull request #115 from teloxide/make_urls_urlier
Use `url::Url` for urls, use `chrono::DateTime<Utc>` for dates in types
This commit is contained in:
commit
3d7ca29681
34 changed files with 246 additions and 215 deletions
|
@ -8,8 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
## [unreleased]
|
||||
|
||||
- Add `EditedMessageIsTooLong` error [#109][pr109]
|
||||
- Use `url::Url` for urls, use `chrono::DateTime<Utc>` for dates in types ([#115][pr115])
|
||||
|
||||
[pr109]: https://github.com/teloxide/teloxide-core/pull/109
|
||||
[pr115]: https://github.com/teloxide/teloxide-core/pull/115
|
||||
|
||||
## 0.3.3
|
||||
|
||||
|
|
|
@ -485,7 +485,8 @@ impl Serializer for PartSerializer {
|
|||
let part = Part::text(format!("attach://{}", uuid));
|
||||
Ok((part, vec![(uuid, f)]))
|
||||
}
|
||||
InputFile::FileId(s) | InputFile::Url(s) => Ok((Part::text(s), Vec::new())),
|
||||
InputFile::FileId(s) => Ok((Part::text(s), Vec::new())),
|
||||
InputFile::Url(s) => Ok((Part::text(String::from(s)), Vec::new())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,7 +587,8 @@ impl SerializeStructVariant for PartFromFile {
|
|||
|
||||
Ok((part, vec![(uuid, f)]))
|
||||
}
|
||||
InputFile::FileId(s) | InputFile::Url(s) => Ok((Part::text(s), vec![])),
|
||||
InputFile::FileId(s) => Ok((Part::text(s), vec![])),
|
||||
InputFile::Url(s) => Ok((Part::text(String::from(s)), vec![])),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -615,9 +617,12 @@ impl SerializeSeq for InnerPartSerializer {
|
|||
value["media"] = serde_json::Value::String(format!("attach://{}", uuid));
|
||||
self.files.push((uuid, f));
|
||||
}
|
||||
InputFile::FileId(s) | InputFile::Url(s) => {
|
||||
InputFile::FileId(s) => {
|
||||
value["media"] = serde_json::Value::String(s);
|
||||
}
|
||||
InputFile::Url(s) => {
|
||||
value["media"] = serde_json::Value::String(String::from(s));
|
||||
}
|
||||
}
|
||||
|
||||
self.array_json_parts.push(value);
|
||||
|
@ -673,10 +678,14 @@ impl SerializeStruct for PartSerializerStruct {
|
|||
|
||||
self.2.push((uuid, f));
|
||||
}
|
||||
InputFile::FileId(s) | InputFile::Url(s) => {
|
||||
InputFile::FileId(s) => {
|
||||
SerializeStruct::serialize_field(&mut ser, key, &s)?;
|
||||
self.1 = get_state(ser)
|
||||
}
|
||||
InputFile::Url(s) => {
|
||||
SerializeStruct::serialize_field(&mut ser, key, s.as_str())?;
|
||||
self.1 = get_state(ser)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SerializeStruct::serialize_field(&mut ser, key, value)?;
|
||||
|
|
|
@ -84,7 +84,7 @@ fn test() {
|
|||
let value = String::from("test");
|
||||
assert_eq!(value.serialize(StringUnserializer), Ok(value));
|
||||
|
||||
let value = InputFile::Url(String::from("url"));
|
||||
let value = InputFile::Url(reqwest::Url::parse("http://example.com").unwrap());
|
||||
assert_eq!(value.serialize(InputFileUnserializer::NotMem), Ok(value));
|
||||
|
||||
let value = InputFile::FileId(String::from("file_id"));
|
||||
|
|
|
@ -62,7 +62,9 @@ impl Serializer for InputFileUnserializer {
|
|||
// TODO
|
||||
match variant {
|
||||
"File" => Ok(InputFile::File(value.serialize(StringUnserializer)?.into())),
|
||||
"Url" => Ok(InputFile::Url(value.serialize(StringUnserializer)?)),
|
||||
"Url" => Ok(InputFile::Url(
|
||||
reqwest::Url::parse(&value.serialize(StringUnserializer)?).unwrap(),
|
||||
)),
|
||||
"FileId" => Ok(InputFile::FileId(value.serialize(StringUnserializer)?)),
|
||||
name => Err(UnserializerError::UnexpectedVariant {
|
||||
name,
|
||||
|
|
48
src/types.rs
48
src/types.rs
|
@ -221,8 +221,8 @@ mod non_telegram_types {
|
|||
}
|
||||
|
||||
pub(crate) mod serde_opt_date_from_unix_timestamp {
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Serialize, Serializer};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub(crate) fn serialize<S>(
|
||||
this: &Option<DateTime<Utc>>,
|
||||
|
@ -234,14 +234,42 @@ pub(crate) mod serde_opt_date_from_unix_timestamp {
|
|||
this.map(|dt| dt.timestamp()).serialize(serializer)
|
||||
}
|
||||
|
||||
// pub(crate) fn deserialize<'de, D>(deserializer: D) ->
|
||||
// Result<Option<DateTime<Utc>>, D::Error> where
|
||||
// D: Deserializer<'de>,
|
||||
// {
|
||||
// Ok(Option::<i64>::deserialize(deserializer)?
|
||||
// .map(|timestamp|
|
||||
// DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)))
|
||||
// }
|
||||
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(Option::<i64>::deserialize(deserializer)?
|
||||
.map(|timestamp| DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)))
|
||||
}
|
||||
|
||||
pub(crate) fn none<T>() -> Option<T> {
|
||||
None
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Struct {
|
||||
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
|
||||
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
|
||||
date: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
{
|
||||
let json = r#"{"date":1}"#;
|
||||
let expected = DateTime::from_utc(NaiveDateTime::from_timestamp(1, 0), Utc);
|
||||
|
||||
let Struct { date } = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(date, Some(expected));
|
||||
}
|
||||
|
||||
{
|
||||
let json = r#"{}"#;
|
||||
|
||||
let Struct { date } = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(date, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod serde_date_from_unix_timestamp {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::User;
|
||||
|
@ -13,9 +14,11 @@ pub struct ChatInviteLink {
|
|||
pub is_primary: bool,
|
||||
/// `true`, if the link is revoked
|
||||
pub is_revoked: bool,
|
||||
/// Point in time (Unix timestamp) when the link will expire or has been
|
||||
/// Point in time when the link will expire or has been
|
||||
/// expired
|
||||
pub expire_date: Option<i64>,
|
||||
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
|
||||
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
|
||||
pub expire_date: Option<DateTime<Utc>>,
|
||||
/// Maximum number of users that can be members of the chat simultaneously
|
||||
/// after joining the chat via this invite link; 1-99999
|
||||
pub member_limit: Option<u32>,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{Chat, ChatInviteLink, ChatMember, User};
|
||||
|
@ -8,8 +9,9 @@ pub struct ChatMemberUpdated {
|
|||
pub chat: Chat,
|
||||
/// Performer of the action, which resulted in the change
|
||||
pub from: User,
|
||||
/// Date the change was done in Unix time
|
||||
pub date: i64,
|
||||
/// Date the change was done
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub date: DateTime<Utc>,
|
||||
/// Previous information about the chat member
|
||||
pub old_chat_member: ChatMember,
|
||||
/// New information about the chat member
|
||||
|
|
|
@ -42,7 +42,7 @@ impl InlineKeyboardButton {
|
|||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InlineKeyboardButtonKind {
|
||||
/// HTTP or tg:// url to be opened when button is pressed.
|
||||
Url(String),
|
||||
Url(reqwest::Url),
|
||||
|
||||
/// An HTTP URL used to automatically authorize the user. Can be used as a
|
||||
/// replacement for the [Telegram Login Widget]().
|
||||
|
@ -104,10 +104,11 @@ pub enum InlineKeyboardButtonKind {
|
|||
/// ```
|
||||
/// use teloxide_core::types::InlineKeyboardButton;
|
||||
///
|
||||
/// let url_button = InlineKeyboardButton::url("Text".to_string(), "http://url.com".to_string());
|
||||
/// let url = url::Url::parse("https://example.com").unwrap();
|
||||
/// let url_button = InlineKeyboardButton::url("Text".to_string(), url);
|
||||
/// ```
|
||||
impl InlineKeyboardButton {
|
||||
pub fn url(text: String, url: String) -> InlineKeyboardButton {
|
||||
pub fn url(text: String, url: reqwest::Url) -> InlineKeyboardButton {
|
||||
InlineKeyboardButton {
|
||||
text,
|
||||
kind: InlineKeyboardButtonKind::Url(url),
|
||||
|
|
|
@ -26,7 +26,8 @@ pub struct InlineKeyboardMarkup {
|
|||
/// ```
|
||||
/// use teloxide_core::types::{InlineKeyboardButton, InlineKeyboardMarkup};
|
||||
///
|
||||
/// let url_button = InlineKeyboardButton::url("text".to_string(), "http://url.com".to_string());
|
||||
/// let url = url::Url::parse("https://example.com").unwrap();
|
||||
/// let url_button = InlineKeyboardButton::url("text".to_string(), url);
|
||||
/// let keyboard = InlineKeyboardMarkup::default().append_row(vec![url_button]);
|
||||
/// ```
|
||||
impl InlineKeyboardMarkup {
|
||||
|
@ -78,10 +79,14 @@ impl InlineKeyboardMarkup {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn url(n: u32) -> reqwest::Url {
|
||||
reqwest::Url::parse(&format!("https://example.com/{n}", n = n)).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn append_row() {
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), url(1));
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), url(2));
|
||||
|
||||
let markup =
|
||||
InlineKeyboardMarkup::default().append_row(vec![button1.clone(), button2.clone()]);
|
||||
|
@ -95,8 +100,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn append_to_row_existent_row() {
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), url(1));
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), url(2));
|
||||
|
||||
let markup = InlineKeyboardMarkup::default()
|
||||
.append_row(vec![button1.clone()])
|
||||
|
@ -111,8 +116,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn append_to_row_nonexistent_row() {
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), "url 1".to_string());
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), "url 2".to_string());
|
||||
let button1 = InlineKeyboardButton::url("text 1".to_string(), url(1));
|
||||
let button2 = InlineKeyboardButton::url("text 2".to_string(), url(2));
|
||||
|
||||
let markup = InlineKeyboardMarkup::default()
|
||||
.append_row(vec![button1.clone()])
|
||||
|
|
|
@ -21,7 +21,7 @@ pub struct InlineQueryResultArticle {
|
|||
pub reply_markup: Option<InlineKeyboardMarkup>,
|
||||
|
||||
/// URL of the result.
|
||||
pub url: Option<String>,
|
||||
pub url: Option<reqwest::Url>,
|
||||
|
||||
/// Pass `true`, if you don't want the URL to be shown in the
|
||||
/// message.
|
||||
|
@ -31,7 +31,7 @@ pub struct InlineQueryResultArticle {
|
|||
pub description: Option<String>,
|
||||
|
||||
/// Url of the thumbnail for the result.
|
||||
pub thumb_url: Option<String>,
|
||||
pub thumb_url: Option<reqwest::Url>,
|
||||
|
||||
/// Thumbnail width.
|
||||
pub thumb_width: Option<i32>,
|
||||
|
@ -86,11 +86,8 @@ impl InlineQueryResultArticle {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.url = Some(val.into());
|
||||
pub fn url(mut self, val: reqwest::Url) -> Self {
|
||||
self.url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -107,11 +104,8 @@ impl InlineQueryResultArticle {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = Some(val.into());
|
||||
pub fn thumb_url<S>(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct InlineQueryResultAudio {
|
|||
pub id: String,
|
||||
|
||||
/// A valid URL for the audio file.
|
||||
pub audio_url: String,
|
||||
pub audio_url: reqwest::Url,
|
||||
|
||||
/// Title.
|
||||
pub title: String,
|
||||
|
@ -52,15 +52,14 @@ pub struct InlineQueryResultAudio {
|
|||
}
|
||||
|
||||
impl InlineQueryResultAudio {
|
||||
pub fn new<S1, S2, S3>(id: S1, audio_url: S2, title: S3) -> Self
|
||||
pub fn new<S1, S2>(id: S1, audio_url: reqwest::Url, title: S2) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
audio_url: audio_url.into(),
|
||||
audio_url,
|
||||
title: title.into(),
|
||||
caption: None,
|
||||
parse_mode: None,
|
||||
|
@ -80,11 +79,8 @@ impl InlineQueryResultAudio {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn audio_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.audio_url = val.into();
|
||||
pub fn audio_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.audio_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ impl InlineQueryResultCachedDocument {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ impl InlineQueryResultCachedMpeg4Gif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ impl InlineQueryResultCachedPhoto {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ impl InlineQueryResultCachedVideo {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ impl InlineQueryResultCachedVoice {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ pub struct InlineQueryResultContact {
|
|||
pub input_message_content: Option<InputMessageContent>,
|
||||
|
||||
/// Url of the thumbnail for the result.
|
||||
pub thumb_url: Option<String>,
|
||||
pub thumb_url: Option<reqwest::Url>,
|
||||
|
||||
/// Thumbnail width.
|
||||
pub thumb_width: Option<i32>,
|
||||
|
@ -119,11 +119,8 @@ impl InlineQueryResultContact {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = Some(val.into());
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ pub struct InlineQueryResultDocument {
|
|||
pub caption_entities: Option<Vec<MessageEntity>>,
|
||||
|
||||
/// A valid URL for the file.
|
||||
pub document_url: String,
|
||||
pub document_url: reqwest::Url,
|
||||
|
||||
/// Mime type of the content of the file, either `application/pdf` or
|
||||
/// `application/zip`.
|
||||
|
@ -53,7 +53,7 @@ pub struct InlineQueryResultDocument {
|
|||
pub input_message_content: Option<InputMessageContent>,
|
||||
|
||||
/// URL of the thumbnail (jpeg only) for the file.
|
||||
pub thumb_url: Option<String>,
|
||||
pub thumb_url: Option<reqwest::Url>,
|
||||
|
||||
/// Thumbnail width.
|
||||
pub thumb_width: Option<i32>,
|
||||
|
@ -87,7 +87,7 @@ impl InlineQueryResultDocument {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
@ -100,11 +100,8 @@ impl InlineQueryResultDocument {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn document_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.document_url = val.into();
|
||||
pub fn document_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.document_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -131,11 +128,8 @@ impl InlineQueryResultDocument {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = Some(val.into());
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct InlineQueryResultGif {
|
|||
pub id: String,
|
||||
|
||||
/// A valid URL for the GIF file. File size must not exceed 1MB.
|
||||
pub gif_url: String,
|
||||
pub gif_url: reqwest::Url,
|
||||
|
||||
/// Width of the GIF.
|
||||
pub gif_width: Option<i32>,
|
||||
|
@ -28,7 +28,7 @@ pub struct InlineQueryResultGif {
|
|||
pub gif_duration: Option<i32>,
|
||||
|
||||
/// URL of the static thumbnail for the result (jpeg or gif).
|
||||
pub thumb_url: String,
|
||||
pub thumb_url: reqwest::Url,
|
||||
|
||||
/// Title for the result.
|
||||
pub title: Option<String>,
|
||||
|
@ -58,19 +58,17 @@ pub struct InlineQueryResultGif {
|
|||
}
|
||||
|
||||
impl InlineQueryResultGif {
|
||||
pub fn new<S1, S2, S3>(id: S1, gif_url: S2, thumb_url: S3) -> Self
|
||||
pub fn new<S>(id: S, gif_url: reqwest::Url, thumb_url: reqwest::Url) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
gif_url: gif_url.into(),
|
||||
gif_url,
|
||||
gif_width: None,
|
||||
gif_height: None,
|
||||
gif_duration: None,
|
||||
thumb_url: thumb_url.into(),
|
||||
thumb_url,
|
||||
title: None,
|
||||
caption: None,
|
||||
parse_mode: None,
|
||||
|
@ -88,11 +86,8 @@ impl InlineQueryResultGif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn gif_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.gif_url = val.into();
|
||||
pub fn gif_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.gif_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -111,11 +106,8 @@ impl InlineQueryResultGif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = val.into();
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ pub struct InlineQueryResultLocation {
|
|||
pub input_message_content: Option<InputMessageContent>,
|
||||
|
||||
/// Url of the thumbnail for the result.
|
||||
pub thumb_url: Option<String>,
|
||||
pub thumb_url: Option<reqwest::Url>,
|
||||
|
||||
/// Thumbnail width.
|
||||
pub thumb_width: Option<u32>,
|
||||
|
@ -137,11 +137,8 @@ impl InlineQueryResultLocation {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = Some(val.into());
|
||||
pub fn thumb_url<S>(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct InlineQueryResultMpeg4Gif {
|
|||
pub id: String,
|
||||
|
||||
/// A valid URL for the MP4 file. File size must not exceed 1MB.
|
||||
pub mpeg4_url: String,
|
||||
pub mpeg4_url: reqwest::Url,
|
||||
|
||||
/// Video width.
|
||||
pub mpeg4_width: Option<i32>,
|
||||
|
@ -29,7 +29,7 @@ pub struct InlineQueryResultMpeg4Gif {
|
|||
pub mpeg4_duration: Option<i32>,
|
||||
|
||||
/// URL of the static thumbnail (jpeg or gif) for the result.
|
||||
pub thumb_url: String,
|
||||
pub thumb_url: reqwest::Url,
|
||||
|
||||
/// Title for the result.
|
||||
pub title: Option<String>,
|
||||
|
@ -59,16 +59,14 @@ pub struct InlineQueryResultMpeg4Gif {
|
|||
}
|
||||
|
||||
impl InlineQueryResultMpeg4Gif {
|
||||
pub fn new<S1, S2, S3>(id: S1, mpeg4_url: S2, thumb_url: S3) -> Self
|
||||
pub fn new<S>(id: S, mpeg4_url: reqwest::Url, thumb_url: reqwest::Url) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
mpeg4_url: mpeg4_url.into(),
|
||||
thumb_url: thumb_url.into(),
|
||||
mpeg4_url,
|
||||
thumb_url,
|
||||
mpeg4_width: None,
|
||||
mpeg4_height: None,
|
||||
mpeg4_duration: None,
|
||||
|
@ -89,11 +87,8 @@ impl InlineQueryResultMpeg4Gif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn mpeg4_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.mpeg4_url = val.into();
|
||||
pub fn mpeg4_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.mpeg4_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -112,11 +107,8 @@ impl InlineQueryResultMpeg4Gif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = val.into();
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -136,7 +128,7 @@ impl InlineQueryResultMpeg4Gif {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ pub struct InlineQueryResultPhoto {
|
|||
|
||||
/// A valid URL of the photo. Photo must be in **jpeg** format. Photo size
|
||||
/// must not exceed 5MB.
|
||||
pub photo_url: String,
|
||||
pub photo_url: reqwest::Url,
|
||||
|
||||
/// URL of the thumbnail for the photo.
|
||||
pub thumb_url: String,
|
||||
pub thumb_url: reqwest::Url,
|
||||
|
||||
/// Width of the photo.
|
||||
pub photo_width: Option<i32>,
|
||||
|
@ -59,16 +59,14 @@ pub struct InlineQueryResultPhoto {
|
|||
}
|
||||
|
||||
impl InlineQueryResultPhoto {
|
||||
pub fn new<S1, S2, S3>(id: S1, photo_url: S2, thumb_url: S3) -> Self
|
||||
pub fn new<S>(id: S, photo_url: reqwest::Url, thumb_url: reqwest::Url) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
photo_url: photo_url.into(),
|
||||
thumb_url: thumb_url.into(),
|
||||
photo_url,
|
||||
thumb_url,
|
||||
photo_width: None,
|
||||
photo_height: None,
|
||||
title: None,
|
||||
|
@ -89,28 +87,22 @@ impl InlineQueryResultPhoto {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn photo_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.photo_url = val.into();
|
||||
pub fn photo_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.photo_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = val.into();
|
||||
pub fn thumb_url<S>(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn photo_width<S>(mut self, val: i32) -> Self {
|
||||
pub fn photo_width(mut self, val: i32) -> Self {
|
||||
self.photo_width = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn photo_height<S>(mut self, val: i32) -> Self {
|
||||
pub fn photo_height(mut self, val: i32) -> Self {
|
||||
self.photo_height = Some(val);
|
||||
self
|
||||
}
|
||||
|
@ -139,7 +131,7 @@ impl InlineQueryResultPhoto {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn parse_mode<S>(mut self, val: ParseMode) -> Self {
|
||||
pub fn parse_mode(mut self, val: ParseMode) -> Self {
|
||||
self.parse_mode = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ pub struct InlineQueryResultVenue {
|
|||
pub input_message_content: Option<InputMessageContent>,
|
||||
|
||||
/// Url of the thumbnail for the result.
|
||||
pub thumb_url: Option<String>,
|
||||
pub thumb_url: Option<reqwest::Url>,
|
||||
|
||||
/// Thumbnail width.
|
||||
pub thumb_width: Option<i32>,
|
||||
|
@ -162,11 +162,8 @@ impl InlineQueryResultVenue {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = Some(val.into());
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = Some(val);
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,14 @@ pub struct InlineQueryResultVideo {
|
|||
pub id: String,
|
||||
|
||||
/// A valid URL for the embedded video player or video file.
|
||||
pub video_url: String,
|
||||
pub video_url: reqwest::Url,
|
||||
|
||||
/// Mime type of the content of video url, `text/html` or `video/mp4`.
|
||||
#[serde(with = "crate::types::non_telegram_types::mime::deser")]
|
||||
pub mime_type: Mime,
|
||||
|
||||
/// URL of the thumbnail (jpeg only) for the video.
|
||||
pub thumb_url: String,
|
||||
pub thumb_url: reqwest::Url,
|
||||
|
||||
/// Title for the result.
|
||||
pub title: String,
|
||||
|
@ -72,24 +72,22 @@ pub struct InlineQueryResultVideo {
|
|||
}
|
||||
|
||||
impl InlineQueryResultVideo {
|
||||
pub fn new<S1, S2, S3, S4>(
|
||||
pub fn new<S1, S2>(
|
||||
id: S1,
|
||||
video_url: S2,
|
||||
video_url: reqwest::Url,
|
||||
mime_type: Mime,
|
||||
thumb_url: S3,
|
||||
title: S4,
|
||||
thumb_url: reqwest::Url,
|
||||
title: S2,
|
||||
) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
S4: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
video_url: video_url.into(),
|
||||
video_url,
|
||||
mime_type,
|
||||
thumb_url: thumb_url.into(),
|
||||
thumb_url,
|
||||
title: title.into(),
|
||||
caption: None,
|
||||
parse_mode: None,
|
||||
|
@ -111,11 +109,8 @@ impl InlineQueryResultVideo {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn video_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.video_url = val.into();
|
||||
pub fn video_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.video_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -124,11 +119,8 @@ impl InlineQueryResultVideo {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn thumb_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.thumb_url = val.into();
|
||||
pub fn thumb_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.thumb_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct InlineQueryResultVoice {
|
|||
pub id: String,
|
||||
|
||||
/// A valid URL for the voice recording.
|
||||
pub voice_url: String,
|
||||
pub voice_url: reqwest::Url,
|
||||
|
||||
/// Recording title.
|
||||
pub title: String,
|
||||
|
@ -50,15 +50,14 @@ pub struct InlineQueryResultVoice {
|
|||
}
|
||||
|
||||
impl InlineQueryResultVoice {
|
||||
pub fn new<S1, S2, S3>(id: S1, voice_url: S2, title: S3) -> Self
|
||||
pub fn new<S1, S2>(id: S1, voice_url: reqwest::Url, title: S2) -> Self
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
{
|
||||
Self {
|
||||
id: id.into(),
|
||||
voice_url: voice_url.into(),
|
||||
voice_url,
|
||||
title: title.into(),
|
||||
caption: None,
|
||||
parse_mode: None,
|
||||
|
@ -77,11 +76,8 @@ impl InlineQueryResultVoice {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn voice_url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.voice_url = val.into();
|
||||
pub fn voice_url(mut self, val: reqwest::Url) -> Self {
|
||||
self.voice_url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use reqwest::Url;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::{borrow::Cow, path::PathBuf};
|
||||
|
@ -13,7 +14,7 @@ pub enum InputFile {
|
|||
file_name: String,
|
||||
data: Cow<'static, [u8]>,
|
||||
},
|
||||
Url(String),
|
||||
Url(Url),
|
||||
FileId(String),
|
||||
}
|
||||
|
||||
|
@ -36,11 +37,8 @@ impl InputFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn url<T>(url: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self::Url(url.into())
|
||||
pub fn url(url: Url) -> Self {
|
||||
Self::Url(url)
|
||||
}
|
||||
|
||||
pub fn file_id<T>(file_id: T) -> Self
|
||||
|
@ -57,7 +55,7 @@ impl InputFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_url(&self) -> Option<&String> {
|
||||
pub fn as_url(&self) -> Option<&Url> {
|
||||
match self {
|
||||
Self::Url(url) => Some(url),
|
||||
_ => None,
|
||||
|
@ -118,7 +116,8 @@ impl InputFile {
|
|||
Ok(Part::stream(Body::wrap_stream(file)).file_name(file_name))
|
||||
}
|
||||
Self::Memory { file_name, data } => Ok(Part::bytes(data).file_name(file_name)),
|
||||
Self::Url(s) | Self::FileId(s) => Ok(Part::text(s)),
|
||||
Self::Url(s) => Ok(Part::text(String::from(s))),
|
||||
Self::FileId(s) => Ok(Part::text(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,18 +15,39 @@ use serde::{Deserialize, Serialize};
|
|||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct LoginUrl {
|
||||
pub url: String,
|
||||
/// An HTTP URL to be opened with user authorization data added to the query
|
||||
/// string when the button is pressed. If the user refuses to provide
|
||||
/// authorization data, the original URL without information about the user
|
||||
/// will be opened. The data added is the same as described in [Receiving
|
||||
/// authorization data].
|
||||
///
|
||||
/// [Receiving authorization data]: https://core.telegram.org/widgets/login#receiving-authorization-data
|
||||
///
|
||||
/// NOTE: You must always check the hash of the received data to verify the
|
||||
/// authentication and the integrity of the data as described in [Checking
|
||||
/// authorization].
|
||||
///
|
||||
/// [Checking authorization]: https://core.telegram.org/widgets/login#checking-authorization
|
||||
pub url: reqwest::Url,
|
||||
/// New text of the button in forwarded messages.
|
||||
pub forward_text: Option<String>,
|
||||
/// Username of a bot, which will be used for user authorization. See
|
||||
/// [Setting up a bot] for more details. If not specified, the current bot's
|
||||
/// username will be assumed. The url's domain must be the same as the
|
||||
/// domain linked with the bot. See [Linking your domain to the bot] for
|
||||
/// more details.
|
||||
///
|
||||
/// [Setting up a bot]: https://core.telegram.org/widgets/login#setting-up-a-bot
|
||||
/// [Linking your domain to the bot]: https://core.telegram.org/widgets/login#linking-your-domain-to-the-bot
|
||||
pub bot_username: Option<String>,
|
||||
/// Pass `true` to request the permission for your bot to send messages to
|
||||
/// the user.
|
||||
pub request_write_access: Option<bool>,
|
||||
}
|
||||
|
||||
impl LoginUrl {
|
||||
pub fn url<S>(mut self, val: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.url = val.into();
|
||||
pub fn url(mut self, val: reqwest::Url) -> Self {
|
||||
self.url = val;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -46,7 +67,7 @@ impl LoginUrl {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn request_write_access<S>(mut self, val: bool) -> Self {
|
||||
pub fn request_write_access(mut self, val: bool) -> Self {
|
||||
self.request_write_access = Some(val);
|
||||
self
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::types::{
|
||||
|
@ -19,7 +20,8 @@ pub struct Message {
|
|||
pub id: i32,
|
||||
|
||||
/// Date the message was sent in Unix time.
|
||||
pub date: i32,
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub date: DateTime<Utc>,
|
||||
|
||||
/// Conversation the message belongs to.
|
||||
pub chat: Chat,
|
||||
|
@ -78,7 +80,9 @@ pub struct MessageCommon {
|
|||
pub forward_kind: ForwardKind,
|
||||
|
||||
/// Date the message was last edited in Unix time.
|
||||
pub edit_date: Option<i32>,
|
||||
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
|
||||
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
|
||||
pub edit_date: Option<DateTime<Utc>>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub media_kind: MediaKind,
|
||||
|
@ -234,7 +238,8 @@ pub enum ForwardKind {
|
|||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForwardChannel {
|
||||
#[serde(rename = "forward_date")]
|
||||
pub date: i32,
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub date: DateTime<Utc>,
|
||||
|
||||
#[serde(rename = "forward_from_chat")]
|
||||
pub chat: Chat,
|
||||
|
@ -249,7 +254,8 @@ pub struct ForwardChannel {
|
|||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ForwardNonChannel {
|
||||
#[serde(rename = "forward_date")]
|
||||
pub date: i32,
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub date: DateTime<Utc>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub from: ForwardedFrom,
|
||||
|
@ -480,6 +486,7 @@ pub struct MessageVoiceChatParticipantsInvited {
|
|||
}
|
||||
|
||||
mod getters {
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::types::{
|
||||
|
@ -570,7 +577,7 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn forward_date(&self) -> Option<&i32> {
|
||||
pub fn forward_date(&self) -> Option<&DateTime<Utc>> {
|
||||
match &self.kind {
|
||||
Common(MessageCommon {
|
||||
forward_kind: ForwardKind::Channel(ForwardChannel { date, .. }),
|
||||
|
@ -597,7 +604,7 @@ mod getters {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn edit_date(&self) -> Option<&i32> {
|
||||
pub fn edit_date(&self) -> Option<&DateTime<Utc>> {
|
||||
match &self.kind {
|
||||
Common(MessageCommon { edit_date, .. }) => edit_date.as_ref(),
|
||||
_ => None,
|
||||
|
|
|
@ -59,7 +59,7 @@ pub enum MessageEntityKind {
|
|||
Italic,
|
||||
Code,
|
||||
Pre { language: Option<String> },
|
||||
TextLink { url: String },
|
||||
TextLink { url: reqwest::Url },
|
||||
TextMention { user: User },
|
||||
Underline,
|
||||
Strikethrough,
|
||||
|
@ -76,13 +76,13 @@ mod tests {
|
|||
assert_eq!(
|
||||
MessageEntity {
|
||||
kind: MessageEntityKind::TextLink {
|
||||
url: "ya.ru".into()
|
||||
url: reqwest::Url::parse("https://example.com").unwrap(),
|
||||
},
|
||||
offset: 1,
|
||||
length: 2,
|
||||
},
|
||||
from_str::<MessageEntity>(
|
||||
r#"{"type":"text_link","url":"ya.ru","offset":1,"length":2}"#
|
||||
r#"{"type":"text_link","url":"https://example.com","offset":1,"length":2}"#
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
@ -100,10 +100,8 @@ mod tests {
|
|||
offset: 1,
|
||||
length: 2,
|
||||
},
|
||||
from_str::<MessageEntity>(
|
||||
r#"{"type":"pre","url":"ya.ru","offset":1,"length":2,"language":"rust"}"#
|
||||
)
|
||||
.unwrap()
|
||||
from_str::<MessageEntity>(r#"{"type":"pre","offset":1,"length":2,"language":"rust"}"#)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a file uploaded to Telegram Passport.
|
||||
|
@ -19,6 +20,7 @@ pub struct PassportFile {
|
|||
/// File size.
|
||||
pub file_size: u64,
|
||||
|
||||
/// Unix time when the file was uploaded.
|
||||
pub file_date: u64,
|
||||
/// Time when the file was uploaded.
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub file_date: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::types::{MessageEntity, PollType};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object contains information about a poll.
|
||||
|
@ -48,9 +50,10 @@ pub struct Poll {
|
|||
/// Amount of time in seconds the poll will be active after creation.
|
||||
pub open_period: Option<i32>,
|
||||
|
||||
/// Point in time (Unix timestamp) when the poll will be automatically
|
||||
/// closed.
|
||||
pub close_date: Option<i32>,
|
||||
/// Point in time when the poll will be automatically closed.
|
||||
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
|
||||
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
|
||||
pub close_date: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// This object contains information about one answer option in a poll.
|
||||
|
|
|
@ -144,9 +144,14 @@ mod test {
|
|||
MessageCommon, MessageKind, Update, UpdateKind, User,
|
||||
};
|
||||
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
|
||||
// TODO: more tests for deserialization
|
||||
#[test]
|
||||
fn message() {
|
||||
let timestamp = 1_569_518_342;
|
||||
let date = DateTime::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc);
|
||||
|
||||
let json = r#"{
|
||||
"update_id":892252934,
|
||||
"message":{
|
||||
|
@ -174,7 +179,7 @@ mod test {
|
|||
kind: UpdateKind::Message(Message {
|
||||
via_bot: None,
|
||||
id: 6557,
|
||||
date: 1_569_518_342,
|
||||
date,
|
||||
chat: Chat {
|
||||
id: 218_485_655,
|
||||
kind: ChatKind::Private(ChatPrivate {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This object represents a service message about a voice chat scheduled in the
|
||||
/// chat.
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct VoiceChatScheduled {
|
||||
/// Point in time (Unix timestamp) when the voice chat is supposed to be
|
||||
/// started by a chat administrator.
|
||||
pub start_date: u64,
|
||||
/// Point in time when the voice chat is supposed to be started by a chat
|
||||
/// administrator.
|
||||
#[serde(with = "crate::types::serde_date_from_unix_timestamp")]
|
||||
pub start_date: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Contains information about the current status of a webhook.
|
||||
|
@ -6,8 +7,8 @@ use serde::{Deserialize, Serialize};
|
|||
#[serde_with_macros::skip_serializing_none]
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub struct WebhookInfo {
|
||||
/// Webhook URL, may be empty if webhook is not set up.
|
||||
pub url: String,
|
||||
/// Webhook URL, `None` if webhook is not set up.
|
||||
pub url: Option<reqwest::Url>,
|
||||
|
||||
/// `true`, if a custom certificate was provided for webhook certificate
|
||||
/// checks.
|
||||
|
@ -19,9 +20,11 @@ pub struct WebhookInfo {
|
|||
/// Currently used webhook IP address.
|
||||
pub ip_address: Option<String>,
|
||||
|
||||
/// Unix time for the most recent error that happened when trying to
|
||||
/// Time of the most recent error that happened when trying to
|
||||
/// deliver an update via webhook.
|
||||
pub last_error_date: Option<u64>,
|
||||
#[serde(with = "crate::types::serde_opt_date_from_unix_timestamp")]
|
||||
#[serde(default = "crate::types::serde_opt_date_from_unix_timestamp::none")]
|
||||
pub last_error_date: Option<DateTime<Utc>>,
|
||||
|
||||
/// Error message in human-readable format for the most recent error that
|
||||
/// happened when trying to deliver an update via webhook.
|
||||
|
|
Loading…
Reference in a new issue