package main import ( "encoding/json" "io/ioutil" "log" "os" "path/filepath" "time" ) //a Procedure is a alphanumerical string lessthan 128 bytes. //when messages comes in we need to differentiate what Procedure is this in. //default Procedure is where command can happen //where the Procedure is stored, // in sessions/openID.json //if there is no such file, we are in default Procedure. //First Procedure is to record user's name, email, and phone number. // for individual Procedure // Procedure/Procedurename/openID.json var sessionDir = "sessions" //where to find the procedure state information var ProcedureDir = "procedure" //openIDSessionData openID user's session type openIDSessionData struct { OpenID string `json:"OpenID"` //who's session this is belongs to Procedure string `json:"Procedure"` //name of the current current process, alphanumerical CreateAt int32 `json:"CreateAt"` //when is this session created UpdateAt int32 `json:"UpdateAt"` //when is this session updated Expire int32 `json:"Expire"` //unix timestamp of when this Procedure expires States map[string]chatState `json:"States"` //each procedure will have a state } func writeSession(openid string, ss openIDSessionData) (err error) { path := getSessionPath(openid) r, err := json.Marshal(ss) if err == nil { err = ioutil.WriteFile(path, r, 0600) if err != nil { log.Printf("Error writing session %s: \r\n %s \r\n", openid, r) log.Println(err) } else { log.Println("write session to " + path) } } else { log.Printf("Error encoding session %s ", openid) log.Println(err) } return } //create if not available func setSessionProcedure(openID, procedure string, expireAfter int32) (updatedSession openIDSessionData, err error) { s, err := getCurrentSesssion(openID) now := int32(time.Now().Unix()) if s.CreateAt == 0 { s.CreateAt = now } s.UpdateAt = now s.Procedure = procedure s.Expire = now + expireAfter err = writeSession(openID, s) return s, err } func isExpired(timestamp int32) bool { now := int32(time.Now().Unix()) return now > timestamp } func getSessionPath(openID string) (path string) { path = sessionDir + string(os.PathSeparator) + openID + ".json" ensurePathExist(path) return path } func getProcedurePath(openID, ProcedureName string) (path string) { path = ProcedureDir + string(os.PathSeparator) + ProcedureName + string(os.PathSeparator) + openID + ".json" ensurePathExist(path) return } func ensurePathExist(path string) { d := filepath.Dir(path) if !isFileExist(d) { log.Println("Creating path [" + d + "]") os.MkdirAll(d, 0700) } } func deleteSession(openID string) { path := getSessionPath(openID) if isFileExist(path) { err := os.Remove(path) if err != nil { log.Println(err) } else { log.Println("delete session :" + path) } } } //OneMessage a description of what to send /receive type OneMessage struct { Type string `json:"Type"` //MessageType Text string `json:"Text"` //for text messages Pic MediaID `json:"Pic"` Voice MediaID `json:"Voice"` ShortVideo MediaID `json:"ShortVideo"` Video MediaID `json:"Video"` } type initProcFunc func(openid string) (newstate chatState) type sendProcMsgFunc func(openid string) (newstate chatState) type recvProcMsgFunc func(openid string, msg OneMessage) (newstate chatState) type cleanProcFunc func(openid string) (newstate chatState) //Procedure a description about all procedure type Procedure struct { init initProcFunc //init function send sendProcMsgFunc //function for sending customized message recv recvProcMsgFunc //function for receiving message, possibly transfer to new state clean cleanProcFunc //function for cleanning up } func (c *openIDSessionData) Save() { //TODO: save real session date to disk } //read sessions/openID.json to check whether its a Procedure for ongoing dialog func getCurrentSesssion(openID string) (result openIDSessionData, err error) { result = createEmptySession(openID, 3600) path := getSessionPath(openID) if isFileExist(path) { log.Printf("read session from %s\r\n", path) body, err := ioutil.ReadFile(path) if err != nil { //read file error log.Println("Error session reading " + path) } else { err = json.Unmarshal(body, &result) if err != nil { log.Printf("Session Content [path=%s] not correct: ", path) result = createEmptySession(openID, 3600) } } } return } func createEmptySession(openID string, expire int32) (result openIDSessionData) { result.OpenID = openID result.Procedure = "" now := int32(time.Now().Unix()) result.CreateAt = now result.UpdateAt = now result.Expire = now + expire result.States = map[string]chatState{} return }