1use serde::{Deserialize, Serialize};
2use serde_json::{Value, json};
3use std::fmt;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(tag = "type")]
8pub enum ButtonAction {
9 #[serde(rename = "text")]
11 Text {
12 label: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 payload: Option<Value>,
17 },
18 #[serde(rename = "callback")]
20 Callback {
21 label: String,
23 payload: Value,
25 },
26 #[serde(rename = "open_link")]
28 OpenLink {
29 link: String,
31 label: String,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 payload: Option<Value>,
36 },
37 #[serde(rename = "location")]
39 Location {
40 #[serde(skip_serializing_if = "Option::is_none")]
42 payload: Option<Value>,
43 },
44 #[serde(rename = "vkpay")]
46 VkPay {
47 hash: String,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 payload: Option<Value>,
52 },
53 #[serde(rename = "open_app")]
55 OpenApp {
56 app_id: i64,
58 owner_id: i64,
60 label: String,
62 hash: String,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 payload: Option<Value>,
67 },
68}
69
70#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
72#[serde(rename_all = "snake_case")]
73pub enum ButtonColor {
74 Primary,
76 Secondary,
78 Negative,
80 Positive,
82}
83
84impl fmt::Display for ButtonColor {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 ButtonColor::Primary => write!(f, "primary"),
88 ButtonColor::Secondary => write!(f, "secondary"),
89 ButtonColor::Negative => write!(f, "negative"),
90 ButtonColor::Positive => write!(f, "positive"),
91 }
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct KeyboardButton {
98 pub action: ButtonAction,
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub color: Option<ButtonColor>,
103}
104
105#[derive(Debug, Clone)]
107pub struct Keyboard {
108 buttons: Vec<Vec<KeyboardButton>>,
109 one_time: bool,
110 inline: bool,
111}
112
113impl Keyboard {
114 pub fn new() -> Self {
116 Self {
117 buttons: Vec::new(),
118 one_time: false,
119 inline: false,
120 }
121 }
122
123 pub fn new_inline() -> Self {
125 Self {
126 buttons: Vec::new(),
127 one_time: false,
128 inline: true,
129 }
130 }
131
132 pub fn new_one_time() -> Self {
134 Self {
135 buttons: Vec::new(),
136 one_time: true,
137 inline: false,
138 }
139 }
140
141 pub fn add_row(mut self, row: Vec<KeyboardButton>) -> Self {
143 self.buttons.push(row);
144 self
145 }
146
147 pub fn add_text_button(
149 mut self,
150 label: &str,
151 payload: Option<Value>,
152 color: Option<ButtonColor>,
153 ) -> Self {
154 if self.buttons.is_empty() {
155 self.buttons.push(Vec::new());
156 }
157
158 let last_row = self.buttons.last_mut().unwrap();
159 last_row.push(KeyboardButton {
160 action: ButtonAction::Text {
161 label: label.to_string(),
162 payload,
163 },
164 color,
165 });
166
167 self
168 }
169
170 pub fn add_callback_button(
172 mut self,
173 label: &str,
174 payload: Value,
175 color: Option<ButtonColor>,
176 ) -> Self {
177 if self.buttons.is_empty() {
178 self.buttons.push(Vec::new());
179 }
180
181 let last_row = self.buttons.last_mut().unwrap();
182 last_row.push(KeyboardButton {
183 action: ButtonAction::Callback {
184 label: label.to_string(),
185 payload,
186 },
187 color,
188 });
189
190 self
191 }
192
193 pub fn add_command_button(
195 mut self,
196 label: &str,
197 payload: Value,
198 color: Option<ButtonColor>,
199 ) -> Self {
200 if self.buttons.is_empty() {
201 self.buttons.push(Vec::new());
202 }
203
204 let last_row = self.buttons.last_mut().unwrap();
205 last_row.push(KeyboardButton {
206 action: ButtonAction::Callback {
207 label: label.to_owned(),
208 payload: json!({"command": payload}),
209 },
210 color,
211 });
212
213 self
214 }
215
216 pub fn add_link_button(
218 mut self,
219 label: &str,
220 link: &str,
221 payload: Option<Value>,
222 color: Option<ButtonColor>,
223 ) -> Self {
224 if self.buttons.is_empty() {
225 self.buttons.push(Vec::new());
226 }
227
228 let last_row = self.buttons.last_mut().unwrap();
229 last_row.push(KeyboardButton {
230 action: ButtonAction::OpenLink {
231 link: link.to_owned(),
232 label: label.to_owned(),
233 payload,
234 },
235 color,
236 });
237
238 self
239 }
240
241 pub fn to_json(&self) -> Value {
243 let buttons_json: Vec<Value> = self
244 .buttons
245 .iter()
246 .map(|row| {
247 let row_json: Vec<Value> = row
248 .iter()
249 .map(|button| {
250 let mut button_json = json!({
251 "action": button.action,
252 });
253
254 if let Some(color) = &button.color {
255 button_json["color"] = json!(color.to_string());
256 }
257
258 button_json
259 })
260 .collect();
261
262 json!(row_json)
263 })
264 .collect();
265
266 json!({
267 "one_time": self.one_time,
268 "inline": self.inline,
269 "buttons": buttons_json,
270 })
271 }
272
273 pub fn to_json_string(&self) -> String {
275 self.to_json().to_string()
276 }
277
278 pub fn create_menu() -> Self {
280 Keyboard::new_inline()
281 .add_text_button(
282 "đ Help",
283 Some(json!({"command": "help"})),
284 Some(ButtonColor::Primary),
285 )
286 .add_text_button(
287 "âšī¸ Info",
288 Some(json!({"command": "info"})),
289 Some(ButtonColor::Secondary),
290 )
291 .add_row(Vec::new()) .add_text_button(
293 "đ˛ Random",
294 Some(json!({"command": "random"})),
295 Some(ButtonColor::Positive),
296 )
297 .add_text_button(
298 "đ Time",
299 Some(json!({"command": "time"})),
300 Some(ButtonColor::Negative),
301 )
302 }
303
304 pub fn create_admin_menu() -> Self {
306 Keyboard::new_inline()
307 .add_text_button(
308 "đ Stats",
309 Some(json!({"command": "stats"})),
310 Some(ButtonColor::Primary),
311 )
312 .add_text_button(
313 "đĸ Broadcast",
314 Some(json!({"command": "broadcast"})),
315 Some(ButtonColor::Secondary),
316 )
317 .add_row(Vec::new())
318 .add_text_button(
319 "đĢ Ban",
320 Some(json!({"command": "ban"})),
321 Some(ButtonColor::Negative),
322 )
323 .add_text_button(
324 "â
Unban",
325 Some(json!({"command": "unban"})),
326 Some(ButtonColor::Positive),
327 )
328 }
329}
330
331impl Default for Keyboard {
332 fn default() -> Self {
333 Self::new()
334 }
335}