discord-kurante/src/main.rs

264 lines
7.9 KiB
Rust

use regex::Regex;
use serenity::async_trait;
use serenity::client::Client;
use serenity::framework::standard::{
macros::{command, group},
CommandResult, StandardFramework,
};
use serenity::model::channel::Message;
use serenity::prelude::{Context, EventHandler, TypeMapKey};
use sqlx::sqlite::SqlitePool;
use std::env;
use unicode_segmentation::UnicodeSegmentation;
const COMMAND_PREFIX: &str = "~";
struct Handler {
ah_regex: Regex,
best_doctor_regex: Regex,
fufufu_regex: Regex,
}
impl Default for Handler {
fn default() -> Self {
Self {
ah_regex: Regex::new(r"A+H{5,}").unwrap(),
best_doctor_regex: Regex::new(r"[iI].*(?:best|genius) doc").unwrap(),
fufufu_regex: Regex::new(r"(?:[fF][uU]){3,}").unwrap(),
}
}
}
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, message: Message) {
let content = &message.content_safe(ctx.clone()).await;
if self.ah_regex.is_match(content) {
message
.channel_id
.say(ctx, "its ok ur gonna get a 6* someday")
.await
.unwrap();
} else if self.best_doctor_regex.is_match(content) {
message
.channel_id
.say(ctx, "no, u is smol brain doctor...")
.await
.unwrap();
} else if self.fufufu_regex.is_match(content) {
message.channel_id.say(ctx, get_desu()).await.unwrap();
}
}
}
const DESU_STRINGS: &[&str] = &[
"です。",
"desu~",
"desu.",
r#"
```
ででででででででででで      すす
     ででで     すすすすすすすすす
    でで  でで      すす
   でで   でで     すすす
  でで           す す
  でで           すすす
   でで           すす
    でで          すす
     でで        すす
```"#,
];
fn get_desu() -> &'static str {
use rand::seq::SliceRandom;
use rand::thread_rng;
DESU_STRINGS.choose(&mut thread_rng()).unwrap()
// // https://imgur.com/a/yOb5n
// messageList.add(channel -> channel.sendMessage(new MessageBuilder()
// .setContent("https://www.youtube.com/watch?v=60mLvBWOMb4").build()));
// messageList.add(channel -> channel.sendFile(Desu.class.getResourceAsStream("/desu/desu.jpg"), "desu.jpg"));
// messageList.add(channel -> channel.sendMessage("desu~"));
}
struct DbConnPool {
pool: sqlx::pool::Pool<sqlx::sqlite::SqliteConnection>,
}
impl DbConnPool {
async fn get_heck(&self) -> i32 {
sqlx::query!("UPDATE Heck SET count = count + 1")
.execute(&self.pool)
.await
.unwrap();
sqlx::query!("SELECT count FROM Heck")
.fetch_one(&self.pool)
.await
.unwrap()
.count
}
}
impl TypeMapKey for DbConnPool {
type Value = Self;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
let framework = StandardFramework::new()
.configure(|c| c.prefix(COMMAND_PREFIX))
.group(&GENERAL_GROUP);
// Login with a bot token from the environment
let mut client =
Client::new_with_extras(&env::var("DISCORD_TOKEN").expect("token"), |extras| {
extras
.event_handler(Handler::default())
.framework(framework)
})
.await
.expect("Error creating client");
let pool = SqlitePool::builder()
.build(&env::var("DATABASE_URL").unwrap())
.await?;
init_pool(&pool).await;
{
let mut data = client.data.write().await;
data.insert::<DbConnPool>(DbConnPool { pool });
}
// start listening for events by starting a single shard
if let Err(why) = client.start().await {
println!("An error occurred while running the client: {:?}", why);
}
Ok(())
}
async fn init_pool(pool: &sqlx::pool::Pool<sqlx::sqlite::SqliteConnection>) {
sqlx::query!(
"CREATE TABLE IF NOT EXISTS Heck (id INTEGER PRIMARY KEY NOT NULL, count INTEGER NOT NULL)"
)
.execute(pool)
.await
.unwrap();
if sqlx::query!("SELECT count FROM Heck")
.fetch_all(pool)
.await
.unwrap()
.is_empty()
{
sqlx::query!("INSERT INTO Heck VALUES (1, 0)")
.execute(pool)
.await
.unwrap();
}
}
#[group]
#[commands(heck, clap, cube)]
struct General;
#[command]
async fn heck(ctx: &mut Context, msg: &Message) -> CommandResult {
let db_pool = ctx.data.clone();
let mut db_pool = db_pool.write().await;
let db_pool = db_pool.get_mut::<DbConnPool>().unwrap();
let value = db_pool.get_heck().await;
msg.channel_id
.say(ctx, format!("This command has been hecked {} times", value))
.await?;
Ok(())
}
#[command]
async fn clap(ctx: &mut Context, msg: &Message) -> CommandResult {
let content = msg.content_safe(ctx.cache.clone()).await;
let tokens: Vec<_> = content.split_whitespace().collect();
let resp = match tokens.len() {
1 => ":clap:".to_string(),
2 => "You can't clap a single word."
.split_whitespace()
.collect::<Vec<_>>()
.join(" :clap: "),
_ => tokens[1..].join(" :clap: "),
};
msg.channel_id.say(ctx, resp).await?;
Ok(())
}
#[command]
async fn cube(ctx: &mut Context, msg: &Message) -> CommandResult {
let mut original_msg = msg.content_safe(ctx.cache.clone()).await;
let tokens = original_msg.split_ascii_whitespace().collect::<Vec<_>>();
let resp = match tokens.len() {
0 => unreachable!("wat"),
1 => "What do you wanna cube?".to_string(),
_ => {
let to_cube: &str = &original_msg.split_off(COMMAND_PREFIX.len() + "cube".len() + 1);
let to_cube = UnicodeSegmentation::graphemes(to_cube, true).collect::<Vec<_>>();
let cube_len = to_cube.len();
let should_reverse = to_cube.last() != to_cube.first();
let offset = cube_len / 2;
let mut field =
vec![vec![" ".to_string(); (cube_len + offset) * 2 - 1]; cube_len + offset];
draw_diagonal(&mut field, cube_len, offset);
draw_box(&mut field, &to_cube, should_reverse, offset * 2, 0);
draw_box(&mut field, &to_cube, should_reverse, 0, offset);
let text = field
.iter()
.map(|r| r.join("").trim_end().to_string())
.collect::<Vec<_>>()
.join("\n");
["```\n".to_string(), text, "```".to_string()].concat()
}
};
msg.channel_id.say(ctx, resp).await?;
Ok(())
}
fn draw_diagonal(field: &mut Vec<Vec<String>>, cube_len: usize, offset: usize) {
let diag_char = "/".to_string();
for x in 0..offset {
field[offset - x][x * 2] = diag_char.clone();
field[cube_len - x + offset - 1][x * 2] = diag_char.clone();
field[offset - x][(x + cube_len - 1) * 2] = diag_char.clone();
field[cube_len - x + offset - 1][(x + cube_len - 1) * 2] = diag_char.clone();
}
}
fn draw_box(field: &mut Vec<Vec<String>>, chars: &[&str], should_rev: bool, x: usize, y: usize) {
let word_len = chars.len();
// Magic numbers are good as long as they're 1 or 2 right?
for i in 0..word_len {
field[y + i][x] = chars[i].to_string();
field[y][x + i * 2] = chars[i].to_string();
field[y + (word_len - 1)][x + (word_len - 1 - i) * 2] = if should_rev {
chars[i].to_string()
} else {
chars[word_len - i - 1].to_string()
};
field[y + (word_len - 1) - i][x + (word_len - 1) * 2] = if should_rev {
chars[i].to_string()
} else {
chars[word_len - i - 1].to_string()
};
}
}