mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-22 22:46:39 +01:00
commit
7a31bcea91
26 changed files with 669 additions and 139 deletions
|
@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## unreleased
|
## unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Support for Telegram Bot API [version 6.2](https://core.telegram.org/bots/api#august-12-2022) ([#251][pr251])
|
||||||
|
|
||||||
|
[pr251]: https://github.com/teloxide/teloxide-core/pull/251
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `Animation`, `Audio`, `Document`, `PassportFile`, `PhotoSize`, `Video`, `VideoNote` and `Voice` now contain `FileMeta` instead of its fields ([#253][pr253])
|
- `Animation`, `Audio`, `Document`, `PassportFile`, `PhotoSize`, `Video`, `VideoNote` and `Voice` now contain `FileMeta` instead of its fields ([#253][pr253])
|
||||||
|
@ -15,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- `Request` now requires `Self: IntoFuture`
|
- `Request` now requires `Self: IntoFuture`
|
||||||
- There is no need for `AutoSend` anymore
|
- There is no need for `AutoSend` anymore
|
||||||
- MSRV (Minimal Supported Rust Version) was bumped from `1.58.0` to `1.64.0`
|
- MSRV (Minimal Supported Rust Version) was bumped from `1.58.0` to `1.64.0`
|
||||||
|
- Refactored `Sticker` and related types ([#251][pr251])
|
||||||
|
|
||||||
[pr253]: https://github.com/teloxide/teloxide-core/pull/253
|
[pr253]: https://github.com/teloxide/teloxide-core/pull/253
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg">
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://core.telegram.org/bots/api">
|
<a href="https://core.telegram.org/bots/api">
|
||||||
<img src="https://img.shields.io/badge/API%20coverage-Up%20to%206.1%20(inclusively)-green.svg">
|
<img src="https://img.shields.io/badge/API%20coverage-Up%20to%206.2%20(inclusively)-green.svg">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://crates.io/crates/teloxide_core">
|
<a href="https://crates.io/crates/teloxide_core">
|
||||||
<img src="https://img.shields.io/crates/v/teloxide_core.svg">
|
<img src="https://img.shields.io/crates/v/teloxide_core.svg">
|
||||||
|
|
22
schema.ron
22
schema.ron
|
@ -3164,6 +3164,20 @@ Schema(
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Method(
|
||||||
|
names: ("getCustomEmojiStickers", "GetCustomEmojiStickers", "get_custom_emoji_stickers"),
|
||||||
|
return_ty: ArrayOf(RawTy("Sticker")),
|
||||||
|
doc: Doc(md: "Use this method to get information about custom emoji stickers by their identifiers. Returns an Array of Sticker objects."),
|
||||||
|
tg_doc: "https://core.telegram.org/bots/api#getcustomemojistickers",
|
||||||
|
tg_category: "Stickers",
|
||||||
|
params: [
|
||||||
|
Param(
|
||||||
|
name: "custom_emoji_ids",
|
||||||
|
ty: ArrayOf(String),
|
||||||
|
descr: Doc(md: "List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified."),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Method(
|
Method(
|
||||||
names: ("uploadStickerFile", "UploadStickerFile", "upload_sticker_file"),
|
names: ("uploadStickerFile", "UploadStickerFile", "upload_sticker_file"),
|
||||||
return_ty: RawTy("FileMeta"),
|
return_ty: RawTy("FileMeta"),
|
||||||
|
@ -3212,7 +3226,7 @@ Schema(
|
||||||
name: "sticker",
|
name: "sticker",
|
||||||
ty: RawTy("InputSticker"),
|
ty: RawTy("InputSticker"),
|
||||||
descr: Doc(
|
descr: Doc(
|
||||||
md: "**PNG** or **TGS** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a _file\\_id_ as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. [More info on Sending Files »]",
|
md: "**PNG** image, **TGS** animation or **WEBM** video with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a _file\\_id_ as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. [More info on Sending Files »]",
|
||||||
md_links: {"More info on Sending Files »": "https://core.telegram.org/bots/api#sending-files"},
|
md_links: {"More info on Sending Files »": "https://core.telegram.org/bots/api#sending-files"},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -3222,9 +3236,9 @@ Schema(
|
||||||
descr: Doc(md: "One or more emoji corresponding to the sticker"),
|
descr: Doc(md: "One or more emoji corresponding to the sticker"),
|
||||||
),
|
),
|
||||||
Param(
|
Param(
|
||||||
name: "contains_masks",
|
name: "sticker_type",
|
||||||
ty: Option(bool),
|
ty: Option(RawTy("StickerType")),
|
||||||
descr: Doc(md: "Pass _True_, if a set of mask stickers should be created"),
|
descr: Doc(md: "Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created via the Bot API at the moment. By default, a regular sticker set is created."),
|
||||||
),
|
),
|
||||||
Param(
|
Param(
|
||||||
name: "mask_position",
|
name: "mask_position",
|
||||||
|
|
|
@ -142,6 +142,7 @@ where
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -169,6 +169,7 @@ where
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -166,6 +166,9 @@ macro_rules! fwd_erased {
|
||||||
(@convert $m:ident, $arg:ident, errors : $T:ty) => {
|
(@convert $m:ident, $arg:ident, errors : $T:ty) => {
|
||||||
$arg.into_iter().collect()
|
$arg.into_iter().collect()
|
||||||
};
|
};
|
||||||
|
(@convert $m:ident, $arg:ident, custom_emoji_ids : $T:ty) => {
|
||||||
|
$arg.into_iter().collect()
|
||||||
|
};
|
||||||
(@convert $m:ident, $arg:ident, $arg_:ident : $T:ty) => {
|
(@convert $m:ident, $arg:ident, $arg_:ident : $T:ty) => {
|
||||||
$arg.into()
|
$arg.into()
|
||||||
};
|
};
|
||||||
|
@ -258,6 +261,7 @@ where
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
@ -713,6 +717,11 @@ trait ErasableRequester<'a> {
|
||||||
|
|
||||||
fn get_sticker_set(&self, name: String) -> ErasedRequest<'a, GetStickerSet, Self::Err>;
|
fn get_sticker_set(&self, name: String) -> ErasedRequest<'a, GetStickerSet, Self::Err>;
|
||||||
|
|
||||||
|
fn get_custom_emoji_stickers(
|
||||||
|
&self,
|
||||||
|
custom_emoji_ids: Vec<String>,
|
||||||
|
) -> ErasedRequest<'a, GetCustomEmojiStickers, Self::Err>;
|
||||||
|
|
||||||
fn upload_sticker_file(
|
fn upload_sticker_file(
|
||||||
&self,
|
&self,
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
|
@ -1426,6 +1435,13 @@ where
|
||||||
Requester::get_sticker_set(self, name).erase()
|
Requester::get_sticker_set(self, name).erase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_custom_emoji_stickers(
|
||||||
|
&self,
|
||||||
|
custom_emoji_ids: Vec<String>,
|
||||||
|
) -> ErasedRequest<'a, GetCustomEmojiStickers, Self::Err> {
|
||||||
|
Requester::get_custom_emoji_stickers(self, custom_emoji_ids).erase()
|
||||||
|
}
|
||||||
|
|
||||||
fn upload_sticker_file(
|
fn upload_sticker_file(
|
||||||
&self,
|
&self,
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
|
|
|
@ -164,6 +164,7 @@ impl<B: Requester> Requester for DefaultParseMode<B> {
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -148,6 +148,7 @@ where
|
||||||
stop_poll,
|
stop_poll,
|
||||||
delete_message,
|
delete_message,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -195,6 +195,7 @@ where
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -911,6 +911,18 @@ impl Requester for Bot {
|
||||||
Self::GetStickerSet::new(self.clone(), payloads::GetStickerSet::new(name))
|
Self::GetStickerSet::new(self.clone(), payloads::GetStickerSet::new(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetCustomEmojiStickers = JsonRequest<payloads::GetCustomEmojiStickers>;
|
||||||
|
|
||||||
|
fn get_custom_emoji_stickers<C>(&self, custom_emoji_ids: C) -> Self::GetCustomEmojiStickers
|
||||||
|
where
|
||||||
|
C: IntoIterator<Item = String>,
|
||||||
|
{
|
||||||
|
Self::GetCustomEmojiStickers::new(
|
||||||
|
self.clone(),
|
||||||
|
payloads::GetCustomEmojiStickers::new(custom_emoji_ids),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type UploadStickerFile = MultipartRequest<payloads::UploadStickerFile>;
|
type UploadStickerFile = MultipartRequest<payloads::UploadStickerFile>;
|
||||||
|
|
||||||
fn upload_sticker_file(
|
fn upload_sticker_file(
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub(crate) mod schema;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
|
io::{Read, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,25 +80,32 @@ pub fn ensure_files_contents<'a>(
|
||||||
) {
|
) {
|
||||||
let mut err_count = 0;
|
let mut err_count = 0;
|
||||||
|
|
||||||
for (file, contents) in files_and_contents {
|
for (path, contents) in files_and_contents {
|
||||||
if let Ok(old_contents) = fs::read_to_string(file) {
|
let mut file = fs::File::options()
|
||||||
if normalize_newlines(&old_contents) == normalize_newlines(contents) {
|
.read(true)
|
||||||
// File is already up to date.
|
.write(true)
|
||||||
continue;
|
.create(true)
|
||||||
}
|
.open(path)
|
||||||
|
.unwrap();
|
||||||
|
let mut old_contents = String::with_capacity(contents.len());
|
||||||
|
file.read_to_string(&mut old_contents).unwrap();
|
||||||
|
|
||||||
err_count += 1;
|
if normalize_newlines(&old_contents) == normalize_newlines(contents) {
|
||||||
|
// File is already up to date.
|
||||||
let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
|
continue;
|
||||||
eprintln!(
|
|
||||||
"\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
|
|
||||||
display_path.display()
|
|
||||||
);
|
|
||||||
if let Some(parent) = file.parent() {
|
|
||||||
let _ = fs::create_dir_all(parent);
|
|
||||||
}
|
|
||||||
fs::write(file, contents).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_count += 1;
|
||||||
|
|
||||||
|
let display_path = path.strip_prefix(&project_root()).unwrap_or(path);
|
||||||
|
eprintln!(
|
||||||
|
"\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
|
||||||
|
display_path.display()
|
||||||
|
);
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
let _ = fs::create_dir_all(parent);
|
||||||
|
}
|
||||||
|
file.write_all(contents.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (s, were) = match err_count {
|
let (s, were) = match err_count {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Core part of the [`teloxide`] library.
|
//! Core part of the [`teloxide`] library.
|
||||||
//!
|
//!
|
||||||
//! This library provides tools for making requests to the [Telegram Bot API]
|
//! This library provides tools for making requests to the [Telegram Bot API]
|
||||||
//! (Currently, version `6.1` is supported) with ease. The library is fully
|
//! (Currently, version `6.2` is supported) with ease. The library is fully
|
||||||
//! asynchronous and built using [`tokio`].
|
//! asynchronous and built using [`tokio`].
|
||||||
//!
|
//!
|
||||||
//!```toml
|
//!```toml
|
||||||
|
@ -86,6 +86,7 @@
|
||||||
//#![deny(missing_docs)]
|
//#![deny(missing_docs)]
|
||||||
#![warn(clippy::print_stdout, clippy::dbg_macro)]
|
#![warn(clippy::print_stdout, clippy::dbg_macro)]
|
||||||
#![allow(clippy::let_and_return)]
|
#![allow(clippy::let_and_return)]
|
||||||
|
#![allow(clippy::bool_assert_comparison)]
|
||||||
// Unless this becomes machine applicable, I'm not adding 334 #[must_use]s (waffle)
|
// Unless this becomes machine applicable, I'm not adding 334 #[must_use]s (waffle)
|
||||||
#![allow(clippy::return_self_not_must_use)]
|
#![allow(clippy::return_self_not_must_use)]
|
||||||
// Workaround for CI
|
// Workaround for CI
|
||||||
|
|
|
@ -1087,6 +1087,14 @@ macro_rules! requester_forward {
|
||||||
$body!(get_sticker_set this (name: N))
|
$body!(get_sticker_set this (name: N))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
(@method get_custom_emoji_stickers $body:ident $ty:ident) => {
|
||||||
|
type GetCustomEmojiStickers = $ty![GetCustomEmojiStickers];
|
||||||
|
|
||||||
|
fn get_custom_emoji_stickers<C>(&self, custom_emoji_ids: C) -> Self::GetCustomEmojiStickers where C: IntoIterator<Item = String> {
|
||||||
|
let this = self;
|
||||||
|
$body!(get_custom_emoji_stickers this (custom_emoji_ids: C))
|
||||||
|
}
|
||||||
|
};
|
||||||
(@method upload_sticker_file $body:ident $ty:ident) => {
|
(@method upload_sticker_file $body:ident $ty:ident) => {
|
||||||
type UploadStickerFile = $ty![UploadStickerFile];
|
type UploadStickerFile = $ty![UploadStickerFile];
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ mod get_chat_member;
|
||||||
mod get_chat_member_count;
|
mod get_chat_member_count;
|
||||||
mod get_chat_members_count;
|
mod get_chat_members_count;
|
||||||
mod get_chat_menu_button;
|
mod get_chat_menu_button;
|
||||||
|
mod get_custom_emoji_stickers;
|
||||||
mod get_file;
|
mod get_file;
|
||||||
mod get_game_high_scores;
|
mod get_game_high_scores;
|
||||||
mod get_me;
|
mod get_me;
|
||||||
|
@ -157,6 +158,7 @@ pub use get_chat_member::{GetChatMember, GetChatMemberSetters};
|
||||||
pub use get_chat_member_count::{GetChatMemberCount, GetChatMemberCountSetters};
|
pub use get_chat_member_count::{GetChatMemberCount, GetChatMemberCountSetters};
|
||||||
pub use get_chat_members_count::{GetChatMembersCount, GetChatMembersCountSetters};
|
pub use get_chat_members_count::{GetChatMembersCount, GetChatMembersCountSetters};
|
||||||
pub use get_chat_menu_button::{GetChatMenuButton, GetChatMenuButtonSetters};
|
pub use get_chat_menu_button::{GetChatMenuButton, GetChatMenuButtonSetters};
|
||||||
|
pub use get_custom_emoji_stickers::{GetCustomEmojiStickers, GetCustomEmojiStickersSetters};
|
||||||
pub use get_file::{GetFile, GetFileSetters};
|
pub use get_file::{GetFile, GetFileSetters};
|
||||||
pub use get_game_high_scores::{GetGameHighScores, GetGameHighScoresSetters};
|
pub use get_game_high_scores::{GetGameHighScores, GetGameHighScoresSetters};
|
||||||
pub use get_me::{GetMe, GetMeSetters};
|
pub use get_me::{GetMe, GetMeSetters};
|
||||||
|
|
|
@ -222,7 +222,9 @@ fn params(params: impl Iterator<Item = impl Borrow<Param>>) -> String {
|
||||||
let field = ¶m.name;
|
let field = ¶m.name;
|
||||||
let ty = ¶m.ty;
|
let ty = ¶m.ty;
|
||||||
let flatten = match ty {
|
let flatten = match ty {
|
||||||
Type::RawTy(s) if s == "InputSticker" || s == "TargetMessage" => {
|
Type::RawTy(s)
|
||||||
|
if s == "InputSticker" || s == "TargetMessage" || s == "StickerType" =>
|
||||||
|
{
|
||||||
"\n #[serde(flatten)]"
|
"\n #[serde(flatten)]"
|
||||||
}
|
}
|
||||||
_ => "",
|
_ => "",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::types::{InputSticker, MaskPosition, True, UserId};
|
use crate::types::{InputSticker, MaskPosition, StickerType, True, UserId};
|
||||||
|
|
||||||
impl_payload! {
|
impl_payload! {
|
||||||
@[multipart = sticker]
|
@[multipart = sticker]
|
||||||
|
@ -16,7 +16,7 @@ impl_payload! {
|
||||||
pub name: String [into],
|
pub name: String [into],
|
||||||
/// Sticker set title, 1-64 characters
|
/// Sticker set title, 1-64 characters
|
||||||
pub title: String [into],
|
pub title: String [into],
|
||||||
/// **PNG** or **TGS** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a _file\_id_ as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. [More info on Sending Files »]
|
/// **PNG** image, **TGS** animation or **WEBM** video with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a _file\_id_ as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. [More info on Sending Files »]
|
||||||
///
|
///
|
||||||
/// [More info on Sending Files »]: crate::types::InputFile
|
/// [More info on Sending Files »]: crate::types::InputFile
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -25,8 +25,9 @@ impl_payload! {
|
||||||
pub emojis: String [into],
|
pub emojis: String [into],
|
||||||
}
|
}
|
||||||
optional {
|
optional {
|
||||||
/// Pass _True_, if a set of mask stickers should be created
|
/// Type of stickers in the set, pass “regular” or “mask”. Custom emoji sticker sets can't be created via the Bot API at the moment. By default, a regular sticker set is created.
|
||||||
pub contains_masks: bool,
|
#[serde(flatten)]
|
||||||
|
pub sticker_type: StickerType,
|
||||||
/// A JSON-serialized object for position where the mask should be placed on faces
|
/// A JSON-serialized object for position where the mask should be placed on faces
|
||||||
pub mask_position: MaskPosition,
|
pub mask_position: MaskPosition,
|
||||||
}
|
}
|
||||||
|
|
16
src/payloads/get_custom_emoji_stickers.rs
Normal file
16
src/payloads/get_custom_emoji_stickers.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//! Generated by `codegen_payloads`, do not edit by hand.
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::types::Sticker;
|
||||||
|
|
||||||
|
impl_payload! {
|
||||||
|
/// Use this method to get information about custom emoji stickers by their identifiers. Returns an Array of Sticker objects.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize)]
|
||||||
|
pub GetCustomEmojiStickers (GetCustomEmojiStickersSetters) => Vec<Sticker> {
|
||||||
|
required {
|
||||||
|
/// List of custom emoji identifiers. At most 200 custom emoji identifiers can be specified.
|
||||||
|
pub custom_emoji_ids: Vec<String> [collect],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,8 @@ pub use crate::payloads::{
|
||||||
EditMessageTextSetters as _, ExportChatInviteLinkSetters as _, ForwardMessageSetters as _,
|
EditMessageTextSetters as _, ExportChatInviteLinkSetters as _, ForwardMessageSetters as _,
|
||||||
GetChatAdministratorsSetters as _, GetChatMemberCountSetters as _, GetChatMemberSetters as _,
|
GetChatAdministratorsSetters as _, GetChatMemberCountSetters as _, GetChatMemberSetters as _,
|
||||||
GetChatMembersCountSetters as _, GetChatMenuButtonSetters as _, GetChatSetters as _,
|
GetChatMembersCountSetters as _, GetChatMenuButtonSetters as _, GetChatSetters as _,
|
||||||
GetFileSetters as _, GetGameHighScoresSetters as _, GetMeSetters as _,
|
GetCustomEmojiStickersSetters as _, GetFileSetters as _, GetGameHighScoresSetters as _,
|
||||||
GetMyCommandsSetters as _, GetMyDefaultAdministratorRightsSetters as _,
|
GetMeSetters as _, GetMyCommandsSetters as _, GetMyDefaultAdministratorRightsSetters as _,
|
||||||
GetStickerSetSetters as _, GetUpdatesSetters as _, GetUserProfilePhotosSetters as _,
|
GetStickerSetSetters as _, GetUpdatesSetters as _, GetUserProfilePhotosSetters as _,
|
||||||
GetWebhookInfoSetters as _, KickChatMemberSetters as _, LeaveChatSetters as _,
|
GetWebhookInfoSetters as _, KickChatMemberSetters as _, LeaveChatSetters as _,
|
||||||
LogOutSetters as _, PinChatMessageSetters as _, PromoteChatMemberSetters as _,
|
LogOutSetters as _, PinChatMessageSetters as _, PromoteChatMemberSetters as _,
|
||||||
|
|
|
@ -750,6 +750,13 @@ pub trait Requester {
|
||||||
where
|
where
|
||||||
N: Into<String>;
|
N: Into<String>;
|
||||||
|
|
||||||
|
type GetCustomEmojiStickers: Request<Payload = GetCustomEmojiStickers, Err = Self::Err>;
|
||||||
|
|
||||||
|
/// For Telegram documentation see [`GetCustomEmojiStickers`].
|
||||||
|
fn get_custom_emoji_stickers<C>(&self, custom_emoji_ids: C) -> Self::GetCustomEmojiStickers
|
||||||
|
where
|
||||||
|
C: IntoIterator<Item = String>;
|
||||||
|
|
||||||
type UploadStickerFile: Request<Payload = UploadStickerFile, Err = Self::Err>;
|
type UploadStickerFile: Request<Payload = UploadStickerFile, Err = Self::Err>;
|
||||||
|
|
||||||
/// For Telegram documentation see [`UploadStickerFile`].
|
/// For Telegram documentation see [`UploadStickerFile`].
|
||||||
|
@ -1019,6 +1026,7 @@ macro_rules! forward_all {
|
||||||
delete_message,
|
delete_message,
|
||||||
send_sticker,
|
send_sticker,
|
||||||
get_sticker_set,
|
get_sticker_set,
|
||||||
|
get_custom_emoji_stickers,
|
||||||
upload_sticker_file,
|
upload_sticker_file,
|
||||||
create_new_sticker_set,
|
create_new_sticker_set,
|
||||||
add_sticker_to_set,
|
add_sticker_to_set,
|
||||||
|
|
|
@ -99,6 +99,13 @@ pub struct ChatPrivate {
|
||||||
///
|
///
|
||||||
/// [`GetChat`]: crate::payloads::GetChat
|
/// [`GetChat`]: crate::payloads::GetChat
|
||||||
pub has_private_forwards: Option<True>,
|
pub has_private_forwards: Option<True>,
|
||||||
|
|
||||||
|
/// `True`, if the privacy settings of the other party restrict sending
|
||||||
|
/// voice and video note messages in the private chat. Returned only in
|
||||||
|
/// [`GetChat`].
|
||||||
|
///
|
||||||
|
/// [`GetChat`]: crate::payloads::GetChat
|
||||||
|
pub has_restricted_voice_and_video_messages: Option<True>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde_with_macros::skip_serializing_none]
|
#[serde_with_macros::skip_serializing_none]
|
||||||
|
@ -492,6 +499,7 @@ mod serde_helper {
|
||||||
last_name: Option<String>,
|
last_name: Option<String>,
|
||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
has_private_forwards: Option<True>,
|
has_private_forwards: Option<True>,
|
||||||
|
has_restricted_voice_and_video_messages: Option<True>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChatPrivate> for super::ChatPrivate {
|
impl From<ChatPrivate> for super::ChatPrivate {
|
||||||
|
@ -503,6 +511,7 @@ mod serde_helper {
|
||||||
last_name,
|
last_name,
|
||||||
bio,
|
bio,
|
||||||
has_private_forwards,
|
has_private_forwards,
|
||||||
|
has_restricted_voice_and_video_messages,
|
||||||
}: ChatPrivate,
|
}: ChatPrivate,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -511,6 +520,7 @@ mod serde_helper {
|
||||||
last_name,
|
last_name,
|
||||||
bio,
|
bio,
|
||||||
has_private_forwards,
|
has_private_forwards,
|
||||||
|
has_restricted_voice_and_video_messages,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -523,6 +533,7 @@ mod serde_helper {
|
||||||
last_name,
|
last_name,
|
||||||
bio,
|
bio,
|
||||||
has_private_forwards,
|
has_private_forwards,
|
||||||
|
has_restricted_voice_and_video_messages,
|
||||||
}: super::ChatPrivate,
|
}: super::ChatPrivate,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -532,6 +543,7 @@ mod serde_helper {
|
||||||
last_name,
|
last_name,
|
||||||
bio,
|
bio,
|
||||||
has_private_forwards,
|
has_private_forwards,
|
||||||
|
has_restricted_voice_and_video_messages,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,6 +588,7 @@ mod tests {
|
||||||
last_name: None,
|
last_name: None,
|
||||||
bio: None,
|
bio: None,
|
||||||
has_private_forwards: None,
|
has_private_forwards: None,
|
||||||
|
has_restricted_voice_and_video_messages: None,
|
||||||
}),
|
}),
|
||||||
photo: None,
|
photo: None,
|
||||||
pinned_message: None,
|
pinned_message: None,
|
||||||
|
@ -596,6 +609,7 @@ mod tests {
|
||||||
last_name: None,
|
last_name: None,
|
||||||
bio: None,
|
bio: None,
|
||||||
has_private_forwards: None,
|
has_private_forwards: None,
|
||||||
|
has_restricted_voice_and_video_messages: None,
|
||||||
}),
|
}),
|
||||||
photo: None,
|
photo: None,
|
||||||
pinned_message: None,
|
pinned_message: None,
|
||||||
|
|
|
@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize};
|
||||||
/// default.
|
/// default.
|
||||||
///
|
///
|
||||||
/// [The official docs](https://core.telegram.org/bots/api#maskposition).
|
/// [The official docs](https://core.telegram.org/bots/api#maskposition).
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct MaskPosition {
|
pub struct MaskPosition {
|
||||||
/// The part of the face relative to which the mask should be placed. One
|
/// The part of the face relative to which the mask should be placed. One
|
||||||
/// of `forehead`, `eyes`, `mouth`, or `chin`.
|
/// of `forehead`, `eyes`, `mouth`, or `chin`.
|
||||||
pub point: String,
|
pub point: MaskPoint,
|
||||||
|
|
||||||
/// Shift by X-axis measured in widths of the mask scaled to the face size,
|
/// Shift by X-axis measured in widths of the mask scaled to the face size,
|
||||||
/// from left to right. For example, choosing `-1.0` will place mask just
|
/// from left to right. For example, choosing `-1.0` will place mask just
|
||||||
|
@ -24,41 +24,45 @@ pub struct MaskPosition {
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The part of the face relative to which the mask should be placed.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum MaskPoint {
|
||||||
|
Forehead,
|
||||||
|
Eyes,
|
||||||
|
Mouth,
|
||||||
|
Chin,
|
||||||
|
}
|
||||||
|
|
||||||
impl MaskPosition {
|
impl MaskPosition {
|
||||||
pub fn new<S>(point: S, x_shift: f64, y_shift: f64, scale: f64) -> Self
|
pub const fn new(point: MaskPoint, x_shift: f64, y_shift: f64, scale: f64) -> Self {
|
||||||
where
|
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
point: point.into(),
|
point,
|
||||||
x_shift,
|
x_shift,
|
||||||
y_shift,
|
y_shift,
|
||||||
scale,
|
scale,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn point<S>(mut self, val: S) -> Self
|
pub const fn point(mut self, val: MaskPoint) -> Self {
|
||||||
where
|
self.point = val;
|
||||||
S: Into<String>,
|
|
||||||
{
|
|
||||||
self.point = val.into();
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn x_shift(mut self, val: f64) -> Self {
|
pub const fn x_shift(mut self, val: f64) -> Self {
|
||||||
self.x_shift = val;
|
self.x_shift = val;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn y_shift(mut self, val: f64) -> Self {
|
pub const fn y_shift(mut self, val: f64) -> Self {
|
||||||
self.y_shift = val;
|
self.y_shift = val;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn scale(mut self, val: f64) -> Self {
|
pub const fn scale(mut self, val: f64) -> Self {
|
||||||
self.scale = val;
|
self.scale = val;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -1435,42 +1435,43 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn de_sticker() {
|
fn de_sticker() {
|
||||||
let json = r#"{
|
let json = r#"{
|
||||||
"message_id": 199787,
|
"message_id": 199787,
|
||||||
"from": {
|
"from": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"is_bot": false,
|
"is_bot": false,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"language_code": "en"
|
"language_code": "en"
|
||||||
},
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"id": 250918540,
|
"id": 250918540,
|
||||||
"first_name": "Андрей",
|
"first_name": "Андрей",
|
||||||
"last_name": "Власов",
|
"last_name": "Власов",
|
||||||
"username": "aka_dude",
|
"username": "aka_dude",
|
||||||
"type": "private"
|
"type": "private"
|
||||||
},
|
},
|
||||||
"date": 1568290188,
|
"date": 1568290188,
|
||||||
"sticker": {
|
"sticker": {
|
||||||
"width": 512,
|
"width": 512,
|
||||||
"height": 512,
|
"height": 512,
|
||||||
"emoji": "😡",
|
"emoji": "😡",
|
||||||
"set_name": "AdvenTimeAnim",
|
"set_name": "AdvenTimeAnim",
|
||||||
"is_animated": true,
|
"is_animated": true,
|
||||||
"is_video": false,
|
"is_video": false,
|
||||||
"thumb": {
|
"type": "regular",
|
||||||
"file_id": "AAQCAAMjAAOw0PgMaabKAcaXKCBLubkPAAQBAAdtAAPGKwACFgQ",
|
"thumb": {
|
||||||
"file_unique_id":"",
|
"file_id": "AAMCAgADGQEAARIt0GMwiZ6n4nRbxdpM3pL8vPX6PVAhAAIjAAOw0PgMaabKAcaXKCABAAdtAAMpBA",
|
||||||
"file_size": 4118,
|
"file_unique_id": "AQADIwADsND4DHI",
|
||||||
"width": 128,
|
"file_size": 4118,
|
||||||
"height": 128
|
"width": 128,
|
||||||
},
|
"height": 128
|
||||||
"file_id": "CAADAgADIwADsND4DGmmygHGlyggFgQ",
|
},
|
||||||
"file_unique_id":"",
|
"file_id": "CAACAgIAAxkBAAESLdBjMImep-J0W8XaTN6S_Lz1-j1QIQACIwADsND4DGmmygHGlyggKQQ",
|
||||||
"file_size": 16639
|
"file_unique_id": "AgADIwADsND4DA",
|
||||||
}
|
"file_size": 16639
|
||||||
}"#;
|
}
|
||||||
|
}"#;
|
||||||
from_str::<Message>(json).unwrap();
|
from_str::<Message>(json).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ impl MessageEntity {
|
||||||
/// If you don't have a complete [`User`] value, please use
|
/// If you don't have a complete [`User`] value, please use
|
||||||
/// [`MessageEntity::text_mention_id`] instead.
|
/// [`MessageEntity::text_mention_id`] instead.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn text_mention(user: User, offset: usize, length: usize) -> Self {
|
pub const fn text_mention(user: User, offset: usize, length: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: MessageEntityKind::TextMention { user },
|
kind: MessageEntityKind::TextMention { user },
|
||||||
offset,
|
offset,
|
||||||
|
@ -158,6 +158,16 @@ impl MessageEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a message entity representing a custom emoji.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn custom_emoji(custom_emoji_id: String, offset: usize, length: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: MessageEntityKind::CustomEmoji { custom_emoji_id },
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn kind(mut self, val: MessageEntityKind) -> Self {
|
pub fn kind(mut self, val: MessageEntityKind) -> Self {
|
||||||
self.kind = val;
|
self.kind = val;
|
||||||
|
@ -300,13 +310,14 @@ pub enum MessageEntityKind {
|
||||||
PhoneNumber,
|
PhoneNumber,
|
||||||
Bold,
|
Bold,
|
||||||
Italic,
|
Italic,
|
||||||
|
Underline,
|
||||||
|
Strikethrough,
|
||||||
|
Spoiler,
|
||||||
Code,
|
Code,
|
||||||
Pre { language: Option<String> },
|
Pre { language: Option<String> },
|
||||||
TextLink { url: reqwest::Url },
|
TextLink { url: reqwest::Url },
|
||||||
TextMention { user: User },
|
TextMention { user: User },
|
||||||
Underline,
|
CustomEmoji { custom_emoji_id: String }, // FIXME(waffle): newtype this
|
||||||
Strikethrough,
|
|
||||||
Spoiler,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -10,25 +10,40 @@ use crate::types::{FileMeta, MaskPosition, PhotoSize};
|
||||||
#[serde_with_macros::skip_serializing_none]
|
#[serde_with_macros::skip_serializing_none]
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Sticker {
|
pub struct Sticker {
|
||||||
/// Identifier for this file.
|
/// Metadata of the sticker file.
|
||||||
pub file_id: String,
|
#[serde(flatten)]
|
||||||
|
pub file: FileMeta,
|
||||||
|
|
||||||
/// Unique identifier for this file, which is supposed to be the same over
|
/// Sticker width, in pixels.
|
||||||
/// time and for different bots. Can't be used to download or reuse the
|
///
|
||||||
/// file.
|
/// You can assume that `max(width, height) = 512`, `min(width, height) <=
|
||||||
pub file_unique_id: String,
|
/// 512`. In other words one dimension is exactly 512 pixels and the other
|
||||||
|
/// is at most 512 pixels.
|
||||||
/// Sticker width.
|
|
||||||
pub width: u16,
|
pub width: u16,
|
||||||
|
|
||||||
/// Sticker height.
|
/// Sticker height, in pixels.
|
||||||
|
///
|
||||||
|
/// You can assume that `max(width, height) = 512`, `min(width, height) <=
|
||||||
|
/// 512`. In other words one dimension is exactly 512 pixels and the other
|
||||||
|
/// is at most 512 pixels.
|
||||||
pub height: u16,
|
pub height: u16,
|
||||||
|
|
||||||
/// Kind of this sticker - webp, animated or video.
|
/// Kind of this sticker - regular, mask or custom emoji.
|
||||||
|
///
|
||||||
|
/// In other words this represent how the sticker is presented, as a big
|
||||||
|
/// picture/video, as a mask while editing pictures or as a custom emoji in
|
||||||
|
/// messages.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub kind: StickerKind,
|
pub kind: StickerKind,
|
||||||
|
|
||||||
/// Sticker thumbnail in the .webp or .jpg format.
|
/// Format of this sticker - raster/`.webp`, animated/`.tgs` or
|
||||||
|
/// video/`.webm`.
|
||||||
|
///
|
||||||
|
/// In other words this represents how the sticker is encoded.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub format: StickerFormat,
|
||||||
|
|
||||||
|
/// Sticker thumbnail in the `.webp` or `.jpg` format.
|
||||||
pub thumb: Option<PhotoSize>,
|
pub thumb: Option<PhotoSize>,
|
||||||
|
|
||||||
/// Emoji associated with the sticker.
|
/// Emoji associated with the sticker.
|
||||||
|
@ -36,29 +51,59 @@ pub struct Sticker {
|
||||||
|
|
||||||
/// Name of the sticker set to which the sticker belongs.
|
/// Name of the sticker set to which the sticker belongs.
|
||||||
pub set_name: Option<String>,
|
pub set_name: Option<String>,
|
||||||
|
|
||||||
/// Premium animation for the sticker, if the sticker is premium.
|
|
||||||
pub premium_animation: Option<FileMeta>,
|
|
||||||
|
|
||||||
/// For mask stickers, the position where the mask should be placed.
|
|
||||||
pub mask_position: Option<MaskPosition>,
|
|
||||||
|
|
||||||
/// File size in bytes.
|
|
||||||
#[serde(default = "crate::types::file::file_size_fallback")]
|
|
||||||
pub file_size: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Kind of a sticker - webp, animated or video.
|
/// Kind of a [`Sticker`] - regular, mask or custom emoji.
|
||||||
|
///
|
||||||
|
/// Dataful version of [`StickerType`].
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(try_from = "StickerKindRaw", into = "StickerKindRaw")]
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum StickerKind {
|
pub enum StickerKind {
|
||||||
/// "Normal", raster sticker.
|
/// "Normal", raster, animated or video sticker.
|
||||||
Webp,
|
Regular {
|
||||||
/// [Animated] sticker.
|
/// Premium animation for the sticker, if the sticker is premium.
|
||||||
|
premium_animation: Option<FileMeta>,
|
||||||
|
},
|
||||||
|
/// Mask sticker.
|
||||||
|
Mask {
|
||||||
|
/// For mask stickers, the position where the mask should be placed.
|
||||||
|
mask_position: MaskPosition,
|
||||||
|
},
|
||||||
|
/// Custom emoji sticker.
|
||||||
|
CustomEmoji {
|
||||||
|
/// A unique identifier of the custom emoji.
|
||||||
|
// FIXME(waffle): newtype
|
||||||
|
custom_emoji_id: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type of a [`Sticker`] - regular, mask or custom emoji.
|
||||||
|
///
|
||||||
|
/// Dataless version of [`StickerType`].
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "sticker_type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum StickerType {
|
||||||
|
/// "Normal", raster, animated or video sticker.
|
||||||
|
Regular,
|
||||||
|
/// Mask sticker.
|
||||||
|
Mask,
|
||||||
|
/// Custom emoji sticker.
|
||||||
|
CustomEmoji,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format of a [`Sticker`] - regular/webp, animated/tgs or video/webm.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(try_from = "StickerFormatRaw", into = "StickerFormatRaw")]
|
||||||
|
pub enum StickerFormat {
|
||||||
|
/// "Normal", raster, `.webp` sticker.
|
||||||
|
Raster,
|
||||||
|
/// [Animated], `.tgs` sticker.
|
||||||
///
|
///
|
||||||
/// [Animated]: https://telegram.org/blog/animated-stickers
|
/// [Animated]: https://telegram.org/blog/animated-stickers
|
||||||
Animated,
|
Animated,
|
||||||
/// [Video] sticker.
|
/// [Video], `.webm` sticker.
|
||||||
///
|
///
|
||||||
/// [Video]: https://telegram.org/blog/video-stickers-better-reactions
|
/// [Video]: https://telegram.org/blog/video-stickers-better-reactions
|
||||||
Video,
|
Video,
|
||||||
|
@ -71,8 +116,11 @@ pub enum StickerKind {
|
||||||
///
|
///
|
||||||
/// let sticker: Sticker = todo!();
|
/// let sticker: Sticker = todo!();
|
||||||
///
|
///
|
||||||
/// let _ = sticker.is_video();
|
/// let _ = sticker.is_regular();
|
||||||
/// let _ = sticker.kind.is_video();
|
/// let _ = sticker.kind.is_regular();
|
||||||
|
///
|
||||||
|
/// let _ = sticker.mask_position();
|
||||||
|
/// let _ = sticker.kind.mask_position();
|
||||||
/// ```
|
/// ```
|
||||||
impl Deref for Sticker {
|
impl Deref for Sticker {
|
||||||
type Target = StickerKind;
|
type Target = StickerKind;
|
||||||
|
@ -82,24 +130,149 @@ impl Deref for Sticker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StickerKind {
|
impl Sticker {
|
||||||
/// Returns `true` is this is a "normal" raster sticker.
|
/// Returns `true` is this is a "normal" raster sticker.
|
||||||
|
///
|
||||||
|
/// Alias to [`self.format.is_raster()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_raster()`]: StickerFormat::is_raster
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_webp(&self) -> bool {
|
pub fn is_raster(&self) -> bool {
|
||||||
matches!(self, Self::Webp)
|
self.format.is_raster()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` is this is an [animated] sticker.
|
/// Returns `true` is this is an [animated] sticker.
|
||||||
///
|
///
|
||||||
|
/// Alias to [`self.format.is_animated()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_animated()`]: StickerFormat::is_animated
|
||||||
/// [animated]: https://telegram.org/blog/animated-stickers
|
/// [animated]: https://telegram.org/blog/animated-stickers
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
pub fn is_animated(&self) -> bool {
|
||||||
|
self.format.is_animated()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` is this is a [video] sticker.
|
||||||
|
///
|
||||||
|
/// Alias to [`self.format.is_video()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_video()`]: StickerFormat::is_video
|
||||||
|
/// [video]: https://telegram.org/blog/video-stickers-better-reactions
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_video(&self) -> bool {
|
||||||
|
self.format.is_video()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StickerKind {
|
||||||
|
/// Converts [`StickerKind`] to [`StickerType`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn type_(&self) -> StickerType {
|
||||||
|
match self {
|
||||||
|
StickerKind::Regular { .. } => StickerType::Regular,
|
||||||
|
StickerKind::Mask { .. } => StickerType::Mask,
|
||||||
|
StickerKind::CustomEmoji { .. } => StickerType::CustomEmoji,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker kind is [`Regular`].
|
||||||
|
///
|
||||||
|
/// [`Regular`]: StickerKind::Regular
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_regular(&self) -> bool {
|
||||||
|
self.type_().is_regular()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker kind is [`Mask`].
|
||||||
|
///
|
||||||
|
/// [`Mask`]: StickerKind::Mask
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_mask(&self) -> bool {
|
||||||
|
self.type_().is_mask()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker kind is [`CustomEmoji`].
|
||||||
|
///
|
||||||
|
/// [`CustomEmoji`]: StickerKind::CustomEmoji
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_custom_emoji(&self) -> bool {
|
||||||
|
self.type_().is_custom_emoji()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getter for [`StickerKind::Regular::premium_animation`].
|
||||||
|
pub fn premium_animation(&self) -> Option<&FileMeta> {
|
||||||
|
if let Self::Regular { premium_animation } = self {
|
||||||
|
premium_animation.as_ref()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getter for [`StickerKind::Mask::mask_position`].
|
||||||
|
pub fn mask_position(&self) -> Option<MaskPosition> {
|
||||||
|
if let Self::Mask { mask_position } = self {
|
||||||
|
Some(*mask_position)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getter for [`StickerKind::CustomEmoji::custom_emoji_id`].
|
||||||
|
pub fn custom_emoji_id(&self) -> Option<&str> {
|
||||||
|
if let Self::CustomEmoji { custom_emoji_id } = self {
|
||||||
|
Some(custom_emoji_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StickerType {
|
||||||
|
/// Returns `true` if the sticker type is [`Regular`].
|
||||||
|
///
|
||||||
|
/// [`Regular`]: StickerType::Regular
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_regular(&self) -> bool {
|
||||||
|
matches!(self, Self::Regular)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker type is [`Mask`].
|
||||||
|
///
|
||||||
|
/// [`Mask`]: StickerType::Mask
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_mask(&self) -> bool {
|
||||||
|
matches!(self, Self::Mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker type is [`CustomEmoji`].
|
||||||
|
///
|
||||||
|
/// [`CustomEmoji`]: StickerType::CustomEmoji
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_custom_emoji(&self) -> bool {
|
||||||
|
matches!(self, Self::CustomEmoji)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StickerFormat {
|
||||||
|
/// Returns `true` if the sticker format is [`Raster`].
|
||||||
|
///
|
||||||
|
/// [`Raster`]: StickerFormat::Raster
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_raster(&self) -> bool {
|
||||||
|
matches!(self, Self::Raster)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the sticker format is [`Animated`].
|
||||||
|
///
|
||||||
|
/// [`Animated`]: StickerFormat::Animated
|
||||||
|
#[must_use]
|
||||||
pub fn is_animated(&self) -> bool {
|
pub fn is_animated(&self) -> bool {
|
||||||
matches!(self, Self::Animated)
|
matches!(self, Self::Animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` is this is a [video] sticker.
|
/// Returns `true` if the sticker format is [`Video`].
|
||||||
///
|
///
|
||||||
/// [video]: https://telegram.org/blog/video-stickers-better-reactions
|
/// [`Video`]: StickerFormat::Video
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_video(&self) -> bool {
|
pub fn is_video(&self) -> bool {
|
||||||
matches!(self, Self::Video)
|
matches!(self, Self::Video)
|
||||||
|
@ -107,22 +280,22 @@ impl StickerKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct StickerKindRaw {
|
struct StickerFormatRaw {
|
||||||
is_animated: bool,
|
is_animated: bool,
|
||||||
is_video: bool,
|
is_video: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<StickerKindRaw> for StickerKind {
|
impl TryFrom<StickerFormatRaw> for StickerFormat {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn try_from(
|
fn try_from(
|
||||||
StickerKindRaw {
|
StickerFormatRaw {
|
||||||
is_animated,
|
is_animated,
|
||||||
is_video,
|
is_video,
|
||||||
}: StickerKindRaw,
|
}: StickerFormatRaw,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> Result<Self, Self::Error> {
|
||||||
let ret = match (is_animated, is_video) {
|
let ret = match (is_animated, is_video) {
|
||||||
(false, false) => Self::Webp,
|
(false, false) => Self::Raster,
|
||||||
(true, false) => Self::Animated,
|
(true, false) => Self::Animated,
|
||||||
(false, true) => Self::Video,
|
(false, true) => Self::Video,
|
||||||
(true, true) => return Err("`is_animated` and `is_video` present at the same time"),
|
(true, true) => return Err("`is_animated` and `is_video` present at the same time"),
|
||||||
|
@ -132,21 +305,146 @@ impl TryFrom<StickerKindRaw> for StickerKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StickerKind> for StickerKindRaw {
|
impl From<StickerFormat> for StickerFormatRaw {
|
||||||
fn from(kind: StickerKind) -> Self {
|
fn from(kind: StickerFormat) -> Self {
|
||||||
match kind {
|
match kind {
|
||||||
StickerKind::Webp => Self {
|
StickerFormat::Raster => Self {
|
||||||
is_animated: false,
|
is_animated: false,
|
||||||
is_video: false,
|
is_video: false,
|
||||||
},
|
},
|
||||||
StickerKind::Animated => Self {
|
StickerFormat::Animated => Self {
|
||||||
is_animated: true,
|
is_animated: true,
|
||||||
is_video: false,
|
is_video: false,
|
||||||
},
|
},
|
||||||
StickerKind::Video => Self {
|
StickerFormat::Video => Self {
|
||||||
is_animated: false,
|
is_animated: false,
|
||||||
is_video: true,
|
is_video: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::types::{MaskPoint, Sticker, StickerFormat, StickerType};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mask_serde() {
|
||||||
|
// Taken from a real (mask) sticker set
|
||||||
|
let json = r#"{
|
||||||
|
"width": 512,
|
||||||
|
"height": 512,
|
||||||
|
"emoji": "🎭",
|
||||||
|
"set_name": "Coronamask",
|
||||||
|
"is_animated": false,
|
||||||
|
"is_video": false,
|
||||||
|
"type": "mask",
|
||||||
|
"mask_position": {
|
||||||
|
"point": "forehead",
|
||||||
|
"x_shift": -0.0125,
|
||||||
|
"y_shift": 0.5525,
|
||||||
|
"scale": 1.94
|
||||||
|
},
|
||||||
|
"thumb": {
|
||||||
|
"file_id": "AAMCAQADFQABYzA0qlYHijpjMzMwBFKnEVE5XdkAAjIKAAK_jJAE1TRw7D936M8BAAdtAAMpBA",
|
||||||
|
"file_unique_id": "AQADMgoAAr-MkARy",
|
||||||
|
"file_size": 11028,
|
||||||
|
"width": 320,
|
||||||
|
"height": 320
|
||||||
|
},
|
||||||
|
"file_id": "CAACAgEAAxUAAWMwNKpWB4o6YzMzMARSpxFROV3ZAAIyCgACv4yQBNU0cOw_d-jPKQQ",
|
||||||
|
"file_unique_id": "AgADMgoAAr-MkAQ",
|
||||||
|
"file_size": 18290
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let sticker: Sticker = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
// Assert some basic properties are correctly deserialized
|
||||||
|
assert_eq!(sticker.type_(), StickerType::Mask);
|
||||||
|
assert_eq!(sticker.mask_position().unwrap().point, MaskPoint::Forehead);
|
||||||
|
assert_eq!(sticker.is_animated(), false);
|
||||||
|
assert_eq!(sticker.is_video(), false);
|
||||||
|
assert_eq!(sticker.thumb.clone().unwrap().file_size, 11028);
|
||||||
|
assert_eq!(sticker.file.file_size, 18290);
|
||||||
|
assert_eq!(sticker.width, 512);
|
||||||
|
assert_eq!(sticker.height, 512);
|
||||||
|
|
||||||
|
let json2 = serde_json::to_string(&sticker).unwrap();
|
||||||
|
let sticker2: Sticker = serde_json::from_str(&json2).unwrap();
|
||||||
|
assert_eq!(sticker, sticker2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn regular_serde() {
|
||||||
|
// Taken from a real sticker set
|
||||||
|
let json = r#"{
|
||||||
|
"width": 463,
|
||||||
|
"height": 512,
|
||||||
|
"emoji": "🍿",
|
||||||
|
"set_name": "menhera2",
|
||||||
|
"is_animated": false,
|
||||||
|
"is_video": false,
|
||||||
|
"type": "regular",
|
||||||
|
"thumb": {
|
||||||
|
"file_id": "AAMCAgADFQABYzBxOJ1GWrttqL7FSRwdAtrq-AkAAtkHAALBGJ4LUUUh5CUew90BAAdtAAMpBA",
|
||||||
|
"file_unique_id": "AQAD2QcAAsEYngty",
|
||||||
|
"file_size": 4558,
|
||||||
|
"width": 116,
|
||||||
|
"height": 128
|
||||||
|
},
|
||||||
|
"file_id": "CAACAgIAAxUAAWMwcTidRlq7bai-xUkcHQLa6vgJAALZBwACwRieC1FFIeQlHsPdKQQ",
|
||||||
|
"file_unique_id": "AgAD2QcAAsEYngs",
|
||||||
|
"file_size": 25734
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let sticker: Sticker = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
// Assert some basic properties are correctly deserialized
|
||||||
|
assert_eq!(sticker.type_(), StickerType::Regular);
|
||||||
|
assert_eq!(sticker.premium_animation(), None);
|
||||||
|
assert_eq!(sticker.is_animated(), false);
|
||||||
|
assert_eq!(sticker.is_video(), false);
|
||||||
|
assert_eq!(sticker.thumb.clone().unwrap().file_size, 4558);
|
||||||
|
assert_eq!(sticker.file.file_size, 25734);
|
||||||
|
assert_eq!(sticker.width, 463);
|
||||||
|
assert_eq!(sticker.height, 512);
|
||||||
|
assert_eq!(sticker.set_name.as_deref(), Some("menhera2"));
|
||||||
|
|
||||||
|
let json2 = serde_json::to_string(&sticker).unwrap();
|
||||||
|
let sticker2: Sticker = serde_json::from_str(&json2).unwrap();
|
||||||
|
assert_eq!(sticker, sticker2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sticker_format_serde() {
|
||||||
|
{
|
||||||
|
let json = r#"{"is_animated":false,"is_video":false}"#;
|
||||||
|
let fmt: StickerFormat = serde_json::from_str(json).unwrap();
|
||||||
|
assert_eq!(fmt, StickerFormat::Raster);
|
||||||
|
|
||||||
|
let json2 = serde_json::to_string(&fmt).unwrap();
|
||||||
|
assert_eq!(json, json2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let json = r#"{"is_animated":true,"is_video":false}"#;
|
||||||
|
let fmt: StickerFormat = serde_json::from_str(json).unwrap();
|
||||||
|
assert_eq!(fmt, StickerFormat::Animated);
|
||||||
|
|
||||||
|
let json2 = serde_json::to_string(&fmt).unwrap();
|
||||||
|
assert_eq!(json, json2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let json = r#"{"is_animated":false,"is_video":true}"#;
|
||||||
|
let fmt: StickerFormat = serde_json::from_str(json).unwrap();
|
||||||
|
assert_eq!(fmt, StickerFormat::Video);
|
||||||
|
|
||||||
|
let json2 = serde_json::to_string(&fmt).unwrap();
|
||||||
|
assert_eq!(json, json2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let json = r#"{"is_animated":true,"is_video":true}"#;
|
||||||
|
let fmt: Result<StickerFormat, _> = serde_json::from_str(json);
|
||||||
|
assert!(fmt.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::types::{PhotoSize, Sticker, StickerKind};
|
use crate::types::{PhotoSize, Sticker, StickerFormat, StickerType};
|
||||||
|
|
||||||
/// This object represents a sticker set.
|
/// This object represents a sticker set.
|
||||||
///
|
///
|
||||||
|
@ -15,33 +15,134 @@ pub struct StickerSet {
|
||||||
/// Sticker set title.
|
/// Sticker set title.
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|
||||||
/// Sticker kind shared by all stickers in this set.
|
/// Sticker type shared by all stickers in this set.
|
||||||
pub kind: StickerKind,
|
#[serde(flatten)]
|
||||||
|
pub kind: StickerType,
|
||||||
|
|
||||||
/// `true`, if the sticker set contains masks.
|
/// Sticker format shared by all stickers in this set.
|
||||||
pub contains_masks: bool,
|
#[serde(flatten)]
|
||||||
|
pub format: StickerFormat,
|
||||||
|
|
||||||
/// List of all set stickers.
|
/// List of all set stickers.
|
||||||
pub stickers: Vec<Sticker>,
|
pub stickers: Vec<Sticker>,
|
||||||
|
|
||||||
/// Sticker set thumbnail in the .WEBP or .TGS format.
|
/// Sticker set thumbnail in the `.webp`, `.tgs` or `.webm` format.
|
||||||
pub thumb: Option<PhotoSize>,
|
pub thumb: Option<PhotoSize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This allows calling [`StickerKind`]'s methods directly on [`StickerSet`].
|
/// This allows calling [`StickerType`]'s methods directly on [`StickerSet`].
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use teloxide_core::types::StickerSet;
|
/// use teloxide_core::types::StickerSet;
|
||||||
///
|
///
|
||||||
/// let sticker: StickerSet = todo!();
|
/// let sticker: StickerSet = todo!();
|
||||||
///
|
///
|
||||||
/// let _ = sticker.is_video();
|
/// let _ = sticker.is_mask();
|
||||||
/// let _ = sticker.kind.is_video();
|
/// let _ = sticker.kind.is_mask();
|
||||||
/// ```
|
/// ```
|
||||||
impl Deref for StickerSet {
|
impl Deref for StickerSet {
|
||||||
type Target = StickerKind;
|
type Target = StickerType;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.kind
|
&self.kind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StickerSet {
|
||||||
|
/// Returns `true` is this is a "normal" raster sticker.
|
||||||
|
///
|
||||||
|
/// Alias to [`self.format.is_raster()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_raster()`]: StickerFormat::is_raster
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_raster(&self) -> bool {
|
||||||
|
self.format.is_raster()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` is this is an [animated] sticker.
|
||||||
|
///
|
||||||
|
/// Alias to [`self.format.is_animated()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_animated()`]: StickerFormat::is_animated
|
||||||
|
/// [animated]: https://telegram.org/blog/animated-stickers
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_animated(&self) -> bool {
|
||||||
|
self.format.is_animated()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` is this is a [video] sticker.
|
||||||
|
///
|
||||||
|
/// Alias to [`self.format.is_video()`].
|
||||||
|
///
|
||||||
|
/// [`self.format.is_video()`]: StickerFormat::is_video
|
||||||
|
/// [video]: https://telegram.org/blog/video-stickers-better-reactions
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_video(&self) -> bool {
|
||||||
|
self.format.is_video()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::types::StickerSet;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn smoke_serde() {
|
||||||
|
// https://t.me/addstickers/teloxide_test
|
||||||
|
let json = r#"{
|
||||||
|
"name": "teloxide_test",
|
||||||
|
"title": "teloxide-test",
|
||||||
|
"is_animated": false,
|
||||||
|
"is_video": false,
|
||||||
|
"sticker_type": "regular",
|
||||||
|
"contains_masks": false,
|
||||||
|
"stickers": [
|
||||||
|
{
|
||||||
|
"width": 512,
|
||||||
|
"height": 512,
|
||||||
|
"emoji": "⚙️",
|
||||||
|
"set_name": "teloxide_test",
|
||||||
|
"is_animated": false,
|
||||||
|
"is_video": false,
|
||||||
|
"type": "regular",
|
||||||
|
"thumb": {
|
||||||
|
"file_id": "AAMCAQADFQABYzB4ATH0sqXx351gZ5GpY1Z3Tl8AAlgCAAJ1t4hFbxNCoAg1-akBAAdtAAMpBA",
|
||||||
|
"file_unique_id": "AQADWAIAAnW3iEVy",
|
||||||
|
"file_size": 7698,
|
||||||
|
"width": 320,
|
||||||
|
"height": 320
|
||||||
|
},
|
||||||
|
"file_id": "CAACAgEAAxUAAWMweAEx9LKl8d-dYGeRqWNWd05fAAJYAgACdbeIRW8TQqAINfmpKQQ",
|
||||||
|
"file_unique_id": "AgADWAIAAnW3iEU",
|
||||||
|
"file_size": 12266
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 512,
|
||||||
|
"height": 512,
|
||||||
|
"emoji": "⚙️",
|
||||||
|
"set_name": "teloxide_test",
|
||||||
|
"is_animated": false,
|
||||||
|
"is_video": false,
|
||||||
|
"type": "regular",
|
||||||
|
"thumb": {
|
||||||
|
"file_id": "AAMCAQADFQABYzB4AcABR8-MuvGagis9Pk6liSAAAs8DAAL2YYBFNbvduoN1p7oBAAdtAAMpBA",
|
||||||
|
"file_unique_id": "AQADzwMAAvZhgEVy",
|
||||||
|
"file_size": 7780,
|
||||||
|
"width": 320,
|
||||||
|
"height": 320
|
||||||
|
},
|
||||||
|
"file_id": "CAACAgEAAxUAAWMweAHAAUfPjLrxmoIrPT5OpYkgAALPAwAC9mGARTW73bqDdae6KQQ",
|
||||||
|
"file_unique_id": "AgADzwMAAvZhgEU",
|
||||||
|
"file_size": 12158
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
let set: StickerSet = serde_json::from_str(json).unwrap();
|
||||||
|
|
||||||
|
assert!(set.is_raster());
|
||||||
|
assert!(set.is_regular());
|
||||||
|
assert!(set.thumb.is_none());
|
||||||
|
assert_eq!(set.stickers.len(), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -344,6 +344,7 @@ mod test {
|
||||||
last_name: None,
|
last_name: None,
|
||||||
bio: None,
|
bio: None,
|
||||||
has_private_forwards: None,
|
has_private_forwards: None,
|
||||||
|
has_restricted_voice_and_video_messages: None,
|
||||||
}),
|
}),
|
||||||
photo: None,
|
photo: None,
|
||||||
pinned_message: None,
|
pinned_message: None,
|
||||||
|
|
Loading…
Reference in a new issue