Просмотр исходного кода

chat session re-invented.

master
Patrick Peng Sun 8 лет назад
Родитель
Сommit
5edb741019
2 измененных файлов: 55 добавлений и 61 удалений
  1. +45
    -57
      chatSession.go
  2. +10
    -4
      chatSession_test.go

+ 45
- 57
chatSession.go Просмотреть файл

"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"path/filepath"
"time" "time"
) )


state chatState state chatState
} }


func writeSession(ss openIDSessionData) (err error) {
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.KvPair = map[string]string{}
return
}

func (ss *openIDSessionData) writeSession() (err error) {
openID := ss.OpenID openID := ss.OpenID
path := getSessionPath(openID) path := getSessionPath(openID)
r, err := json.Marshal(ss) r, err := json.Marshal(ss)
log.Println(err) log.Println(err)
} else { } else {
log.Println("write session to " + path) log.Println("write session to " + path)
//save state
err = ss.state.Save(openID, ss.Procedure)
if err != nil {
log.Printf("Error: cannot write session data %s", openID)
log.Println(err)
}
} }
} else { } else {
log.Printf("Error encoding session %s ", openID) log.Printf("Error encoding session %s ", openID)
return 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)
func (ss *openIDSessionData) Delete() {
path := getSessionPath(ss.OpenID)
if isFileExist(path) { if isFileExist(path) {
err := os.Remove(path) err := os.Remove(path)
if err != nil { if err != nil {
} }
} }


type initProcFunc func(openid string) (newstate chatState)
type sendProcMsgFunc func(openid string) (newstate chatState)
type recvProcMsgFunc func(openid string) (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() {
func (ss *openIDSessionData) Save() {
//invalid procedure //invalid procedure
if isExpired(c.Expire) || c.Procedure == "" {
deleteSession(c.OpenID)
if isExpired(ss.Expire) || ss.Procedure == "" {
ss.Delete()
} else { } else {
writeSession(*c)
ss.writeSession()
} }
} }


//read sessions/openID.json to check whether its a Procedure for ongoing dialog //read sessions/openID.json to check whether its a Procedure for ongoing dialog
func getCurrentSesssion(openID string) (result openIDSessionData, err error) {
func (ss *openIDSessionData) Load(openID string) (result openIDSessionData, err error) {
result = createEmptySession(openID, 3600) result = createEmptySession(openID, 3600)
path := getSessionPath(openID) path := getSessionPath(openID)
if isFileExist(path) { if isFileExist(path) {
if err != nil { if err != nil {
log.Printf("Session Content [path=%s] not correct: ", path) log.Printf("Session Content [path=%s] not correct: ", path)
result = createEmptySession(openID, 3600) result = createEmptySession(openID, 3600)
} else { //load procedure state if any
procedure := result.Procedure
result.state, err = getCurrentState(openID, procedure)
} }
} }
} }
*ss = result
return 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.KvPair = map[string]string{}
return
}

func (c *openIDSessionData) setProcedure(procedure string) {
c.Procedure = procedure
c.UpdateAt = int32(time.Now().Unix())
func (ss *openIDSessionData) setProcedure(procedure string) {
ss.Procedure = procedure
ss.UpdateAt = int32(time.Now().Unix())
} }


func (c *openIDSessionData) setKvPair(key, val string) {
c.KvPair[key] = val
c.UpdateAt = int32(time.Now().Unix())
func (ss *openIDSessionData) setKvPair(key, val string) {
ss.KvPair[key] = val
ss.UpdateAt = int32(time.Now().Unix())
} }


//main entry point for processing each incoming message //main entry point for processing each incoming message
//this stage has session date available //this stage has session date available
func (c *openIDSessionData) incomingMsg(v InWechatMsg) {
func (ss *openIDSessionData) incomingMsg(v InWechatMsg) {
openID := v.header.FromUserName openID := v.header.FromUserName
//kfSendTxtAs(openID, "信息收到"+v.header.MsgType, "孙鹏")
//are we in an existing procedure //are we in an existing procedure
inProc, state := isInProc(openID) //if inside a procedure, resume last saved state inProc, state := isInProc(openID) //if inside a procedure, resume last saved state
if inProc { if inProc {
c.state = serveProc(state, v) //transit to new state
ss.state = serveProc(state, v) //transit to new state
} else { } else {
state, processed := serveCommand(openID, v) //menu or txt command e.g. search state, processed := serveCommand(openID, v) //menu or txt command e.g. search
if !processed { // transfer to Customer Service (kf) if !processed { // transfer to Customer Service (kf)
kfSendTxt(openID, "未识别的命令,已转接校友会理事会,稍后答复您")
ss.state.response, _ = BuildTextMsg(openID, "已转接校友会理事会,稍后答复您")
} else {
ss.state = state
} }
c.state = state
}

if ss.state.response != "" {
v.instantResponse <- ss.state.response
} }


if !isEndingState(state) { if !isEndingState(state) {

+ 10
- 4
chatSession_test.go Просмотреть файл

id := "testopenid" id := "testopenid"
procedure := "test procedure" procedure := "test procedure"


s := openIDSessionData{}
s.OpenID = id
s.Procedure = procedure

//delete any existing //delete any existing
deleteSession(id)
s.Delete()
path := getSessionPath(id) path := getSessionPath(id)
AssertEqual(t, isFileExist(path), false, "Session file should not exist") AssertEqual(t, isFileExist(path), false, "Session file should not exist")


log.Print("......waiting done") log.Print("......waiting done")


//read back , check state //read back , check state
s, _ := getCurrentSesssion(id)
s.Load(id)
AssertEqual(t, s.OpenID, id, "")
AssertEqual(t, s.Procedure, procedure, "") AssertEqual(t, s.Procedure, procedure, "")
now := int32(time.Now().Unix()) now := int32(time.Now().Unix())


AssertEqual(t, s.CreateAt+1 == s.UpdateAt, true, "second Update should be bigger create") AssertEqual(t, s.CreateAt+1 == s.UpdateAt, true, "second Update should be bigger create")


s.Save() s.Save()
n, _ := getCurrentSesssion(id)
n := openIDSessionData{}
n.Load(id)
AssertEqual(t, n.KvPair["key"], "value", "key = value ") AssertEqual(t, n.KvPair["key"], "value", "key = value ")
AssertEqual(t, n.KvPair["key1"], "value1", "key1 = value1") AssertEqual(t, n.KvPair["key1"], "value1", "key1 = value1")


//delete session, for clean up //delete session, for clean up
deleteSession(id)
n.Delete()
path = getSessionPath(id) path = getSessionPath(id)
AssertEqual(t, isFileExist(path), false, "Session file should not exist") AssertEqual(t, isFileExist(path), false, "Session file should not exist")
} }

Загрузка…
Отмена
Сохранить