tg_flows/types/keyboard_button.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use crate::types::{KeyboardButtonPollType, True, WebAppInfo};
/// This object represents one button of the reply keyboard.
///
/// For filter text buttons String can be used instead of this object to specify
/// text of the button.
///
/// [The official docs](https://core.telegram.org/bots/api#keyboardbutton).
#[serde_with_macros::skip_serializing_none]
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButton {
/// Text of the button. If none of the optional fields are used, it will
/// be sent as a message when the button is pressed.
pub text: String,
/// Request something from user. This is available in private chats only.
///
/// See [`ButtonRequest`] documentation for options on what can be
/// requested.
#[serde(flatten)]
pub request: Option<ButtonRequest>,
}
impl KeyboardButton {
pub fn new<T>(text: T) -> Self
where
T: Into<String>,
{
Self { text: text.into(), request: None }
}
pub fn request<T>(mut self, val: T) -> Self
where
T: Into<ButtonRequest>,
{
self.request = Some(val.into());
self
}
}
/// Request something from user, when a button is pressed.
///
/// See individual variants documentation for more info.
#[derive(Clone, Debug, Eq, Hash, PartialEq /*, Serialize, Deserialize */)]
pub enum ButtonRequest {
/// If this variant is used, the user's current location will be sent when
/// the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 9 April, 2016. Older clients will display unsupported message.
Location,
/// If this variant is used, the user's phone number will be sent as a
/// contact when the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 9 April, 2016. Older clients will display unsupported message.
Contact,
/// If this variant is used, the user will be asked to create a poll and
/// send it to the bot when the button is pressed.
///
/// **Note:** this option will only work in Telegram versions released after
/// 23 January, 2020. Older clients will display unsupported message.
Poll(KeyboardButtonPollType),
/// If this variant is used, the described Web App will be launched when the
/// button is pressed. The Web App will be able to send a “web_app_data”
/// service message.
///
/// **Note:** this option will only work in Telegram versions released after
/// 16 April, 2022. Older clients will display unsupported message.
WebApp(WebAppInfo),
}
/// Helper struct for (de)serializing [`ButtonRequest`](ButtonRequest)
#[serde_with_macros::skip_serializing_none]
#[derive(Serialize, Deserialize)]
struct RawRequest {
/// If `true`, the user's phone number will be sent as a contact
/// when the button is pressed. Available in private chats only.
#[serde(rename = "request_contact")]
contact: Option<True>,
/// If `true`, the user's current location will be sent when the
/// button is pressed. Available in private chats only.
#[serde(rename = "request_location")]
location: Option<True>,
/// If specified, the user will be asked to create a poll and
/// send it to the bot when the button is pressed. Available in private
/// chats only.
#[serde(rename = "request_poll")]
poll: Option<KeyboardButtonPollType>,
/// If specified, the described Web App will be launched when the button is
/// pressed. The Web App will be able to send a “web_app_data” service
/// message. Available in private chats only.
web_app: Option<WebAppInfo>,
}
impl<'de> Deserialize<'de> for ButtonRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let raw = RawRequest::deserialize(deserializer)?;
match raw {
RawRequest { contact, location, poll, web_app }
if 1 < (contact.is_some() as u8
+ location.is_some() as u8
+ poll.is_some() as u8
+ web_app.is_some() as u8) =>
{
Err(D::Error::custom(
"`request_contact`, `request_location`, `request_poll` and `web_app` fields \
are mutually exclusive",
))
}
RawRequest { contact: Some(_), .. } => Ok(Self::Contact),
RawRequest { location: Some(_), .. } => Ok(Self::Location),
RawRequest { poll: Some(poll_type), .. } => Ok(Self::Poll(poll_type)),
RawRequest { web_app: Some(web_app), .. } => Ok(Self::WebApp(web_app)),
_ => Err(D::Error::custom(
"Either one of `request_contact`, `request_location`, `request_poll` and \
`web_app` fields is required",
)),
}
}
}
impl Serialize for ButtonRequest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut raw = RawRequest { contact: None, location: None, poll: None, web_app: None };
match self {
Self::Contact => raw.contact = Some(True),
Self::Location => raw.location = Some(True),
Self::Poll(poll_type) => raw.poll = Some(poll_type.clone()),
Self::WebApp(web_app) => raw.web_app = Some(web_app.clone()),
};
raw.serialize(serializer)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serialize_no_request() {
let button = KeyboardButton { text: String::from(""), request: None };
let expected = r#"{"text":""}"#;
let actual = serde_json::to_string(&button).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn serialize_request_contact() {
let button =
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
let expected = r#"{"text":"","request_contact":true}"#;
let actual = serde_json::to_string(&button).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn deserialize_no_request() {
let json = r#"{"text":""}"#;
let expected = KeyboardButton { text: String::from(""), request: None };
let actual = serde_json::from_str(json).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn deserialize_request_contact() {
let json = r#"{"text":"","request_contact":true}"#;
let expected =
KeyboardButton { text: String::from(""), request: Some(ButtonRequest::Contact) };
let actual = serde_json::from_str(json).unwrap();
assert_eq!(expected, actual);
}
}