diff --git a/.gitignore b/.gitignore index f943098..16ec4a8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ target/ Cargo.lock **/*.rs.bk *.pdb -.vscode/ \ No newline at end of file +.vscode/ + +# program-created data +data/reddit_data.json \ No newline at end of file diff --git a/TODO.md b/TODO.md index f2911d1..8feac39 100644 --- a/TODO.md +++ b/TODO.md @@ -2,8 +2,8 @@ - [x] Embed creation tool ### Medium priority: -- [ ] JSON -> Rules list - * Structure: /rules [rulename/index] +- [x] JSON -> Rules list +- [ ] View single rule (/rule {rulename}) - [ ] Postfix calculator - [ ] Postfic generator - [ ] JSON -> BPS class init diff --git a/data/reddit_data_preset.json b/data/reddit_data_preset.json new file mode 100644 index 0000000..9541a4f --- /dev/null +++ b/data/reddit_data_preset.json @@ -0,0 +1,4 @@ +{ + "file_created_correctly": true, + "bk_manually_added_posts": [] +} \ No newline at end of file diff --git a/data/write_json.json b/data/write_json.json index 0f6039e..d70785a 100644 --- a/data/write_json.json +++ b/data/write_json.json @@ -1,4 +1,12 @@ [ - "my name", - "is not jeff." + { + "title": "my name", + "desc": "may be jeff", + "index": 1.0 + }, + { + "title": "is not jeff", + "desc": "my name?", + "index": 1.1 + } ] \ No newline at end of file diff --git a/src/cmds.rs b/src/cmds.rs index 116296d..7e30cae 100644 --- a/src/cmds.rs +++ b/src/cmds.rs @@ -129,10 +129,10 @@ pub async fn write_json( edit_msg(ctx, progress.unwrap(), "Deleting all messages in channel... Done!".to_string()).await; } - let json_str = json.unwrap_or_else(|| + let json_str = json.clone().unwrap_or_else(|| std::fs::read_to_string("./data/write_json.json") - .expect("No JSON preset file exists.") - ); + .expect("No JSON preset file exists.") + ).to_string(); let json_json: serde_json::Value = serde_json::from_str(&json_str).expect("JSON was improperly formatted"); if !json_json.is_array() { @@ -141,14 +141,72 @@ pub async fn write_json( } for i in json_json.as_array().unwrap() { - if !i.is_string() { continue; } - let i_str = i.to_string(); - send_msg(ctx, i_str[1..i_str.len() - 1].to_string(), false, false).await; + if !i.is_object() { continue; } + + let title = i["title"].to_string(); + let title_str = title[1..title.len() - 1].to_string(); + + let desc = i["desc"].to_string(); + let desc_str = desc[1..desc.len() - 1].to_string(); + + let index_str = i["index"].to_string(); + + let title_format = if index_str.len() > 0 + { format!("{} - {}", index_str, title_str) } + else { title_str }; + + let embed = EmbedOptions { + title: Some(title_format), + desc: desc_str, + ..Default::default() + }; + + send_embed(ctx, embed, false).await; } - if include_cmd { + if include_cmd && json.is_none() { send_msg(ctx, "Use /rules thank you".to_string(), false, false).await; } + return Ok(()); +} + + +async fn autocomplete_rule_list(_: Context<'_>, _partial: &str) -> Vec { + let json_str = std::fs::read_to_string("./data/write_json.json") + .expect("No JSON preset file exists."); + let json_json: serde_json::Value = serde_json::from_str(&json_str).expect("JSON was improperly formatted"); + + if json_json.is_array() { + let mut titles: Vec = vec![]; + + for i in json_json.as_array().unwrap() { + let title = i["title"].to_string(); + let title_str = title[1..title.len() - 1].to_string(); + let index_str = i["index"].to_string(); + + let title_format = if index_str.len() > 0 + { format!("{} - {}", index_str, title_str) } + else { title_str }; + + titles.push(title_format); + } + + return titles; + } + else { + return vec!["JSON data not found".to_string()]; + } +} + + +#[poise::command(slash_command, prefix_command)] +pub async fn rule( + _ctx: Context<'_>, + #[description = "The name of the rule to display"] + #[autocomplete = "autocomplete_rule_list"] + _rule: Vec +) -> Result<(), Error> +{ return Ok(()); } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3320739..5803b7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,11 @@ mod cmds; mod events; -use poise::{serenity_prelude::CreateMessage, CreateReply, ReplyHandle}; +use poise::{serenity_prelude::{Client, CreateMessage}, CreateReply, ReplyHandle}; +use core::str; use std::env; +use std::process::Command; +use std::process; use poise::serenity_prelude::{self as serenity, Color, CreateEmbed, Timestamp}; @@ -40,17 +43,45 @@ impl Default for EmbedOptions { async fn main() { let args: Vec = env::args().collect(); + if args.contains(&"--py".to_string()) { + let output = Command::new("python") + .arg("./src/python/main.py") + .output() + .expect("Failed to launch main.py"); + + let stdout = str::from_utf8(&output.stdout).unwrap_or("Invalid UTF-8 in stdout"); + let stderr = str::from_utf8(&output.stderr).unwrap_or("Invalid UTF-8 in stderr"); + + println!("PYTHON OUTPUT:\n{}\n", stdout); + println!("PYTHON ERROR:\n{}", stderr); + + process::exit(1); + } + else { + let data = gen_data(args); + let mut bot = gen_bot(data).await; + + println!("Starting bot..."); + bot.start().await.unwrap(); + } +} + + +fn gen_data(args: Vec) -> Data { let ball_classic_str = std::fs::read_to_string("./data/8-ball_classic.txt").unwrap(); let ball_quirk_str = std::fs::read_to_string("./data/8-ball_quirky.txt").unwrap(); let ball_classic: Vec = ball_classic_str.lines().map(String::from).collect(); let ball_quirk: Vec = ball_quirk_str .lines().map(String::from).collect(); - let data = Data { + return Data { dev: args.contains(&"--dev".to_string()), ball_prompts: [ball_classic, ball_quirk] }; +} + +async fn gen_bot(data: Data) -> Client { let token = std::env::var("ASSISTANT_TOKEN").expect("missing ASSISTANT_TOKEN env var"); let intents = serenity::GatewayIntents::all(); @@ -67,7 +98,8 @@ async fn main() { cmds::embed(), cmds::stop(), cmds::eight_ball(), - cmds::write_json() + cmds::write_json(), + cmds::rule() ], event_handler: events::event_handler, ..Default::default() @@ -80,13 +112,10 @@ async fn main() { }) .build(); - let mut bot = serenity::ClientBuilder::new(token, intents) + return serenity::ClientBuilder::new(token, intents) .framework(framework) .await .unwrap(); - - println!("Starting bot..."); - bot.start().await.unwrap(); } diff --git a/src/python/main.py b/src/python/main.py new file mode 100644 index 0000000..d98a818 --- /dev/null +++ b/src/python/main.py @@ -0,0 +1,62 @@ +from io import TextIOWrapper +from praw import models +import praw +import os +import json + + +class Bot: + password: str = os.environ["ASSISTANT_R_PASS"] + secret: str = os.environ["ASSISTANT_R_TOKEN"] + + r: praw.Reddit = praw.Reddit( + client_id = "iCSRWS6PMlTLwmylCJRYmA", + client_secret = secret, + username = "ByteDiceAssistant", + password = password, + user_agent = "Byte Dice Assistant by u/RandomPersonDotExe aka u/Byte_Dice" + ) + sr: models.Subreddit = r.subreddit("bytedicetesting") #r.subreddit("boykisser") + data_f: TextIOWrapper = None + data: dict = {} + + +def main(): + bot = Bot() + read_data(bot) + + posts = fetch_posts_with_flair(bot, "Original Art") + + for post in posts: + print(post.id, post.link_flair_text, post.title) + + +def read_data(bot: Bot): + # Intentionally unreadable >:] + data_path = os.path.abspath(os.path.join(os.path.join(os.getcwd(), "data"))) + + try: + bot.data_f = open(data_path + "\\reddit_data.json", "r+") + except FileNotFoundError: + bot.data_f = open(data_path + "\\reddit_data.json", "w+") + bot.data_f.write(open(data_path + "\\reddit_data_preset.json", "r").read()) + + data_str = bot.data_f.read() + bot.data = json.loads(data_str) + + if not bot.data["file_created_correctly"]: + raise Exception("reddit_data.json file wasn't created properly. Delete the file and retry.") + + +def fetch_posts_with_flair(bot: Bot, flair_name: str) -> list[models.Submission]: + posts: list[models.Submission] = [] + + # ~36 OG-art posts per week, round limit to 50 or 75 + for post in bot.sr.search(f"flair:\"{flair_name}\"", sort="new", limit=10): + posts.append(post) + + return posts + + +if __name__ == "__main__": + main() \ No newline at end of file