123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package tg
- import (
- "fmt"
- "io"
- "net/http"
- tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
- //"path"
- )
- // Interface to interact with the user.
- type Context struct {
- session *Session
- // The update that called the Context usage.
- update Update
- // Used as way to provide outer values redirection
- // into widgets and actions. It is like arguments
- // for REST API request etc.
- arg any
- typ ContextType
- // Instead of updates as argument.
- input *UpdateChan
- }
- // Run commands as other user. Was implemented to
- // make other user to leave the bot at first but
- // maybe you will find another usage for this.
- // Returns users context by specified session ID
- // or nil if the user is not logged in.
- func (c Context) As(sid SessionId) Context {
- n, ok := c.Bot.contexts[sid]
- if !ok {
- return nil
- }
- return &Context{
- context: n,
- }
- }
- // General type function to define actions, single component widgets
- // and components themselves.
- type Func func(Context)
- func (f Func) Act(c Context) {
- f(c)
- }
- func (f Func) Serve(c Context) {
- f(c)
- }
- func(f Func) Filter(_ *Update) bool {
- return false
- }
- func (f Func) Render(_ *Context) UI {
- return UI{
- f,
- }
- }
- type ContextType uint8
- const (
- NoContextType ContextType = iota
- WidgetContextType
- ActionContextType
- )
- // Goroutie function to handle each user.
- func (c Context) serve() {
- beh := c.Bot.behaviour
- c.Run(beh.Init)
- beh.Root.Serve(c)
- }
- func (c Context) Path() Path {
- ln := len(c.pathHistory)
- if ln == 0 {
- return ""
- }
- return c.pathHistory[ln-1]
- }
- func (c Context) Arg() any {
- return c.arg
- }
- func (c Context) Run(a Action) {
- if a != nil {
- a.Act(c)
- }
- }
- // Only for the root widget usage.
- // Skip the update sending it down to
- // the underlying widget.
- func (c Context) Skip(u Update) {
- c.skippedUpdates.Send(u)
- }
- // Sends to the Sendable object.
- func (c Context) Send(v Sendable) (Message, error) {
- config := v.SendConfig(c.Session.Id, c.Bot)
- if config.Error != nil {
- return nil, config.Error
- }
- msg, err := c.Bot.Api.Send(config.ToApi())
- if err != nil {
- return nil, err
- }
- return &msg, nil
- }
- // Sends the formatted with fmt.Sprintf message to the user
- // using default Markdown parsing format.
- func (c Context) Sendf(format string, v ...any) (Message, error) {
- return c.Send(NewMessage(format, v...))
- }
- // Same as Sendf but uses Markdown 2 format for parsing.
- func (c Context) Sendf2(format string, v ...any) (Message, error) {
- return c.Send(NewMessage(fmt.Sprintf(format, v...)).MD2())
- }
- // Same as Sendf but uses HTML format for parsing.
- func (c Context) SendfHTML(format string, v ...any) (Message, error) {
- return c.Send(NewMessage(fmt.Sprintf(format, v...)).HTML())
- }
- // Send the message in raw format escaping all the special characters.
- func (c Context) SendfR(format string, v ...any) (Message, error) {
- return c.Send(NewMessage(Escape2(fmt.Sprintf(format, v...))).MD2())
- }
- // Get the input for current widget.
- // Should be used inside handlers (aka "Serve").
- func (c Context) Input() chan Update {
- return c.input.Chan()
- }
- func (c Context) WithArg(v any) Context {
- c.arg = v
- return c
- }
- func (c Context) WithUpdate(u *Update) Context {
- c.Update = u
- return c
- }
- func (c Context) WithInput(input *UpdateChan) Context {
- c.input = input
- return c
- }
- func (c Context) Go(pth Path) error {
- return c.session.go_(pth, nil)
- }
- func (c Context) GoWithArg(pth Path, arg any) error {
- return c.session.go_(pth, arg)
- }
- // Customized actions for the bot.
- type Action interface {
- Act(Context)
- }
- type ActionFunc func(Context)
- func (af ActionFunc) Act(c *Context) {
- af(c)
- }
- func (c Context) History() []Path {
- return c.session.pathHistory
- }
- func (c Context) PathExist(pth Path) bool {
- return c.bot.behaviour.PathExist(pth)
- }
- // Simple way to read strings for widgets with
- // the specified prompt.
- func (c Context) ReadString(promptf string, args ...any) string {
- var text string
- if pref != "" {
- c.Sendf(promptf, args...)
- }
- for u := range c.Input() {
- if u == nil {
- break
- }
- if u.Message == nil {
- continue
- }
- text = u.Message.Text
- break
- }
- return text
- }
- func (c Context) Update() Update {
- return c.update
- }
- // Returns the reader for specified file ID and path.
- func (c *Context) GetFile(fileId FileId) (io.ReadCloser, string, error) {
- file, err := c.Bot.Api.GetFile(tgbotapi.FileConfig{FileID:string(fileId)})
- if err != nil {
- return nil, "", err
- }
- r, err := http.Get(fmt.Sprintf(
- "https://api.telegram.org/file/bot%s/%s",
- c.Bot.Api.Token,
- file.FilePath,
- ))
- if err != nil {
- return nil, "", err
- }
- if r.StatusCode != 200 {
- return nil, "", StatusCodeErr
- }
- return r.Body, file.FilePath, nil
- }
- func (c *Context) ReadFile(fileId FileId) ([]byte, string, error) {
- file, pth, err := c.GetFile(fileId)
- if err != nil {
- return nil, "", err
- }
- defer file.Close()
- bts, err := io.ReadAll(file)
- if err != nil {
- return nil, "", err
- }
- return bts, pth, nil
- }
|