您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

172 行
4.6KB

  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "log"
  7. "os"
  8. "time"
  9. )
  10. //chat state that we might be use for collecting infomation from user
  11. type chatState struct {
  12. //back pointer style information
  13. OpenID string `json:"OpenID"` //whom is this belongs to
  14. Procedure string `json:"Procedure"` //which procedure this belongs to
  15. //real state information
  16. Name string `json:"Name"` //state name
  17. Expire int32 `json:"Expire"` //unix timestamp when this state expire
  18. Send struct { //anything we need to send?
  19. Sent bool `json:"Sent"` //whether the message has been sent or not
  20. Type string `json:"Type"` //what type of message
  21. Message map[string]string `json:"Message"` //the message to be sent,key value pair to describe the message
  22. } `json:"Send"`
  23. Receive struct { //anything we expect to receive
  24. Received bool `json:"Received"` //whether the expected message has been received or not
  25. Validator string `json:"Validator"`
  26. Hint string `json:"Hint"`
  27. Message map[string]string `json:"Message"` //the description for receiving message
  28. } `json:"Receive"`
  29. Save map[string]string `json:"Save"` //the state save some data for later usage
  30. }
  31. //for individual state
  32. func getCurrentState(openID string, procedure string) (result chatState, err error) {
  33. path := getProcedurePath(openID, procedure)
  34. log.Printf("read state from %s\r\n", path)
  35. body, err := ioutil.ReadFile(path)
  36. if err != nil { //read file error
  37. if isFileExist(path) {
  38. log.Println("Error session reading " + path)
  39. }
  40. return //empty and expired session
  41. }
  42. err = json.Unmarshal(body, &result)
  43. if err != nil {
  44. log.Printf("Session Content [path=%s] not correct: ", path)
  45. log.Println(err)
  46. }
  47. //we don't check Expire, we give the caller full control on
  48. //how to deal wiht expired session
  49. //check whether state is for the correct openID and procedure
  50. if result.OpenID != openID {
  51. err = errors.New("Error: State for " + openID + " is actually for " + result.OpenID)
  52. return
  53. }
  54. if result.Procedure != procedure {
  55. err = errors.New("Error: Proecdure for " + procedure + " is actually for " + result.Procedure)
  56. return
  57. }
  58. return
  59. }
  60. func setCurrentState(openID, procedure string, state chatState) (newState chatState, err error) {
  61. state.OpenID = openID
  62. state.Procedure = procedure
  63. j, err := json.Marshal(state)
  64. if err != nil {
  65. return
  66. }
  67. path := getProcedurePath(openID, procedure)
  68. err = ioutil.WriteFile(path, j, 0600)
  69. if err != nil {
  70. log.Println("write state error" + path)
  71. log.Println(err)
  72. return
  73. }
  74. newState = state
  75. return
  76. }
  77. func deleteChatState(openID, procedure string) (err error) {
  78. path := getProcedurePath(openID, procedure)
  79. err = os.Remove(path)
  80. return
  81. }
  82. //ValidationResult After input validation, what is the result
  83. type ValidationResult struct {
  84. accept bool
  85. Hint string
  86. Error string
  87. Warning string
  88. }
  89. //Validator function type for validating all wechat inputs
  90. type Validator func(s chatState) ValidationResult
  91. //start a procedure
  92. func startProcedure(openID, procedure string) (err error) {
  93. //init procedure state
  94. init := getProcedureInit(openID, procedure)
  95. if init == nil {
  96. msg := "FATAL: cannot initialize procedure [" + procedure + "] "
  97. err = errors.New(msg)
  98. return
  99. }
  100. //init and get initial state
  101. state := init(openID)
  102. //do the real concret work for processing the state
  103. err = processProcedureState(state)
  104. return
  105. }
  106. //resume a previous Procedure's state
  107. func resumeProcedure(openID, procedure string) (err error) {
  108. state, err := getCurrentState(openID, procedure)
  109. if err != nil {
  110. return
  111. }
  112. return processProcedureState(state)
  113. }
  114. //finish a procedure, regardless its been finished or not
  115. //normally not finished normally
  116. func stopProcedure(openID, procedure string) {
  117. path := getProcedurePath(openID, procedure)
  118. os.Remove(path)
  119. log.Println("Clearing [" + openID + "] @ [" + procedure + "]")
  120. }
  121. func processProcedureState(state chatState) (err error) {
  122. //send what we need to send
  123. if isExpired(state.Expire) {
  124. return errors.New("State has expired " + state.Name)
  125. }
  126. //mark we have sent.
  127. //do we need input? waiting for input
  128. //if not, what is next state
  129. log.Println(state)
  130. return
  131. }
  132. type initProcedureFunction func(openid string) (initState chatState)
  133. func getProcedureInit(openID, procedure string) initProcedureFunction {
  134. initFunc := map[string]initProcedureFunction{
  135. "TestDummy": nil,
  136. "TestEcho": initTestEcho,
  137. "GetBasicUserInfo": initGetBasicUserInfo,
  138. "GetEmailAddr": initGetBasicUserInfo,
  139. }
  140. return initFunc[procedure]
  141. }
  142. func initTestEcho(openid string) (r chatState) {
  143. r.Name = openid
  144. r.Expire = int32(time.Now().Unix() + 100)
  145. return
  146. }