rivescript-js
rivescript-js copied to clipboard
Pass a JSON string/object as args to a sub routine
Is there any way to pass in a JSON string as an argument to a subroutine like this:
> + Do Something
> - <call>doSomething {action:"print", path:"/one/two"}</call>
>
I tried a few ways to parse the args as a JSON object and pass it onto another function but I have been unable to get a JSON object that mirrors the JSON string as passed to the subroutine from the rivescript. Is there another approach I can take to accomplish the same?
I'd like to know this too. some way to handle extra metadata from scripts would be a great addition.
This isn't currently supported. You also can't really "try anyway" because rivescript-js was updated a while back to use shell-style arguments to object macros, where a "quoted string of words" is sent into the function as a single array item of the args
array, rather than the args
being a naive list of words separated by space characters. The shell-style parsing would break JSON objects pretty badly. :wink:
It might be a good idea for a new feature to support in the future though: if the contents of a <call>
tag begins with a curly bracket {
, it could treat the entire contents as a JSON object with a certain minimal schema (such as the JSON has to name the object macro it wants to be sent to), like
+ do something
- <call>{ "object": "doSomething", "action": "print", "path": "/one/two" }</call>
It would be backwards compatible because current object macro names aren't allowed to contain special symbols like the {
, so the processing logic could look for the opening {
and try to JSON decode the whole thing and go from there. It would probably be given to the object macro as a new argument named data
or something, where you could check data.action === "print"
etc., so that the existing args
argument wouldn't have to change its format and potentially break existing code.
In the mean time this workaround might give some limited success: https://play.rivescript.com/s/4ls3BcNRoo
Put the JSON into a user variable (should be fine as long as your JSON contains no closing angle bracket >
anywhere within), and parse it from the function.
@kirsle, I will be happy to help with the implementation of this new feature! We can further simplify the JSON schema where "object" key specifies the subroutine to be called and "params" contains an arbitrary key/value pairs as follows:
+ do something
- <call>{ "object": "doSomething", "params": {"action":"print", "path": "/one/two" } }</call>
Please let me know your thoughts on how I can contribute.
Regarding the workaround, I am not sure if it can handle concurrency since the uservar (tmp) could change between multiple invocation of the same trigger by the same user and could potentially run into one invocation getting the args of another invocation, right?
Yeah, go ahead and send a pull request for this. :smile:
The high level logic I imagine would go something like,
- Search for the
<call>(.+?)</call>
regexp and put the captured text into atext
variable. - See if
text
starts with a{
symbol- If so:
JSON.parse()
it (in a try/catch block, and give an error if the JSON parse fails) - Validate its format (pull out the macro name and parameters)
- Call the macro like,
.call(rs, functionName, null, jsonParams)
- If so:
- Otherwise, continue as normal:
- Separate the function name from its arguments
- Call the macro like,
.call(rs, functionName, args, null)
The macro function prototype should be updated to function foo(rs, args, params)
where the new params
argument is the JSON param object. The args
argument would be empty or null or something when the macro was called via JSON.
would it work to pass the whole thing as a quoted string?
- <call>'{ "object": "doSomething", "params": {"action":"print", "path": "/one/two" } }'</call>
I have been playing around and tried the following:
- Passed in a JSON style string using single quotes & parenthesis (see below for an example)
- The arg parser passed along the args string pretty much as is except one thing: it added an extra comma wherever it found a comma in the original string
- In my subroutine, I simply replaced single quotes with double quotes, '(' with '{', ')' with '}', and double commas with a single comma.
- Then I was able to parse the JSON string!
Here is one of the snippets I have tried.
! version = 2.0
+ json test * *
- <call>doSomething ('params':('props':('prop1':'val1', 'level2':('prop2':'<star1>', 'prop3':'<star2>'))))</call>
> object doSomething javascript
var args2 = args.join('');
args2 = args2.replace(/'/g, '"');
args2 = args2.replace(/\(/g, '{');
args2 = args2.replace(/\)/g, '}');
args2 = args2.replace(/,,/g, ',');
var json = JSON.parse(args2);
return JSON.stringify(json);
< object
This approach does not require a major change and continues to keep the <call>
tag sane and simple! What do you think?