Skip to content

Commit

Permalink
Merge pull request #32 from aichaos/feature/session-handler
Browse files Browse the repository at this point in the history
Session Handler and Developer Friendly Parser
  • Loading branch information
kirsle committed Jul 24, 2016
2 parents 264cdad + bb53770 commit 5c2be37
Show file tree
Hide file tree
Showing 27 changed files with 1,756 additions and 947 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1 +1,2 @@
__pycache__
*.pyc
41 changes: 3 additions & 38 deletions docs/rivescript.rst
Expand Up @@ -12,12 +12,11 @@ rivescript.rivescript module
:undoc-members:
:show-inheritance:

rivescript.brain module
-----------------------
rivescript.sessions module
--------------------------

.. automodule:: rivescript.brain
.. automodule:: rivescript.sessions
:members:
:undoc-members:
:show-inheritance:

rivescript.exceptions module
Expand All @@ -28,28 +27,18 @@ rivescript.exceptions module
:undoc-members:
:show-inheritance:

rivescript.inheritance module
-----------------------------

.. automodule:: rivescript.inheritance
:members:
:undoc-members:
:show-inheritance:

rivescript.interactive module
-----------------------------

.. automodule:: rivescript.interactive
:members:
:undoc-members:
:show-inheritance:

rivescript.parser module
------------------------

.. automodule:: rivescript.parser
:members:
:undoc-members:
:show-inheritance:

rivescript.python module
Expand All @@ -59,27 +48,3 @@ rivescript.python module
:members:
:undoc-members:
:show-inheritance:

rivescript.regexp module
------------------------

.. automodule:: rivescript.regexp
:members:
:undoc-members:
:show-inheritance:

rivescript.sorting module
-------------------------

.. automodule:: rivescript.sorting
:members:
:undoc-members:
:show-inheritance:

rivescript.utils module
-----------------------

.. automodule:: rivescript.utils
:members:
:undoc-members:
:show-inheritance:
4 changes: 4 additions & 0 deletions eg/README.md
Expand Up @@ -19,6 +19,10 @@ RiveScript-Python.

## Code Snippets

* [sessions](sessions/) - Demonstrates replacing the default in-memory session
storage with ones that support persistent back-end storage systems.
* [parser](parser/) - Use the `rivescript.parser` module to parse RiveScript
code yourself.
* [js-objects](js-objects/) - Demonstrates adding JavaScript object macro
support to RiveScript-Python. This example assumes a Python RiveScript bot is
serving its responses via a web front-end, so that the JS macros are being
Expand Down
2 changes: 1 addition & 1 deletion eg/brain/admin.rive
Expand Up @@ -16,5 +16,5 @@

> object shutdown python
# Shut down
exit(0)
quit()
< object
111 changes: 111 additions & 0 deletions eg/parser/README.md
@@ -0,0 +1,111 @@
# RiveScript Parser Example

The RiveScript parser was broken out into its own stand-alone module usable by
third party developers, and this is an example of how to use it.

The bare minimum code to use the RiveScript parser are as follows:

```python
from rivescript.parser import Parser

# Instantiate it.
p = Parser()

# And parse some RiveScript code!
ast = p.parse("any-file-name.rive", [
"+ hello bot",
"- Hello human!",
])
```

The `Parser.parse()` function requires a file name (used only for syntax
reporting purposes) and an array of lines of source code to parse.

## Running the Example

```bash
# Simply run it! It will parse and dump ../brain/clients.rive by default.
% python parse.py

# Or give it the name of a different RiveScript file.
% python parse.py ../brain/eliza.rive

# Or enable debug logging.
% env PARSER_DEBUG=1 python parse.py ../brain/begin.rive

# (For the Windows/Command Prompt users)
> set PARSER_DEBUG=1
> python parse.py ../brain/myself.rive
```

The parse script will parse the RiveScript file given on the command line,
and dump its "abstract syntax tree" as JSON to the standard output so you can
see what the data structure looks like.

Check the source code of `parse.py` for the details. The environment variable
`PARSER_DEBUG` is something the example script uses, not the underlying
`rivescript.parser` module; debugging is made possible by using the `on_debug`
and `on_warn` arguments to the Parser constructor.

## Example Output

`python parse.py ../brain/admin.rive`

