Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support arg+ syntax for argument capture (w/ fixes) #116

Merged
merged 2 commits into from
Nov 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions core/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,34 @@ func isValidHitChatRule(message *models.Message, rule models.Rule, processedInpu
args := utils.RuleArgTokenizer(processedInput)
var optionalArgs int
var requiredArgs int
var varArgs int
// take note of all optional args that end with a '?'
for _, arg := range rule.Args {
if strings.HasSuffix(arg, "?") {
optionalArgs++
}
if strings.HasSuffix(arg, "+") {
varArgs++
}
}
if varArgs > 1 {
// error, can ony have 1
msg := fmt.Sprintf("You cannot specify more than 1 variable argument")
message.Output = msg
return false
}
if len(rule.Args) > 0 && strings.HasSuffix(rule.Args[len(rule.Args)-1], "+") {
if optionalArgs > 0 {
// error, cannot combine optional and varargs
msg := fmt.Sprintf("You cannot combine optional arguments with variable arguments")
message.Output = msg
return false
}
} else if varArgs == 1 {
// error, vararg but not in last position
msg := fmt.Sprintf("You must specify the variable argument in the last argument position")
message.Output = msg
return false
}
// ensure we only require args that don't end with '?'
requiredArgs = len(rule.Args) - optionalArgs
Expand All @@ -190,6 +213,11 @@ func isValidHitChatRule(message *models.Message, rule models.Rule, processedInpu
}
// Go through the supplied args and make them available as variables
for index, arg := range rule.Args {
// If this is a varag method, then join all the remaining args and end
if strings.HasSuffix(arg, "+") {
message.Vars[strings.TrimSuffix(arg, "+")] = strings.Join(args[index:], " ")
break
}
// strip '?' from end of arg
arg = strings.TrimSuffix(arg, "?")
// index starts at 0 so we need to account for that
Expand Down
40 changes: 30 additions & 10 deletions core/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,13 @@ func Test_handleChatServiceRule(t *testing.T) {
HelpText: "foo <arg1> <arg2>",
}

ruleVarg := models.Rule{
Name: "Test Rules with varargs",
Respond: "foo",
Args: []string{"arg1", "argv+"},
HelpText: "foo <arg1> <argv...>",
}

ruleHearWithArgs := models.Rule{
Name: "Hear rule with Args set",
Hear: "/hi/",
Expand Down Expand Up @@ -720,6 +727,12 @@ func Test_handleChatServiceRule(t *testing.T) {
BotMentioned: true,
}

testMessageVargs := models.Message{
Input: "foo arg1 arg2 arg3 arg4",
Vars: map[string]string{},
BotMentioned: true,
}

testMessageIgnoreThread := models.Message{
Input: "we have a thread",
Vars: map[string]string{},
Expand All @@ -733,17 +746,19 @@ func Test_handleChatServiceRule(t *testing.T) {
want bool
want1 bool
expectMsg string
expectedVars map[string]string
}{
{"basic", args{}, false, false, ""},
{"respond + hear", args{rule: models.Rule{Respond: "hi", Hear: "/hi/"}, hit: false, bot: testBot, message: testMessage}, false, false, ""},
{"hear + rule args", args{rule: ruleHearWithArgs, hit: false, bot: testBot, message: testMessage}, false, false, ""},
{"respond rule - hit false", args{rule: rule, hit: false}, false, false, ""},
{"respond rule - hit true - valid", args{rule: rule, hit: true, bot: testBot, message: testMessage, processedInput: "arg1 arg2"}, true, true, "hmm, the 'format_output' field in your configuration is empty"},
{"respond rule - hit true - bot not mentioned", args{rule: rule, hit: true, bot: testBot, message: testMessageBotNotMentioned, processedInput: "arg1 arg2"}, false, false, ""},
{"respond rule - hit true - valid - not enough args", args{rule: rule, hit: true, bot: testBot, message: testMessageNotEnoughArgs, processedInput: "arg1"}, true, true, "You might be missing an argument or two. This is what I'm looking for\n```foo <arg1> <arg2>```"},
{"respond rule - hit true - valid optional arg", args{rule: ruleOpt, hit: true, bot: testBot, message: testMessageOptionalArgs, processedInput: "arg1"}, true, true, ""},
{"respond rule - hit true - invalid", args{rule: rule, hit: true, bot: testBot, message: testMessage}, true, true, "You might be missing an argument or two. This is what I'm looking for\n```foo <arg1> <arg2>```"},
{"hear rule - ignore thread", args{rule: ruleIgnoreThread, hit: true, bot: testBot, message: testMessageIgnoreThread}, true, true, ""},
{"basic", args{}, false, false, "", map[string]string{}},
{"respond + hear", args{rule: models.Rule{Respond: "hi", Hear: "/hi/"}, hit: false, bot: testBot, message: testMessage}, false, false, "", map[string]string{}},
{"hear + rule args", args{rule: ruleHearWithArgs, hit: false, bot: testBot, message: testMessage}, false, false, "", map[string]string{}},
{"respond rule - hit false", args{rule: rule, hit: false}, false, false, "", map[string]string{}},
{"respond rule - hit true - valid", args{rule: rule, hit: true, bot: testBot, message: testMessage, processedInput: "arg1 arg2"}, true, true, "hmm, the 'format_output' field in your configuration is empty", map[string]string{"arg1": "arg1", "arg2": "arg2"}},
{"respond rule - hit true - bot not mentioned", args{rule: rule, hit: true, bot: testBot, message: testMessageBotNotMentioned, processedInput: "arg1 arg2"}, false, false, "", map[string]string{}},
{"respond rule - hit true - valid - not enough args", args{rule: rule, hit: true, bot: testBot, message: testMessageNotEnoughArgs, processedInput: "arg1"}, true, true, "You might be missing an argument or two. This is what I'm looking for\n```foo <arg1> <arg2>```", map[string]string{}},
{"respond rule - hit true - valid optional arg", args{rule: ruleOpt, hit: true, bot: testBot, message: testMessageOptionalArgs, processedInput: "arg1"}, true, true, "", map[string]string{"arg1": "arg1"}},
{"respond rule - hit true - valid vargs", args{rule: ruleVarg, hit: true, bot: testBot, message: testMessageVargs, processedInput: "arg1 arg2 arg3 arg4"}, true, true, "", map[string]string{"arg1": "arg1", "argv": "arg2 arg3 arg4"}},
{"respond rule - hit true - invalid", args{rule: rule, hit: true, bot: testBot, message: testMessage}, true, true, "You might be missing an argument or two. This is what I'm looking for\n```foo <arg1> <arg2>```", map[string]string{}},
{"hear rule - ignore thread", args{rule: ruleIgnoreThread, hit: true, bot: testBot, message: testMessageIgnoreThread}, true, true, "", map[string]string{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -772,6 +787,11 @@ func Test_handleChatServiceRule(t *testing.T) {
if got1 != tt.want1 {
t.Errorf("handleChatServiceRule() got1 = %v, want %v", got1, tt.want1)
}
for argk, argv := range tt.expectedVars {
if tt.args.message.Vars[argk] != argv {
t.Errorf("handleChatServiceRules() did not extract argument %v. got = %v, want %v", argk, tt.args.message.Vars[argk], argv)
}
}
}
})
}
Expand Down