first commit

This commit is contained in:
Victor Martinez Montane 2021-08-24 04:29:35 +02:00
commit deeed9be09
13 changed files with 1902 additions and 0 deletions

2
.env.example Normal file
View file

@ -0,0 +1,2 @@
TELOXIDE_TOKEN=""
REDIS_URL=""

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
.env

1697
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "gofish"
version = "0.1.0"
authors = ["JasterV <jaster.victor@gmail.com>"]
edition = "2018"
[dependencies]
teloxide = { version="0.5.1", features = ["macros", "auto-send"] }
dotenv = "0.15.0"
log = "0.4.8"
thiserror = "1.0.x"
anyhow = "1.0.x"
pretty_env_logger = "0.4.0"
tokio = { version = "1.3.0", features = ["rt-multi-thread", "macros"] }
mobc-redis = "0.7.0"
mobc = "0.7.3"

6
commandslist.txt Normal file
View file

@ -0,0 +1,6 @@
start - start a game
stop - stop the game
ask - ask someone for cards
status - ask the bot to show the game general status
mystatus - ask the bot to send you your status
help - display commands info

12
src/actions/help.rs Normal file
View file

@ -0,0 +1,12 @@
use crate::alias::Cx;
use crate::command::Command;
use anyhow::Result;
use teloxide::{prelude::*, utils::command::BotCommand};
pub async fn help(cx: &Cx) -> Result<()> {
cx.answer(Command::descriptions())
.send()
.await
.map(|_| ())?;
Ok(())
}

5
src/actions/mod.rs Normal file
View file

@ -0,0 +1,5 @@
mod help;
mod say_hi;
pub use help::help;
pub use say_hi::say_hi;

21
src/actions/say_hi.rs Normal file
View file

@ -0,0 +1,21 @@
use crate::alias::Cx;
use anyhow::Result;
use teloxide::prelude::*;
pub async fn say_hi(cx: &Cx) -> Result<()> {
let bot = cx.requester.clone();
let message = cx.update.clone();
let sender = message.from().expect("User data not found");
// let sender_chat = message.sender_chat().expect("Sender chat not found");
let info = format!(
"Your data: \n \t fullname: {} \n \t username: {} \n \t id: {}",
sender.full_name(),
sender.username.clone().unwrap_or("unknown".into()),
sender.id
);
bot.send_message(cx.update.chat_id(), info.clone())
.send()
.await?;
bot.send_message(sender.id, info).send().await?;
Ok(())
}

7
src/alias.rs Normal file
View file

@ -0,0 +1,7 @@
use mobc::{Connection, Pool};
use mobc_redis::RedisConnectionManager;
use teloxide::{adaptors::AutoSend, prelude::*, Bot};
pub type Cx = UpdateWithCx<AutoSend<Bot>, Message>;
pub type MobcPool = Pool<RedisConnectionManager>;
pub type MobcCon = Connection<RedisConnectionManager>;

31
src/command.rs Normal file
View file

@ -0,0 +1,31 @@
use teloxide::utils::command::BotCommand;
// Derive BotCommand to parse text with a command into this enumeration.
//
// 1. rename = "lowercase" turns all the commands into lowercase letters.
// 2. `description = "..."` specifies a text before all the commands.
//
// That is, you can just call Command::descriptions() to get a description of
// your commands in this format:
// %GENERAL-DESCRIPTION%
// %PREFIX%%COMMAND% - %DESCRIPTION%
#[derive(BotCommand)]
#[command(
rename = "lowercase",
description = "Use commands in format /command <arg1> <arg2> ... <argN> ",
parse_with = "split"
)]
pub enum Command {
#[command(description = "start a game")]
Start,
#[command(description = "stop the game")]
Stop,
#[command(description = "ask someone for cards")]
Ask,
#[command(description = "ask the bot to show the game general status")]
Status,
#[command(description = "ask the bot to send you your status")]
MyStatus,
#[command(description = "Show bot commands")]
Help,
}

