1use crate::error::{ApiError, BotError, Result};
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use std::borrow::Cow;
5use std::fmt::*;
6use std::time::Duration;
7#[cfg(feature = "templates")]
8use tera::{Context, Tera};
9use tracing::debug;
10
11pub const VKTEAMS_BOT_API_URL: &str = "VKTEAMS_BOT_API_URL";
13pub const VKTEAMS_BOT_API_TOKEN: &str = "VKTEAMS_BOT_API_TOKEN";
15pub const VKTEAMS_PROXY: &str = "VKTEAMS_PROXY";
17pub const POLL_TIME: u64 = 30;
19pub const POLL_DURATION: &Duration = &Duration::from_secs(POLL_TIME + 10);
22
23pub const SERVICE_NAME: &str = "BOT";
24#[derive(Debug)]
26pub enum APIVersionUrl {
27 V1,
29}
30#[derive(Debug, Default)]
32pub enum HTTPMethod {
33 #[default]
34 GET,
35 POST,
36}
37
38#[derive(Debug, Default)]
39pub enum HTTPBody {
40 MultiPart(MultipartName),
42 #[default]
43 None,
44}
45pub trait BotRequest {
47 type Args;
48
49 const METHOD: &'static str;
50 const HTTP_METHOD: HTTPMethod = HTTPMethod::GET;
51 type RequestType: Serialize + Debug + Default;
52 type ResponseType: Serialize + DeserializeOwned + Debug + Default;
53 fn get_multipart(&self) -> &MultipartName;
54 fn new(args: Self::Args) -> Self;
55 fn get_chat_id(&self) -> Option<&ChatId>;
56}
57pub type EventId = u32;
59#[derive(Serialize, Clone, Debug)]
61pub enum MessageTextFormat {
62 Plain(String),
64 Bold(String),
66 Italic(String),
68 Underline(String),
70 Strikethrough(String),
72 Link(String, String),
74 Mention(ChatId),
76 Code(String),
78 Pre(String, Option<String>),
80 OrderedList(Vec<String>),
82 UnOrderedList(Vec<String>),
84 Quote(String),
86 None,
87}
88#[derive(Default, Clone, Debug)]
90pub struct MessageTextParser {
91 pub text: Vec<MessageTextFormat>,
93 #[cfg(feature = "templates")]
95 pub ctx: Context,
96 #[cfg(feature = "templates")]
98 pub name: String,
99 #[cfg(feature = "templates")]
101 pub tmpl: Tera,
102 pub parse_mode: ParseMode,
106}
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
112#[serde(rename_all = "camelCase")]
113pub struct ButtonKeyboard {
114 pub text: String, #[serde(skip_serializing_if = "Option::is_none")]
116 pub url: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub callback_data: Option<String>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub style: Option<ButtonStyle>,
121}
122#[derive(Serialize, Deserialize, Clone, Debug)]
123pub struct Keyboard {
125 pub buttons: Vec<Vec<ButtonKeyboard>>,
126}
127#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
129#[serde(rename_all = "camelCase")]
130pub enum ButtonStyle {
131 Primary,
132 Attention,
133 #[default]
134 Base,
135}
136#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
138pub enum ParseMode {
139 MarkdownV2,
140 #[default]
141 HTML,
142 #[cfg(feature = "templates")]
143 Template,
144}
145#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
147#[serde(rename_all = "camelCase")]
148pub struct EventMessage {
149 pub event_id: EventId,
150 #[serde(flatten)]
151 pub event_type: EventType,
152}
153#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
155#[serde(rename_all = "camelCase", tag = "type", content = "payload")]
156pub enum EventType {
157 NewMessage(Box<EventPayloadNewMessage>),
158 EditedMessage(Box<EventPayloadEditedMessage>),
159 DeleteMessage(Box<EventPayloadDeleteMessage>),
160 PinnedMessage(Box<EventPayloadPinnedMessage>),
161 UnpinnedMessage(Box<EventPayloadUnpinnedMessage>),
162 NewChatMembers(Box<EventPayloadNewChatMembers>),
163 LeftChatMembers(Box<EventPayloadLeftChatMembers>),
164 CallbackQuery(Box<EventPayloadCallbackQuery>),
165 #[default]
166 None,
167}
168#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
170#[serde(rename_all = "camelCase")]
171pub struct EventPayloadNewMessage {
172 pub msg_id: MsgId,
173 #[serde(default)]
174 pub text: String,
175 pub chat: Chat,
176 pub from: From,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub format: Option<MessageFormat>,
179 #[serde(default)]
180 pub parts: Vec<MessageParts>,
181 pub timestamp: Timestamp,
182}
183#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
185#[serde(rename_all = "camelCase")]
186pub struct EventPayloadEditedMessage {
187 pub msg_id: MsgId,
188 pub text: String,
189 pub timestamp: Timestamp,
190 pub chat: Chat,
191 pub from: From,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub format: Option<MessageFormat>,
194 pub edited_timestamp: Timestamp,
195}
196#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
198#[serde(rename_all = "camelCase")]
199pub struct EventPayloadDeleteMessage {
200 pub msg_id: MsgId,
201 pub chat: Chat,
202 pub timestamp: Timestamp,
203}
204#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
206#[serde(rename_all = "camelCase")]
207pub struct EventPayloadPinnedMessage {
208 pub msg_id: MsgId,
209 pub chat: Chat,
210 pub from: From,
211 #[serde(default)]
212 pub text: String,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub format: Option<MessageFormat>,
215 #[serde(default)]
216 pub parts: Vec<MessageParts>,
217 pub timestamp: Timestamp,
218}
219#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
221#[serde(rename_all = "camelCase")]
222pub struct EventPayloadUnpinnedMessage {
223 pub msg_id: MsgId,
224 pub chat: Chat,
225 pub timestamp: Timestamp,
226}
227#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
229#[serde(rename_all = "camelCase")]
230pub struct EventPayloadNewChatMembers {
231 pub chat: Chat,
232 pub new_members: Vec<From>,
233 pub added_by: From,
234}
235#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
237#[serde(rename_all = "camelCase")]
238pub struct EventPayloadLeftChatMembers {
239 pub chat: Chat,
240 pub left_members: Vec<From>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub removed_by: Option<From>,
243}
244#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
246#[serde(rename_all = "camelCase")]
247pub struct EventPayloadCallbackQuery {
248 pub query_id: QueryId,
249 pub from: From,
250 #[serde(default)]
251 pub chat: Chat,
252 pub message: EventPayloadNewMessage,
253 pub callback_data: String,
254}
255#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
257#[serde(rename_all = "camelCase")]
258pub struct MessageParts {
259 #[serde(rename = "type", flatten)]
260 pub part_type: MessagePartsType,
261 }
263#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
265#[serde(rename_all = "camelCase", tag = "type", content = "payload")]
266pub enum MessagePartsType {
267 Sticker(MessagePartsPayloadSticker),
268 Mention(MessagePartsPayloadMention),
269 Voice(MessagePartsPayloadVoice),
270 File(Box<MessagePartsPayloadFile>),
271 Forward(Box<MessagePartsPayloadForward>),
272 Reply(Box<MessagePartsPayloadReply>),
273 InlineKeyboardMarkup(Vec<Vec<MessagePartsPayloadInlineKeyboard>>),
274}
275#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
277#[serde(rename_all = "camelCase")]
278pub struct MessagePartsPayloadSticker {
279 pub file_id: FileId,
280}
281#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
283#[serde(rename_all = "camelCase")]
284pub struct MessagePartsPayloadMention {
285 #[serde(flatten)]
286 pub user_id: From,
287}
288#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
290#[serde(rename_all = "camelCase")]
291pub struct MessagePartsPayloadVoice {
292 pub file_id: FileId,
293}
294#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
296#[serde(rename_all = "camelCase")]
297pub struct MessagePartsPayloadFile {
298 pub file_id: FileId,
299 #[serde(rename = "type", default)]
300 pub file_type: String,
301 #[serde(default)]
302 pub caption: String,
303 #[serde(default)]
304 pub format: MessageFormat,
305}
306#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
308#[serde(rename_all = "camelCase")]
309pub struct MessagePartsPayloadForward {
310 message: MessagePayload,
311}
312#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
314#[serde(rename_all = "camelCase")]
315pub struct MessagePartsPayloadReply {
316 message: MessagePayload,
317}
318#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
320#[serde(rename_all = "camelCase")]
321pub struct MessagePartsPayloadInlineKeyboard {
322 #[serde(default)]
323 pub callback_data: String,
324 pub style: ButtonStyle,
325 pub text: String,
326 #[serde(default)]
327 pub url: String,
328}
329#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
331#[serde(rename_all = "camelCase")]
332pub struct MessageFormat {
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub bold: Option<Vec<MessageFormatStruct>>,
335 #[serde(skip_serializing_if = "Option::is_none")]
336 pub italic: Option<Vec<MessageFormatStruct>>,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub underline: Option<Vec<MessageFormatStruct>>,
339 #[serde(skip_serializing_if = "Option::is_none")]
340 pub strikethrough: Option<Vec<MessageFormatStruct>>,
341 #[serde(skip_serializing_if = "Option::is_none")]
342 pub link: Option<Vec<MessageFormatStructLink>>,
343 #[serde(skip_serializing_if = "Option::is_none")]
344 pub mention: Option<Vec<MessageFormatStruct>>,
345 #[serde(skip_serializing_if = "Option::is_none")]
346 pub inline_code: Option<Vec<MessageFormatStruct>>,
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub pre: Option<Vec<MessageFormatStructPre>>,
349 #[serde(skip_serializing_if = "Option::is_none")]
350 pub ordered_list: Option<Vec<MessageFormatStruct>>,
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub quote: Option<Vec<MessageFormatStruct>>,
353}
354#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
356#[serde(rename_all = "camelCase")]
357pub struct MessageFormatStruct {
358 pub offset: i32,
359 pub length: i32,
360}
361#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
363#[serde(rename_all = "camelCase")]
364pub struct MessageFormatStructLink {
365 pub offset: i32,
366 pub length: i32,
367 pub url: String,
368}
369#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
371#[serde(rename_all = "camelCase")]
372pub struct MessageFormatStructPre {
373 pub offset: i32,
374 pub length: i32,
375 #[serde(skip_serializing_if = "Option::is_none")]
376 pub code: Option<String>,
377}
378#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
380#[serde(rename_all = "camelCase")]
381pub struct MessagePayload {
382 pub from: From,
383 pub msg_id: MsgId,
384 #[serde(default)]
385 pub text: String,
386 pub timestamp: u64,
387 #[serde(default)]
388 pub parts: Vec<MessageParts>,
389}
390#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
392pub struct ChatId(pub Cow<'static, str>);
393#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
395pub struct MsgId(pub String);
396#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
398pub struct UserId(pub String);
399#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
401pub struct FileId(pub String);
402#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
404pub struct QueryId(pub String);
405#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Hash, Eq)]
407pub struct Timestamp(pub u32);
408#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
410#[serde(rename_all = "camelCase")]
411pub struct Chat {
412 pub chat_id: ChatId,
413 #[serde(skip_serializing_if = "Option::is_none")]
414 pub title: Option<String>,
415 #[serde(rename = "type")]
416 pub chat_type: String,
417}
418#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
420#[serde(rename_all = "camelCase")]
421pub struct From {
422 pub first_name: String,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub last_name: Option<String>, pub user_id: UserId,
426}
427#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
429#[serde(rename_all = "camelCase")]
430pub enum Languages {
431 #[default]
432 Ru,
433 En,
434}
435#[derive(Serialize, Deserialize, Clone, Debug, Default)]
437#[serde(rename_all = "camelCase")]
438pub enum ChatType {
439 #[default]
440 Private,
441 Group,
442 Channel,
443}
444#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
446#[serde(rename_all = "camelCase")]
447pub enum ChatActions {
448 Looking,
449 #[default]
450 Typing,
451}
452#[derive(Serialize, Deserialize, Debug, Default, Clone)]
454pub enum MultipartName {
455 FilePath(String),
456 ImagePath(String),
457 FileContent {
458 filename: String,
459 content: Vec<u8>,
460 },
461 ImageContent {
462 filename: String,
463 content: Vec<u8>,
464 },
465 #[default]
466 None,
467}
468#[derive(Serialize, Deserialize, Clone, Debug)]
470#[serde(rename_all = "camelCase")]
471pub struct Admin {
472 pub user_id: UserId,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub creator: Option<bool>,
475}
476#[derive(Serialize, Deserialize, Clone, Debug)]
478#[serde(rename_all = "camelCase")]
479pub struct Users {
480 pub user_id: UserId,
481}
482#[derive(Serialize, Deserialize, Clone, Debug)]
484#[serde(rename_all = "camelCase")]
485pub struct Member {
486 pub user_id: UserId,
487 #[serde(skip_serializing_if = "Option::is_none")]
488 pub creator: Option<bool>,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub admin: Option<bool>,
491}
492#[derive(Serialize, Deserialize, Clone, Debug)]
494#[serde(rename_all = "camelCase")]
495pub struct Sn {
496 pub sn: String,
497 pub user_id: UserId,
498}
499#[derive(Serialize, Deserialize, Clone, Debug)]
501pub struct PhotoUrl {
502 pub url: String,
503}
504#[derive(Serialize, Deserialize, Debug, Clone)]
506#[serde(untagged)]
507pub enum ApiResponseWrapper<T> {
508 PayloadWithOk {
509 ok: bool,
510 #[serde(flatten)]
511 payload: T,
512 },
513 PayloadOnly(T),
514 Error {
515 ok: bool,
516 description: String,
517 },
518}
519
520impl<T> std::convert::From<ApiResponseWrapper<T>> for Result<T>
522where
523 T: Default + Serialize + DeserializeOwned,
524{
525 fn from(wrapper: ApiResponseWrapper<T>) -> Self {
526 match wrapper {
527 ApiResponseWrapper::PayloadWithOk { ok, payload } => {
528 if ok {
529 debug!("Answer is ok, payload received");
530 Ok(payload)
531 } else {
532 debug!("Answer is not ok, but description is not provided");
533 Err(BotError::Api(ApiError {
534 description: "Unspecified error".to_string(),
535 }))
536 }
537 }
538 ApiResponseWrapper::PayloadOnly(payload) => {
539 debug!("Answer is ok, payload received");
540 Ok(payload)
541 }
542 ApiResponseWrapper::Error { ok, description } => {
543 if ok {
544 debug!("Answer is ok, BUT error description is provided");
545 } else {
546 debug!("Answer is NOT ok and error description is provided");
547 }
548 Err(BotError::Api(ApiError { description }))
549 }
550 }
551 }
552}
553
554impl Display for ChatId {
556 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
558 write!(f, "{}", self.0)
559 }
560}
561
562impl std::convert::From<String> for ChatId {
564 fn from(s: String) -> Self {
565 ChatId(Cow::Owned(s))
566 }
567}
568
569impl std::convert::From<&'static str> for ChatId {
570 fn from(s: &'static str) -> Self {
572 ChatId(Cow::Borrowed(s))
573 }
574}
575
576impl std::convert::From<Cow<'static, str>> for ChatId {
577 fn from(cow: Cow<'static, str>) -> Self {
578 ChatId(cow)
579 }
580}
581
582impl AsRef<str> for ChatId {
583 fn as_ref(&self) -> &str {
584 &self.0
585 }
586}
587
588impl ChatId {
589 pub fn from_static(s: &'static str) -> Self {
593 ChatId::from(s)
594 }
595
596 pub fn from_borrowed_str(s: &str) -> Self {
600 ChatId(Cow::Owned(s.to_string()))
601 }
602
603 pub fn from_owned(s: String) -> Self {
605 ChatId(Cow::Owned(s))
606 }
607
608 pub fn as_str(&self) -> &str {
610 &self.0
611 }
612
613 pub fn into_string(self) -> String {
615 self.0.into_owned()
616 }
617}
618
619impl Display for APIVersionUrl {
621 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
623 match self {
624 APIVersionUrl::V1 => write!(f, "bot/v1/"),
625 }
626 }
627}
628impl Display for MultipartName {
630 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
632 match self {
633 MultipartName::FilePath(..) | MultipartName::FileContent { .. } => write!(f, "file"),
634 MultipartName::ImagePath(..) | MultipartName::ImageContent { .. } => write!(f, "image"),
635 _ => write!(f, ""),
636 }
637 }
638}
639
640impl Default for Keyboard {
642 fn default() -> Self {
644 Self {
645 buttons: vec![vec![]],
647 }
648 }
649}
650
651impl std::fmt::Display for UserId {
652 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
653 write!(f, "{}", self.0)
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660
661 #[test]
662 fn test_chat_id_display() {
663 let id = ChatId::from("test_id");
664 assert_eq!(format!("{id}"), "test_id");
665 }
666
667 #[test]
668 fn test_chat_id_from_implementations() {
669 let static_id = ChatId::from("static_chat_id");
671 assert_eq!(static_id.as_str(), "static_chat_id");
672
673 let dynamic_string = format!("dynamic_{}", 123);
675 let dynamic_id = ChatId::from_borrowed_str(&dynamic_string);
676 assert_eq!(dynamic_id.as_str(), "dynamic_123");
677
678 let owned_id = ChatId::from("owned_string".to_string());
680 assert_eq!(owned_id.as_str(), "owned_string");
681
682 let static_method_id = ChatId::from_static("static_method");
684 assert_eq!(static_method_id.as_str(), "static_method");
685
686 let static_literal = ChatId::from("literal");
688 match static_literal.0 {
689 Cow::Borrowed(_) => (), Cow::Owned(_) => panic!("Expected Cow::Borrowed for static string literal"),
691 }
692
693 let dynamic = ChatId::from_borrowed_str("not_static");
695 match dynamic.0 {
696 Cow::Owned(_) => (), Cow::Borrowed(_) => panic!("Expected Cow::Owned for dynamic string"),
698 }
699 }
700
701 #[test]
702 fn test_apiversionurl_display() {
703 assert_eq!(format!("{}", APIVersionUrl::V1), "bot/v1/");
704 }
705
706 #[test]
707 fn test_multipartname_display() {
708 let f = MultipartName::FilePath("file.txt".to_string());
709 let i = MultipartName::ImagePath("img.png".to_string());
710 let n = MultipartName::None;
711 assert_eq!(format!("{f}"), "file");
712 assert_eq!(format!("{i}"), "image");
713 assert_eq!(format!("{n}"), "");
714 }
715
716 #[test]
717 fn test_keyboard_default() {
718 let kb = Keyboard::default();
719 assert_eq!(kb.buttons, vec![vec![]]);
720 }
721
722 #[test]
723 fn test_userid_display() {
724 let id = UserId("u123".to_string());
725 assert_eq!(format!("{id}"), "u123");
726 }
727
728 #[test]
729 fn test_parsemode_default_and_eq() {
730 assert_eq!(ParseMode::default(), ParseMode::HTML);
731 assert_eq!(ParseMode::HTML, ParseMode::HTML);
732 assert_ne!(ParseMode::HTML, ParseMode::MarkdownV2);
733 }
734
735 #[test]
736 fn test_buttonstyle_default_and_eq() {
737 assert_eq!(ButtonStyle::default(), ButtonStyle::Base);
738 assert_eq!(ButtonStyle::Primary, ButtonStyle::Primary);
739 assert_ne!(ButtonStyle::Primary, ButtonStyle::Attention);
740 }
741
742 #[test]
743 fn test_apiresponsewrapper_from_payloadonly() {
744 let wrap = ApiResponseWrapper::PayloadOnly(42);
745 let res: Result<i32> = wrap.into();
746 assert_eq!(res.unwrap(), 42);
747 }
748
749 #[test]
750 fn test_apiresponsewrapper_from_payloadwithok() {
751 let wrap = ApiResponseWrapper::PayloadWithOk {
752 ok: true,
753 payload: 7,
754 };
755 let res: Result<i32> = wrap.into();
756 assert_eq!(res.unwrap(), 7);
757 let wrap = ApiResponseWrapper::PayloadWithOk {
758 ok: false,
759 payload: 0,
760 };
761 let res: Result<i32> = wrap.into();
762 assert!(res.is_err());
763 }
764
765 #[test]
766 fn test_apiresponsewrapper_from_error() {
767 let wrap = ApiResponseWrapper::<i32>::Error {
768 ok: false,
769 description: "fail".to_string(),
770 };
771 let res: Result<i32> = wrap.into();
772 assert!(res.is_err());
773 }
774
775 #[test]
776 fn test_message_text_format_variants() {
777 let _ = MessageTextFormat::Plain("text".to_string());
778 let _ = MessageTextFormat::Bold("b".to_string());
779 let _ = MessageTextFormat::Italic("i".to_string());
780 let _ = MessageTextFormat::Underline("u".to_string());
781 let _ = MessageTextFormat::Strikethrough("s".to_string());
782 let _ = MessageTextFormat::Link("t".to_string(), "url".to_string());
783 let _ = MessageTextFormat::Mention(ChatId::from("cid"));
784 let _ = MessageTextFormat::Code("c".to_string());
785 let _ = MessageTextFormat::Pre("p".to_string(), Some("lang".to_string()));
786 let _ = MessageTextFormat::OrderedList(vec!["1".to_string()]);
787 let _ = MessageTextFormat::UnOrderedList(vec!["2".to_string()]);
788 let _ = MessageTextFormat::Quote("q".to_string());
789 let _ = MessageTextFormat::None;
790 }
791
792 #[test]
793 fn test_eventtype_default() {
794 assert_eq!(EventType::default(), EventType::None);
795 }
796}