use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ButtonAction {
#[serde(rename = "text")]
Text {
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Value>,
},
#[serde(rename = "callback")]
Callback {
label: String,
payload: Value,
},
#[serde(rename = "open_link")]
OpenLink {
link: String,
label: String,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Value>,
},
#[serde(rename = "location")]
Location {
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Value>,
},
#[serde(rename = "vkpay")]
VkPay {
hash: String,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Value>,
},
#[serde(rename = "open_app")]
OpenApp {
app_id: i64,
owner_id: i64,
label: String,
hash: String,
#[serde(skip_serializing_if = "Option::is_none")]
payload: Option<Value>,
},
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ButtonColor {
Primary,
Secondary,
Negative,
Positive,
}
impl fmt::Display for ButtonColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ButtonColor::Primary => write!(f, "primary"),
ButtonColor::Secondary => write!(f, "secondary"),
ButtonColor::Negative => write!(f, "negative"),
ButtonColor::Positive => write!(f, "positive"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyboardButton {
pub action: ButtonAction,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ButtonColor>,
}
#[derive(Debug, Clone)]
pub struct Keyboard {
buttons: Vec<Vec<KeyboardButton>>,
one_time: bool,
inline: bool,
}
impl Keyboard {
pub fn new() -> Self {
Self {
buttons: Vec::new(),
one_time: false,
inline: false,
}
}
pub fn new_inline() -> Self {
Self {
buttons: Vec::new(),
one_time: false,
inline: true,
}
}
pub fn new_one_time() -> Self {
Self {
buttons: Vec::new(),
one_time: true,
inline: false,
}
}
pub fn add_row(mut self, row: Vec<KeyboardButton>) -> Self {
self.buttons.push(row);
self
}
pub fn add_text_button(
mut self,
label: &str,
payload: Option<Value>,
color: Option<ButtonColor>,
) -> Self {
if self.buttons.is_empty() {
self.buttons.push(Vec::new());
}
let last_row = self.buttons.last_mut().unwrap();
last_row.push(KeyboardButton {
action: ButtonAction::Text {
label: label.to_string(),
payload,
},
color,
});
self
}
pub fn add_callback_button(
mut self,
label: &str,
payload: Value,
color: Option<ButtonColor>,
) -> Self {
if self.buttons.is_empty() {
self.buttons.push(Vec::new());
}
let last_row = self.buttons.last_mut().unwrap();
last_row.push(KeyboardButton {
action: ButtonAction::Callback {
label: label.to_string(),
payload,
},
color,
});
self
}
pub fn add_command_button(
mut self,
label: &str,
payload: Value,
color: Option<ButtonColor>,
) -> Self {
if self.buttons.is_empty() {
self.buttons.push(Vec::new());
}
let last_row = self.buttons.last_mut().unwrap();
last_row.push(KeyboardButton {
action: ButtonAction::Callback {
label: label.to_owned(),
payload: json!({"command": payload}),
},
color,
});
self
}
pub fn add_link_button(
mut self,
label: &str,
link: &str,
payload: Option<Value>,
color: Option<ButtonColor>,
) -> Self {
if self.buttons.is_empty() {
self.buttons.push(Vec::new());
}
let last_row = self.buttons.last_mut().unwrap();
last_row.push(KeyboardButton {
action: ButtonAction::OpenLink {
link: link.to_owned(),
label: label.to_owned(),
payload,
},
color,
});
self
}
pub fn to_json(&self) -> Value {
let buttons_json: Vec<Value> = self
.buttons
.iter()
.map(|row| {
let row_json: Vec<Value> = row
.iter()
.map(|button| {
let mut button_json = json!({
"action": button.action,
});
if let Some(color) = &button.color {
button_json["color"] = json!(color.to_string());
}
button_json
})
.collect();
json!(row_json)
})
.collect();
json!({
"one_time": self.one_time,
"inline": self.inline,
"buttons": buttons_json,
})
}
pub fn to_json_string(&self) -> String {
self.to_json().to_string()
}
pub fn create_menu() -> Self {
Keyboard::new_inline()
.add_text_button(
"đ Help",
Some(json!({"command": "help"})),
Some(ButtonColor::Primary),
)
.add_text_button(
"âšī¸ Info",
Some(json!({"command": "info"})),
Some(ButtonColor::Secondary),
)
.add_row(Vec::new()) .add_text_button(
"đ˛ Random",
Some(json!({"command": "random"})),
Some(ButtonColor::Positive),
)
.add_text_button(
"đ Time",
Some(json!({"command": "time"})),
Some(ButtonColor::Negative),
)
}
pub fn create_admin_menu() -> Self {
Keyboard::new_inline()
.add_text_button(
"đ Stats",
Some(json!({"command": "stats"})),
Some(ButtonColor::Primary),
)
.add_text_button(
"đĸ Broadcast",
Some(json!({"command": "broadcast"})),
Some(ButtonColor::Secondary),
)
.add_row(Vec::new())
.add_text_button(
"đĢ Ban",
Some(json!({"command": "ban"})),
Some(ButtonColor::Negative),
)
.add_text_button(
"â
Unban",
Some(json!({"command": "unban"})),
Some(ButtonColor::Positive),
)
}
}
impl Default for Keyboard {
fn default() -> Self {
Self::new()
}
}