use super::Contact;
use super::chat::UserInfo;
use super::content::ContentType;
#[derive(Debug, Clone)]
pub struct IncomingUpdate {
pub chat_id: super::ChatId,
pub user: UserInfo,
pub message_id: Option<super::MessageId>,
pub kind: UpdateKind,
}
#[derive(Debug, Clone)]
pub enum UpdateKind {
Message {
text: Option<String>,
},
CallbackQuery {
id: String,
data: Option<String>,
inline_message_id: Option<String>,
},
Photo {
file_id: String,
file_unique_id: String,
caption: Option<String>,
},
Document {
file_id: String,
file_unique_id: String,
filename: Option<String>,
caption: Option<String>,
},
InlineQuery {
id: String,
query: String,
offset: String,
},
ChosenInlineResult {
result_id: String,
inline_message_id: Option<String>,
query: String,
},
PreCheckoutQuery {
id: String,
currency: String,
total_amount: i64,
payload: String,
},
SuccessfulPayment {
currency: String,
total_amount: i64,
payload: String,
},
WebAppData {
data: String,
},
MessageEdited {
text: Option<String>,
},
Voice {
file_id: String,
file_unique_id: String,
duration: i32,
caption: Option<String>,
},
VideoNote {
file_id: String,
file_unique_id: String,
duration: i32,
},
Video {
file_id: String,
file_unique_id: String,
caption: Option<String>,
},
Sticker {
file_id: String,
file_unique_id: String,
},
ContactReceived {
contact: Contact,
},
LocationReceived {
latitude: f64,
longitude: f64,
},
ChatMemberJoined,
ChatMemberLeft,
}
impl IncomingUpdate {
pub fn chat_id(&self) -> super::ChatId {
self.chat_id
}
pub fn user(&self) -> &UserInfo {
&self.user
}
pub fn type_name(&self) -> &'static str {
self.kind.type_name()
}
pub fn deep_link(&self) -> Option<&str> {
match &self.kind {
UpdateKind::Message { text: Some(text) } => {
let text = text.trim();
let rest = text.strip_prefix("/start")?;
let rest = if rest.is_empty() {
return None; } else if let Some(after_at) = rest.strip_prefix('@') {
after_at.find(' ').map(|i| &after_at[i..]).unwrap_or("")
} else if rest.starts_with(' ') {
rest
} else {
return None; };
let payload = rest.trim();
if payload.is_empty() {
None
} else {
Some(payload)
}
}
_ => None,
}
}
}
impl UpdateKind {
pub fn type_name(&self) -> &'static str {
match self {
Self::Message { .. } => "message",
Self::CallbackQuery { .. } => "callback_query",
Self::Photo { .. } => "photo",
Self::Document { .. } => "document",
Self::InlineQuery { .. } => "inline_query",
Self::ChosenInlineResult { .. } => "chosen_inline_result",
Self::PreCheckoutQuery { .. } => "pre_checkout_query",
Self::SuccessfulPayment { .. } => "successful_payment",
Self::WebAppData { .. } => "web_app_data",
Self::MessageEdited { .. } => "message_edited",
Self::Voice { .. } => "voice",
Self::VideoNote { .. } => "video_note",
Self::Video { .. } => "video",
Self::Sticker { .. } => "sticker",
Self::ContactReceived { .. } => "contact",
Self::LocationReceived { .. } => "location",
Self::ChatMemberJoined => "chat_member_joined",
Self::ChatMemberLeft => "chat_member_left",
}
}
pub fn to_received_media(&self) -> Option<ReceivedMedia> {
match self {
Self::Photo {
file_id,
file_unique_id,
caption,
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::Photo,
caption: caption.clone(),
filename: None,
}),
Self::Document {
file_id,
file_unique_id,
filename,
caption,
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::Document,
caption: caption.clone(),
filename: filename.clone(),
}),
Self::Voice {
file_id,
file_unique_id,
caption,
..
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::Voice,
caption: caption.clone(),
filename: None,
}),
Self::VideoNote {
file_id,
file_unique_id,
..
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::VideoNote,
caption: None,
filename: None,
}),
Self::Video {
file_id,
file_unique_id,
caption,
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::Video,
caption: caption.clone(),
filename: None,
}),
Self::Sticker {
file_id,
file_unique_id,
} => Some(ReceivedMedia {
file_id: file_id.clone(),
file_unique_id: file_unique_id.clone(),
file_type: ContentType::Sticker,
caption: None,
filename: None,
}),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ReceivedMedia {
pub file_id: String,
pub file_unique_id: String,
pub file_type: ContentType,
pub caption: Option<String>,
pub filename: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{ChatId, MessageId, UserId};
fn make_msg(text: &str) -> IncomingUpdate {
IncomingUpdate {
chat_id: ChatId(1),
user: UserInfo {
id: UserId(1),
first_name: "Test".into(),
last_name: None,
username: None,
language_code: None,
},
message_id: Some(MessageId(1)),
kind: UpdateKind::Message {
text: Some(text.into()),
},
}
}
#[test]
fn deep_link_with_payload() {
assert_eq!(make_msg("/start payload").deep_link(), Some("payload"));
}
#[test]
fn deep_link_with_bot_name() {
assert_eq!(make_msg("/start@bot payload").deep_link(), Some("payload"));
}
#[test]
fn deep_link_bare_start_is_none() {
assert_eq!(make_msg("/start").deep_link(), None);
}
#[test]
fn deep_link_starting_not_matched() {
assert_eq!(make_msg("/starting something").deep_link(), None);
}
#[test]
fn deep_link_starter_not_matched() {
assert_eq!(make_msg("/starter foo").deep_link(), None);
}
}