A set of classes for parsing, evaluating, and formatting die roll strings.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //
  2. // SA_DiceBot.m
  3. // RPGBot
  4. //
  5. // Created by Sandy Achmiz on 12/30/15.
  6. //
  7. //
  8. #import "SA_DiceBot.h"
  9. #import "SA_BotDelegate.h"
  10. #import "SA_CommandResponder.h"
  11. #import "SA_BotCommandResponder.h"
  12. #import "SA_LegacyCommandResponder.h"
  13. #import "SA_ErrorCatalog.h"
  14. #import "NSString+SA_NSStringExtensions.h"
  15. #import "NSRange-Conventional.h"
  16. /*********************************************/
  17. #pragma mark - SA_DiceBot class implementation
  18. /*********************************************/
  19. @implementation SA_DiceBot
  20. /**************************/
  21. #pragma mark - Initializers
  22. /**************************/
  23. - (instancetype)init
  24. {
  25. return [self initWithName:@"DIE_BOT"];
  26. }
  27. - (instancetype)initWithName:(NSString *)name
  28. {
  29. if(self = [super initWithName:name])
  30. {
  31. NSLog(NSLocalizedString(@"Initializing SA_DiceBot with name \"%@\"", @"{display name of the bot}"), name);
  32. self.commandDesignatorCharacters = @".!/";
  33. [self loadDefaultCommandResponders];
  34. }
  35. return self;
  36. }
  37. /****************************/
  38. #pragma mark - Public methods
  39. /****************************/
  40. - (void)message:(NSString *)messageBody withInfo:(NSDictionary *)messageInfo
  41. {
  42. if([messageBody isEqualToString:@""])
  43. {
  44. // Ignore empty messages.
  45. }
  46. else
  47. {
  48. NSRange commandRange;
  49. BOOL byName;
  50. // Is the message a possible command? That is, does it start with any of
  51. // the permitted initial characters that designate a command?
  52. NSString *firstChar = [messageBody substringToIndex:1];
  53. if([self.commandDesignatorCharacters containsCharactersInString:firstChar])
  54. {
  55. commandRange = NSMakeRange(1, messageBody.length - 1);
  56. byName = NO;
  57. }
  58. else
  59. {
  60. // We also recognize commands that come after mentions of the bot's
  61. // name at the beginning of the message.
  62. NSRange possibleNameRange = NSRangeMake(0, self.name.length);
  63. if(messageBody.length > self.name.length &&
  64. [[messageBody substringWithRange:possibleNameRange] isEqualToString:self.name])
  65. {
  66. commandRange = [messageBody rangeToEndFrom:[messageBody firstNonWhitespaceAfterRange:[messageBody firstWhitespaceAfterRange:possibleNameRange]]];
  67. byName = YES;
  68. }
  69. else
  70. {
  71. // Does not begin with a command. Ignore.
  72. return;
  73. }
  74. }
  75. // Extract the part of the string that is the actual command.
  76. NSString *commandString = [messageBody substringWithRange:commandRange];
  77. // Get the replies for this command.
  78. NSArray <NSDictionary *> *replies = [self repliesForCommandString:commandString messageInfo:messageInfo byName:byName];
  79. // Send the replies.
  80. [replies enumerateObjectsUsingBlock:^(NSDictionary *reply, NSUInteger idx, BOOL *stop) {
  81. [self.delegate SA_botMessage:reply[SA_DB_MESSAGE_BODY]
  82. from:self
  83. withInfo:reply[SA_DB_MESSAGE_INFO]];
  84. }];
  85. }
  86. }
  87. /****************************/
  88. #pragma mark - Helper methods
  89. /****************************/
  90. - (NSArray <NSDictionary *> *)repliesForCommandString:(NSString *)commandString messageInfo:(NSDictionary *)messageInfo byName:(BOOL)byName
  91. {
  92. NSError *error;
  93. NSArray <NSDictionary *> *replies = [self.botCommandresponder repliesForCommandString:commandString messageInfo:messageInfo error:&error];
  94. if(error && error.code == SA_DiceBotErrorUnknownCommand)
  95. {
  96. error = nil;
  97. replies = [self.currentCommandResponder repliesForCommandString:commandString messageInfo:messageInfo error:&error];
  98. }
  99. if(error)
  100. {
  101. // Is outputting the provided error the right way to do error handling
  102. // here? I don't know. Maybe not. For now, that's what it is.
  103. NSString *errorReply = [NSString stringWithFormat:NSLocalizedString(@"ERROR: %@ (%@ %@)", @"{description}, {failure reason}, {recovery suggestion}"),
  104. error.localizedDescription,
  105. error.localizedFailureReason,
  106. error.localizedRecoverySuggestion];
  107. replies = [replies arrayByAddingObject:@{ SA_DB_MESSAGE_BODY : errorReply,
  108. SA_DB_MESSAGE_INFO : messageInfo }];
  109. }
  110. return replies;
  111. }
  112. - (void)loadDefaultCommandResponders
  113. {
  114. self.legacyCommandResponder = [SA_LegacyCommandResponder new];
  115. self.botCommandresponder = [SA_BotCommandResponder new];
  116. // The default command responder, in the current implementation, is the
  117. // legacy command responder.
  118. self.currentCommandResponder = self.legacyCommandResponder;
  119. }
  120. @end