選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

184 行
5.1KB

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