Skip to content

Commit

Permalink
Implement pluggable session store and make it threadsafe
Browse files Browse the repository at this point in the history
  • Loading branch information
kirsle committed Dec 9, 2016
1 parent ffebfa3 commit 7df064e
Show file tree
Hide file tree
Showing 16 changed files with 707 additions and 200 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export GOPATH="$(pwd)/.gopath"
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ setup:
run: gopath
GOPATH=$(GOPATH) GO15VENDOREXPERIMENT=1 go run cmd/rivescript/main.go eg/brain

# `make debug` to run the rivescript cmd in debug mode
debug: gopath
GOPATH=$(GOPATH) GO15VENDOREXPERIMENT=1 go run cmd/rivescript/main.go -debug eg/brain

# `make fmt` to run gofmt
fmt:
gofmt -w .
Expand Down
22 changes: 13 additions & 9 deletions cmd/rivescript/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@ import (
"bufio"
"flag"
"fmt"
rivescript "github.com/aichaos/rivescript-go"
js "github.com/aichaos/rivescript-go/lang/javascript"
"os"
"strings"

"github.com/aichaos/rivescript-go"
"github.com/aichaos/rivescript-go/config"
"github.com/aichaos/rivescript-go/lang/javascript"
)

func main() {
// Collect command line arguments.
version := flag.Bool("version", false, "Show the version number and exit.")
debug := flag.Bool("debug", false, "Enable debug mode.")
utf8 := flag.Bool("utf8", false, "Enable UTF-8 mode.")
depth := flag.Int("depth", 50, "Recursion depth limit (default 50)")
depth := flag.Uint("depth", 50, "Recursion depth limit (default 50)")
nostrict := flag.Bool("nostrict", false, "Disable strict syntax checking")
flag.Parse()
args := flag.Args()

Expand All @@ -48,14 +51,15 @@ func main() {
root := args[0]

// Initialize the bot.
bot := rivescript.New()
bot.SetDebug(*debug)
bot.SetUTF8(*utf8)
bot.SetDepth(*depth)
bot := rivescript.New(&config.Config{
Debug: *debug,
Strict: !*nostrict,
Depth: *depth,
UTF8: *utf8,
})

// JavaScript object macro handler.
jsHandler := js.New(bot)
bot.SetHandler("javascript", jsHandler)
bot.SetHandler("javascript", javascript.New(bot))

// Load the target directory.
err := bot.LoadDirectory(root)
Expand Down
54 changes: 54 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Package config provides the RiveScript configuration type.
package config

import (
"github.com/aichaos/rivescript-go/sessions"
"github.com/aichaos/rivescript-go/sessions/memory"
)

// Type Config configures a RiveScript instance.
type Config struct {
// Debug enables verbose debug logging to your standard output.
Debug bool

// Strict enables strict syntax checking.
Strict bool

// Depth sets the recursion depth limit. The zero value will default to
// 50 levels deep.
Depth uint

// UTF8 enables UTF-8 support for user messages and triggers.
UTF8 bool

// SessionManager chooses a session manager for user variables.
SessionManager sessions.SessionManager
}

// Basic creates a default configuration:
//
// - Strict: true
// - Depth: 50
// - UTF8: false
func Basic() *Config {
return &Config{
Strict: true,
Depth: 50,
UTF8: false,
SessionManager: memory.New(),
}
}

// UTF8 creates a default configuration with UTF-8 mode enabled.
//
// - Strict: true
// - Depth: 50
// - UTF8: true
func UTF8() *Config {
return &Config{
Strict: true,
Depth: 50,
UTF8: true,
SessionManager: memory.New(),
}
}
16 changes: 9 additions & 7 deletions rivescript.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ package rivescript
*/

import (
"github.com/aichaos/rivescript-go/config"
"github.com/aichaos/rivescript-go/macro"
"github.com/aichaos/rivescript-go/sessions"
"github.com/aichaos/rivescript-go/src"
)

Expand All @@ -23,9 +25,9 @@ type RiveScript struct {
rs *src.RiveScript
}

func New() *RiveScript {
func New(config *config.Config) *RiveScript {
bot := new(RiveScript)
bot.rs = src.New()
bot.rs = src.New(config)
return bot
}

Expand Down Expand Up @@ -62,12 +64,12 @@ func (self *RiveScript) SetUnicodePunctuation(value string) {
}

// SetDepth lets you override the recursion depth limit (default 50).
func (self *RiveScript) SetDepth(value int) {
func (self *RiveScript) SetDepth(value uint) {
self.rs.Depth = value
}

// GetDepth returns the current recursion depth limit.
func (self *RiveScript) GetDepth() int {
func (self *RiveScript) GetDepth() uint {
return self.rs.Depth
}

Expand Down Expand Up @@ -277,7 +279,7 @@ GetUservars gets all the variables for a user.
This returns a `map[string]string` containing all the user's variables.
*/
func (self *RiveScript) GetUservars(username string) (map[string]string, error) {
func (self *RiveScript) GetUservars(username string) (*sessions.UserData, error) {
return self.rs.GetUservars(username)
}

Expand All @@ -287,7 +289,7 @@ GetAllUservars gets all the variables for all the users.
This returns a map of username (strings) to `map[string]string` of their
variables.
*/
func (self *RiveScript) GetAllUservars() map[string]map[string]string {
func (self *RiveScript) GetAllUservars() map[string]*sessions.UserData {
return self.rs.GetAllUservars()
}

Expand Down Expand Up @@ -319,7 +321,7 @@ The `action` can be one of the following:
* discard: Don't restore the variables, just delete the frozen copy.
* keep: Keep the frozen copy after restoring.
*/
func (self *RiveScript) ThawUservars(username, action string) error {
func (self *RiveScript) ThawUservars(username string, action sessions.ThawAction) error {
return self.rs.ThawUservars(username, action)
}

Expand Down
106 changes: 106 additions & 0 deletions sessions/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Package sessions provides the interface and default session store for
// RiveScript.
package sessions

/*
Interface SessionManager describes a session manager for user variables
in RiveScript.
The session manager keeps track of getting and setting user variables,
for example when the `<set>` or `<get>` tags are used in RiveScript
or when API functions like `SetUservar()` are called.
By default RiveScript stores user sessions in memory and provides methods
to export and import them (e.g. to persist them when the bot shuts down
so they can be reloaded). If you'd prefer a more 'active' session storage,
for example one that puts user variables into a database or cache, you can
create your own session manager that implements this interface.
*/
type SessionManager interface {
// Init makes sure a username has a session (creates one if not). It returns
// the pointer to the user data in either case.
Init(username string) *UserData

// Set user variables from a map.
Set(username string, vars map[string]string)

// AddHistory adds input and reply to the user's history.
AddHistory(username, input, reply string)

// SetLastMatch sets the last matched trigger.
SetLastMatch(username, trigger string)

// Get a user variable.
Get(username string, key string) (string, error)

// Get all variables for a user.
GetAny(username string) (*UserData, error)

// Get all variables about all users.
GetAll() map[string]*UserData

// GetLastMatch returns the last trigger the user matched.
GetLastMatch(username string) (string, error)

// GetHistory returns the user's history.
GetHistory(username string) (*History, error)

// Clear all variables for a given user.
Clear(username string)

// Clear all variables for all users.
ClearAll()

// Freeze makes a snapshot of a user's variables.
Freeze(string) error

// Thaw unfreezes a snapshot of a user's variables and returns an error
// if the user had no frozen variables.
Thaw(username string, ThawAction ThawAction) error
}

// HistorySize is the number of entries stored in the history.
const HistorySize int = 9

// Type UserData is a container for user variables.
type UserData struct {
Variables map[string]string
LastMatch string
*History
}

// Type History keeps track of recent input and reply history.
type History struct {
Input []string
Reply []string
}

// NewHistory creates a new History object with the history arrays filled out.
func NewHistory() *History {
h := &History{
Input: []string{},
Reply: []string{},
}

for i := 0; i < HistorySize; i++ {
h.Input = append(h.Input, "undefined")
h.Reply = append(h.Reply, "undefined")
}

return h
}

// Type ThawAction describes the action for the `Thaw()` method.
type ThawAction int

// Valid options for ThawAction.
const (
// Thaw means to restore the user variables and erase the frozen copy.
Thaw = iota

// Discard means to cancel the frozen copy and not restore them.
Discard

// Keep means to restore the user variables and still keep the frozen copy.
Keep
)
Loading

0 comments on commit 7df064e

Please sign in to comment.