-
Notifications
You must be signed in to change notification settings - Fork 15
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 #14 from aichaos/examples
Add JSON Server Example
- Loading branch information
Showing
7 changed files
with
703 additions
and
2 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,15 @@ | ||
# Examples | ||
|
||
These directories include example snippets for how to do various things with | ||
RiveScript-Go. | ||
|
||
## RiveScript Example | ||
|
||
* [brain](brain/) - The standard default RiveScript brain (`.rive` files) that | ||
implements an Eliza-like bot with added triggers to demonstrate other | ||
features of RiveScript. | ||
|
||
## Client Examples | ||
|
||
* [json-server](json-server/) - A minimal Go web server that makes a RiveScript | ||
bot available at a JSON POST endpoint. |
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 @@ | ||
json-server |
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,171 @@ | ||
# JSON Server | ||
|
||
This example demonstrates embedding RiveScript in a Go web app, accessible via | ||
a JSON endpoint. | ||
|
||
## Run the Example | ||
|
||
Run one of these in a terminal: | ||
|
||
```bash | ||
# Quick run | ||
go run main.go | ||
|
||
# Build and run | ||
go build -o json-server main.go | ||
./json-server [options] [path/to/brain] | ||
``` | ||
|
||
Then you can visit the web server at <http://localhost:8000/> where you can | ||
find an example `curl` command to run from a terminal, and an in-browser demo | ||
that makes an ajax request to the endpoint. | ||
|
||
From another terminal, you can use `curl` to test a JSON endpoint for the | ||
chatbot. Or, you can use your favorite REST client. | ||
|
||
```bash | ||
curl -X POST -H 'Content-Type: application/json' \ | ||
-d '{"username": "kirsle", "message": "Hello, robot"}' \ | ||
http://localhost:8000/reply | ||
``` | ||
|
||
### Options | ||
|
||
The JSON server accepts the following command line options. | ||
|
||
``` | ||
json-server [-host=string -port=int -debug -utf8 -forgetful -help] [path] | ||
``` | ||
|
||
#### Server Options | ||
|
||
* `-host string` | ||
|
||
The interface to listen on (default `"0.0.0.0"`) | ||
|
||
* `-port int` | ||
|
||
The port number to bind to (default `8000`) | ||
|
||
#### RiveScript Options | ||
|
||
* `-debug` | ||
|
||
Enable debug mode within RiveScript (default `false`) | ||
|
||
* `-utf8` | ||
|
||
Enable UTF-8 mode within RiveScript (default `true`) | ||
|
||
* `-forgetful` | ||
|
||
Do not store user variables in server memory between requests (default | ||
`false`). See [User Variables](#user-variables) for more information about | ||
how user variables are dealt with in this program. | ||
|
||
* `path` | ||
|
||
Specify a path on disk where RiveScript source files (`*.rive`) can be found. | ||
The default is `../brain`, or `/eg/brain` relative to the git root | ||
of rivescript-go. | ||
|
||
## API Documentation | ||
|
||
### POST /reply | ||
|
||
Post a JSON message (`Content-Type: application/json`) to this endpoint to get | ||
a response from the chatbot. | ||
|
||
Request payload follows this format (all types are strings): | ||
|
||
```javascript | ||
{ | ||
"username": "demo", // Unique user ID (for user variables in the bot) | ||
"message": "Hello robot", // The message to send. | ||
"vars": { // Optional user variables to include. | ||
"name": "Demo User" | ||
} | ||
} | ||
``` | ||
|
||
The only **required** parameter is the `username`. A missing or blank `message` | ||
would be handled by the chatbot's fall-back `*` trigger. | ||
|
||
The response follows this format (all types are strings): | ||
|
||
```javascript | ||
// On successful outputs. | ||
{ | ||
"status": "ok", | ||
"reply": "Hello human.", | ||
"vars": { // All user variables the bot has for that user. | ||
"topic": "random", | ||
"name": "Demo User" | ||
} | ||
} | ||
|
||
// On errors. | ||
{ | ||
"status": "error", | ||
"error": "username is required" | ||
} | ||
``` | ||
|
||
The only key guaranteed to be in the response is `status`. Other keys are | ||
excluded when empty. | ||
|
||
## User Variables | ||
|
||
The server keeps a shared RiveScript instance in memory for the lifetime of | ||
the program. When the server exits, the user variables are lost. | ||
|
||
The REST client that consumes this API *should* always send the full set of | ||
user vars that it knows about on each request. This is the safest way to keep | ||
consistent state for the end user. However, the client does not need to provide | ||
these variables; the server will temporarily use its own and send its current | ||
state to the client with each response. | ||
|
||
A client that cares about long-term consistency of user variables should take | ||
the `vars` returned by the server and store them somewhere, and send them back | ||
to the server on the next request. This way the server could be rebooted | ||
between requests and the bot won't forget the user's name, because the client | ||
always sends its variables to the server. | ||
|
||
To ensure that the server does not keep user variables around after the | ||
request, you can provide the `-forgetful` command line option to the program. | ||
This will clear the user's variables at the end of every request, forcing the | ||
REST client to manage them on their end. | ||
|
||
## Disclaimer for Deployment | ||
|
||
This code is only intended for demonstration purposes, but as a Go web server | ||
it can be used in a production environment. To that end, you should be aware of | ||
some security and performance considerations: | ||
|
||
* **The API is non-authenticated.** If the server is publicly accessible, then | ||
anybody on the Internet can interact with it, providing *any* data for the | ||
`username`, `message` and `vars` fields. | ||
|
||
If this is a problem (for example, if you're implementing some sort of User | ||
Access Control within RiveScript keyed off the user's username, or `<id>`), | ||
then you should bind the server to a non-public interface, for example by | ||
using the command line option: `-host localhost` | ||
|
||
You could put a reverse proxy like Nginx in front of the server to provide | ||
authentication on public interfaces if needed. | ||
|
||
* **The server remembers users by default.** RiveScript stores user variables in | ||
memory by default, and this server doesn't change that behavior. This may be | ||
a memory leak concern if your bot interacts with large amounts of distinct | ||
usernames, or stores a ton of user variables per user. | ||
|
||
To prevent the server from holding onto user variables in memory, use the | ||
`-forgetful` command line option. The bot will then clear its user variables | ||
after every request. | ||
|
||
See [User Variables](#user-variables) for tips on how the client should | ||
then keep track of variables on its end rather than depend on the server. | ||
|
||
## License | ||
|
||
This example is released under the same license as rivescript-go itself. |
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,128 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>RiveScript json-server</title> | ||
<style type="text/css"> | ||
body { | ||
background-color: #FFFFFF; | ||
font-family: Helvetica,Arial,Verdana,sans-serif; | ||
font-size: medium; | ||
color: #000000; | ||
} | ||
pre, code { | ||
font-family: "Ubuntu Mono", "DejaVu Sans Mono", "Lucida Console", "Monospace", monospace; | ||
font-size: medium; | ||
} | ||
pre { | ||
display: block; | ||
border: 1px dashed #000000; | ||
padding: 12px; | ||
} | ||
pre#output { | ||
display: none; | ||
clear: both; | ||
margin: 20px 0; | ||
} | ||
.row { | ||
clear: both; | ||
padding: 4px 0; | ||
} | ||
.row label { | ||
width: 150px; | ||
float: left; | ||
text-align: right; | ||
padding-right: 4px; | ||
} | ||
.row input { | ||
float: left; | ||
} | ||
button { | ||
display: none; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
|
||
<h1>RiveScript json-server</h1> | ||
|
||
Usage via <code>curl</code>: | ||
|
||
<pre>curl -X POST -H 'Content-Type: application/json' \ | ||
-d '{"username": "soandso", "message": "Hello, bot"}' \ | ||
http://localhost:8000/reply</pre> | ||
|
||
<h2>In-Browser Demo</h2> | ||
|
||
<form id="demo" action="/" method="POST"> | ||
<div class="row"> | ||
<label for="username">Username:</label> <input type="text" id="username" size="40" value="demo" placeholder="Required."> | ||
</div> | ||
<div class="row"> | ||
<label for="message">Message:</label> <input type="text" id="message" size="40" placeholder="Press Return to send." autocomplete="off"> | ||
</div> | ||
<button type="submit"></button> | ||
</form> | ||
|
||
<br> | ||
<pre id="output"></pre> | ||
|
||
<script type="text/javascript"> | ||
function ready(fn) { | ||
if (document.readyState !== "loading") { | ||
fn(); | ||
} else { | ||
document.addEventListener('DOMContentLoaded', fn); | ||
} | ||
} | ||
|
||
ready(function() { | ||
var $form = document.getElementById("demo"); | ||
var $username = document.getElementById("username"); | ||
var $message = document.getElementById("message"); | ||
var $output = document.getElementById("output"); | ||
|
||
var write = function(message) { | ||
$output.innerText += message+"\n"; | ||
}; | ||
|
||
$form.addEventListener("submit", function(e) { | ||
e.preventDefault(); | ||
$output.style.display = "block"; | ||
$output.innerText = ""; | ||
var message = $message.value; | ||
$message.value = ""; | ||
|
||
var req = new XMLHttpRequest(); | ||
req.open("POST", "/reply"); | ||
req.setRequestHeader("Content-Type", "application/json; charset=utf-8"); | ||
|
||
req.onload = req.onerror = function() { | ||
var data; | ||
try { | ||
data = JSON.parse(req.responseText); | ||
} catch(e) { | ||
write("Error: " + e); | ||
} | ||
|
||
if (req.status !== 200) { | ||
write("Error: got non-200 status code (" + req.status + ")"); | ||
} | ||
|
||
if (data.status === "error") { | ||
write("API Error: " + data.error); | ||
} else { | ||
write("Reply: " + data.reply); | ||
write("Vars: " + JSON.stringify(data.vars, null, 2)); | ||
} | ||
} | ||
|
||
req.send(JSON.stringify({ | ||
username: $username.value, | ||
message: message, | ||
})); | ||
}); | ||
}) | ||
</script> | ||
|
||
</body> | ||
</html> |
Oops, something went wrong.