You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 satır
4.7KB

  1. package main
  2. import (
  3. "encoding/json"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "time"
  8. )
  9. //openSession is the biggest description for a user's openID
  10. // | it contains a name called procedure, whose data is stored elsewhere
  11. // |
  12. // +-- a Procedure name is a alphanumerical string lessthan 128 bytes.
  13. // |
  14. // +--- state is part of procedure, a procedure consists of sequence of states
  15. //where to find session data
  16. var sessionDir = "sessions"
  17. //where to find the procedure state information
  18. var procedureDir = "procedure"
  19. //openIDSessionData openID user's session
  20. type openIDSessionData struct {
  21. OpenID string `json:"OpenID"` //who's session this is belongs to
  22. Procedure string `json:"Procedure"` //name of the current current process, alphanumerical
  23. CreateAt int32 `json:"CreateAt"` //when is this session created
  24. UpdateAt int32 `json:"UpdateAt"` //when is this session updated
  25. Expire int32 `json:"Expire"` //unix timestamp of when this Procedure expires
  26. KvPair map[string]string `json:"KvPair"` //key value pair persistant for this session
  27. //
  28. //
  29. //current state for the prcedure
  30. state chatState
  31. }
  32. func createEmptySession(openID string, expire int32) (result openIDSessionData) {
  33. result.OpenID = openID
  34. result.Procedure = ""
  35. now := int32(time.Now().Unix())
  36. result.CreateAt = now
  37. result.UpdateAt = now
  38. result.Expire = now + expire
  39. result.KvPair = map[string]string{}
  40. return
  41. }
  42. func (ss *openIDSessionData) writeSession() (err error) {
  43. openID := ss.OpenID
  44. path := getSessionPath(openID)
  45. r, err := json.Marshal(ss)
  46. if err == nil {
  47. err = ioutil.WriteFile(path, r, 0600)
  48. if err != nil {
  49. log.Printf("Error writing session %s: \r\n %s \r\n", openID, r)
  50. log.Println(err)
  51. } else {
  52. log.Println("write session to " + path)
  53. //save state
  54. err = ss.state.Save(openID, ss.Procedure)
  55. if err != nil {
  56. log.Printf("Error: cannot write session data %s", openID)
  57. log.Println(err)
  58. }
  59. }
  60. } else {
  61. log.Printf("Error encoding session %s ", openID)
  62. log.Println(err)
  63. }
  64. return
  65. }
  66. func isExpired(timestamp int32) bool {
  67. now := int32(time.Now().Unix())
  68. return now > timestamp
  69. }
  70. func getSessionPath(openID string) (path string) {
  71. path = sessionDir + string(os.PathSeparator) + openID + ".json"
  72. ensurePathExist(path)
  73. return path
  74. }
  75. func (ss *openIDSessionData) Delete() {
  76. path := getSessionPath(ss.OpenID)
  77. if isFileExist(path) {
  78. err := os.Remove(path)
  79. if err != nil {
  80. log.Println(err)
  81. }
  82. }
  83. }
  84. func (ss *openIDSessionData) Save() {
  85. //invalid procedure
  86. if isExpired(ss.Expire) || ss.Procedure == "" {
  87. ss.Delete()
  88. } else {
  89. ss.writeSession()
  90. }
  91. }
  92. //read sessions/openID.json to check whether its a Procedure for ongoing dialog
  93. func (ss *openIDSessionData) Load(openID string) (result openIDSessionData, err error) {
  94. result = createEmptySession(openID, 3600)
  95. path := getSessionPath(openID)
  96. if isFileExist(path) {
  97. log.Printf("read session from %s\r\n", path)
  98. body, err := ioutil.ReadFile(path)
  99. if err != nil { //read file error
  100. log.Println("Error session reading " + path)
  101. } else {
  102. err = json.Unmarshal(body, &result)
  103. if err != nil {
  104. log.Printf("Session Content [path=%s] not correct: ", path)
  105. result = createEmptySession(openID, 3600)
  106. } else { //load procedure state if any
  107. procedure := result.Procedure
  108. result.state, err = getCurrentState(openID, procedure)
  109. }
  110. }
  111. }
  112. *ss = result
  113. return
  114. }
  115. func (ss *openIDSessionData) setProcedure(procedure string) {
  116. ss.Procedure = procedure
  117. ss.UpdateAt = int32(time.Now().Unix())
  118. }
  119. func (ss *openIDSessionData) setKvPair(key, val string) {
  120. ss.KvPair[key] = val
  121. ss.UpdateAt = int32(time.Now().Unix())
  122. }
  123. //main entry point for processing each incoming message
  124. //this stage has session date available
  125. func (ss *openIDSessionData) incomingMsg(v InWechatMsg) {
  126. openID := v.header.FromUserName
  127. //kfSendTxtAs(openID, "信息收到"+v.header.MsgType, "孙鹏")
  128. //are we in an existing procedure
  129. inProc, state := isInProc(openID) //if inside a procedure, resume last saved state
  130. if inProc {
  131. ss.state = serveProc(state, v) //transit to new state
  132. } else {
  133. state, processed := serveCommand(openID, v) //menu or txt command e.g. search
  134. if !processed { // transfer to Customer Service (kf)
  135. ss.state.response, _ = BuildTextMsg(openID, "已转接校友会理事会,稍后答复您")
  136. } else {
  137. ss.state = state
  138. }
  139. }
  140. if ss.state.response != "" {
  141. v.instantResponse <- ss.state.response
  142. }
  143. if !isEndingState(state) {
  144. err := saveChatState(openID, state.Procedure, state)
  145. if err != nil {
  146. log.Println("Error Cannot Save chat sate")
  147. log.Println(err)
  148. log.Println(state)
  149. }
  150. } else { //state ending
  151. cleanProcedure(openID, state.Procedure)
  152. }
  153. return
  154. }