51
src/db/mod.rs Normal file
View file

@ -0,0 +1,51 @@
use crate::alias::{MobcCon, MobcPool};
use crate::errors::MobcError::*;
use anyhow::Result;
use mobc::Pool;
use mobc_redis::redis::{AsyncCommands, FromRedisValue, ToRedisArgs};
use mobc_redis::{redis, RedisConnectionManager};
use std::time::Duration;
pub const CACHE_POOL_MAX_OPEN: u64 = 16;
pub const CACHE_POOL_MAX_IDLE: u64 = 8;
pub const CACHE_POOL_TIMEOUT_SECONDS: u64 = 1;
pub const CACHE_POOL_EXPIRE_SECONDS: u64 = 60;
pub async fn connect(url: &str) -> Result<MobcPool> {
let client = redis::Client::open(url).map_err(RedisClientError)?;
let manager = RedisConnectionManager::new(client);
Ok(Pool::builder()
.get_timeout(Some(Duration::from_secs(CACHE_POOL_TIMEOUT_SECONDS)))
.max_open(CACHE_POOL_MAX_OPEN)
.max_idle(CACHE_POOL_MAX_IDLE)
.max_lifetime(Some(Duration::from_secs(CACHE_POOL_EXPIRE_SECONDS)))
.build(manager))
}
async fn get_con(pool: &MobcPool) -> Result<MobcCon> {
pool.get().await.map_err(|e| {
eprintln!("error connecting to redis: {}", e);
RedisPoolError(e).into()
})
}
pub async fn set_data<T>(pool: &MobcPool, key: &str, value: T, ttl_seconds: usize) -> Result<()>
where
T: ToRedisArgs + Send + Sync,
{
let mut con = get_con(&pool).await?;
con.set(key, value).await.map_err(RedisCMDError)?;
if ttl_seconds > 0 {
con.expire(key, ttl_seconds).await.map_err(RedisCMDError)?;
}
Ok(())
}
pub async fn get_data<T>(pool: &MobcPool, key: &str) -> Result<T>
where
T: mobc_redis::redis::FromRedisValue,
{
let mut con = get_con(&pool).await?;
let value = con.get(key).await.map_err(RedisCMDError)?;
FromRedisValue::from_redis_value(&value).map_err(|e| RedisTypeError(e).into())
}

13
src/errors.rs Normal file
View file

@ -0,0 +1,13 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MobcError {
#[error("could not get redis connection from pool : {0}")]
RedisPoolError(mobc::Error<mobc_redis::redis::RedisError>),
#[error("error parsing string from redis result: {0}")]
RedisTypeError(mobc_redis::redis::RedisError),
#[error("error executing redis command: {0}")]
RedisCMDError(mobc_redis::redis::RedisError),
#[error("error creating Redis client: {0}")]
RedisClientError(mobc_redis::redis::RedisError),
}

39
src/main.rs Normal file
View file

@ -0,0 +1,39 @@
mod actions;
mod alias;
mod command;
mod db;
mod errors;
use alias::Cx;
use anyhow::Result;
use command::Command;
use dotenv;
use teloxide::{prelude::*, types::Me};
#[tokio::main]
async fn main() {
dotenv::dotenv().ok();
teloxide::enable_logging!();
run().await;
}
async fn run() {
log::info!("Starting bot...");
let redis_url = std::env::var("REDIS_URL").expect("REDIS_URL not found");
let bot = Bot::from_env().auto_send();
let Me { user: bot_user, .. } = bot.get_me().await.unwrap();
let bot_name = bot_user.username.expect("Bots must have usernames");
log::info!("listening...");
teloxide::commands_repl(bot, bot_name, execute).await;
}
async fn execute(cx: Cx, command: Command) -> Result<()> {
match command {
Command::Help => actions::help(&cx).await?,
_ => actions::say_hi(&cx).await?,
};
Ok(())
}