This commit is contained in:
parent
7b4dc5d9a7
commit
96f69a3a10
2405
Cargo.lock
generated
2405
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
23
Cargo.toml
23
Cargo.toml
@ -2,15 +2,20 @@
|
|||||||
name = "telegram-leetbot"
|
name = "telegram-leetbot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Andreas Larsen <andreas@northcode.no>"]
|
authors = ["Andreas Larsen <andreas@northcode.no>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
telegram-bot = "0.6.1"
|
chrono = "0.4.19"
|
||||||
futures = "*"
|
regex = "1.6.0"
|
||||||
tokio-core = "*"
|
# frankenstein = { version = "0.19.0", features = ["async-http-client"] }
|
||||||
serde = "1.0"
|
sqlx = { version = "0.6", features = [ "runtime-tokio-rustls", "postgres", "chrono" ] }
|
||||||
serde_json = "1.0"
|
teloxide = "0.10.1"
|
||||||
chrono = "0.4.6"
|
thiserror = "1.0.31"
|
||||||
|
# futures = "*"
|
||||||
|
tokio = { version = "1", features = [ "full" ] }
|
||||||
|
# serde = "1.0"
|
||||||
|
# serde_json = "1.0"
|
||||||
|
# chrono = "0.4.19"
|
||||||
|
|
||||||
[patch.crates-io]
|
# [patch.crates-io]
|
||||||
openssl = { git = "https://github.com/ishitatsuyuki/rust-openssl", branch = "0.9.x" }
|
# openssl = { git = "https://github.com/ishitatsuyuki/rust-openssl", branch = "0.9.x" }
|
||||||
17
src/lib.rs
17
src/lib.rs
@ -0,0 +1,17 @@
|
|||||||
|
use chrono::{Local, NaiveTime};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn text_is_leet(text: &str) -> bool {
|
||||||
|
text.eq_ignore_ascii_case("leet") || text == "l33t" || text == "1337"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn time_is_leet() -> bool {
|
||||||
|
let now = Local::now().time();
|
||||||
|
let leet = NaiveTime::from_hms(13,37,30);
|
||||||
|
|
||||||
|
let allowed_timegap = 30;
|
||||||
|
|
||||||
|
let distance = now - leet;
|
||||||
|
|
||||||
|
distance.num_seconds().abs() <= allowed_timegap
|
||||||
|
}
|
||||||
228
src/main.rs
228
src/main.rs
@ -1,142 +1,128 @@
|
|||||||
extern crate telegram_bot;
|
mod lib;
|
||||||
extern crate futures;
|
|
||||||
extern crate tokio_core;
|
|
||||||
extern crate serde;
|
|
||||||
extern crate serde_json;
|
|
||||||
extern crate chrono;
|
|
||||||
|
|
||||||
use std::io::Read;
|
use crate::lib::*;
|
||||||
use std::io::Write;
|
|
||||||
use crate::futures::Stream;
|
|
||||||
use tokio_core::reactor::Core;
|
|
||||||
use std::env;
|
|
||||||
use telegram_bot::*;
|
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::{NaiveTime, NaiveDateTime, NaiveDate, Local};
|
||||||
|
use tokio;
|
||||||
|
use teloxide::{prelude::*, RequestError, dispatching::{DispatcherBuilder, UpdateFilterExt}};
|
||||||
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use std::sync::*;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
const SCORE_FILE_PATH : &str = "score.json";
|
async fn init_pg_pool() -> Result<PgPool, sqlx::Error> {
|
||||||
|
let pool = PgPoolOptions::new()
|
||||||
|
.max_connections(5)
|
||||||
|
.connect("postgres://leetbot:leetbot@localhost:5432/leetbot").await?;
|
||||||
|
|
||||||
fn main() {
|
Ok(pool)
|
||||||
let mut core = Core::new().unwrap();
|
}
|
||||||
|
|
||||||
let token = env::var("TELEGRAM_BOT_TOKEN").unwrap();
|
|
||||||
let api = Api::configure(token).build(core.handle()).unwrap();
|
|
||||||
|
|
||||||
let leet_time = NaiveTime::from_hms(13, 37, 30);
|
async fn setup_tables(pool: &PgPool) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query("create table if not exists leet_log (
|
||||||
|
id int generated always as identity,
|
||||||
|
username varchar(255) not null,
|
||||||
|
log_time timestamp not null
|
||||||
|
)")
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let scores : Arc<Mutex<HashMap<String, usize>>> = Arc::new(Mutex::new(HashMap::new()));
|
sqlx::query("create table if not exists leet_score (
|
||||||
let time_scored : Arc<Mutex<HashMap<String, DateTime<Local>>>> = Arc::new(Mutex::new(HashMap::new()));
|
id int generated always as identity,
|
||||||
|
username varchar(255) not null,
|
||||||
|
score int not null
|
||||||
|
)")
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
{
|
Ok(())
|
||||||
if let Ok(mut score_file) = std::fs::File::open(SCORE_FILE_PATH) {
|
}
|
||||||
|
|
||||||
let mut json_str = String::new();
|
#[derive(Debug,Error)]
|
||||||
|
enum LeetError {
|
||||||
|
#[error("It's not leet right now")]
|
||||||
|
NotLeet,
|
||||||
|
#[error("Already hit leet for today")]
|
||||||
|
AlreadyHitLeet,
|
||||||
|
#[error("Sql error while checking for leet status: {0}")]
|
||||||
|
DbError(#[from] sqlx::Error)
|
||||||
|
}
|
||||||
|
|
||||||
score_file.read_to_string(&mut json_str).expect("Failed to read score file");
|
async fn check_leet(username: &str, pool: &PgPool) -> Result<(), LeetError> {
|
||||||
|
if ! time_is_leet() {
|
||||||
let mut map = scores.lock().unwrap();
|
return Err(LeetError::NotLeet);
|
||||||
|
|
||||||
*map = serde_json::from_str(&json_str).expect("Failed to deserialize scores");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
let now = Local::now().naive_local();
|
||||||
let scores = scores.clone();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
let latest_leet : Option<(i32, String, NaiveDateTime)> = sqlx::query_as("select * from leet_log where username = $1 order by log_time desc")
|
||||||
loop {
|
.bind(username)
|
||||||
{
|
.fetch_optional(pool)
|
||||||
let map = scores.lock().unwrap();
|
.await?;
|
||||||
|
|
||||||
let json_str = serde_json::to_string(&*map).unwrap();
|
if let Some(latest_leet) = latest_leet {
|
||||||
|
let distance = now - latest_leet.2;
|
||||||
let mut score_file = std::fs::File::create(SCORE_FILE_PATH).expect("Unable to open or create file");
|
|
||||||
|
|
||||||
score_file.write_all(json_str.as_bytes()).expect("unable to write to score file!");
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("written score file");
|
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(5*60));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let future = api.stream().for_each(|update| {
|
|
||||||
if let UpdateKind::Message(message) = update.kind {
|
|
||||||
if let MessageKind::Text {ref data, ..} = message.kind {
|
|
||||||
println!("<{}>: {}", message.from.first_name, data);
|
|
||||||
|
|
||||||
let current_time = Local::now().time();
|
|
||||||
|
|
||||||
let allowed_timegap = 30;
|
|
||||||
|
|
||||||
if data.to_lowercase() == "leet" || data == "1337" {
|
|
||||||
dbg!(data);
|
|
||||||
|
|
||||||
let leet_distance = (current_time - leet_time).num_seconds().abs();
|
|
||||||
|
|
||||||
if leet_distance > allowed_timegap {
|
|
||||||
api.spawn(message.text_reply(
|
|
||||||
"It is not leet right now".to_string()
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
let mut time_scored_map = time_scored.lock().unwrap();
|
|
||||||
|
|
||||||
let time_scored = time_scored_map.entry(message.from.first_name.clone()).or_insert(Local.timestamp(0,0));
|
|
||||||
|
|
||||||
let time_scored_distance = (Local::now() - *time_scored).num_seconds().abs();
|
|
||||||
|
|
||||||
if time_scored_distance < allowed_timegap * 4 {
|
|
||||||
api.spawn(message.text_reply(
|
|
||||||
"You already hit leet today you dumbass, don't try to cheat!".to_string()
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
let mut map = scores.lock().unwrap();
|
|
||||||
|
|
||||||
let entry = map.entry(message.from.first_name.clone()).or_insert(0);
|
|
||||||
|
|
||||||
*entry += 1;
|
|
||||||
*time_scored = Local::now();
|
|
||||||
|
|
||||||
api.spawn(message.text_reply(
|
|
||||||
format!("{} just hit leet! New score: {}", message.from.first_name, *entry)
|
|
||||||
));
|
|
||||||
|
|
||||||
let current_leet_count = time_scored_map.iter().filter(|(_,t)| (**t - Local::now()).num_seconds().abs() < allowed_timegap * 2).count();
|
|
||||||
|
|
||||||
if current_leet_count == 3 {
|
|
||||||
api.spawn(message.chat.text("OH BABY A TRIPPLE!!!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if data == "-score" {
|
|
||||||
let map = scores.lock().unwrap();
|
|
||||||
|
|
||||||
let json = serde_json::to_string(&*map).unwrap();
|
|
||||||
|
|
||||||
api.spawn(message.text_reply(
|
|
||||||
format!("Score json: {}", json)
|
|
||||||
));
|
|
||||||
} else if data == "-time" {
|
|
||||||
|
|
||||||
api.spawn(message.text_reply(
|
|
||||||
format!("local time: {}, leet time: {}, time distance to leet: {:#?}", current_time, leet_time, (current_time - leet_time).num_seconds())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if distance.num_hours() < 24 {
|
||||||
|
return Err(LeetError::AlreadyHitLeet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
}
|
||||||
|
|
||||||
core.run(future).unwrap();
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
|
let pg_pool = init_pg_pool().await?;
|
||||||
|
|
||||||
|
setup_tables(&pg_pool).await?;
|
||||||
|
|
||||||
|
let test_query : Option<(i32,String,NaiveDateTime)> = sqlx::query_as("SELECT * from leet_log")
|
||||||
|
.fetch_optional(&pg_pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
|
||||||
|
dbg!(&test_query);
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
|
||||||
|
let telegram_token = std::env::var("TELEGRAM_BOT_TOKEN")
|
||||||
|
.expect("TELEGRAM_TOKEN env not set!");
|
||||||
|
|
||||||
|
let bot = Bot::new(&telegram_token).auto_send();
|
||||||
|
|
||||||
|
let pool_clone = pg_pool.clone();
|
||||||
|
|
||||||
|
// todo: switch to dispatcher to allow for resource injection
|
||||||
|
Dispatcher::builder(
|
||||||
|
bot,
|
||||||
|
Update::filter_message()
|
||||||
|
)
|
||||||
|
|
||||||
|
teloxide::repl(bot, |message: Message, bot: AutoSend<Bot>| async move {
|
||||||
|
let pool = pool_clone.clone();
|
||||||
|
if let Some(text) = message.text() {
|
||||||
|
respond_to_leet(text, &message, bot).await?;
|
||||||
|
}
|
||||||
|
respond(())
|
||||||
|
}).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn respond_to_leet(text: &str, message: &Message, bot: AutoSend<Bot>) -> Result<(), RequestError>
|
||||||
|
{
|
||||||
|
if text_is_leet(text) {
|
||||||
|
let msg = format!("you said leet!: {}", text);
|
||||||
|
bot.send_message(message.chat.id, msg).await?;
|
||||||
|
|
||||||
|
let username = message.from().unwrap().username.clone().unwrap();
|
||||||
|
|
||||||
|
if time_is_leet() {
|
||||||
|
bot.send_message(message.chat.id, "it is leet right now!").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
respond(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user