Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

173 lines
4.7KB

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