Browse Source

Add macro persistence (JSON)

master
Graham Littlewood 4 years ago
parent
commit
95e7ae3842
3 changed files with 77 additions and 18 deletions
  1. +1
    -1
      Cargo.toml
  2. +75
    -15
      src/commands/cmd_macro.rs
  3. +1
    -2
      src/main.rs

+ 1
- 1
Cargo.toml View File

@@ -9,5 +9,5 @@ edition = "2018"
[dependencies]
serenity = "0.8"
clap = "2.33.0"
json = "0.12.4"
serde_json = "1.0"
regex = "1.3.6"

+ 75
- 15
src/commands/cmd_macro.rs View File

@@ -5,7 +5,9 @@ use serenity::{
utils::MessageBuilder,
};

extern crate json;
extern crate serde_json;
//use serde::{Deserialize, Serialize};
use serde_json::Result;

extern crate regex;
use regex::{Regex, Match};
@@ -13,33 +15,44 @@ use regex::{Regex, Match};
use core::option::Option;
use std::collections::HashMap;
use std::string::String;
use std::fs::{File, OpenOptions};
use std::io::BufReader;

use crate::traits::Command;

const MACRO_PATH: &str = "macros.json";

pub struct CmdMacro {
format: Regex,
macros: HashMap<String, String>
macros: HashMap<u64,HashMap<String, String>>
}

impl Command for CmdMacro {
fn execute(& mut self, ctx: &Context, msg: &Message) {
let id: u64;
match msg.guild_id {
Some(g) => id = *g.as_u64(),
None => id = *msg.channel_id.as_u64(),
}
if let Some(parsed) = self.format.captures(&msg.content) {
if let Some(op) = parsed.get(1) {
let mut response = MessageBuilder::new();
if op.as_str().to_string().to_lowercase() == "add" {
if self.add(parsed.get(2), parsed.get(3)) {
if self.add(&id, parsed.get(2), parsed.get(3)) {
response.push("Successfully added macro.");
self.save_json();
} else {
response.push("Missing macro argument or macro already exists.");
}
} else if op.as_str().to_string().to_lowercase() == "remove" {
if self.remove(parsed.get(2)) {
if self.remove(&id, parsed.get(2)) {
response.push("Successfully removed macro.");
self.save_json();
} else {
response.push("Macro does not exist.");
}
} else {
response.push(self.get(&op.as_str().to_string()));
response.push(self.get(&id, &op.as_str().to_string()));
}
if let Err(why) = msg.channel_id.say(&ctx.http, &response.build()) {
println!("Failed to send macro message {:?}", why);
@@ -62,40 +75,87 @@ impl Command for CmdMacro {

impl CmdMacro {
pub fn new() -> CmdMacro{
// load macro map from JSON
let mut file = File::open(MACRO_PATH);
match file {
Err(why) => {
file = File::create(MACRO_PATH);
println!("Creating file (error {:?})", why);
},
_ => (),
}
let reader = BufReader::new(file.unwrap());
let macros: HashMap<u64, HashMap<String, String>> = serde_json::from_reader(reader).unwrap_or(HashMap::<u64, HashMap<String, String>>::new());
return CmdMacro {
format:
Regex::new(r#"^!macro\s+([A-Za-z0-9]+|"[^\s]+"|'[^\s]+')(?:\s+([A-Za-z0-9]+|"[^\s]+"|'[^\s]+')\s+(.+))?"#)
.unwrap(),
macros: HashMap::<String, String>::new(), // TODO: load map from JSON
macros: macros,
};
}

fn add<'t>(&mut self, key: Option<Match<'t>>, value: Option<Match<'t>>) -> bool {
fn add<'t>(&mut self, id: &u64, key: Option<Match<'t>>, value: Option<Match<'t>>) -> bool {
let map: &mut HashMap<String, String>;
match self.macros.get_mut(id) {
Some(m) => map = m,
None => {
self.macros.insert(*id, HashMap::new());
map = self.macros.get_mut(id).unwrap();
},
}
let rk; let rv;
if let Some(k) = key {
rk = k;
if self.macros.get(&key.to_owned()).is_some() {
rk = k.as_str().trim_matches('\"').trim_matches('\'').to_string();
if map.get(&rk).is_some() {
return false;
}
} else {return false;}
if let Some(v) = value {
rv = v;
rv = v.as_str().to_string();
} else {return false;}
self.macros.insert(rk.as_str().to_string(), rv.as_str().to_string());
map.insert(rk, rv);
return true;
}

fn remove<'t>(&mut self, key: Option<Match<'t>>) -> bool {
fn remove<'t>(&mut self, id: &u64, key: Option<Match<'t>>) -> bool {
let map: &mut HashMap<String, String>;
match self.macros.get_mut(id) {
Some(m) => map = m,
None => {
self.macros.insert(*id, HashMap::new());
map = self.macros.get_mut(id).unwrap();
},
}
match key {
Some(k) => return self.macros.remove(&k.as_str().to_string()).is_some(),
Some(k) => return map.remove(&k.as_str().trim_matches('\"').trim_matches('\'').to_string()).is_some(),
None => return false,
}
}

fn get(&self, key: &String) -> String {
if let Some(s) = self.macros.get(&key.to_owned()) {
fn get(&mut self, id: &u64, key: &String) -> String {
let map: &HashMap<String, String>;
match self.macros.get_mut(id) {
Some(m) => map = m,
None => {
self.macros.insert(*id, HashMap::new());
map = self.macros.get(id).unwrap();
},
}
if let Some(s) = map.get(&key.to_owned()) {
return s.to_string();
}
return "Invalid macro".to_string();
}

fn save_json(&self) {
let mut file = OpenOptions::new()
.write(true)
.open(MACRO_PATH)
.unwrap();
//let writer = BufWriter::new(file.unwrap());
match serde_json::to_writer_pretty(file, &self.macros) {
Err(why) => println!("Macro saving failed ({:?})", why),
Ok(_) => (),
}
}
}

+ 1
- 2
src/main.rs View File

@@ -8,7 +8,6 @@ extern crate serenity;
use serenity::{
model::{channel::Message, gateway::{Ready, Activity}},
prelude::*,
utils::MessageBuilder,
};

mod traits;
@@ -55,7 +54,7 @@ impl Handler {
}

fn main() {
println!("Leo42 v{} is starting", crate_version!());
println!("Leo42 v{} is starting in {}", crate_version!(), env::current_dir().unwrap().to_str().unwrap());
let token = env::var("DISCORD_TOKEN")
.expect("Expected a Discord API token in DISCORD_TOKEN environment variable");
let mut event_handler = Handler::new();