mirror of
https://codeberg.org/JasterV/gofish_bot.git
synced 2026-04-26 18:10:09 +00:00
first commit
This commit is contained in:
commit
deeed9be09
13 changed files with 1902 additions and 0 deletions
2
.env.example
Normal file
2
.env.example
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
TELOXIDE_TOKEN=""
|
||||||
|
REDIS_URL=""
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.env
|
||||||
1697
Cargo.lock
generated
Normal file
1697
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
Cargo.toml
Normal file
16
Cargo.toml
Normal 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
6
commandslist.txt
Normal 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
12
src/actions/help.rs
Normal 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
5
src/actions/mod.rs
Normal 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
21
src/actions/say_hi.rs
Normal 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
7
src/alias.rs
Normal 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
31
src/command.rs
Normal 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
51
src/db/mod.rs
Normal 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
13
src/errors.rs
Normal 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
39
src/main.rs
Normal 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(())
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue