use maxbot::{
Attachment, Dispatcher, InlineKeyboard, InlineKeyboardButton, InlineKeyboardBuilder, MaxClient,
SendMessageParamsBuilder,
};
fn make_keyboard(buttons: Vec<InlineKeyboardButton>) -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
let mut builder = InlineKeyboardBuilder::new();
for btn in buttons {
builder = builder.button(btn);
}
builder.build()
}
fn btn_stand() -> InlineKeyboardButton {
InlineKeyboardButton::callback("Встать", "stand")
}
fn fruit_buttons() -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
make_keyboard(vec![
InlineKeyboardButton::callback("🍏", "fruit:apple"),
InlineKeyboardButton::callback("🍐", "fruit:pear"),
InlineKeyboardButton::callback("🍊", "fruit:orange"),
InlineKeyboardButton::callback("🍅", "fruit:tomato"),
])
}
fn btn_restart() -> Result<InlineKeyboard, maxbot::KeyboardValidationError> {
make_keyboard(vec![InlineKeyboardButton::callback("Начать заново", "stand")])
}
fn greeting() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
Ok(SendMessageParamsBuilder::new()
.text("Ну-ка, фрукты, встаньте в ряд!")
.attachment(Attachment::inline_keyboard(
make_keyboard(vec![btn_stand()])?,
)))
}
fn fruit_menu() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
Ok(SendMessageParamsBuilder::new()
.text("Перед вами, друзья, фруктов дружная семья.")
.attachment(Attachment::inline_keyboard(fruit_buttons()?)))
}
fn apple_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
let caption = "Сочное, спелое, наливное. Яблоко — вот кто я!";
let image = Attachment::image_url("https://i0.wp.com/static.kinoafisha.info/upload/articles/362065694948.jpg");
Ok(SendMessageParamsBuilder::new()
.text(caption)
.attachment(image)
.attachment(Attachment::inline_keyboard(fruit_buttons()?)))
}
fn pear_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
let caption = "Я спелая грушка — яблоку подружка!";
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");
Ok(SendMessageParamsBuilder::new()
.text(caption)
.attachment(image)
.attachment(Attachment::inline_keyboard(fruit_buttons()?)))
}
fn orange_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
let caption = "Я — пузатый апельсин. Солнышка весёлый сын.";
let image = Attachment::image_url("https://click-or-die.ru/app/uploads/2024/07/apelsinn.jpg");
Ok(SendMessageParamsBuilder::new()
.text(caption)
.attachment(image)
.attachment(Attachment::inline_keyboard(fruit_buttons()?)))
}
fn tomato_message() -> Result<SendMessageParamsBuilder, maxbot::KeyboardValidationError> {
let caption = "А я томат";
let image = Attachment::image_url("https://cont.ws/uploads/pic/2021/2/jW0A4Rs8fKkqffwK.jpg");
Ok(SendMessageParamsBuilder::new()
.text(caption)
.attachment(image)
.attachment(Attachment::inline_keyboard(btn_restart()?)))
}
async fn builder_to_json(
_bot: &MaxClient,
builder: SendMessageParamsBuilder,
) -> Result<serde_json::Value, maxbot::Error> {
let params = builder.build();
let mut body = serde_json::Map::new();
if !params.text.is_empty() {
body.insert("text".into(), params.text.into());
}
if let Some(fmt) = params.format {
body.insert("format".into(), fmt.into());
}
if let Some(disable) = params.disable_link_preview {
body.insert("disable_link_preview".into(), disable.into());
}
if !params.attachments.is_empty() {
let attachments_json = serde_json::to_value(¶ms.attachments)
.map_err(|e| maxbot::Error::Json(e))?;
body.insert("attachments".into(), attachments_json);
}
Ok(serde_json::Value::Object(body))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
let mut dp = Dispatcher::new(bot);
dp.on_command("/start", |ctx| async move {
let chat_id = ctx.chat_id().unwrap();
let params = greeting()?.chat_id(chat_id).build();
ctx.bot().send_message(params).await?;
Ok(())
});
dp.on_bot_started(|ctx| async move {
let chat_id = ctx.chat_id().unwrap();
let params = greeting()?.chat_id(chat_id).build();
ctx.bot().send_message(params).await?;
Ok(())
});
dp.on_callback(|ctx| async move {
let callback = match &ctx.update {
maxbot::Update::MessageCallback { callback, .. } => callback,
_ => return Ok(()),
};
let payload = &callback.payload;
let message_json = match payload.as_str() {
"stand" => Some(builder_to_json(ctx.bot(), fruit_menu()?).await?),
"fruit:apple" => Some(builder_to_json(ctx.bot(), apple_message()?).await?),
"fruit:pear" => Some(builder_to_json(ctx.bot(), pear_message()?).await?),
"fruit:orange" => Some(builder_to_json(ctx.bot(), orange_message()?).await?),
"fruit:tomato" => Some(builder_to_json(ctx.bot(), tomato_message()?).await?),
_ => None,
};
if let Some(json) = message_json {
ctx.answer_callback_raw(None, Some(json)).await?;
} else {
ctx.answer_callback_raw(None, None).await?;
}
Ok(())
});
println!("🍎🍐🍊🍅 Фруктовый бот запущен! Начните общение с ботом или выполните /start в существующем диалоге. Нажмите Ctrl+C для остановки.");
dp.start_polling().await;
Ok(())
}