|
- package main
-
- import (
- "encoding/json"
- "errors"
- "io/ioutil"
- "log"
- "os"
- "time"
- )
-
- //chat state that we might be use for collecting infomation from user
- type chatState struct {
- //back pointer style information
- OpenID string `json:"OpenID"` //whom is this belongs to
- Procedure string `json:"Procedure"` //which procedure this belongs to
-
- //real state information
- Name string `json:"Name"` //state name
- Expire int32 `json:"Expire"` //unix timestamp when this state expire
- Send struct { //anything we need to send?
- Sent bool `json:"Sent"` //whether the message has been sent or not
- Type string `json:"Type"` //what type of message
- Message map[string]string `json:"Message"` //the message to be sent,key value pair to describe the message
- } `json:"Send"`
-
- Receive struct { //anything we expect to receive
- Received bool `json:"Received"` //whether the expected message has been received or not
- Validator string `json:"Validator"`
- Hint string `json:"Hint"`
- Message map[string]string `json:"Message"` //the description for receiving message
- } `json:"Receive"`
-
- Save map[string]string `json:"Save"` //the state save some data for later usage
- }
-
- //for individual state
- func getCurrentState(openID string, procedure string) (result chatState, err error) {
- path := getProcedurePath(openID, procedure)
- log.Printf("read state from %s\r\n", path)
- body, err := ioutil.ReadFile(path)
- if err != nil { //read file error
- if isFileExist(path) {
- log.Println("Error session reading " + path)
- }
- return //empty and expired session
- }
- err = json.Unmarshal(body, &result)
- if err != nil {
- log.Printf("Session Content [path=%s] not correct: ", path)
- log.Println(err)
- }
- //we don't check Expire, we give the caller full control on
- //how to deal wiht expired session
-
- //check whether state is for the correct openID and procedure
- if result.OpenID != openID {
- err = errors.New("Error: State for " + openID + " is actually for " + result.OpenID)
- return
- }
-
- if result.Procedure != procedure {
- err = errors.New("Error: Proecdure for " + procedure + " is actually for " + result.Procedure)
- return
- }
- return
- }
-
- func setCurrentState(openID, procedure string, state chatState) (newState chatState, err error) {
- state.OpenID = openID
- state.Procedure = procedure
-
- j, err := json.Marshal(state)
- if err != nil {
- return
- }
- path := getProcedurePath(openID, procedure)
- err = ioutil.WriteFile(path, j, 0600)
- if err != nil {
- log.Println("write state error" + path)
- log.Println(err)
- return
- }
- newState = state
- return
- }
-
- func deleteChatState(openID, procedure string) (err error) {
- path := getProcedurePath(openID, procedure)
- err = os.Remove(path)
- return
- }
-
- //ValidationResult After input validation, what is the result
- type ValidationResult struct {
- accept bool
- Hint string
- Error string
- Warning string
- }
-
- //Validator function type for validating all wechat inputs
- type Validator func(s chatState) ValidationResult
-
- //start a procedure
- func startProcedure(openID, procedure string) (err error) {
- //init procedure state
- init := getProcedureInit(openID, procedure)
- if init == nil {
- msg := "FATAL: cannot initialize procedure [" + procedure + "] "
- err = errors.New(msg)
- return
- }
- //init and get initial state
- state := init(openID)
-
- //do the real concret work for processing the state
- err = processProcedureState(state)
-
- return
- }
-
- //resume a previous Procedure's state
- func resumeProcedure(openID, procedure string) (err error) {
- state, err := getCurrentState(openID, procedure)
- if err != nil {
- return
- }
- return processProcedureState(state)
- }
-
- //finish a procedure, regardless its been finished or not
- //normally not finished normally
- func stopProcedure(openID, procedure string) {
- path := getProcedurePath(openID, procedure)
- os.Remove(path)
- log.Println("Clearing [" + openID + "] @ [" + procedure + "]")
- }
-
- func processProcedureState(state chatState) (err error) {
- //send what we need to send
- if isExpired(state.Expire) {
- return errors.New("State has expired " + state.Name)
- }
- //mark we have sent.
-
- //do we need input? waiting for input
-
- //if not, what is next state
-
- log.Println(state)
- return
- }
-
- type initProcedureFunction func(openid string) (initState chatState)
-
- func getProcedureInit(openID, procedure string) initProcedureFunction {
- initFunc := map[string]initProcedureFunction{
- "TestDummy": nil,
- "TestEcho": initTestEcho,
- "GetBasicUserInfo": initGetBasicUserInfo,
- "GetEmailAddr": initGetBasicUserInfo,
- }
- return initFunc[procedure]
- }
-
- func initTestEcho(openid string) (r chatState) {
- r.Name = openid
- r.Expire = int32(time.Now().Unix() + 100)
- return
- }
|