use std::fmt::Debug;
use chrono::{DateTime, Utc};
use grammers_tl_types as tl;
use super::{Downloadable, PhotoSize};
#[derive(Clone, Debug, PartialEq)]
pub struct Photo {
pub raw: tl::types::MessageMediaPhoto,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Document {
pub raw: tl::types::MessageMediaDocument,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Sticker {
pub document: Document,
pub raw_attrs: tl::types::DocumentAttributeSticker,
animated: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Uploaded {
pub raw: tl::enums::InputFile,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Contact {
pub raw: tl::types::MessageMediaContact,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Poll {
pub raw: tl::types::Poll,
pub raw_results: tl::types::PollResults,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Geo {
pub raw: tl::types::GeoPoint,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Dice {
pub raw: tl::types::MessageMediaDice,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Venue {
pub geo: Option<Geo>,
pub raw_venue: tl::types::MessageMediaVenue,
}
#[derive(Clone, Debug, PartialEq)]
pub struct GeoLive {
pub geo: Option<Geo>,
pub raw_geolive: tl::types::MessageMediaGeoLive,
}
#[derive(Clone, Debug, PartialEq)]
pub struct WebPage {
pub raw: tl::types::MessageMediaWebPage,
}
#[derive(Clone, Debug, PartialEq)]
pub struct ChatPhoto {
pub raw: tl::enums::InputFileLocation,
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum Media {
Photo(Photo),
Document(Document),
Sticker(Sticker),
Contact(Contact),
Poll(Poll),
Geo(Geo),
Dice(Dice),
Venue(Venue),
GeoLive(GeoLive),
WebPage(WebPage),
}
impl Photo {
pub fn from_raw(photo: tl::enums::Photo) -> Self {
Self {
raw: tl::types::MessageMediaPhoto {
spoiler: false,
photo: Some(photo),
ttl_seconds: None,
},
}
}
pub fn from_raw_media(photo: tl::types::MessageMediaPhoto) -> Self {
Self { raw: photo }
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaPhoto {
use tl::{
enums::{InputPhoto as eInputPhoto, Photo},
types::InputPhoto,
};
tl::types::InputMediaPhoto {
spoiler: false,
id: match self.raw.photo {
Some(Photo::Photo(ref photo)) => InputPhoto {
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference.clone(),
}
.into(),
_ => eInputPhoto::Empty,
},
ttl_seconds: self.raw.ttl_seconds,
}
}
pub fn id(&self) -> i64 {
use tl::enums::Photo as P;
match self.raw.photo.as_ref().unwrap() {
P::Empty(photo) => photo.id,
P::Photo(photo) => photo.id,
}
}
pub fn size(&self) -> Option<usize> {
self.thumbs()
.iter()
.max_by_key(|x| x.size())
.map(|thumb| thumb.size())
}
pub fn thumbs(&self) -> Vec<PhotoSize> {
use tl::enums::Photo as P;
let photo = match self.raw.photo.as_ref() {
Some(photo) => photo,
None => return vec![],
};
match photo {
P::Empty(_) => vec![],
P::Photo(photo) => photo
.sizes
.iter()
.map(|x| PhotoSize::make_from(x, photo))
.collect(),
}
}
pub fn is_spoiler(&self) -> bool {
self.raw.spoiler
}
pub fn ttl_seconds(&self) -> Option<i32> {
self.raw.ttl_seconds
}
}
impl Downloadable for Photo {
fn to_raw_input_location(&self) -> Option<tl::enums::InputFileLocation> {
use tl::enums::Photo as P;
self.raw.photo.as_ref().and_then(|p| match p {
P::Empty(_) => None,
P::Photo(photo) => Some(
tl::types::InputPhotoFileLocation {
id: photo.id,
access_hash: photo.access_hash,
file_reference: photo.file_reference.clone(),
thumb_size: self
.thumbs()
.iter()
.max_by_key(|x| x.size())
.map(|ps| ps.photo_type())
.unwrap_or(String::from("w")),
}
.into(),
),
})
}
}
impl Document {
pub fn from_raw_media(document: tl::types::MessageMediaDocument) -> Self {
Self { raw: document }
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaDocument {
use tl::{
enums::{Document, InputDocument as eInputDocument},
types::InputDocument,
};
tl::types::InputMediaDocument {
spoiler: false,
id: match self.raw.document {
Some(Document::Document(ref document)) => InputDocument {
id: document.id,
access_hash: document.access_hash,
file_reference: document.file_reference.clone(),
}
.into(),
_ => eInputDocument::Empty,
},
ttl_seconds: self.raw.ttl_seconds,
query: None,
video_cover: None,
video_timestamp: None,
}
}
pub fn id(&self) -> i64 {
use tl::enums::Document as D;
match self.raw.document.as_ref().unwrap() {
D::Empty(document) => document.id,
D::Document(document) => document.id,
}
}
pub fn name(&self) -> Option<&str> {
use tl::enums::Document as D;
match self.raw.document.as_ref().unwrap() {
D::Empty(_) => None,
D::Document(document) => document.attributes.iter().find_map(|attr| match attr {
tl::enums::DocumentAttribute::Filename(attr) => Some(attr.file_name.as_ref()),
_ => None,
}),
}
}
pub fn mime_type(&self) -> Option<&str> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => Some(d.mime_type.as_str()),
_ => None,
}
}
pub fn creation_date(&self) -> Option<DateTime<Utc>> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
Some(DateTime::<Utc>::from_timestamp(d.date as i64, 0).expect("date out of range"))
}
_ => None,
}
}
pub fn size(&self) -> Option<usize> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => Some(d.size as usize),
_ => None,
}
}
pub fn thumbs(&self) -> Vec<PhotoSize> {
use tl::enums::Document as D;
let document = match self.raw.document.as_ref() {
Some(document) => document,
None => return vec![],
};
match document {
D::Empty(_) => vec![],
D::Document(document) => match &document.thumbs {
Some(thumbs) => thumbs
.iter()
.map(|x| PhotoSize::make_from_document(x, document))
.collect(),
None => vec![],
},
}
}
pub fn duration(&self) -> Option<f64> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
for attr in &d.attributes {
match attr {
tl::enums::DocumentAttribute::Video(v) => return Some(v.duration),
tl::enums::DocumentAttribute::Audio(a) => return Some(a.duration as _),
_ => {}
}
}
None
}
_ => None,
}
}
pub fn resolution(&self) -> Option<(i32, i32)> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
for attr in &d.attributes {
match attr {
tl::enums::DocumentAttribute::Video(v) => return Some((v.w, v.h)),
tl::enums::DocumentAttribute::ImageSize(i) => return Some((i.w, i.h)),
_ => {}
}
}
None
}
_ => None,
}
}
pub fn audio_title(&self) -> Option<&str> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
for attr in &d.attributes {
#[allow(clippy::single_match)]
match attr {
tl::enums::DocumentAttribute::Audio(a) => return a.title.as_deref(),
_ => {}
}
}
None
}
_ => None,
}
}
pub fn performer(&self) -> Option<&str> {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
for attr in &d.attributes {
#[allow(clippy::single_match)]
match attr {
tl::enums::DocumentAttribute::Audio(a) => return a.performer.as_deref(),
_ => {}
}
}
None
}
_ => None,
}
}
pub fn is_animated(&self) -> bool {
match self.raw.document.as_ref() {
Some(tl::enums::Document::Document(d)) => {
for attr in &d.attributes {
#[allow(clippy::single_match)]
match attr {
tl::enums::DocumentAttribute::Animated => return true,
_ => {}
}
}
false
}
_ => false,
}
}
pub fn is_spoiler(&self) -> bool {
self.raw.spoiler
}
}
impl Downloadable for Document {
fn to_raw_input_location(&self) -> Option<tl::enums::InputFileLocation> {
use tl::enums::Document as D;
self.raw.document.as_ref().and_then(|p| match p {
D::Empty(_) => None,
D::Document(document) => Some(
tl::types::InputDocumentFileLocation {
id: document.id,
access_hash: document.access_hash,
file_reference: document.file_reference.clone(),
thumb_size: String::new(),
}
.into(),
),
})
}
fn size(&self) -> Option<usize> {
self.size()
}
}
impl Sticker {
pub fn from_document(document: &Document) -> Option<Self> {
match document.raw.document {
Some(tl::enums::Document::Document(ref doc)) => {
let mut animated = false;
let mut sticker_attrs: Option<tl::types::DocumentAttributeSticker> = None;
for attr in &doc.attributes {
match attr {
tl::enums::DocumentAttribute::Sticker(s) => sticker_attrs = Some(s.clone()),
tl::enums::DocumentAttribute::Animated => animated = true,
_ => (),
}
}
Some(Self {
document: document.clone(),
raw_attrs: sticker_attrs?,
animated,
})
}
_ => None,
}
}
pub fn emoji(&self) -> &str {
self.raw_attrs.alt.as_str()
}
pub fn is_animated(&self) -> bool {
self.animated
}
}
impl Contact {
pub fn from_raw_media(contact: tl::types::MessageMediaContact) -> Self {
Self { raw: contact }
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaContact {
tl::types::InputMediaContact {
phone_number: self.raw.phone_number.clone(),
first_name: self.raw.first_name.clone(),
last_name: self.raw.last_name.clone(),
vcard: self.raw.vcard.clone(),
}
}
pub fn phone_number(&self) -> &str {
self.raw.phone_number.as_str()
}
pub fn first_name(&self) -> &str {
self.raw.first_name.as_str()
}
pub fn last_name(&self) -> &str {
self.raw.last_name.as_str()
}
pub fn vcard(&self) -> &str {
self.raw.vcard.as_str()
}
}
impl Poll {
pub fn from_raw_media(poll: tl::types::MessageMediaPoll) -> Self {
Self {
raw: match poll.poll {
tl::enums::Poll::Poll(poll) => poll,
},
raw_results: match poll.results {
tl::enums::PollResults::Results(results) => results,
},
}
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaPoll {
tl::types::InputMediaPoll {
poll: grammers_tl_types::enums::Poll::Poll(self.raw.clone()),
correct_answers: None,
solution: None,
solution_entities: None,
}
}
pub fn question(&self) -> &grammers_tl_types::enums::TextWithEntities {
&self.raw.question
}
pub fn is_quiz(&self) -> bool {
self.raw.quiz
}
pub fn closed(&self) -> bool {
self.raw.closed
}
pub fn iter_answers(&self) -> impl Iterator<Item = &tl::types::PollAnswer> {
self.raw.answers.iter().map(|answer| match answer {
tl::enums::PollAnswer::Answer(answer) => answer,
})
}
pub fn total_voters(&self) -> Option<i32> {
self.raw_results.total_voters
}
pub fn iter_voters_summary(
&self,
) -> Option<impl Iterator<Item = &tl::types::PollAnswerVoters>> {
self.raw_results.results.as_ref().map(|results| {
results.iter().map(|result| match result {
tl::enums::PollAnswerVoters::Voters(voters) => voters,
})
})
}
}
impl Geo {
pub fn from_raw_media(geo: tl::types::MessageMediaGeo) -> Option<Self> {
use tl::enums::GeoPoint as eGeoPoint;
match &geo.geo {
eGeoPoint::Empty => None,
eGeoPoint::Point(point) => Some(Self { raw: point.clone() }),
}
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaGeoPoint {
use tl::types::InputGeoPoint;
tl::types::InputMediaGeoPoint {
geo_point: InputGeoPoint {
lat: self.raw.lat,
long: self.raw.long,
accuracy_radius: self.raw.accuracy_radius,
}
.into(),
}
}
pub fn to_raw_input_geo_point(&self) -> tl::enums::InputGeoPoint {
use tl::{enums::InputGeoPoint as eInputGeoPoint, types::InputGeoPoint};
eInputGeoPoint::Point(InputGeoPoint {
lat: self.raw.lat,
long: self.raw.long,
accuracy_radius: self.raw.accuracy_radius,
})
}
pub fn latitue(&self) -> f64 {
self.raw.lat
}
pub fn longitude(&self) -> f64 {
self.raw.long
}
pub fn accuracy_radius(&self) -> Option<i32> {
self.raw.accuracy_radius
}
}
impl Dice {
pub fn from_raw_media(dice: tl::types::MessageMediaDice) -> Self {
Self { raw: dice }
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaDice {
tl::types::InputMediaDice {
emoticon: self.raw.emoticon.clone(),
}
}
pub fn emoji(&self) -> &str {
&self.raw.emoticon
}
pub fn value(&self) -> i32 {
self.raw.value
}
}
impl Venue {
pub fn from_raw_media(venue: tl::types::MessageMediaVenue) -> Self {
use tl::types::MessageMediaGeo;
Self {
geo: Geo::from_raw_media(MessageMediaGeo {
geo: venue.geo.clone(),
}),
raw_venue: venue,
}
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaVenue {
tl::types::InputMediaVenue {
geo_point: match self.geo {
Some(ref geo) => geo.to_raw_input_geo_point(),
None => tl::enums::InputGeoPoint::Empty,
},
title: self.raw_venue.title.clone(),
address: self.raw_venue.address.clone(),
provider: self.raw_venue.provider.clone(),
venue_id: self.raw_venue.venue_id.clone(),
venue_type: self.raw_venue.venue_type.clone(),
}
}
pub fn title(&self) -> &str {
&self.raw_venue.title
}
pub fn address(&self) -> &str {
&self.raw_venue.address
}
pub fn provider(&self) -> &str {
&self.raw_venue.provider
}
pub fn venue_id(&self) -> &str {
&self.raw_venue.venue_id
}
pub fn venue_type(&self) -> &str {
&self.raw_venue.venue_type
}
}
impl GeoLive {
pub fn from_raw_media(geolive: tl::types::MessageMediaGeoLive) -> Self {
use tl::types::MessageMediaGeo;
Self {
geo: Geo::from_raw_media(MessageMediaGeo {
geo: geolive.geo.clone(),
}),
raw_geolive: geolive,
}
}
pub fn to_raw_input_media(&self) -> tl::types::InputMediaGeoLive {
tl::types::InputMediaGeoLive {
geo_point: match self.geo {
Some(ref geo) => geo.to_raw_input_geo_point(),
None => tl::enums::InputGeoPoint::Empty,
},
heading: self.raw_geolive.heading,
period: Some(self.raw_geolive.period),
proximity_notification_radius: self.raw_geolive.proximity_notification_radius,
stopped: false,
}
}
pub fn heading(&self) -> Option<i32> {
self.raw_geolive.heading
}
pub fn period(&self) -> i32 {
self.raw_geolive.period
}
pub fn proximity_notification_radius(&self) -> Option<i32> {
self.raw_geolive.proximity_notification_radius
}
}
impl WebPage {
pub fn from_raw_media(webpage: tl::types::MessageMediaWebPage) -> Self {
Self { raw: webpage }
}
}
impl Uploaded {
pub fn from_raw(input_file: tl::enums::InputFile) -> Self {
Self { raw: input_file }
}
pub(crate) fn name(&self) -> &str {
match &self.raw {
tl::enums::InputFile::File(f) => f.name.as_ref(),
tl::enums::InputFile::Big(f) => f.name.as_ref(),
tl::enums::InputFile::StoryDocument(_) => "",
}
}
}
impl Media {
pub fn from_raw(media: tl::enums::MessageMedia) -> Option<Self> {
use tl::enums::MessageMedia as M;
match media {
M::Empty => None,
M::Photo(photo) => Some(Self::Photo(Photo::from_raw_media(photo))),
M::Geo(geo) => Geo::from_raw_media(geo).map(Self::Geo),
M::Contact(contact) => Some(Self::Contact(Contact::from_raw_media(contact))),
M::Unsupported => None,
M::Document(document) => {
let document = Document::from_raw_media(document);
Some(if let Some(sticker) = Sticker::from_document(&document) {
Self::Sticker(sticker)
} else {
Self::Document(document)
})
}
M::WebPage(webpage) => Some(Self::WebPage(WebPage::from_raw_media(webpage))),
M::Venue(venue) => Some(Self::Venue(Venue::from_raw_media(venue))),
M::Game(_) => None,
M::Invoice(_) => None,
M::GeoLive(geolive) => Some(Self::GeoLive(GeoLive::from_raw_media(geolive))),
M::Poll(poll) => Some(Self::Poll(Poll::from_raw_media(poll))),
M::Dice(dice) => Some(Self::Dice(Dice::from_raw_media(dice))),
M::Story(_) => None,
M::Giveaway(_) => None,
M::GiveawayResults(_) => None,
M::PaidMedia(_) => None,
M::ToDo(_) => None,
M::VideoStream(_) => None,
}
}
pub fn to_raw_input_media(&self) -> Option<tl::enums::InputMedia> {
match self {
Media::Photo(photo) => Some(photo.to_raw_input_media().into()),
Media::Document(document) => Some(document.to_raw_input_media().into()),
Media::Sticker(sticker) => Some(sticker.document.to_raw_input_media().into()),
Media::Contact(contact) => Some(contact.to_raw_input_media().into()),
Media::Poll(poll) => Some(poll.to_raw_input_media().into()),
Media::Geo(geo) => Some(geo.to_raw_input_media().into()),
Media::Dice(dice) => Some(dice.to_raw_input_media().into()),
Media::Venue(venue) => Some(venue.to_raw_input_media().into()),
Media::GeoLive(geolive) => Some(geolive.to_raw_input_media().into()),
Media::WebPage(_) => None,
}
}
}
impl Downloadable for Media {
fn to_raw_input_location(&self) -> Option<tl::enums::InputFileLocation> {
match self {
Media::Photo(photo) => photo.to_raw_input_location(),
Media::Document(document) => document.to_raw_input_location(),
Media::Sticker(sticker) => sticker.document.to_raw_input_location(),
Media::Contact(_) => None,
Media::Poll(_) => None,
Media::Geo(_) => None,
Media::Dice(_) => None,
Media::Venue(_) => None,
Media::GeoLive(_) => None,
Media::WebPage(_) => None,
}
}
}
impl From<Photo> for Media {
fn from(photo: Photo) -> Self {
Self::Photo(photo)
}
}
impl Downloadable for ChatPhoto {
fn to_raw_input_location(&self) -> Option<tl::enums::InputFileLocation> {
Some(self.raw.clone())
}
}