Correct output bug and remove extraneous files
This commit is contained in:
parent
a8d4fdf24b
commit
9f45d5b5d3
183
SA_DiceBot.h
183
SA_DiceBot.h
@ -1,183 +0,0 @@
|
||||
//
|
||||
// SA_DiceBot.h
|
||||
//
|
||||
// Copyright (c) 2016 Said Achmiz.
|
||||
//
|
||||
// This software is licensed under the MIT license.
|
||||
// See the file "LICENSE" for more information.
|
||||
/*
|
||||
The SA_DiceBot class hierarchy works like this:
|
||||
|
||||
SA_DiceBot is the root class. A user of the SA_DiceBot package usually only
|
||||
needs to create and manage objects of this class. An SA_DiceBot creates and
|
||||
manages all other objects and classes in the package (directly or through its
|
||||
other members), as needed.
|
||||
|
||||
An SA_DiceBot owns a single SA_DiceRoller and a single SA_DiceFormatter.
|
||||
|
||||
The objects owned by an SA_DiceBot are for the SA_DiceBot's internal use
|
||||
only, and not accessible to users of the package. (But see SA_DiceRoller.h
|
||||
and SA_DiceFormatter.h for information on standalone use.)
|
||||
|
||||
An SA_DiceRoller owns an SA_DiceParser and an SA_DiceEvaluator. An
|
||||
SA_DiceEvaluator owns an SA_DiceBag. (See Figure 1, below.)
|
||||
|
||||
*--------------------------------------------------------------------------*
|
||||
| Fig. 1. SA_DiceBot class hierarchy diagram. |
|
||||
| |
|
||||
| |
|
||||
| SA_DiceBot |
|
||||
| / \ |
|
||||
| SA_DiceFormatter SA_DiceRoller |
|
||||
| / \ |
|
||||
| SA_DiceParser SA_DiceEvaluator |
|
||||
| | |
|
||||
| SA_DiceBag |
|
||||
| |
|
||||
*--------------------------------------------------------------------------*
|
||||
|
||||
When the SA_DiceBot receives a recognized command that involves rolling some
|
||||
dice or otherwise evaluating some sort of string of semantically meaningful
|
||||
text (like just adding some numbers), it extracts that string (which may
|
||||
optionally include some text label) from the rest of the message body, and
|
||||
passes it to the SA_DiceRoller.
|
||||
|
||||
The SA_DiceRoller strips off the label (if any), and passes the 'pure' string
|
||||
to the SA_DiceParser.
|
||||
|
||||
The SA_DiceParser parses the string and produces an 'expression tree'
|
||||
representation of the string. An expression tree is an NSDictionary with a
|
||||
certain structure (which is described in SA_DiceExpressionStringConstants.h).
|
||||
|
||||
The SA_DiceRoller then passes the expression tree to the SA_DiceEvaluator.
|
||||
The evaluator recursively traverses the expression tree in a depth-first
|
||||
manner, computing the results of each subtree, and storing those results in
|
||||
(a mutable copy of) the expression tree itself. Once the entire tree has been
|
||||
evaluated, (a mutable copy of) it is returned to the SA_DiceRoller. The
|
||||
SA_DiceRoller reattaches the stripped-off label (inserting it into the top
|
||||
level of the expresion tree) and returns the evaluated expression tree
|
||||
(now called a result) to the SA_DiceBot.
|
||||
|
||||
The SA_DiceBot passes the evaluated result tree to its SA_DiceFormatter.
|
||||
The formatter traverses the tree, constructing a human-readable string form of
|
||||
the results, with whatever formatting it (the formatter) has been configured
|
||||
to provide. The formatter then returns this formatted result string to the
|
||||
SA_DiceBot.
|
||||
|
||||
The SA_DiceBot then incorporates the formatted result string into some
|
||||
appropriate reply message or messages, and sends the reply(ies) back to its
|
||||
delegate, for transmission to the appropriate endpoint.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SA_Bot.h"
|
||||
|
||||
@class SA_CommandResponder;
|
||||
|
||||
/*
|
||||
######################################
|
||||
#### SA_DiceBot Usage Information ####
|
||||
######################################
|
||||
|
||||
I. SUPPORTED COMMANDS & PARSER MODES
|
||||
|
||||
The set of commands, and the syntax and behavior of those commands, that an
|
||||
SA_DiceBot supports depends on its currently set parser behavior mode (and, in
|
||||
some cases, also on the currently set formatter behavior mode). (Read more
|
||||
about parser behavior modes in SA_DiceParser.h, and about formatter behavior
|
||||
modes in SA_DiceFormatter.h.)
|
||||
|
||||
Below is a list of available parser modes, along with the commands supported in
|
||||
each mode. (See section II for a list of commands that are supported in all
|
||||
parser modes.)
|
||||
|
||||
NOTE: Commands are not case-sensitive; e.g., 'roll', 'ROLL', and 'rOLl' all
|
||||
work equally well.
|
||||
|
||||
1. DEFAULT mode
|
||||
|
||||
"Default" mode is an alias for whatever default behavior is currently set for
|
||||
new SA_DiceParser instances. (The "default default" behavior for the current
|
||||
implementation is "legacy".)
|
||||
|
||||
2. LEGACY mode
|
||||
|
||||
"Legacy" mode (mostly) emulates DiceBot by Sabin (and Dawn by xthemage before
|
||||
it). The following commands are available in legacy mode.
|
||||
|
||||
1. ROLL command.
|
||||
|
||||
Takes 1 or more whitespace-delimited roll strings as parameters. The roll
|
||||
command is executed once for every parameter, and each execution generates
|
||||
a separate result string. One reply message is sent for each result string
|
||||
(or, if the simple formatter is being used, the results may optionally be
|
||||
collapsed into a single reply message).
|
||||
|
||||
Each roll string may optionally be suffixed with with a (configurable)
|
||||
delimiter character (such as ';'), which may be followed with an arbitrary
|
||||
text label (which may not contain whitespace, however). That label may then
|
||||
be included in the result string (depending on the selected formatter
|
||||
behavior mode and other formatter settings).
|
||||
|
||||
The body of the roll string (up to the label delimiter, if any) is simply
|
||||
parsed and evaluated.
|
||||
|
||||
EXAMPLES (assuming legacy formatter behavior):
|
||||
|
||||
Obormot: !roll 1d20
|
||||
SA_DiceBot: 1d20 < 14 = 14 > = 14
|
||||
Obormot: .roll 2+4-5
|
||||
SA_DiceBot: 2 + 4 - 5 = 1
|
||||
Obormot: /roll 2d4 1d20+19 4d10
|
||||
SA_DiceBot: 2d4 < 3 1 = 4 > = 4
|
||||
SA_DiceBot: 1d20 < 5 = 5 > + 19 = 24
|
||||
SA_DiceBot: 4d10 < 2 2 1 2 = 7 > = 7
|
||||
Obormot: !roll 1d20+4;fort_save
|
||||
SA_DiceBot: (fort_save) 1d20 < 8 = 8 > + 4 = 12
|
||||
Obormot: SA_DiceBot: 1d8+6;longsword 1d6+3;shortsword
|
||||
SA_DiceBot: (longsword) 1d8 < 4 = 4 > + 6 = 10
|
||||
SA_DiceBot: (shortsword) 1d6 < 2 = 2 > + 3 = 5
|
||||
|
||||
2. TRY command.
|
||||
|
||||
Takes 1 or more whitespace-delimited roll strings. Prepends "1d20+" to each
|
||||
roll string, and otherwise behaves in the same way as the ROLL command.
|
||||
|
||||
3. CHAR command.
|
||||
|
||||
II. OTHER COMMANDS
|
||||
|
||||
III. CONFIGURATION
|
||||
*/
|
||||
/****************************************/
|
||||
#pragma mark SA_DiceBot class declaration
|
||||
/****************************************/
|
||||
|
||||
@interface SA_DiceBot : SA_Bot
|
||||
|
||||
/************************/
|
||||
#pragma mark - Properties
|
||||
/************************/
|
||||
|
||||
@property (strong) SA_CommandResponder *botCommandresponder;
|
||||
@property (strong) SA_CommandResponder *legacyCommandResponder;
|
||||
|
||||
@property (strong) SA_CommandResponder *currentCommandResponder;
|
||||
|
||||
@property (copy) NSString *commandDesignatorCharacters;
|
||||
|
||||
/****************************/
|
||||
#pragma mark - Public methods
|
||||
/****************************/
|
||||
|
||||
- (void)message:(NSString *)messageBody withInfo:(NSDictionary *)messageInfo;
|
||||
|
||||
/****************************/
|
||||
#pragma mark - Helper methods
|
||||
/****************************/
|
||||
|
||||
- (NSArray <NSDictionary *> *)repliesForCommandString:(NSString *)commandString messageInfo:(NSDictionary *)messageInfo byName:(BOOL)byName;
|
||||
|
||||
- (void)loadDefaultCommandResponders;
|
||||
|
||||
@end
|
||||
144
SA_DiceBot.m
144
SA_DiceBot.m
@ -1,144 +0,0 @@
|
||||
//
|
||||
// SA_DiceBot.m
|
||||
//
|
||||
// Copyright (c) 2016 Said Achmiz.
|
||||
//
|
||||
// This software is licensed under the MIT license.
|
||||
// See the file "LICENSE" for more information.
|
||||
|
||||
#import "SA_DiceBot.h"
|
||||
|
||||
#import "SA_BotDelegate.h"
|
||||
#import "SA_CommandResponder.h"
|
||||
#import "SA_BotCommandResponder.h"
|
||||
#import "SA_LegacyCommandResponder.h"
|
||||
#import "SA_ErrorCatalog.h"
|
||||
#import "NSString+SA_NSStringExtensions.h"
|
||||
#import "NSRange-Conventional.h"
|
||||
|
||||
/*********************************************/
|
||||
#pragma mark - SA_DiceBot class implementation
|
||||
/*********************************************/
|
||||
|
||||
@implementation SA_DiceBot
|
||||
|
||||
/**************************/
|
||||
#pragma mark - Initializers
|
||||
/**************************/
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithName:@"DIE_BOT"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
{
|
||||
if(self = [super initWithName:name])
|
||||
{
|
||||
NSLog(NSLocalizedString(@"Initializing SA_DiceBot with name \"%@\"", @"{display name of the bot}"), name);
|
||||
|
||||
self.commandDesignatorCharacters = @".!/";
|
||||
|
||||
[self loadDefaultCommandResponders];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/****************************/
|
||||
#pragma mark - Public methods
|
||||
/****************************/
|
||||
|
||||
- (void)message:(NSString *)messageBody withInfo:(NSDictionary *)messageInfo
|
||||
{
|
||||
if([messageBody isEqualToString:@""])
|
||||
{
|
||||
// Ignore empty messages.
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRange commandRange;
|
||||
BOOL byName;
|
||||
|
||||
// Is the message a possible command? That is, does it start with any of
|
||||
// the permitted initial characters that designate a command?
|
||||
NSString *firstChar = [messageBody substringToIndex:1];
|
||||
if([self.commandDesignatorCharacters containsCharactersInString:firstChar])
|
||||
{
|
||||
commandRange = NSMakeRange(1, messageBody.length - 1);
|
||||
byName = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We also recognize commands that come after mentions of the bot's
|
||||
// name at the beginning of the message.
|
||||
NSRange possibleNameRange = NSRangeMake(0, self.name.length);
|
||||
if(messageBody.length > self.name.length &&
|
||||
[[messageBody substringWithRange:possibleNameRange] isEqualToString:self.name])
|
||||
{
|
||||
commandRange = [messageBody rangeToEndFrom:[messageBody firstNonWhitespaceAfterRange:[messageBody firstWhitespaceAfterRange:possibleNameRange]]];
|
||||
byName = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Does not begin with a command. Ignore.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the part of the string that is the actual command.
|
||||
NSString *commandString = [messageBody substringWithRange:commandRange];
|
||||
|
||||
// Get the replies for this command.
|
||||
NSArray <NSDictionary *> *replies = [self repliesForCommandString:commandString messageInfo:messageInfo byName:byName];
|
||||
|
||||
// Send the replies.
|
||||
[replies enumerateObjectsUsingBlock:^(NSDictionary *reply, NSUInteger idx, BOOL *stop) {
|
||||
[self.delegate SA_botMessage:reply[SA_DB_MESSAGE_BODY]
|
||||
from:self
|
||||
withInfo:reply[SA_DB_MESSAGE_INFO]];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/****************************/
|
||||
#pragma mark - Helper methods
|
||||
/****************************/
|
||||
|
||||
- (NSArray <NSDictionary *> *)repliesForCommandString:(NSString *)commandString messageInfo:(NSDictionary *)messageInfo byName:(BOOL)byName
|
||||
{
|
||||
NSError *error;
|
||||
NSArray <NSDictionary *> *replies = [self.botCommandresponder repliesForCommandString:commandString messageInfo:messageInfo error:&error];
|
||||
|
||||
if(error && error.code == SA_DiceBotErrorUnknownCommand)
|
||||
{
|
||||
error = nil;
|
||||
replies = [self.currentCommandResponder repliesForCommandString:commandString messageInfo:messageInfo error:&error];
|
||||
}
|
||||
|
||||
if(error)
|
||||
{
|
||||
// Is outputting the provided error the right way to do error handling
|
||||
// here? I don't know. Maybe not. For now, that's what it is.
|
||||
NSString *errorReply = [NSString stringWithFormat:NSLocalizedString(@"ERROR: %@ (%@ %@)", @"{description}, {failure reason}, {recovery suggestion}"),
|
||||
error.localizedDescription,
|
||||
error.localizedFailureReason,
|
||||
error.localizedRecoverySuggestion];
|
||||
|
||||
replies = [replies arrayByAddingObject:@{ SA_DB_MESSAGE_BODY : errorReply,
|
||||
SA_DB_MESSAGE_INFO : messageInfo }];
|
||||
}
|
||||
|
||||
return replies;
|
||||
}
|
||||
|
||||
- (void)loadDefaultCommandResponders
|
||||
{
|
||||
self.legacyCommandResponder = [SA_LegacyCommandResponder new];
|
||||
self.botCommandresponder = [SA_BotCommandResponder new];
|
||||
|
||||
// The default command responder, in the current implementation, is the
|
||||
// legacy command responder.
|
||||
self.currentCommandResponder = self.legacyCommandResponder;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -173,7 +173,7 @@ static NSDictionary *_stringFormatRules;
|
||||
{
|
||||
[formattedString appendFormat:@" = %@", expression[SA_DB_RESULT]];
|
||||
}
|
||||
else if(_legacyModeErrorReportingEnabled == YES && [getErrorsForExpression(expression) count] > 1)
|
||||
else if(_legacyModeErrorReportingEnabled == YES && [getErrorsForExpression(expression) count] > 0)
|
||||
{
|
||||
if([getErrorsForExpression(expression) count] == 1)
|
||||
{
|
||||
@ -187,7 +187,7 @@ static NSDictionary *_stringFormatRules;
|
||||
[formattedString appendString:[SA_DiceFormatter descriptionForError:error]];
|
||||
if(idx != [getErrorsForExpression(expression) count] - 1)
|
||||
{
|
||||
[formattedString appendFormat:@", "];
|
||||
[formattedString appendFormat:@" / "];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user