lol what are good commits

master
Edward Shen 2020-05-02 01:04:51 -04:00
parent d543802181
commit 4b7a981a88
Signed by: edward
GPG Key ID: 19182661E818369F
13 changed files with 189 additions and 112 deletions

49
Cargo.lock generated
View File

@ -304,7 +304,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"dotenv", "dotenv",
"env_logger",
"futures", "futures",
"lazy_static",
"log",
"rand", "rand",
"regex", "regex",
"serenity", "serenity",
@ -334,6 +337,19 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "env_logger"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "fake-simd" name = "fake-simd"
version = "0.1.2" version = "0.1.2"
@ -561,6 +577,15 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "humantime"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
]
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.13.5" version = "0.13.5"
@ -967,6 +992,12 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.3" version = "1.0.3"
@ -1401,6 +1432,15 @@ dependencies = [
"winapi 0.3.8", "winapi 0.3.8",
] ]
[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
@ -1799,6 +1839,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi 0.3.8",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View File

@ -15,4 +15,7 @@ futures = "0.3"
tokio = { version = "0.2", features = ["full"] } tokio = { version = "0.2", features = ["full"] }
dotenv = "0.15" dotenv = "0.15"
rand = "0.7" rand = "0.7"
unicode-segmentation = "1.6.0" unicode-segmentation = "1.6"
log = "0.4"
env_logger = "0.7"
lazy_static = "1.4"

View File

@ -1,3 +1,4 @@
use log::error;
use serenity::framework::standard::{macros::command, Args, CommandResult}; use serenity::framework::standard::{macros::command, Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::Context; use serenity::prelude::Context;
@ -6,7 +7,16 @@ use serenity::prelude::Context;
async fn clap(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { async fn clap(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let resp = match args let resp = match args
.iter() .iter()
.map(|e: Result<String, _>| e.unwrap()) .map(|arg: Result<String, _>| match arg {
Ok(arg) => arg,
Err(e) => {
error!(
"Failed to cast clap arg to String; returning empty string instead: {:?}",
e
);
String::default()
}
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.as_slice() .as_slice()
{ {

View File

@ -1,3 +1,4 @@
/// This was directly ported from the Java version. I make no quality assurances.
use serenity::framework::standard::{macros::command, Args, CommandResult}; use serenity::framework::standard::{macros::command, Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::Context; use serenity::prelude::Context;

View File

@ -7,10 +7,15 @@ use serenity::prelude::Context;
async fn heck(ctx: &mut Context, msg: &Message) -> CommandResult { async fn heck(ctx: &mut Context, msg: &Message) -> CommandResult {
let db_pool = ctx.data.clone(); let db_pool = ctx.data.clone();
let mut db_pool = db_pool.write().await; let mut db_pool = db_pool.write().await;
let db_pool = db_pool.get_mut::<DbConnPool>().unwrap(); let db_pool = db_pool
.get_mut::<DbConnPool>()
.expect("No db pool in context?!");
let value = db_pool.get_heck().await; let value = db_pool.get_heck().await;
msg.channel_id msg.channel_id
.say(ctx, format!("This command has been hecked {} times", value)) .say(
ctx,
format!("This command has been hecked {} times", value?),
)
.await?; .await?;
Ok(()) Ok(())

View File

@ -1,9 +1,12 @@
use crate::commands::GENERAL_GROUP; use crate::commands::GENERAL_GROUP;
use crate::passive::Handler; use crate::passive::Handler;
use serenity::client::Client; use env_logger;
use log::error;
use serenity::client::{validate_token, Client};
use serenity::framework::standard::StandardFramework; use serenity::framework::standard::StandardFramework;
use std::env; use std::env;
use util::db::DbConnPool; use util::db::DbConnPool;
use util::error::KuranteError;
mod commands; mod commands;
mod passive; mod passive;
@ -12,28 +15,48 @@ mod util;
pub(crate) const COMMAND_PREFIX: &str = "~"; pub(crate) const COMMAND_PREFIX: &str = "~";
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() {
std::process::exit(match run().await {
Ok(_) => 0,
Err(_) => 1,
});
}
async fn run() -> Result<(), KuranteError> {
// Init dotenv vars before the env logger
dotenv::dotenv().ok(); dotenv::dotenv().ok();
env_logger::init();
let framework = StandardFramework::new() let framework = StandardFramework::new()
.configure(|c| c.prefix(COMMAND_PREFIX)) .configure(|c| c.prefix(COMMAND_PREFIX))
.group(&GENERAL_GROUP); .group(&GENERAL_GROUP);
let mut client = let token = match env::var("DISCORD_TOKEN") {
Client::new_with_extras(&env::var("DISCORD_TOKEN").expect("token"), |extras| { Ok(token) => match validate_token(&token) {
extras Ok(_) => token,
.event_handler(Handler::default()) Err(_) => {
.framework(framework) error!("Provided token was invalid.");
}) return Err(KuranteError::InvalidToken);
.await }
.expect("Error creating client"); },
Err(e) => {
error!("`DISCORD_TOKEN` environment variable was not set. Unable to start bot!");
return Err(e.into());
}
};
let mut client = Client::new_with_extras(token, |extras| {
extras
.event_handler(Handler::default())
.framework(framework)
})
.await?;
{ {
let mut data = client.data.write().await; let mut data = client.data.write().await;
data.insert::<DbConnPool>(DbConnPool::new().await); data.insert::<DbConnPool>(DbConnPool::new().await?);
} }
// start listening for events by starting a single shard
if let Err(why) = client.start().await { if let Err(why) = client.start().await {
println!("An error occurred while running the client: {:?}", why); println!("An error occurred while running the client: {:?}", why);
} }

View File

@ -1,30 +1,12 @@
use crate::simple_responder;
use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serenity::async_trait; use serenity::async_trait;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::{Context, EventHandler}; use serenity::prelude::{Context, EventHandler};
pub(crate) struct YellResponder { simple_responder!(
regex: Regex, YellResponder,
} r"A+H{5,}",
"its ok ur gonna get a 6* someday"
impl Default for YellResponder { );
fn default() -> Self {
Self {
regex: Regex::new(r"A+H{5,}").unwrap(),
}
}
}
#[async_trait]
impl EventHandler for YellResponder {
async fn message(&self, ctx: Context, message: Message) {
let content = &message.content_safe(ctx.clone()).await;
if self.regex.is_match(content) {
message
.channel_id
.say(ctx, "its ok ur gonna get a 6* someday")
.await
.unwrap();
}
}
}

View File

@ -1,30 +1,12 @@
use crate::simple_responder;
use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serenity::async_trait; use serenity::async_trait;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::{Context, EventHandler}; use serenity::prelude::{Context, EventHandler};
pub(crate) struct BestDoctorResponder { simple_responder!(
regex: Regex, BestDoctorResponder,
} r"[iI].*(?:best|genius) doc",
"smol brain doctor..."
impl Default for BestDoctorResponder { );
fn default() -> Self {
Self {
regex: Regex::new(r"[iI].*(?:best|genius) doc").unwrap(),
}
}
}
#[async_trait]
impl EventHandler for BestDoctorResponder {
async fn message(&self, ctx: Context, message: Message) {
let content = &message.content_safe(ctx.clone()).await;
if self.regex.is_match(content) {
message
.channel_id
.say(ctx, "smol brain doctor..")
.await
.unwrap();
}
}
}

View File

@ -1,29 +1,11 @@
use crate::simple_responder;
use lazy_static::lazy_static;
use regex::Regex; use regex::Regex;
use serenity::async_trait; use serenity::async_trait;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::{Context, EventHandler}; use serenity::prelude::{Context, EventHandler};
pub(crate) struct FufufuResponder { simple_responder!(FufufuResponder, r"(?:[fF][uU]){3,}", (get_desu()));
regex: Regex,
}
impl Default for FufufuResponder {
fn default() -> Self {
Self {
regex: Regex::new(r"(?:[fF][uU]){3,}").unwrap(),
}
}
}
#[async_trait]
impl EventHandler for FufufuResponder {
async fn message(&self, ctx: Context, message: Message) {
let content = &message.content_safe(ctx.clone()).await;
if self.regex.is_match(content) {
message.channel_id.say(ctx, get_desu()).await.unwrap();
}
}
}
const DESU_STRINGS: &[&str] = &[ const DESU_STRINGS: &[&str] = &[
"です。", "です。",

View File

@ -17,9 +17,9 @@ impl Default for Handler {
fn default() -> Self { fn default() -> Self {
Self { Self {
responders: vec![ responders: vec![
Box::new(BestDoctorResponder::default()), Box::new(BestDoctorResponder),
Box::new(FufufuResponder::default()), Box::new(FufufuResponder),
Box::new(YellResponder::default()), Box::new(YellResponder),
], ],
} }
} }
@ -33,3 +33,25 @@ impl EventHandler for Handler {
} }
} }
} }
#[macro_export]
macro_rules! simple_responder {
// $phrase should be `expr`, see https://github.com/dtolnay/async-trait/issues/46
// above issue is blocked on rustc bug, see https://github.com/rust-lang/rust/issues/43081
($name:tt, $regex:expr, $phrase:tt) => {
lazy_static! {
static ref REGEX: Regex = Regex::new($regex).unwrap();
}
pub(crate) struct $name;
#[async_trait]
impl EventHandler for $name {
async fn message(&self, ctx: Context, message: Message) {
if REGEX.is_match(&message.content_safe(ctx.clone()).await) {
message.channel_id.say(ctx, $phrase).await.unwrap();
}
}
}
};
}

View File

@ -1,7 +1,7 @@
use serenity::prelude::TypeMapKey; use serenity::prelude::TypeMapKey;
use sqlx::{ use sqlx::{
sqlite::{SqliteConnection, SqlitePool}, sqlite::{SqliteConnection, SqlitePool},
Pool, Error, Pool,
}; };
use std::env; use std::env;
@ -12,22 +12,21 @@ pub(crate) struct DbConnPool {
} }
impl DbConnPool { impl DbConnPool {
pub async fn new() -> Self { pub async fn new() -> Result<Self, Error> {
Self { Ok(Self {
pool: init_pool().await, pool: init_pool().await?,
} })
} }
pub async fn get_heck(&self) -> i32 {
pub async fn get_heck(&self) -> Result<i32, Error> {
sqlx::query!("UPDATE Heck SET count = count + 1") sqlx::query!("UPDATE Heck SET count = count + 1")
.execute(&self.pool) .execute(&self.pool)
.await .await?;
.unwrap();
sqlx::query!("SELECT count FROM Heck") Ok(sqlx::query!("SELECT count FROM Heck")
.fetch_one(&self.pool) .fetch_one(&self.pool)
.await .await?
.unwrap() .count)
.count
} }
} }
@ -35,30 +34,26 @@ impl TypeMapKey for DbConnPool {
type Value = Self; type Value = Self;
} }
async fn init_pool() -> DbPool { async fn init_pool() -> Result<DbPool, Error> {
let pool = SqlitePool::builder() let pool = SqlitePool::builder()
.build(&env::var("DATABASE_URL").unwrap()) .build(&env::var("DATABASE_URL").unwrap())
.await .await?;
.unwrap();
sqlx::query!( sqlx::query!(
"CREATE TABLE IF NOT EXISTS Heck (id INTEGER PRIMARY KEY NOT NULL, count INTEGER NOT NULL)" "CREATE TABLE IF NOT EXISTS Heck (id INTEGER PRIMARY KEY NOT NULL, count INTEGER NOT NULL)"
) )
.execute(&pool) .execute(&pool)
.await .await?;
.unwrap();
if sqlx::query!("SELECT count FROM Heck") if sqlx::query!("SELECT count FROM Heck")
.fetch_all(&pool) .fetch_all(&pool)
.await .await?
.unwrap()
.is_empty() .is_empty()
{ {
sqlx::query!("INSERT INTO Heck VALUES (1, 0)") sqlx::query!("INSERT INTO Heck VALUES (1, 0)")
.execute(&pool) .execute(&pool)
.await .await?;
.unwrap();
} }
pool Ok(pool)
} }

22
src/util/error.rs Normal file
View File

@ -0,0 +1,22 @@
pub enum KuranteError {
MissingEnvVar(std::env::VarError),
Bot(serenity::Error),
InvalidToken,
Database(sqlx::Error),
}
/// Generates a from implementation from the specified type to the provided
/// bunbun error.
macro_rules! from_error {
($from:ty, $to:ident) => {
impl From<$from> for KuranteError {
fn from(e: $from) -> Self {
Self::$to(e)
}
}
};
}
from_error!(std::env::VarError, MissingEnvVar);
from_error!(serenity::Error, Bot);
from_error!(sqlx::Error, Database);

View File

@ -1 +1,2 @@
pub mod db; pub mod db;
pub mod error;