context.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package tg
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
  7. //"path"
  8. )
  9. // Interface to interact with the user.
  10. type Context struct {
  11. session *Session
  12. // The update that called the Context usage.
  13. update Update
  14. // Used as way to provide outer values redirection
  15. // into widgets and actions. It is like arguments
  16. // for REST API request etc.
  17. arg any
  18. typ ContextType
  19. // Instead of updates as argument.
  20. input *UpdateChan
  21. }
  22. // Run commands as other user. Was implemented to
  23. // make other user to leave the bot at first but
  24. // maybe you will find another usage for this.
  25. // Returns users context by specified session ID
  26. // or nil if the user is not logged in.
  27. func (c Context) As(sid SessionId) Context {
  28. n, ok := c.Bot.contexts[sid]
  29. if !ok {
  30. return nil
  31. }
  32. return &Context{
  33. context: n,
  34. }
  35. }
  36. // General type function to define actions, single component widgets
  37. // and components themselves.
  38. type Func func(Context)
  39. func (f Func) Act(c Context) {
  40. f(c)
  41. }
  42. func (f Func) Serve(c Context) {
  43. f(c)
  44. }
  45. func(f Func) Filter(_ *Update) bool {
  46. return false
  47. }
  48. func (f Func) Render(_ *Context) UI {
  49. return UI{
  50. f,
  51. }
  52. }
  53. type ContextType uint8
  54. const (
  55. NoContextType ContextType = iota
  56. WidgetContextType
  57. ActionContextType
  58. )
  59. // Goroutie function to handle each user.
  60. func (c Context) serve() {
  61. beh := c.Bot.behaviour
  62. c.Run(beh.Init)
  63. beh.Root.Serve(c)
  64. }
  65. func (c Context) Path() Path {
  66. ln := len(c.pathHistory)
  67. if ln == 0 {
  68. return ""
  69. }
  70. return c.pathHistory[ln-1]
  71. }
  72. func (c Context) Arg() any {
  73. return c.arg
  74. }
  75. func (c Context) Run(a Action) {
  76. if a != nil {
  77. a.Act(c)
  78. }
  79. }
  80. // Only for the root widget usage.
  81. // Skip the update sending it down to
  82. // the underlying widget.
  83. func (c Context) Skip(u Update) {
  84. c.skippedUpdates.Send(u)
  85. }
  86. // Sends to the Sendable object.
  87. func (c Context) Send(v Sendable) (Message, error) {
  88. config := v.SendConfig(c.Session.Id, c.Bot)
  89. if config.Error != nil {
  90. return nil, config.Error
  91. }
  92. msg, err := c.Bot.Api.Send(config.ToApi())
  93. if err != nil {
  94. return nil, err
  95. }
  96. return &msg, nil
  97. }
  98. // Sends the formatted with fmt.Sprintf message to the user
  99. // using default Markdown parsing format.
  100. func (c Context) Sendf(format string, v ...any) (Message, error) {
  101. return c.Send(NewMessage(format, v...))
  102. }
  103. // Same as Sendf but uses Markdown 2 format for parsing.
  104. func (c Context) Sendf2(format string, v ...any) (Message, error) {
  105. return c.Send(NewMessage(fmt.Sprintf(format, v...)).MD2())
  106. }
  107. // Same as Sendf but uses HTML format for parsing.
  108. func (c Context) SendfHTML(format string, v ...any) (Message, error) {
  109. return c.Send(NewMessage(fmt.Sprintf(format, v...)).HTML())
  110. }
  111. // Send the message in raw format escaping all the special characters.
  112. func (c Context) SendfR(format string, v ...any) (Message, error) {
  113. return c.Send(NewMessage(Escape2(fmt.Sprintf(format, v...))).MD2())
  114. }
  115. // Get the input for current widget.
  116. // Should be used inside handlers (aka "Serve").
  117. func (c Context) Input() chan Update {
  118. return c.input.Chan()
  119. }
  120. func (c Context) WithArg(v any) Context {
  121. c.arg = v
  122. return c
  123. }
  124. func (c Context) WithUpdate(u *Update) Context {
  125. c.Update = u
  126. return c
  127. }
  128. func (c Context) WithInput(input *UpdateChan) Context {
  129. c.input = input
  130. return c
  131. }
  132. func (c Context) Go(pth Path) error {
  133. return c.session.go_(pth, nil)
  134. }
  135. func (c Context) GoWithArg(pth Path, arg any) error {
  136. return c.session.go_(pth, arg)
  137. }
  138. // Customized actions for the bot.
  139. type Action interface {
  140. Act(Context)
  141. }
  142. type ActionFunc func(Context)
  143. func (af ActionFunc) Act(c *Context) {
  144. af(c)
  145. }
  146. func (c Context) History() []Path {
  147. return c.session.pathHistory
  148. }
  149. func (c Context) PathExist(pth Path) bool {
  150. return c.bot.behaviour.PathExist(pth)
  151. }
  152. // Simple way to read strings for widgets with
  153. // the specified prompt.
  154. func (c Context) ReadString(promptf string, args ...any) string {
  155. var text string
  156. if pref != "" {
  157. c.Sendf(promptf, args...)
  158. }
  159. for u := range c.Input() {
  160. if u == nil {
  161. break
  162. }
  163. if u.Message == nil {
  164. continue
  165. }
  166. text = u.Message.Text
  167. break
  168. }
  169. return text
  170. }
  171. func (c Context) Update() Update {
  172. return c.update
  173. }
  174. // Returns the reader for specified file ID and path.
  175. func (c *Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
  176. file, err := c.Bot.Api.GetFile(tgbotapi.FileConfig{FileID:string(fileId)})
  177. if err != nil {
  178. return nil, "", err
  179. }
  180. r, err := http.Get(fmt.Sprintf(
  181. "https://api.telegram.org/file/bot%s/%s",
  182. c.Bot.Api.Token,
  183. file.FilePath,
  184. ))
  185. if err != nil {
  186. return nil, "", err
  187. }
  188. if r.StatusCode != 200 {
  189. return nil, "", StatusCodeErr
  190. }
  191. return r.Body, file.FilePath, nil
  192. }
  193. func (c *Context) ReadFile(fileId FileId) ([]byte, string, error) {
  194. file, pth, err := c.GetFile(fileId)
  195. if err != nil {
  196. return nil, "", err
  197. }
  198. defer file.Close()
  199. bts, err := io.ReadAll(file)
  200. if err != nil {
  201. return nil, "", err
  202. }
  203. return bts, pth, nil
  204. }