From d348b548c182deb198a7ce35d977eeeafa4e21df Mon Sep 17 00:00:00 2001 From: "Eric S. Londres" Date: Sat, 21 Aug 2021 22:05:29 -0400 Subject: [PATCH] add bovine --- src/main.rs | 299 +++++++++++++++++++++++++++------------------------- 1 file changed, 158 insertions(+), 141 deletions(-) diff --git a/src/main.rs b/src/main.rs index 75d7a53..28161ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,25 @@ - // BeerHolderBot - // Copyright (C) 2021 Eric S. Londres +// BeerHolderBot +// Copyright (C) 2021 Eric S. Londres - // This program is free software: you can redistribute it and/or modify - // it under the terms of the GNU Affero General Public License as published - // by the Free Software Foundation, either version 3 of the License, or - // (at your option) any later version. +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. - // This program is distributed in the hope that it will be useful, - // but WITHOUT ANY WARRANTY; without even the implied warranty of - // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - // GNU Affero General Public License for more details. +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. - // You should have received a copy of the GNU Affero General Public License - // along with this program. If not, see . +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #![allow(non_snake_case)] use teloxide::{prelude::*, utils::command::BotCommand, requests::ResponseResult}; use tokio::sync::Mutex; use lazy_static::lazy_static; -use std::error::Error; -use std::fs::File; -use std::io::prelude::*; +use std::{error::Error, fs::File, io::prelude::*}; use rand::prelude::*; type AsyncResult = Result>; @@ -43,15 +41,15 @@ async fn die() -> AsyncResult { // open the file (read-only) let mut file = match File::open(&path) { - Ok(f) => f, - Err(e) => return Err(Box::new(e)) + Ok(f) => f, + Err(e) => return Err(Box::new(e)) }; // read the file's contents into a string let mut contents = String::new(); match file.read_to_string(&mut contents) { - Ok(_) => (), - Err(reason) => return Err(Box::new(reason)) + Ok(_) => (), + Err(reason) => return Err(Box::new(reason)) } // vectorize contents @@ -75,7 +73,7 @@ async fn initialize_database() -> AsyncResult<()> { async fn create_beer(chat_id: i64, content: String) -> AsyncResult<()> { CONNECTION.lock().await - .execute(format!("INSERT INTO tap (chat_id, text) VALUES ('{}', '{}')", chat_id, content))?; + .execute(format!("INSERT INTO tap (chat_id, text) VALUES ('{}', '{}')", chat_id, content))?; Ok(()) } @@ -84,10 +82,10 @@ async fn get_all_beers(chat_id: i64) -> AsyncResult> { let c = CONNECTION.lock().await; let mut statement = c.prepare(format!("SELECT id, text FROM tap WHERE chat_id={}", chat_id))?; while let sqlite::State::Row = statement.next().unwrap() { - beers.push(Beer { - id: statement.read::(0)?, - text: statement.read::(1)? - }); + beers.push(Beer { + id: statement.read::(0)?, + text: statement.read::(1)? + }); } Ok(beers) } @@ -96,9 +94,9 @@ async fn get_beer_count(chat_id: i64) -> AsyncResult { let c = CONNECTION.lock().await; let mut statement = c.prepare(format!("SELECT COUNT(id) FROM tap WHERE chat_id={}", chat_id))?; if let sqlite::State::Row = statement.next().unwrap() { - Ok(statement.read::(0)?) + Ok(statement.read::(0)?) } else { - Err("could not retrieve beer count".into()) + Err("could not retrieve beer count".into()) } } @@ -106,32 +104,40 @@ async fn quaff(id: i64) -> AsyncResult { let c = CONNECTION.lock().await; let mut statement = c.prepare(format!("SELECT text FROM tap WHERE id={}", id))?; if let sqlite::State::Row = statement.next()? { - let text = statement.read::(0)?; - // remove the beer from the database - c.execute(format!("DELETE FROM tap WHERE id={}", id))?; - Ok(text) + let text = statement.read::(0)?; + // remove the beer from the database + c.execute(format!("DELETE FROM tap WHERE id={}", id))?; + Ok(text) } else { - Err("could not retrieve beer text".into()) + Err("could not retrieve beer text".into()) + } +} + +async fn grab_photo(query: &'static str) -> AsyncResult { + if let Some(access) = std::env::var_os("UNSPLASH_ACCESS") { + // call API to get a random picture of corn + let auth_uri = format!("https://api.unsplash.com/photos/random/?client_id={}&query={}", access.into_string().unwrap(), query); + let response = reqwest::get(&auth_uri) + .await.unwrap().text().await.unwrap(); + // response format is some pretty nested json + let parsed_response = json::parse(&response); + if let Ok(url) = parsed_response { + let img_url = &url["urls"]["regular"]; + Ok(img_url.to_string()) + } else { + Err("No corn to harvest.".into()) + } + } else { + Err("You don't have a farm.".into()) } } async fn harvest_corn() -> AsyncResult { - if let Some(access) = std::env::var_os("UNSPLASH_ACCESS") { - // call API to get a random picture of corn - let auth_uri = format!("https://api.unsplash.com/photos/random/?client_id={}&query={}", access.into_string().unwrap(), "corn"); - let response = reqwest::get(&auth_uri) - .await.unwrap().text().await.unwrap(); - // response format is some pretty nested json - let parsed_response = json::parse(&response); - if let Ok(url) = parsed_response { - let img_url = &url["urls"]["regular"]; - Ok(img_url.to_string()) - } else { - Err("No corn to harvest.".into()) - } - } else { - Err("You don't have a farm.".into()) - } + grab_photo("corn").await +} + +async fn bovine() -> AsyncResult { + grab_photo("cow").await } //// TO IMPLEMENT A NEW COMMAND @@ -156,104 +162,115 @@ enum Command { #[command(description = "Get the number of beers on tap")] Count, #[command(description = "Die stupidly")] - Yasd + Yasd, + #[command(description = "Take a tripe to the pasture")] + Bovine } async fn answer(cx: UpdateWithCx, Message>, command: Command) -> ResponseResult<()> { match command { - Command::Help => cx.answer(Command::descriptions()).await?, - Command::Beer(b) => { - if !b.is_empty() { - log::info!("Adding {} to list of beers", b); - // add the beer to the database - match create_beer(cx.chat_id(), b).await { - Err(e) => cx.reply_to(format!("Er, something went wrong.\n{}", e)).await?, - Ok(_) => { - // increment the global beer counter - let cur = get_beer_count(cx.chat_id()).await.unwrap(); - // respond with how many beers are held (globally) - cx.reply_to(format!("Currently holding {} beer{}", cur, if cur == 1 { "" } else { "s" })) - .await? - } + Command::Help => cx.answer(Command::descriptions()).await?, + Command::Beer(b) => { + if !b.is_empty() { + log::info!("Adding {} to list of beers", b); + // add the beer to the database + match create_beer(cx.chat_id(), b).await { + Err(e) => cx.reply_to(format!("Er, something went wrong.\n{}", e)).await?, + Ok(_) => { + // increment the global beer counter + let cur = get_beer_count(cx.chat_id()).await.unwrap(); + // respond with how many beers are held (globally) + cx.reply_to(format!("Currently holding {} beer{}", cur, if cur == 1 { "" } else { "s" })) + .await? + } - } - } else { - // the given beer was an empty string, so don't actually store it - cx.reply_to("Sorry, I can't hold that beer.").await? - } - }, + } + } else { + // the given beer was an empty string, so don't actually store it + cx.reply_to("Sorry, I can't hold that beer.").await? + } + }, Command::OnTap => { - log::info!("Printing list of beers"); - // if the tap is empty, print a special message so the Telegram API doesn't freak out - match get_all_beers(cx.chat_id()).await { - Err(e) => cx.reply_to(format!("Uh, something went wrong.\n{}", e)).await?, - Ok(beers) => { - let mut m: String = String::new(); - if beers.is_empty() { - cx.reply_to("Sorry, I'm all empty.").await? - } else { - for beer in beers { - m += format!("[{}] {}\n", beer.id, beer.text).as_str(); - } - cx.reply_to(m.as_str()).await? - } - } - } - }, - Command::Quaff(beer) => { - log::info!("Quaffing beer #{}", beer); - // try to parse the user input as an integer - if let Ok(index) = beer.parse::() { - let quaff_attempt = quaff(index); + log::info!("Printing list of beers"); + // if the tap is empty, print a special message so the Telegram API doesn't freak out + match get_all_beers(cx.chat_id()).await { + Err(e) => cx.reply_to(format!("Uh, something went wrong.\n{}", e)).await?, + Ok(beers) => { + let mut m: String = String::new(); + if beers.is_empty() { + cx.reply_to("Sorry, I'm all empty.").await? + } else { + for beer in beers { + m += format!("[{}] {}\n", beer.id, beer.text).as_str(); + } + cx.reply_to(m.as_str()).await? + } + } + } + }, + Command::Quaff(beer) => { + log::info!("Quaffing beer #{}", beer); + // try to parse the user input as an integer + if let Ok(index) = beer.parse::() { + let quaff_attempt = quaff(index); - match quaff_attempt.await { - Err(e) => cx.reply_to(format!("Sorry, we can't do that.\n{}", e)).await?, - Ok(m) => { - // send a message informing which beer was quaffed - cx.reply_to(format!("You have quaffed \"{}\"", m)).await? - } - } - } else { - cx.reply_to("Sorry, we don't have that beer on tap.").await? - } - }, - Command::Corn => { - // harvest corn - log::info!("Harvesting corn"); - if let Ok(corn) = harvest_corn().await { - cx.answer_photo(teloxide::types::InputFile::url(corn)).await? - // cx.reply_to(format!("", corn)).parse_mode(teloxide::types::ParseMode::Html).await? - } else { - log::error!("An error occurred within harvest_corn()"); - cx.reply_to("You don't have a farm.").await? - } - }, - Command::Post => { - log::info!("Generating new message"); - let new_msg = telegram_markov_chain::chain(); - log::info!("Posting new message"); - cx.reply_to(new_msg).await? - }, - Command::Count => { - log::info!("Counting bottles of beer on the wall"); - if let Ok(count) = get_beer_count(cx.chat_id()).await { - cx.reply_to(format!("{} bottles of beer on the wall.", count)).await? - } else { - cx.reply_to("I can't seem to find any beers.").await? - } - }, - Command::Yasd => { - log::info!("Dying stupidly..."); - if let Ok(death) = die().await { - let caller = cx.update.from().unwrap().username.as_ref().unwrap(); - cx.reply_to(format!(" + match quaff_attempt.await { + Err(e) => cx.reply_to(format!("Sorry, we can't do that.\n{}", e)).await?, + Ok(m) => { + // send a message informing which beer was quaffed + cx.reply_to(format!("You have quaffed \"{}\"", m)).await? + } + } + } else { + cx.reply_to("Sorry, we don't have that beer on tap.").await? + } + }, + Command::Corn => { + // harvest corn + log::info!("Harvesting corn"); + if let Ok(corn) = harvest_corn().await { + cx.answer_photo(teloxide::types::InputFile::url(corn)).await? + // cx.reply_to(format!("", corn)).parse_mode(teloxide::types::ParseMode::Html).await? + } else { + log::error!("An error occurred within harvest_corn()"); + cx.reply_to("You don't have a farm.").await? + } + }, + Command::Bovine => { + log::info!("cow time"); + if let Ok(bovine) = bovine().await { + cx.answer_photo(teloxide::types::InputFile::url(bovine)).await? + } else { + log::error!("An error occurred within bovine()"); + cx.reply_to("No bovine").await? + } + }, + Command::Post => { + log::info!("Generating new message"); + let new_msg = telegram_markov_chain::chain(); + log::info!("Posting new message"); + cx.reply_to(new_msg).await? + }, + Command::Count => { + log::info!("Counting bottles of beer on the wall"); + if let Ok(count) = get_beer_count(cx.chat_id()).await { + cx.reply_to(format!("{} bottles of beer on the wall.", count)).await? + } else { + cx.reply_to("I can't seem to find any beers.").await? + } + }, + Command::Yasd => { + log::info!("Dying stupidly..."); + if let Ok(death) = die().await { + let caller = cx.update.from().unwrap().username.as_ref().unwrap(); + cx.reply_to(format!(" ---------- / \\ - / REST \\ - / IN \\ - / PEACE \\ - / \\ - | @{} + / REST \\ + / IN \\ + / PEACE \\ + / \\ + | @{} | {} | | | | @@ -261,11 +278,11 @@ async fn answer(cx: UpdateWithCx, Message>, command: Command) -> R | 2021 | *| * * * | * _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______", caller, death)).parse_mode(teloxide::types::ParseMode::Html).await? - } else { - cx.reply_to("I just can't seem to die.").await? - } - - } + } else { + cx.reply_to("I just can't seem to die.").await? + } + + } }; Ok(())