use serde::de::{Deserialize, Deserializer, Error};
use crate::types::*;
use crate::url::*;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum MessageOrChannelPost {
Message(Message),
ChannelPost(ChannelPost),
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Message {
pub id: MessageId,
pub from: User,
pub date: Integer,
pub chat: MessageChat,
pub forward: Option<Forward>,
pub reply_to_message: Option<Box<MessageOrChannelPost>>,
pub edit_date: Option<Integer>,
pub kind: MessageKind,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct ChannelPost {
pub id: MessageId,
pub date: Integer,
pub chat: Channel,
pub forward: Option<Forward>,
pub reply_to_message: Option<Box<MessageOrChannelPost>>,
pub edit_date: Option<Integer>,
pub kind: MessageKind,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Forward {
pub date: Integer,
pub from: ForwardFrom,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ForwardFrom {
User {
user: User,
},
Channel {
channel: Channel,
message_id: Integer,
},
ChannelHiddenUser {
sender_name: String,
},
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum MessageKind {
Text {
data: String,
entities: Vec<MessageEntity>,
},
Audio {
data: Audio,
},
Document {
data: Document,
caption: Option<String>,
},
Photo {
data: Vec<PhotoSize>,
caption: Option<String>,
media_group_id: Option<String>,
},
Sticker {
data: Sticker,
},
Video {
data: Video,
caption: Option<String>,
media_group_id: Option<String>,
},
Voice {
data: Voice,
},
VideoNote {
data: VideoNote,
},
Contact {
data: Contact,
},
Location {
data: Location,
},
Poll {
data: Poll,
},
Venue {
data: Venue,
},
NewChatMembers {
data: Vec<User>,
},
LeftChatMember {
data: User,
},
NewChatTitle {
data: String,
},
NewChatPhoto {
data: Vec<PhotoSize>,
},
DeleteChatPhoto,
GroupChatCreated,
SupergroupChatCreated,
ChannelChatCreated,
MigrateToChatId {
data: Integer,
},
MigrateFromChatId {
data: Integer,
},
PinnedMessage {
data: Box<MessageOrChannelPost>,
},
#[doc(hidden)]
Unknown { raw: RawMessage },
}
impl Message {
fn from_raw_message(raw: RawMessage) -> Result<Self, String> {
let id = raw.message_id;
let from = match raw.from.clone() {
Some(from) => from,
None => return Err(format!("Missing `from` field for Message")),
};
let date = raw.date;
let chat = match raw.chat.clone() {
Chat::Private(x) => MessageChat::Private(x),
Chat::Group(x) => MessageChat::Group(x),
Chat::Supergroup(x) => MessageChat::Supergroup(x),
Chat::Unknown(x) => MessageChat::Unknown(x),
Chat::Channel(_) => return Err(format!("Channel chat in Message")),
};
let reply_to_message = raw.reply_to_message.clone();
let edit_date = raw.edit_date;
let forward = match (
raw.forward_date,
&raw.forward_from,
&raw.forward_from_chat,
raw.forward_from_message_id,
&raw.forward_sender_name,
) {
(None, &None, &None, None, &None) => None,
(Some(date), &Some(ref from), &None, None, &None) => Some(Forward {
date: date,
from: ForwardFrom::User { user: from.clone() },
}),
(Some(date), &None, &Some(Chat::Channel(ref channel)), Some(message_id), &None) => {
Some(Forward {
date: date,
from: ForwardFrom::Channel {
channel: channel.clone(),
message_id: message_id,
},
})
}
(Some(date), &None, &None, None, &Some(ref sender_name)) => Some(Forward {
date,
from: ForwardFrom::ChannelHiddenUser {
sender_name: sender_name.clone(),
},
}),
_ => return Err(format!("invalid forward fields combination")),
};
let make_message = |kind| {
Ok(Message {
id: id.into(),
from: from,
date: date,
chat: chat,
forward: forward,
reply_to_message: reply_to_message,
edit_date: edit_date,
kind: kind,
})
};
macro_rules! maybe_field {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant { data: val });
}
}};
}
macro_rules! maybe_field_with_caption {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant {
data: val,
caption: raw.caption,
});
}
}};
}
macro_rules! maybe_field_with_caption_and_group {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant {
data: val,
caption: raw.caption,
media_group_id: raw.media_group_id,
});
}
}};
}
macro_rules! maybe_true_field {
($name:ident, $variant:ident) => {{
if let Some(True) = raw.$name {
return make_message(MessageKind::$variant);
}
}};
}
if let Some(text) = raw.text {
let entities = raw.entities.unwrap_or_else(Vec::new);
return make_message(MessageKind::Text {
data: text,
entities: entities,
});
}
maybe_field!(audio, Audio);
maybe_field_with_caption!(document, Document);
maybe_field_with_caption_and_group!(photo, Photo);
maybe_field!(sticker, Sticker);
maybe_field_with_caption_and_group!(video, Video);
maybe_field!(voice, Voice);
maybe_field!(video_note, VideoNote);
maybe_field!(contact, Contact);
maybe_field!(location, Location);
maybe_field!(poll, Poll);
maybe_field!(venue, Venue);
maybe_field!(new_chat_members, NewChatMembers);
maybe_field!(left_chat_member, LeftChatMember);
maybe_field!(new_chat_title, NewChatTitle);
maybe_field!(new_chat_photo, NewChatPhoto);
maybe_true_field!(delete_chat_photo, DeleteChatPhoto);
maybe_true_field!(delete_chat_photo, DeleteChatPhoto);
maybe_true_field!(group_chat_created, GroupChatCreated);
maybe_true_field!(supergroup_chat_created, SupergroupChatCreated);
maybe_true_field!(channel_chat_created, ChannelChatCreated);
maybe_field!(migrate_to_chat_id, MigrateToChatId);
maybe_field!(migrate_from_chat_id, MigrateFromChatId);
maybe_field!(pinned_message, PinnedMessage);
make_message(MessageKind::Unknown { raw: raw })
}
}
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
let raw: RawMessage = Deserialize::deserialize(deserializer)?;
Self::from_raw_message(raw).map_err(|err| D::Error::custom(err))
}
}
impl ChannelPost {
fn from_raw_message(raw: RawMessage) -> Result<Self, String> {
let id = raw.message_id;
let date = raw.date;
let chat = match raw.chat.clone() {
Chat::Channel(channel) => channel,
_ => return Err(format!("Expected channel chat type for ChannelMessage")),
};
let reply_to_message = raw.reply_to_message.clone();
let edit_date = raw.edit_date;
let forward = match (
raw.forward_date,
&raw.forward_from,
&raw.forward_from_chat,
raw.forward_from_message_id,
&raw.forward_sender_name,
) {
(None, &None, &None, None, &None) => None,
(Some(date), &Some(ref from), &None, None, &None) => Some(Forward {
date: date,
from: ForwardFrom::User { user: from.clone() },
}),
(Some(date), &None, &Some(Chat::Channel(ref channel)), Some(message_id), &None) => {
Some(Forward {
date: date,
from: ForwardFrom::Channel {
channel: channel.clone(),
message_id: message_id,
},
})
}
(Some(date), &None, &None, None, &Some(ref sender_name)) => Some(Forward {
date,
from: ForwardFrom::ChannelHiddenUser {
sender_name: sender_name.clone(),
},
}),
_ => return Err(format!("invalid forward fields combination")),
};
let make_message = |kind| {
Ok(ChannelPost {
id: id.into(),
date: date,
chat: chat,
forward: forward,
reply_to_message: reply_to_message,
edit_date: edit_date,
kind: kind,
})
};
macro_rules! maybe_field {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant { data: val });
}
}};
}
macro_rules! maybe_field_with_caption {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant {
data: val,
caption: raw.caption,
});
}
}};
}
macro_rules! maybe_field_with_caption_and_group {
($name:ident, $variant:ident) => {{
if let Some(val) = raw.$name {
return make_message(MessageKind::$variant {
data: val,
caption: raw.caption,
media_group_id: raw.media_group_id,
});
}
}};
}
macro_rules! maybe_true_field {
($name:ident, $variant:ident) => {{
if let Some(True) = raw.$name {
return make_message(MessageKind::$variant);
}
}};
}
if let Some(text) = raw.text {
let entities = raw.entities.unwrap_or_else(Vec::new);
return make_message(MessageKind::Text {
data: text,
entities: entities,
});
}
maybe_field!(audio, Audio);
maybe_field_with_caption!(document, Document);
maybe_field_with_caption_and_group!(photo, Photo);
maybe_field!(sticker, Sticker);
maybe_field_with_caption_and_group!(video, Video);
maybe_field!(voice, Voice);
maybe_field!(video_note, VideoNote);
maybe_field!(contact, Contact);
maybe_field!(location, Location);
maybe_field!(poll, Poll);
maybe_field!(venue, Venue);
maybe_field!(new_chat_members, NewChatMembers);
maybe_field!(left_chat_member, LeftChatMember);
maybe_field!(new_chat_title, NewChatTitle);
maybe_field!(new_chat_photo, NewChatPhoto);
maybe_true_field!(delete_chat_photo, DeleteChatPhoto);
maybe_true_field!(delete_chat_photo, DeleteChatPhoto);
maybe_true_field!(group_chat_created, GroupChatCreated);
maybe_true_field!(supergroup_chat_created, SupergroupChatCreated);
maybe_true_field!(channel_chat_created, ChannelChatCreated);
maybe_field!(migrate_to_chat_id, MigrateToChatId);
maybe_field!(migrate_from_chat_id, MigrateFromChatId);
maybe_field!(pinned_message, PinnedMessage);
make_message(MessageKind::Unknown { raw: raw })
}
}
impl<'de> Deserialize<'de> for ChannelPost {
fn deserialize<D>(deserializer: D) -> Result<ChannelPost, D::Error>
where
D: Deserializer<'de>,
{
let raw: RawMessage = Deserialize::deserialize(deserializer)?;
Self::from_raw_message(raw).map_err(|err| D::Error::custom(err))
}
}
impl<'de> Deserialize<'de> for MessageOrChannelPost {
fn deserialize<D>(deserializer: D) -> Result<MessageOrChannelPost, D::Error>
where
D: Deserializer<'de>,
{
let raw: RawMessage = Deserialize::deserialize(deserializer)?;
let is_channel = match raw.chat {
Chat::Channel(_) => true,
_ => false,
};
let res = if is_channel {
ChannelPost::from_raw_message(raw).map(MessageOrChannelPost::ChannelPost)
} else {
Message::from_raw_message(raw).map(MessageOrChannelPost::Message)
};
res.map_err(|err| D::Error::custom(err))
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct RawMessage {
pub message_id: Integer,
pub from: Option<User>,
pub date: Integer,
pub chat: Chat,
pub forward_from: Option<User>,
pub forward_from_chat: Option<Chat>,
pub forward_from_message_id: Option<Integer>,
pub forward_date: Option<Integer>,
pub reply_to_message: Option<Box<MessageOrChannelPost>>,
pub edit_date: Option<Integer>,
pub media_group_id: Option<String>,
pub text: Option<String>,
pub entities: Option<Vec<MessageEntity>>,
pub audio: Option<Audio>,
pub document: Option<Document>,
pub photo: Option<Vec<PhotoSize>>,
pub sticker: Option<Sticker>,
pub video: Option<Video>,
pub voice: Option<Voice>,
pub video_note: Option<VideoNote>,
pub caption: Option<String>,
pub contact: Option<Contact>,
pub location: Option<Location>,
pub poll: Option<Poll>,
pub venue: Option<Venue>,
pub new_chat_members: Option<Vec<User>>,
pub left_chat_member: Option<User>,
pub new_chat_title: Option<String>,
pub new_chat_photo: Option<Vec<PhotoSize>>,
pub delete_chat_photo: Option<True>,
pub group_chat_created: Option<True>,
pub supergroup_chat_created: Option<True>,
pub channel_chat_created: Option<True>,
pub migrate_to_chat_id: Option<Integer>,
pub migrate_from_chat_id: Option<Integer>,
pub pinned_message: Option<Box<MessageOrChannelPost>>,
pub forward_sender_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct MessageEntity {
pub offset: Integer,
pub length: Integer,
pub kind: MessageEntityKind,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum MessageEntityKind {
Mention,
Hashtag,
BotCommand,
Url,
Email,
Bold,
Italic,
Code,
Pre,
TextLink(String),
TextMention(User),
#[doc(hidden)]
Unknown(RawMessageEntity),
}
impl<'de> Deserialize<'de> for MessageEntity {
fn deserialize<D>(deserializer: D) -> Result<MessageEntity, D::Error>
where
D: Deserializer<'de>,
{
use self::MessageEntityKind::*;
let raw: RawMessageEntity = Deserialize::deserialize(deserializer)?;
let offset = raw.offset;
let length = raw.length;
macro_rules! required_field {
($name:ident) => {{
match raw.$name {
Some(val) => val,
None => return Err(D::Error::missing_field(stringify!($name))),
}
}};
}
let kind = match raw.type_.as_str() {
"mention" => Mention,
"hashtag" => Hashtag,
"bot_command" => BotCommand,
"url" => Url,
"email" => Email,
"bold" => Bold,
"italic" => Italic,
"code" => Code,
"pre" => Pre,
"text_link" => TextLink(required_field!(url)),
"text_mention" => TextMention(required_field!(user)),
_ => Unknown(raw),
};
Ok(MessageEntity {
offset: offset,
length: length,
kind: kind,
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct RawMessageEntity {
#[serde(rename = "type")]
pub type_: String,
pub offset: Integer,
pub length: Integer,
pub url: Option<String>,
pub user: Option<User>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct PhotoSize {
pub file_id: String,
pub width: Integer,
pub height: Integer,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Audio {
pub file_id: String,
pub duration: Integer,
pub performer: Option<String>,
pub title: Option<String>,
pub mime_type: Option<String>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Document {
pub file_id: String,
pub thumb: Option<PhotoSize>,
pub file_name: Option<String>,
pub mime_type: Option<String>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Sticker {
pub file_id: String,
pub file_unique_id: String,
pub width: Integer,
pub height: Integer,
pub thumb: Option<PhotoSize>,
pub emoji: Option<String>,
pub set_name: Option<String>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Video {
pub file_id: String,
pub width: Integer,
pub height: Integer,
pub duration: Integer,
pub thumb: Option<PhotoSize>,
pub mime_type: Option<String>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Voice {
pub file_id: String,
pub duration: Integer,
pub mime_type: Option<String>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct VideoNote {
pub file_id: String,
pub length: Integer,
pub duration: Integer,
pub thumb: Option<PhotoSize>,
pub file_size: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Contact {
pub phone_number: String,
pub first_name: String,
pub last_name: Option<String>,
pub user_id: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Location {
pub longitude: Float,
pub latitude: Float,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Venue {
pub location: Location,
pub title: String,
pub address: String,
pub foursquare_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct Poll {
pub id: String,
pub question: String,
pub options: Vec<PollOption>,
pub total_voter_count: Integer,
pub is_closed: bool,
pub is_anonymous: bool,
#[serde(rename = "type")]
pub type_: PollType,
pub allows_multiple_answers: bool,
pub correct_option_id: Option<Integer>,
pub explanation: Option<String>,
pub explanation_entities: Option<Vec<MessageEntity>>,
pub open_period: Option<Integer>,
pub close_date: Option<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct PollAnswer {
pub poll_id: String,
pub user: User,
pub option_ids: Vec<Integer>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct PollOption {
pub text: String,
pub voter_count: Integer,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum PollType {
#[serde(rename = "regular")]
Regular,
#[serde(rename = "quiz")]
Quiz,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct UserProfilePhotos {
pub total_count: Integer,
pub photos: Vec<Vec<PhotoSize>>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
pub struct File {
pub file_id: String,
pub file_size: Option<Integer>,
pub file_path: Option<String>,
}
impl File {
pub fn get_url(&self, token: &str) -> Option<String> {
self.file_path
.as_ref()
.map(|path| format!("{}file/bot{}/{}", telegram_api_url(), token, path))
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize)]
pub enum ParseMode {
Markdown,
MarkdownV2,
#[serde(rename = "HTML")]
Html,
}
impl ::std::fmt::Display for ParseMode {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
ParseMode::Markdown => write!(f, "Markdown"),
ParseMode::MarkdownV2 => write!(f, "MarkdownV2"),
ParseMode::Html => write!(f, "HTML"),
}
}
}