-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #207 from matteoredaelli/master
json-server-async: a merge of json-reply and async-reply examples
- Loading branch information
Showing
4 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# replyAsync Example | ||
|
||
This example demonstrates using replyAsync function. You should use | ||
**replyAsync** instead of **reply** if you have subroutines that return | ||
promises. | ||
|
||
## Important Note About Conditionals | ||
|
||
Asynchronous object macros are *not* supported within the conditional side of | ||
`*Conditions` in RiveScript. That is, RiveScript code like the following will | ||
not work (where the `weather` macro calls out to an HTTP API and returns a | ||
promise with the result): | ||
|
||
```rivescript | ||
+ is it sunny outside | ||
* <call>weather <get zipcode></call> == sunny => It appears it is! | ||
- It doesn't look sunny outside. | ||
``` | ||
|
||
Conditionals in RiveScript require their results to be immediately available | ||
so it can check if the comparison is truthy. So, even if you use `replyAsync()`, | ||
the `<call>` tags in conditionals only support running synchronous object | ||
macros (ones that return a string result and not a promise). | ||
|
||
However, asynchronous object macros can be used in the *reply* portion of the | ||
conditional (the part on the right side of the `=>` separator). This is the | ||
text eventually returned to the user and it can return as a promise when you use | ||
`replyAsync()` just like if it were in a `-Reply` command. | ||
|
||
## Running the example | ||
|
||
To fully experience this example, you'll need to get an API key from | ||
[OpenWeatherMap](http://openweathermap.org/appid) (don't worry, they're free!), | ||
and edit `weatherman.js` to fill in your `APPID` near the top of the file. | ||
|
||
```bash | ||
npm install && node weatherman.js | ||
``` | ||
|
||
Refer to weatherman.rive for the list of supported commands | ||
|
||
## Using async subroutines | ||
|
||
Whenever you have a subroutine that needs to call some sort of asynchronous | ||
function in order to return a value back to the script, you should use promises: | ||
|
||
```javascript | ||
var rs = new RiveScript(); | ||
rs.setSubroutine("asyncHelper", function(rs, args) { | ||
// RiveScript comes bundled with RSVP.js which you can | ||
// access through RiveScript.Promise alias | ||
// but you are free to use your own Promise implementation | ||
return new rs.Promise(function(resolve, reject) { | ||
resolve('hello there'); | ||
}); | ||
}) | ||
``` | ||
|
||
Async responses in RiveScript come in 2 flavors: | ||
|
||
```javascript | ||
// using promises | ||
rs.replyAsync(username, message).then(function(reply) { | ||
// good to go | ||
}).catch(function(error){ | ||
// something went wrong | ||
}); | ||
|
||
// or using callbacks | ||
rs.replyAsync(username, message, this, function(error, reply) { | ||
if (!error) { | ||
// you can use reply here | ||
} else { | ||
// something went wrong, error has more info | ||
} | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
// Asynchronous Objects Example | ||
// See the accompanying README.md for details. | ||
|
||
// Run this demo: `node weatherman.js` | ||
|
||
var readline = require("readline"); | ||
var request = require("request"); | ||
var colors = require('colors'); | ||
|
||
// Configuration: get an API key from http://openweathermap.org/appid and | ||
// put it in this variable. | ||
const APPID = 'CHAMGEME'; | ||
|
||
// This would just be require("rivescript") if not for running this | ||
// example from within the RiveScript project. | ||
|
||
var express = require("express"), | ||
cookieParser = require('cookie-parser') | ||
bodyParser = require("body-parser"), | ||
RiveScript = require("../../lib/rivescript.js"); | ||
|
||
var rs = new RiveScript({utf8: true}); | ||
|
||
var mytest = function(location, callback) { | ||
callback.call(this, null, location + " parsed"); | ||
}; | ||
|
||
|
||
rs.setSubroutine("mytest", function (rs, args) { | ||
return new rs.Promise(function(resolve, reject) { | ||
mytest(args.join(' '), function(error, data){ | ||
if(error) { | ||
reject(error); | ||
} else { | ||
resolve(data); | ||
} | ||
}); | ||
}); | ||
}) | ||
|
||
var getWeather = function(location, callback) { | ||
request.get({ | ||
url: "http://api.openweathermap.org/data/2.5/weather", | ||
qs: { | ||
q: location, | ||
APPID: APPID | ||
}, | ||
json: true | ||
}, function(error, response) { | ||
if (response.statusCode !== 200) { | ||
callback.call(this, response.body); | ||
} else { | ||
callback.call(this, null, response.body); | ||
} | ||
}); | ||
}; | ||
|
||
|
||
rs.setSubroutine("getWeather", function (rs, args) { | ||
return new rs.Promise(function(resolve, reject) { | ||
getWeather(args.join(' '), function(error, data){ | ||
if(error) { | ||
reject(error); | ||
} else { | ||
resolve(data.weather[0].description); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
rs.setSubroutine("checkForRain", function(rs, args) { | ||
return new rs.Promise(function(resolve, reject) { | ||
getWeather(args.join(' '), function(error, data){ | ||
if(error) { | ||
console.error(''); | ||
reject(error); | ||
} else { | ||
var rainStatus = data.rain ? 'yup :(' : 'nope'; | ||
resolve(rainStatus); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
// Create a prototypical class for our own chatbot. | ||
var AsyncBot = function(onReady) { | ||
var self = this; | ||
|
||
if (APPID === 'change me') { | ||
console.log('Error -- edit weatherman.js and provide the APPID for Open Weathermap.'.bold.yellow); | ||
} | ||
|
||
// Load the replies and process them. | ||
//rs.loadDirectory("../brain", function() { | ||
// rs.sortReplies(); | ||
// onReady(); | ||
//}); | ||
|
||
|
||
// Load the replies and process them. | ||
rs.loadFile("weatherman.rive", function() { | ||
rs.sortReplies(); | ||
onReady(); | ||
}); | ||
|
||
// This is a function for delivering the message to a user. Its actual | ||
// implementation could vary; for example if you were writing an IRC chatbot | ||
// this message could deliver a private message to a target username. | ||
self.sendMessage = function(username, message) { | ||
// This just logs it to the console like "[Bot] @username: message" | ||
console.log( | ||
["[Brick Tamland]", message].join(": ").underline.green | ||
); | ||
}; | ||
|
||
// This is a function for a user requesting a reply. It just proxies through | ||
// to RiveScript. | ||
self.getReply = function(username, message, callback) { | ||
return rs.replyAsync(username, message, self).then(function(reply){ | ||
callback.call(this, null, reply); | ||
}).catch(function(error) { | ||
callback.call(this, error); | ||
}); | ||
} | ||
}; | ||
|
||
// Create and run the example bot. | ||
var bot = new AsyncBot(function() { | ||
|
||
// Set up the Express app. | ||
var app = express(); | ||
|
||
app.use(cookieParser()); | ||
|
||
// Parse application/json inputs. | ||
app.use(bodyParser.json()); | ||
app.set("json spaces", 4); | ||
|
||
// Set up routes. | ||
app.post("/reply", getReply); | ||
app.get("/", showUsage); | ||
app.get("*", showUsage); | ||
|
||
// Start listening. | ||
app.listen(2001, function() { | ||
console.log("Listening on http://localhost:2001"); | ||
}); | ||
}); | ||
|
||
// POST to /reply to get a RiveScript reply. | ||
function getReply(req, res) { | ||
// Get data from the JSON post. | ||
var message = req.body.message; | ||
var vars = req.body.vars; | ||
var username; | ||
|
||
if (req.cookies.username) { | ||
username = req.cookies.username; | ||
} else { | ||
username = req.connection.remoteAddress; | ||
console.log(username); | ||
res.cookie('username', username, { maxAge: 100000 * 60 }); | ||
} | ||
|
||
// Make sure username and message are included. | ||
if (typeof(message) === "undefined") { | ||
return error(res, "message is a required key"); | ||
} | ||
|
||
// Copy any user vars from the post into RiveScript. | ||
if (typeof(vars) !== "undefined") { | ||
for (var key in vars) { | ||
if (vars.hasOwnProperty(key)) { | ||
rs.setUservar(username, key, vars[key]); | ||
} | ||
} | ||
rs.setUservar(username, "username", username); | ||
} | ||
|
||
var reply = bot.getReply(username, message, function(error, reply){ | ||
if (error) { | ||
res.json({ | ||
"status": "ko", | ||
"reply": error, | ||
"vars": vars | ||
}); | ||
} else { | ||
// Get all the user's vars back out of the bot to include in the response. | ||
vars = rs.getUservars(username); | ||
|
||
// Send the JSON response. | ||
res.json({ | ||
"status": "ok", | ||
"reply": reply, | ||
"vars": vars | ||
}); | ||
} | ||
}); | ||
|
||
}; | ||
|
||
// All other routes shows the usage to test the /reply route. | ||
function showUsage(req, res) { | ||
var egPayload = { | ||
"username": "soandso", | ||
"message": "Hello bot", | ||
"vars": { | ||
"name": "Soandso" | ||
} | ||
}; | ||
res.writeHead(200, {"Content-Type": "text/plain"}); | ||
res.write("Usage: curl -i \\\n"); | ||
res.write(" -H \"Content-Type: application/json\" \\\n"); | ||
res.write(" -X POST -d '" + JSON.stringify(egPayload) + "' \\\n"); | ||
res.write(" http://localhost:2001/reply"); | ||
res.end(); | ||
} | ||
|
||
// Send a JSON error to the browser. | ||
function error(res, message) { | ||
res.json({ | ||
"status": "error", | ||
"message": message | ||
}); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "rive-weatherman", | ||
"version": "1.0.0", | ||
"description": "Your weather report", | ||
"main": "weatherman.js", | ||
"dependencies": { | ||
"body-parser": "~1.14.2", | ||
"colors": "^1.1.2", | ||
"cookie-parser": "^1.4.3", | ||
"elasticsearch": "^12.1.3", | ||
"express": "~4.13.3", | ||
"request": "^2.69.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
! sub how's = how is | ||
! sub is it going to = will it | ||
! sub is it gonna = will it | ||
! sub is it raining = will it rain | ||
|
||
+ how is [the] weather in * | ||
- <call>getWeather <star></call> | ||
|
||
+ will it rain in * | ||
- <call>checkForRain <star></call> | ||
|
||
+ mytest1 * | ||
- <call>mytest <star1></call> |