Added random commands and story command

+ Clippy + Fmt
This commit is contained in:
2022-12-17 15:28:14 -07:00
parent 10b471f25a
commit 35321fc3c2
10 changed files with 136237 additions and 44 deletions
+5 -8
View File
@@ -127,18 +127,15 @@ pub async fn parse_album(
msg: &Message,
album_name: &str,
tags: Vec<&str>,
) -> Result<(), Error> {
) -> Result<bool, Error> {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
match global_data.bot_state.get_image(album_name, tags) {
Ok(match global_data.bot_state.get_image(album_name, tags) {
Some(image) => {
msg.reply(&ctx.http, &image.link).await?;
true
}
None => {
msg.reply(&ctx.http, "No image ;(").await?;
}
};
Ok(())
None => false,
})
}
+24 -23
View File
@@ -12,7 +12,7 @@ use std::collections::HashMap;
use std::time::Duration;
#[group]
#[commands(dad_joke, fortune, roll, bad_apple)]
#[commands(dad_joke, roll, bad_apple)]
pub struct Joke;
#[derive(Clone, Serialize, Deserialize)]
@@ -43,12 +43,12 @@ async fn dad_joke(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
}
#[derive(Serialize, Deserialize)]
struct FortuneCtx {
user: serenity::model::guild::Member,
struct RandomCtx {
user: String,
random_image: HashMap<String, String>,
}
impl FortuneCtx {
impl RandomCtx {
pub async fn new(
user: serenity::model::guild::Member,
global_data: &GlobalData,
@@ -63,25 +63,29 @@ impl FortuneCtx {
}
}
Ok(Self { user, random_image })
Ok(Self {
user: (*user.display_name()).clone(),
random_image,
})
}
}
#[command]
#[only_in(guilds)]
#[aliases("8ball")]
#[description("Use as your own risk")]
async fn fortune(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandResult {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
let fortune_template = {
let templates = global_data.bot_state.templates.get(random_name);
let templates = if let Some(templates) = templates {
templates
} else {
return Ok(());
};
let template = {
let mut rng = thread_rng();
global_data
.bot_state
.fortune_templates
.get_template_names()
.choose(&mut rng)
templates.get_template_names().choose(&mut rng)
};
let guild_member = msg
@@ -90,15 +94,12 @@ async fn fortune(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
.member(&ctx.http, msg.author.id)
.await?;
let fortune_ctx = FortuneCtx::new(guild_member, global_data).await?;
let random_ctx = RandomCtx::new(guild_member, global_data).await?;
let reply = if let Some(fortune_template) = fortune_template {
global_data.bot_state.fortune_templates.render(
fortune_template,
&tera::Context::from_serialize(&fortune_ctx)?,
)?
let reply = if let Some(template) = template {
templates.render(template, &tera::Context::from_serialize(&random_ctx)?)?
} else {
"Sorry kid, all out of fortunes.".to_string()
"Sorry kid, all out of messages.".to_string()
};
msg.reply(&ctx.http, reply).await?;
+15 -2
View File
@@ -3,7 +3,9 @@ pub mod album;
pub mod celeryman;
pub mod color;
pub mod joke;
pub mod story;
use crate::discord::joke::random;
use crate::{help, hook, GlobalData};
use serenity::async_trait;
use serenity::client::Context;
@@ -100,8 +102,19 @@ pub async fn unrecognised_command_hook(
Vec::new()
};
if let Err(e) = album::parse_album(ctx, msg, unrecognised_command_name, tags).await {
println!("Error processing album command: {}", e)
let parsed_album = match album::parse_album(ctx, msg, unrecognised_command_name, tags).await {
Ok(parsed) => parsed,
Err(e) => {
println!("Error processing album command: {}", e);
true
}
};
if !parsed_album {
match random(ctx, msg, unrecognised_command_name).await {
Ok(_) => {}
Err(e) => println!("Error processing random command: {}", e),
}
}
}
+216
View File
@@ -0,0 +1,216 @@
use crate::config::{BotConfig, StoryRecv, StorySend};
use crate::{command, group, GlobalData};
use rand::prelude::SliceRandom;
use rand::thread_rng;
use regex::Regex;
use serenity::client::Context;
use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::{Attachment, Message};
use serenity::utils::MessageBuilder;
use std::collections::HashMap;
use std::path::PathBuf;
#[group]
#[commands(story, word, list_stories, upload_story)]
pub struct Story;
async fn get_all_stories(cfg: &BotConfig) -> Vec<PathBuf> {
let mut dir = tokio::fs::read_dir(&cfg.story_path).await.unwrap();
let mut stories = Vec::new();
while let Some(file) = &dir.next_entry().await.unwrap() {
stories.push(file.path());
}
stories
}
fn get_all_blanks(story: &str) -> Vec<String> {
let re = Regex::new(r"\{%(?P<global>.*?)%}").unwrap();
let mut globals: Vec<String> = Vec::new();
for cap in re.captures_iter(story) {
if let Some(global) = cap.name("global") {
if !globals.contains(&global.as_str().to_string()) {
globals.push(global.as_str().to_string())
}
}
}
globals
}
#[command]
#[only_in(guilds)]
#[description("List Stories")]
async fn list_stories(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
let stories = get_all_stories(&global_data.cfg).await;
let mut resp = MessageBuilder::default();
resp.push_line("**Stories I know:**");
for story in stories {
resp.push_line(
story
.file_stem()
.unwrap()
.to_str()
.unwrap()
.replace('_', " "),
);
}
msg.reply(&ctx.http, resp.build()).await?;
Ok(())
}
#[command]
#[only_in(guilds)]
#[description("Let me tell you a tail")]
async fn story(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global_data = data.get::<GlobalData>().unwrap();
let _story_lock = match global_data.bot_state.story_in_progress.try_lock() {
Ok(lock) => lock,
Err(_) => {
msg.reply(&ctx.http, "Let me finish telling this story bub.")
.await?;
return Ok(());
}
};
let story_recv = data.get::<StoryRecv>().unwrap();
let mut story_recv = story_recv.lock().await;
let stories = get_all_stories(&global_data.cfg).await;
let story_path = if args.is_empty() {
let mut rng = thread_rng();
stories.choose(&mut rng)
} else {
let story_name = args.rest().to_lowercase().replace(' ', "_");
stories.iter().find(|story_p| {
let story_p_name = story_p.file_stem().unwrap().to_str().unwrap();
story_p_name == story_name
})
};
let story_path = match story_path {
None => {
msg.reply(&ctx.http, "No story found :(").await?;
return Ok(());
}
Some(story) => story,
};
let mut story_contents = tokio::fs::read_to_string(story_path).await?;
let mut story_globals: HashMap<String, String> = HashMap::new();
let msg_channel = msg.channel(&ctx.http).await?;
let guild = msg.guild(&ctx.cache).unwrap();
let msg_channel = guild
.channels(&ctx.http)
.await?
.get(&msg_channel.id())
.unwrap()
.clone();
for global in get_all_blanks(&story_contents) {
msg_channel
.say(&ctx.http, format!("Give me {}", global))
.await?;
story_globals.insert(global, story_recv.recv.recv().await.unwrap());
}
for (prompt, response) in story_globals {
story_contents = story_contents.replace(&format!("{{%{}%}}", prompt), &response)
}
let mut msg_builder = MessageBuilder::default();
for part in story_contents.split('\n') {
if (msg_builder.0.len() + part.len()) > serenity::constants::MESSAGE_CODE_LIMIT {
msg_channel.say(&ctx.http, msg_builder.build()).await?;
msg_builder = MessageBuilder::default();
}
msg_builder.push_line(part);
}
if !msg_builder.0.is_empty() {
msg_channel.say(&ctx.http, msg_builder.build()).await?;
}
Ok(())
}
#[command]
#[only_in(guilds)]
#[description("Give me a word")]
async fn word(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global = data.get::<GlobalData>().unwrap();
if global.bot_state.story_in_progress.try_lock().is_ok() {
msg.reply(&ctx.http, "No story in progress!").await?;
return Ok(());
}
let story_send = data.get::<StorySend>().unwrap();
let story_send = story_send.lock().await;
let resp = MessageBuilder::default().push_safe(args.rest()).build();
story_send.send.send(resp).await?;
Ok(())
}
#[command]
#[description("Attach stories as .txt files")]
async fn upload_story(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global = data.get::<GlobalData>().unwrap();
let stories: Vec<&Attachment> = msg
.attachments
.iter()
.filter(|f| f.filename.ends_with(".txt"))
.collect();
for story in stories {
let story_txt = String::from_utf8(story.download().await?)?;
let globals = get_all_blanks(&story_txt);
let mut resp = MessageBuilder::new();
resp.push_line(format!("{} contains the following blanks:", story.filename));
for blank in globals {
resp.push_line(format!("* {}", blank));
}
msg.reply(&ctx.http, resp.build()).await?;
let story_path = global.cfg.story_path.join(&story.filename);
tokio::fs::write(&story_path, story_txt).await?;
}
Ok(())
}