```json
{
"begin": {
"array": {},
"global": {},
"person": {},
"sub": {},
"var": {}
},
"objects": [
{
"code": [
"\tmy ($rs) = @_;\n",
"\t# Shut down.\n",
"\texit(0);\n"
],
"language": "perl",
"name": "shutdown"
},
{
"code": [
"\t# Shut down\n",
"\tquit()\n"
],
"language": "python",
"name": "shutdown"
}
],
"topics": {
"random": {
"includes": {},
"inherits": {},
"triggers": [
{
"condition": [
"<id> eq <bot master> => Shutting down... <call>shutdown</call>"
],
"previous": null,
"redirect": null,
"reply": [
"{@botmaster only}"
],
"trigger": "shutdown{weight=10000}"
},
{
"condition": [],
"previous": null,
"redirect": null,
"reply": [
"This command can only be used by my botmaster. <id> != <bot master>"
],
"trigger": "botmaster only"
}
]
}
}
}
```
63 changes: 63 additions & 0 deletions eg/parser/parse.py
@@ -0,0 +1,63 @@
#!/usr/bin/env python

from __future__ import print_function
import codecs
import os
import json
import sys
from rivescript.parser import Parser

"""Example use of the RiveScript Parser module.
Usage: python parse.py [path/to/file.rive]
By default it will parse ../brain/clients.rive but you can pass it any
RiveScript file you want.
"""

def main():
# Get the command line argument.
filename = "../brain/clients.rive"
if len(sys.argv) > 1:
filename = sys.argv[1]

# Make sure it's really a file.
if not os.path.isfile(filename):
print("{}: Not a file.".format(filename))
sys.exit(1)

# Make sure it looks like a RiveScript file.
if not filename.lower().endswith(".rive"):
print("{}: Doesn't look like a RiveScript file (no .rive extension)")
sys.exit(1)

# Create a parser instance.
if os.environ.get("PARSER_DEBUG"):
parser = Parser(
on_debug=on_debug,
on_warn=on_warn,
)
else:
parser = Parser()

# Read the file's contents.
with codecs.open(filename, "r", "utf-8") as fh:
source = fh.readlines()

# Create a parser and parse it.
ast = parser.parse(filename, source)

# Dump the "Abstract Syntax Tree" to the console as JSON.
print(json.dumps(ast, indent=2, sort_keys=True))

def on_debug(message):
print("[DEBUG]", message)

def on_warn(message, filename=None, lineno=None):
if filename is not None and lineno is not None:
print("[WARN]", message, "at", filename, "line", lineno)
else:
print("[WARN]", message)

if __name__ == "__main__":
main()
94 changes: 94 additions & 0 deletions eg/sessions/README.md
@@ -0,0 +1,94 @@
# Session Storage Example

By default RiveScript stores all user variables and state in memory, with no
automatic persistence to disk when the bot exits and restarts. RiveScript does
provide functions to export and import user variables in bulk so that you can
manually store and reload data, but an alternative is to use a third party
session storage driver.

The example bot at `redis-bot.py` uses a [Redis cache](http://redis.io/) to
store user variables. To run the example you'll need to install and start a
Redis server.

## Setup

```bash
###
# 1. Install Redis
###

# Fedora users:
sudo dnf install redis

# RHEL and CentOS <= 7
sudo yum install redis

# Debian/Ubuntu
sudo apt-get install redis-server

###
# 2. Start the Redis server
###

# Either do this to start it in a terminal window:
redis-server

# Or enable/start it with systemd.
sudo systemctl enable redis.service
sudo systemctl start redis.service

###
# 3. Run this example bot.
###

pip install -r requirements.txt
python redis_bot.py
```

## Example Output

```
% python redis_bot.py
RiveScript Redis Session Storage Example
This example uses a Redis server to store user variables. For the sake of the
example, choose a username to store your variables under. You can re-run this
script with the same username (or a different one!) and verify that your
variables are kept around!
Type '/quit' to quit.
What is your username? kirsle
You> Hello bot.
Bot> How do you do. Please state your problem.
You> My name is Noah.
Bot> Noah, nice to meet you.
You> Who am I?
Bot> You told me your name is Noah.
You> /quit
% python redis_bot.py
RiveScript Redis Session Storage Example
This example uses a Redis server to store user variables. For the sake of the
example, choose a username to store your variables under. You can re-run this
script with the same username (or a different one!) and verify that your
variables are kept around!
Type '/quit' to quit.
What is your username? kirsle
You> What's my name?
Bot> Your name is Noah.
You> /quit
```

And the Redis cache can be verified via the `redis-cli` command:

```
% redis-cli
127.0.0.1:6379> keys *
1) "rs-users/kirsle"
127.0.0.1:6379> get rs-users/kirsle
...redacted large JSON blob...
```

0 comments on commit 5c2be37

Please sign in to comment.