1use maxbot::{
15 Attachment, Dispatcher, InlineKeyboard, InlineKeyboardButton, InlineKeyboardBuilder, MaxClient,
16 SendMessageParamsBuilder,
17};
18
19fn make_keyboard(buttons: Vec<InlineKeyboardButton>) -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
21 let mut builder = InlineKeyboardBuilder::new();
22 for btn in buttons {
23 builder = builder.button(btn);
24 }
25 builder.build()
26}
27
28fn btn_stand() -> InlineKeyboardButton {
30 InlineKeyboardButton::callback("Встать", "stand")
31}
32
33fn fruit_buttons() -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
35 make_keyboard(vec![
36 InlineKeyboardButton::callback("🍏", "fruit:apple"),
37 InlineKeyboardButton::callback("🍐", "fruit:pear"),
38 InlineKeyboardButton::callback("🍊", "fruit:orange"),
39 InlineKeyboardButton::callback("🍅", "fruit:tomato"),
40 ])
41}
42
43fn btn_restart() -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
45 make_keyboard(vec![InlineKeyboardButton::callback("Начать заново", "stand")])
46}
47
48fn greeting() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
50 Ok(SendMessageParamsBuilder::new()
51 .text("Ну-ка, фрукты, встаньте в ряд!")
52 .attachment(Attachment::inline_keyboard(
53 make_keyboard(vec![btn_stand()])?,
54 )))
55}
56
57fn fruit_menu() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
59 Ok(SendMessageParamsBuilder::new()
60 .text("Перед вами, друзья, фруктов дружная семья.")
61 .attachment(Attachment::inline_keyboard(fruit_buttons()?)))
62}
63
64fn apple_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
66 let caption = "Сочное, спелое, наливное. Яблоко — вот кто я!";
67 let image = Attachment::image_url("https://i0.wp.com/static.kinoafisha.info/upload/articles/362065694948.jpg");
68 Ok(SendMessageParamsBuilder::new()
69 .text(caption)
70 .attachment(image)
71 .attachment(Attachment::inline_keyboard(fruit_buttons()?)))
72}
73
74fn pear_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
76 let caption = "Я спелая грушка — яблоку подружка!";
77 let image = Attachment::image_url("https://img0.reactor.cc/pics/comment/%D0%9A%D0%BE%D0%BC%D0%B8%D0%BA%D1%81%D1%8B-%D0%9A%D0%B0%D0%B6%D0%B4%D1%8B%D0%B9-%D1%82%D1%80%D0%B5%D1%82%D0%B8%D0%B9-%D1%81%D0%B0%D0%BC%D0%B0-%D1%81%D1%83%D1%82%D1%8C-%D0%BF%D0%B5%D1%81%D0%BE%D1%87%D0%BD%D0%B8%D1%86%D0%B0-1156516.jpeg");
78 Ok(SendMessageParamsBuilder::new()
79 .text(caption)
80 .attachment(image)
81 .attachment(Attachment::inline_keyboard(fruit_buttons()?)))
82}
83
84fn orange_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
86 let caption = "Я — пузатый апельсин. Солнышка весёлый сын.";
87 let image = Attachment::image_url("https://click-or-die.ru/app/uploads/2024/07/apelsinn.jpg");
88 Ok(SendMessageParamsBuilder::new()
89 .text(caption)
90 .attachment(image)
91 .attachment(Attachment::inline_keyboard(fruit_buttons()?)))
92}
93
94fn tomato_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
96 let caption = "А я томат";
97 let image = Attachment::image_url("https://cont.ws/uploads/pic/2021/2/jW0A4Rs8fKkqffwK.jpg");
98 Ok(SendMessageParamsBuilder::new()
99 .text(caption)
100 .attachment(image)
101 .attachment(Attachment::inline_keyboard(btn_restart()?)))
102}
103
104async fn builder_to_json(
106 _bot: &MaxClient,
107 builder: SendMessageParamsBuilder,
108) -> Result<serde_json::Value, maxbot::Error> {
109 let params = builder.build();
110 let mut body = serde_json::Map::new();
111 if !params.text.is_empty() {
112 body.insert("text".into(), params.text.into());
113 }
114 if let Some(fmt) = params.format {
115 body.insert("format".into(), fmt.into());
116 }
117 if let Some(disable) = params.disable_link_preview {
118 body.insert("disable_link_preview".into(), disable.into());
119 }
120 if !params.attachments.is_empty() {
121 let attachments_json = serde_json::to_value(¶ms.attachments)
123 .map_err(|e| maxbot::Error::Json(e))?;
124 body.insert("attachments".into(), attachments_json);
125 }
126 Ok(serde_json::Value::Object(body))
127}
128
129#[tokio::main]
130async fn main() -> Result<(), Box<dyn std::error::Error>> {
131 let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
132 let mut dp = Dispatcher::new(bot);
133
134 dp.on_command("/start", |ctx| async move {
136 let chat_id = ctx.chat_id().unwrap();
137 let params = greeting()?.chat_id(chat_id).build();
138 ctx.bot().send_message(params).await?;
139 Ok(())
140 });
141
142 dp.on_bot_started(|ctx| async move {
144 let chat_id = ctx.chat_id().unwrap();
145 let params = greeting()?.chat_id(chat_id).build();
146 ctx.bot().send_message(params).await?;
147 Ok(())
148 });
149
150 dp.on_callback(|ctx| async move {
152 let callback = match &ctx.update {
154 maxbot::Update::MessageCallback { callback, .. } => callback,
155 _ => return Ok(()),
156 };
157 let payload = &callback.payload;
158
159 let message_json = match payload.as_str() {
161 "stand" => Some(builder_to_json(ctx.bot(), fruit_menu()?).await?),
162 "fruit:apple" => Some(builder_to_json(ctx.bot(), apple_message()?).await?),
163 "fruit:pear" => Some(builder_to_json(ctx.bot(), pear_message()?).await?),
164 "fruit:orange" => Some(builder_to_json(ctx.bot(), orange_message()?).await?),
165 "fruit:tomato" => Some(builder_to_json(ctx.bot(), tomato_message()?).await?),
166 _ => None,
167 };
168
169 if let Some(json) = message_json {
170 ctx.answer_callback_raw(None, Some(json)).await?;
172 } else {
173 ctx.answer_callback_raw(None, None).await?;
175 }
176 Ok(())
177 });
178
179 println!("🍎🍐🍊🍅 Фруктовый бот запущен! Начните общение с ботом или выполните /start в существующем диалоге. Нажмите Ctrl+C для остановки.");
180 dp.start_polling().await;
181 Ok(())
182}