Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

170 lines
4.5KB

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