forked from projectmoon/tenebrous-dicebot
Implement basic, not-well-formatted help.
This commit is contained in:
parent
8484e9ffde
commit
531844fbb7
|
@ -172,6 +172,7 @@ dependencies = [
|
|||
"async-trait",
|
||||
"dirs",
|
||||
"env_logger",
|
||||
"indoc",
|
||||
"itertools",
|
||||
"log",
|
||||
"matrix-sdk",
|
||||
|
@ -680,6 +681,15 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644defcefee68d7805653a682e99a2e2a5014a1fc3cc9be7059a215844eeea6f"
|
||||
dependencies = [
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.6"
|
||||
|
@ -2051,6 +2061,12 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af41d708427f8fd0e915dcebb2cae0f0e6acb2a939b2d399c265c39a38a18942"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
|
|
|
@ -21,6 +21,7 @@ itertools = "0.9"
|
|||
async-trait = "0.1"
|
||||
url = "2.1"
|
||||
dirs = "3.0"
|
||||
indoc = "1.0"
|
||||
|
||||
# The versioning of the matrix SDK follows its Cargo.toml. The SDK and
|
||||
# macros are on master, but it imports the common and base from 0.1.0.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::cofd::dice::DicePool;
|
||||
use crate::dice::ElementExpression;
|
||||
use crate::help::HelpTopic;
|
||||
use crate::parser::trim;
|
||||
use crate::roll::Roll;
|
||||
|
||||
|
@ -61,10 +62,29 @@ impl Command for PoolRollCommand {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a command string into a dynamic command execution trait object.
|
||||
/// Returns an error if a command was recognized but not parsed correctly. Returns None if no
|
||||
/// command was recognized.
|
||||
pub fn parse_command(s: &str) -> Result<Option<Box<dyn Command>>, String> {
|
||||
pub struct HelpCommand(Option<HelpTopic>);
|
||||
|
||||
impl Command for HelpCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"help information"
|
||||
}
|
||||
|
||||
fn execute(&self) -> Execution {
|
||||
let help = match &self.0 {
|
||||
Some(topic) => topic.message(),
|
||||
_ => "There is no help for this topic",
|
||||
};
|
||||
|
||||
let plain = format!("Help: {}", help);
|
||||
let html = format!("<p><strong>Help:</strong> {}", help.replace("\n", "<br/>"));
|
||||
Execution { plain, html }
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a command string into a dynamic command execution trait
|
||||
/// object. Returns an error if a command was recognized but not
|
||||
/// parsed correctly. Returns None if no command was recognized.
|
||||
pub fn parse_command<'a>(s: &'a str) -> Result<Option<Box<dyn Command + 'a>>, String> {
|
||||
match parser::parse_command(s) {
|
||||
Ok((input, command)) => match (input, &command) {
|
||||
//This clause prevents bot from spamming messages to itself
|
||||
|
@ -115,6 +135,19 @@ mod tests {
|
|||
.expect("was error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn help_whitespace_test() {
|
||||
assert!(parse_command("!help stuff ")
|
||||
.map(|p| p.is_some())
|
||||
.expect("was error"));
|
||||
assert!(parse_command(" !help stuff")
|
||||
.map(|p| p.is_some())
|
||||
.expect("was error"));
|
||||
assert!(parse_command(" !help stuff ")
|
||||
.map(|p| p.is_some())
|
||||
.expect("was error"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roll_whitespace_test() {
|
||||
assert!(parse_command("!roll 1d4 + 5d6 -3 ")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use nom::{alt, complete, named, tag, take_while, tuple, IResult};
|
||||
|
||||
use crate::cofd::parser::{create_chance_die, parse_dice_pool};
|
||||
use crate::commands::{Command, PoolRollCommand, RollCommand};
|
||||
use crate::commands::{Command, HelpCommand, PoolRollCommand, RollCommand};
|
||||
use crate::dice::parser::parse_element_expression;
|
||||
use crate::parser::eat_whitespace;
|
||||
use crate::help::parse_help_topic;
|
||||
use crate::parser::{eat_whitespace, trim};
|
||||
|
||||
// Parse a roll expression.
|
||||
fn parse_roll(input: &str) -> IResult<&str, Box<dyn Command>> {
|
||||
|
@ -23,6 +24,12 @@ fn chance_die() -> IResult<&'static str, Box<dyn Command>> {
|
|||
Ok((input, Box::new(PoolRollCommand(pool))))
|
||||
}
|
||||
|
||||
fn help(topic: &str) -> IResult<&str, Box<dyn Command>> {
|
||||
let (topic, _) = eat_whitespace(topic)?;
|
||||
let topic = parse_help_topic(&trim(topic));
|
||||
Ok(("", Box::new(HelpCommand(topic))))
|
||||
}
|
||||
|
||||
/// Potentially parse a command expression. If we recognize the command, an error should be raised
|
||||
/// if the command is misparsed. If we don't recognize the command, ignore it and return none
|
||||
pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Command>>> {
|
||||
|
@ -32,7 +39,9 @@ pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Comma
|
|||
named!(command(&str) -> (&str, &str), tuple!(
|
||||
complete!(tag!("!")),
|
||||
alt!(
|
||||
complete!(tag!("chance")) | //TODO figure out how to just have it read single commands.
|
||||
//TODO figure out how to gracefully handle arbitrary single commands.
|
||||
complete!(tag!("chance")) |
|
||||
complete!(tag!("help")) |
|
||||
complete!(take_while!(char::is_alphabetic))
|
||||
)
|
||||
));
|
||||
|
@ -40,13 +49,16 @@ pub fn parse_command(original_input: &str) -> IResult<&str, Option<Box<dyn Comma
|
|||
let (input, command) = match command(input) {
|
||||
// Strip the exclamation mark
|
||||
Ok((input, (_, result))) => (input, result),
|
||||
Err(_e) => return Ok((original_input, None)),
|
||||
Err(_e) => {
|
||||
return Ok((original_input, None));
|
||||
}
|
||||
};
|
||||
|
||||
match command {
|
||||
"r" | "roll" => parse_roll(input).map(|(input, command)| (input, Some(command))),
|
||||
"rp" | "pool" => parse_pool_roll(input).map(|(input, command)| (input, Some(command))),
|
||||
"chance" => chance_die().map(|(input, command)| (input, Some(command))),
|
||||
"help" => help(input).map(|(input, command)| (input, Some(command))),
|
||||
// No recognized command, ignore this.
|
||||
_ => Ok((original_input, None)),
|
||||
}
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
use indoc::indoc;
|
||||
|
||||
pub fn parse_help_topic(input: &str) -> Option<HelpTopic> {
|
||||
match input {
|
||||
"cofd" => Some(HelpTopic::ChroniclesOfDarkness),
|
||||
"dicepool" => Some(HelpTopic::DicePool),
|
||||
"dice" => Some(HelpTopic::RollingDice),
|
||||
"" => Some(HelpTopic::General),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub enum HelpTopic {
|
||||
ChroniclesOfDarkness,
|
||||
DicePool,
|
||||
RollingDice,
|
||||
General,
|
||||
}
|
||||
|
||||
const COFD_HELP: &'static str = indoc! {"
|
||||
Chronicles of Darkness
|
||||
|
||||
Commands available:
|
||||
!pool, !rp: roll a dice pool
|
||||
!chance: roll a chance die
|
||||
|
||||
See also:
|
||||
!help dicepool
|
||||
"};
|
||||
|
||||
const DICE_HELP: &'static str = indoc! {"
|
||||
Rolling basic dice
|
||||
|
||||
Command: !roll, !r
|
||||
|
||||
Syntax !roll <dice-expression>
|
||||
|
||||
Dice expression can be a basic die (e.g. 1d4), with a bonus (1d4+3),
|
||||
or a more complex series of dice rolls or arbitrary numbers.
|
||||
Parentheses are not supported.
|
||||
|
||||
Examples:
|
||||
!roll 1d4
|
||||
!roll 1d4+5
|
||||
!roll 2d6+8
|
||||
!roll 2d8 + 4d6 - 3
|
||||
"};
|
||||
|
||||
const DICEPOOL_HELP: &'static str = indoc! {"
|
||||
Rolling dice pools
|
||||
|
||||
Command: !pool, !rp
|
||||
|
||||
Syntax: !pool <num><modifiers>
|
||||
|
||||
Modifiers:
|
||||
n = nine-again
|
||||
e = eight-again
|
||||
r = rote quality
|
||||
x = do not re-roll 10s
|
||||
s<num> = number of successes for exceptional
|
||||
|
||||
Examples:
|
||||
!pool 8 (roll a regular pool of 8 dice)
|
||||
!pool 5n (roll dice pool of 5, nine-again)
|
||||
!pool 6rs3 (roll dice pool of 6, rote quality, 3 successes for exceptional)
|
||||
"};
|
||||
|
||||
const GENERAL_HELP: &'static str = indoc! {"
|
||||
General Help
|
||||
|
||||
Try these help commands:
|
||||
!help cofd
|
||||
!help dice
|
||||
"};
|
||||
|
||||
impl HelpTopic {
|
||||
pub fn message(&self) -> &str {
|
||||
match self {
|
||||
HelpTopic::ChroniclesOfDarkness => COFD_HELP,
|
||||
HelpTopic::DicePool => DICEPOOL_HELP,
|
||||
HelpTopic::RollingDice => DICE_HELP,
|
||||
HelpTopic::General => GENERAL_HELP,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,5 +2,6 @@ pub mod bot;
|
|||
pub mod cofd;
|
||||
pub mod commands;
|
||||
pub mod dice;
|
||||
mod help;
|
||||
mod parser;
|
||||
pub mod roll;
|
||||
|
|
Loading…
Reference in New Issue