From 1471d7cdae310f9e24bc69007ce027186862cee4 Mon Sep 17 00:00:00 2001 From: surdeus Date: Sun, 21 Jul 2024 18:02:47 +0500 Subject: [PATCH] feat: another refactoring. Less code needed. No focus on marshalling now. --- beh.go | 13 +++++-- bot.go | 23 ++++++----- build.sh => btest | 0 button.go | 8 ++-- cmd/test/cmd.go | 2 +- cmd/test/incdec.go | 7 ++-- cmd/test/keyboard.go | 4 +- cmd/test/location.go | 4 -- cmd/test/main.go | 61 +++++++---------------------- cmd/test/mutate.go | 21 ++++------ cmd/test/panel.go | 6 +-- cmd/test/start.go | 28 ++++++------- command.go | 18 +++++---- context.go | 93 +++++++++++++++++++++----------------------- devel-loop | 4 ++ file.go | 7 ++-- go.go | 29 +++++++++----- inline.go | 16 ++++---- invoice.go | 2 +- location.go | 4 +- message.go | 12 +++--- reply.go | 2 +- screen.go | 66 ++++++++++++------------------- send.go | 4 +- session.go | 20 +++++----- update.go | 11 +++--- 26 files changed, 205 insertions(+), 260 deletions(-) rename build.sh => btest (100%) create mode 100755 devel-loop diff --git a/beh.go b/beh.go index c40c8f0..4bf9306 100644 --- a/beh.go +++ b/beh.go @@ -4,13 +4,12 @@ package tg type Behaviour struct { Root Component Init Action - Screens ScreenMap + //Screens ScreenMap } // Returns new empty behaviour. func NewBehaviour() *Behaviour { return &Behaviour{ - Screens: make(ScreenMap), } } @@ -21,6 +20,11 @@ func (b *Behaviour) SetInit(a Action) *Behaviour { return b } +/*func (b *Behaviour) SetScreens(screens ScreenMap) *Behaviour { + b.Screens = screens + return b +} + // Sets the root node of the Behaviour. // Mostly used for commands and such stuff. func (b *Behaviour) SetRootNode(node *RootNode) *Behaviour { @@ -28,6 +32,7 @@ func (b *Behaviour) SetRootNode(node *RootNode) *Behaviour { return b } +*/ // The function sets as the standard root widget CommandWidget // and its commands.. func (b *Behaviour) SetRootWidget(root Component) *Behaviour { @@ -35,6 +40,7 @@ func (b *Behaviour) SetRootWidget(root Component) *Behaviour { return b } +/* // Check whether the screen exists in the behaviour. func (beh *Behaviour) PathExist(pth Path) bool { _, ok := beh.Screens[pth] @@ -43,7 +49,6 @@ func (beh *Behaviour) PathExist(pth Path) bool { // Returns the screen by it's ID. func (beh *Behaviour) GetScreen(pth Path) *Screen { - pth = pth.Clean() if !beh.PathExist(pth) { panic(ScreenNotExistErr) } @@ -51,4 +56,4 @@ func (beh *Behaviour) GetScreen(pth Path) *Screen { screen := beh.Screens[pth] return screen } - +*/ diff --git a/bot.go b/bot.go index d5cce6d..f0fb99d 100644 --- a/bot.go +++ b/bot.go @@ -43,7 +43,7 @@ func (bot *Bot) SetDebug(debug bool) *Bot { return bot } -func (bot *Bot) Api() *tgbotapi.BotAPI { +func (bot *Bot) API() *tgbotapi.BotAPI { return bot.api } @@ -53,16 +53,16 @@ func (bot *Bot) Me() User { // Send the Renderable to the specified session client side. // Can be used for both group and private sessions because -// SessionId represents both for chat IDs. +// SessionID represents both for chat IDs. func (bot *Bot) Send( - sid SessionId, v Sendable, + sid SessionID, v Sendable, ) (*Message, error) { config := v.SendConfig(sid, bot) if config.Error != nil { return nil, config.Error } - msg, err := bot.api.Send(config.ToApi()) + msg, err := bot.api.Send(config.ToAPI()) if err != nil { return nil, err } @@ -71,7 +71,7 @@ func (bot *Bot) Send( } func (bot *Bot) Sendf( - sid SessionId, format string, v ...any, + sid SessionID, format string, v ...any, ) (*Message, error){ return bot.Send( sid, @@ -81,7 +81,7 @@ func (bot *Bot) Sendf( // Send to the session specified its ID raw chattable from the tgbotapi. func (bot *Bot) SendRaw( - sid SessionId, v tgbotapi.Chattable, + sid SessionID, v tgbotapi.Chattable, ) (*Message, error) { msg, err := bot.api.Send(v) if err != nil { @@ -93,7 +93,7 @@ func (bot *Bot) SendRaw( // Get session by its ID. Can be used for any scope // including private, group and channel. func (bot *Bot) GotSession( - sid SessionId, + sid SessionID, ) (*Session, bool) { session, ok := bot.sessions[sid] return session, ok @@ -158,7 +158,7 @@ func (bot *Bot) SetCommands( botCmds := []tgbotapi.BotCommand{} for _, cmd := range cmds { - botCmds = append(botCmds, cmd.ToApi()) + botCmds = append(botCmds, cmd.ToAPI()) } //tgbotapi.NewBotCommandScopeAllPrivateChats(), @@ -210,7 +210,7 @@ func (bot *Bot) Run() error { go bot.handleGroup(chn) }*/ - me, _ := bot.Api().GetMe() + me, _ := bot.API().GetMe() bot.me = me for up := range updates { u := Update{ @@ -238,11 +238,10 @@ func (bot *Bot) Run() error { // The function handles updates supposed for the private // chat with the bot. func (bot *Bot) handlePrivate(updates chan Update) { - var sid SessionId + var sid SessionID for u := range updates { - sid = SessionId(u.FromChat().ID) + sid = SessionID(u.FromChat().ID) session, sessionOk := bot.sessions[sid] - if u.Message != nil && !sessionOk { // Creating session if we have none // but only on text messages. diff --git a/build.sh b/btest similarity index 100% rename from build.sh rename to btest diff --git a/button.go b/button.go index d921ccd..7c5b0ba 100644 --- a/button.go +++ b/button.go @@ -78,14 +78,14 @@ func (btn Button) WithSendLocation(ok bool) Button { return btn } -func (btn Button) Go(pth Path) Button { - return btn.WithAction(ScreenGo{ +func (btn Button) Go(pth Widget) Button { + return btn.WithAction(WidgetGo{ Path: pth, }) } -func (btn Button) GoWithArg(pth Path, arg any) Button { - return btn.WithAction(ScreenGo{ +func (btn Button) GoWithArg(pth Widget, arg any) Button { + return btn.WithAction(WidgetGo{ Path: pth, Arg: arg, }) diff --git a/cmd/test/cmd.go b/cmd/test/cmd.go index f2f606a..7ee8dad 100644 --- a/cmd/test/cmd.go +++ b/cmd/test/cmd.go @@ -20,7 +20,7 @@ var BotCommands = []tg.Command{ tg.NewCommand( "start", "start or restart the bot or move to the start screen", - ).Go(StartAbsPath), + ).Go(StartWidget), tg.NewCommand( "info", "info desc", diff --git a/cmd/test/incdec.go b/cmd/test/incdec.go index a8c4fa5..a3cdc62 100644 --- a/cmd/test/incdec.go +++ b/cmd/test/incdec.go @@ -5,10 +5,9 @@ import ( "fmt" ) -const ( - IncDecPath tg.Path = "inc-dec" -) - +// A simple example widget to show +// how to store and get session data values +// and working with dynamic panels. var IncDecWidget = tg.RenderFunc(func(c tg.Context) tg.UI { var ( kbd *tg.InlineCompo diff --git a/cmd/test/keyboard.go b/cmd/test/keyboard.go index fa5f60e..b84de79 100644 --- a/cmd/test/keyboard.go +++ b/cmd/test/keyboard.go @@ -4,8 +4,8 @@ import ( "surdeus.su/core/tg" ) -var HomeButton = tg.Buttonf("Home").Go("/") -var BackButton = tg.Buttonf("Back").Go("-") +var HomeButton = tg.Buttonf("Home").Go(StartWidget) +var BackButton = tg.Buttonf("Back").Go(tg.Back) var BackKeyboard = tg.NewKeyboard().Row( BackButton, ) diff --git a/cmd/test/location.go b/cmd/test/location.go index a03eeea..86ef727 100644 --- a/cmd/test/location.go +++ b/cmd/test/location.go @@ -4,10 +4,6 @@ import ( "surdeus.su/core/tg" ) -const ( - LocationPath tg.Path = "location" -) - var LocationWidget = tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( diff --git a/cmd/test/main.go b/cmd/test/main.go index e5b7c37..5cf81e4 100644 --- a/cmd/test/main.go +++ b/cmd/test/main.go @@ -22,67 +22,36 @@ func ExtractSessionData(c tg.Context) *SessionData { var beh = tg.NewBehaviour().SetInit(tg.Func(func(c tg.Context) { // The session initialization. c.SetSessionData(&SessionData{}) -})).SetRootNode(tg.NewRootNode( - // The "/" widget. - StartWidget, - - tg.NewNode( - PanelPath, - PanelWidget, +})).SetRootWidget( + // Setting as the most top + // widget command handling + // so we can call them at any screen. + tg.NewCommandCompo().SetUsage( + UsageAction, + ).SetPreStart( + PreStartAction, + ).SetCommands( + BotCommands..., ), - - tg.NewNode( - MutateMessagesPath, - MutateMessagesWidget, - - tg.NewNode( - UpperCasePath, - MutateMessagesToUpperCaseWidget, - ), - tg.NewNode( - LowerCasePath, - MutateMessagesToLowerCaseWidget, - ), - tg.NewNode( - EscapePath, - MutateMessagesEscapeWidget, - ), - ), - - tg.NewNode( - IncDecPath, - IncDecWidget, - ), - - tg.NewNode( - LocationPath, - LocationWidget, - ), -)).SetRootWidget(tg.NewCommandCompo().SetUsage( - UsageAction, -).SetPreStart( - PreStartAction, -).SetCommands( - BotCommands..., -)) +) func main() { token := os.Getenv("BOT_TOKEN") bot, err := tg.NewBot(token) if err != nil { - log.Panic(err) + log.Fatalf("tg.NewBot(...): %s", err) } bot = bot.SetBehaviour(beh) - //SetDebug(true) + //bot.API().Debug = true bot.SetData(&BotData{ Name: "Jay", }) - log.Printf("Authorized on account %s", bot.Api().Self.UserName) + log.Printf("Authorized on account %s", bot.API().Self.UserName) err = bot.Run() if err != nil { - panic(err) + log.Fatalf("bot.Run(...): %s", err) } } diff --git a/cmd/test/mutate.go b/cmd/test/mutate.go index 84c6eb1..3094bee 100644 --- a/cmd/test/mutate.go +++ b/cmd/test/mutate.go @@ -43,22 +43,15 @@ func (w *MutateMessageCompo) Filter(u tg.Update) bool { return false } -const ( - UpperCasePath tg.Path = "upper-case" - LowerCasePath = "lower-case" - EscapePath = "escape" - MutateMessagesPath = "mutate-messages" -) - -var MutateMessagesWidget = tg.RenderFunc(func(c tg.Context) tg.UI { +var MutateMessagesWidget= tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( "Choose widget to mutate strings", ).Reply( tg.NewKeyboard().Row( - tg.Buttonf("Upper case").Go(UpperCasePath), - tg.Buttonf("Lower case").Go(LowerCasePath), - tg.Buttonf("Escape chars").Go(EscapePath), + tg.Buttonf("Upper case").Go(UpperCaseWidget), + tg.Buttonf("Lower case").Go(LowerCaseWidget), + tg.Buttonf("Escape chars").Go(EscapeWidget), ).Row( BackButton, ).Reply(), @@ -66,7 +59,7 @@ var MutateMessagesWidget = tg.RenderFunc(func(c tg.Context) tg.UI { } }) -var MutateMessagesToLowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { +var LowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( "Type a string and the bot will convert it to lower case", @@ -77,7 +70,7 @@ var MutateMessagesToLowerCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { } }) -var MutateMessagesToUpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { +var UpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( "Type a string and the bot will convert it to upper case", @@ -87,7 +80,7 @@ var MutateMessagesToUpperCaseWidget = tg.RenderFunc(func(c tg.Context) tg.UI { NewMutateMessageCompo(strings.ToUpper), } }) -var MutateMessagesEscapeWidget = tg.RenderFunc(func(c tg.Context) tg.UI { +var EscapeWidget = tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( "Type a string and the bot will escape characters in it", diff --git a/cmd/test/panel.go b/cmd/test/panel.go index 0156431..fd77ff7 100644 --- a/cmd/test/panel.go +++ b/cmd/test/panel.go @@ -4,11 +4,7 @@ import ( "surdeus.su/core/tg" ) -const ( - PanelPath tg.Path = "panel" -) - -var PanelWidget = tg.RenderFunc(func(c tg.Context) tg.UI { +var DynamicPanelWidget = tg.RenderFunc(func(c tg.Context) tg.UI { var ( n = 0 ln = 4 diff --git a/cmd/test/start.go b/cmd/test/start.go index 0dc5389..6403f5b 100644 --- a/cmd/test/start.go +++ b/cmd/test/start.go @@ -5,36 +5,30 @@ import ( "fmt" ) -const ( - StartAbsPath tg.Path = "/" -) - var StartWidget = tg.RenderFunc(func(c tg.Context) tg.UI { return tg.UI{ tg.Messagef( fmt.Sprint( - "Hello, %s!\n", - "The testing bot started!\n", + "Hello, %s!", + "The testing bot started!", "You can see the basics of usage in the ", - "cmd/test/main.go file!", + "cmd/test/main.go file and other files in the cmd/test!", ), c.CallbackUpdate().SentFrom().UserName, ).Inline( tg.NewKeyboard().Row( - tg.Buttonf("TeleGopher Vultras page"). - WithUrl("https://vultras.su/core/tg"), + tg.Buttonf("TeleGopher surdeus.su page"). + WithUrl("https://surdeus.su/core/tg"), ).Inline(), ), tg.Messagef("Choose your interest").Reply( - tg.NewKeyboard().Row( - tg.Buttonf("Inc/Dec").Go(IncDecPath), - ).Row( - tg.Buttonf("Mutate messages").Go(MutateMessagesPath), - ).Row( - tg.Buttonf("Send location").Go(LocationPath), - ).Row( - tg.Buttonf("Dynamic panel").Go(PanelPath), + tg.NewKeyboard().List( + tg.Buttonf("Inc/Dec").Go(IncDecWidget), + tg.Buttonf("Mutate messages").Go(MutateMessagesWidget), + tg.Buttonf("Send location").Go(LocationWidget), + tg.Buttonf("Dynamic panel").Go(DynamicPanelWidget), + tg.Buttonf("Check panic").Go(nil), ).Reply(), ), diff --git a/command.go b/command.go index e1adbe4..9f3b5a9 100644 --- a/command.go +++ b/command.go @@ -4,6 +4,7 @@ import ( tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) + type CommandType uint8 const ( PrivateCommandType CommandType = iota @@ -44,7 +45,7 @@ func (c Command) WithWidget(w Widget) Command { } // Convert command into the tgbotapi.BotCommand -func (c Command) ToApi() tgbotapi.BotCommand { +func (c Command) ToAPI() tgbotapi.BotCommand { ret := tgbotapi.BotCommand{} ret.Command = string(c.Name) ret.Description = c.Description @@ -52,14 +53,14 @@ func (c Command) ToApi() tgbotapi.BotCommand { } // Simple command to go to another screen. -func (c Command) Go(pth Path) Command { - return c.WithAction(ScreenGo{ +func (c Command) Go(pth Widget) Command { + return c.WithAction(WidgetGo{ Path: pth, }) } -func (c Command) GoWithArg(pth Path, arg any) Command { - return c.WithAction(ScreenGo{ +func (c Command) GoWithArg(pth Widget, arg any) Command { + return c.WithAction(WidgetGo{ Path: pth, Arg: arg, }) @@ -125,7 +126,7 @@ func (compo *CommandCompo) Serve(c Context) { // First should bring the new command into the action. c.Bot().DeleteCommands() err := c.Bot().SetCommands( - tgbotapi.NewBotCommandScopeChat(c.SessionId().ToApi()), + tgbotapi.NewBotCommandScopeChat(c.SessionID().ToAPI()), compo.Commands, ) @@ -135,11 +136,12 @@ func (compo *CommandCompo) Serve(c Context) { var cmdUpdates *UpdateChan for u := range c.Input() { - if c.Path() == "" && u.Message != nil { + if c.Path() == nil && u.Message != nil { // Skipping and executing the preinit action // while we have the empty screen. // E. g. the session did not start. - if !(u.Message.IsCommand() && u.Message.Command() == "start") { + if !u.Message.IsCommand() || + u.Message.Command() != "start" { c.WithUpdate(u).Run(compo.PreStart) continue } diff --git a/context.go b/context.go index 0e35b4c..23a392f 100644 --- a/context.go +++ b/context.go @@ -30,7 +30,7 @@ type Context struct { // maybe you will find another usage for this. // Returns users context by specified session ID // or false if the user is not logged in. -func (c Context) As(sid SessionId) (Context, bool) { +func (c Context) As(sid SessionID) (Context, bool) { s, ok := c.Bot().GotSession(sid) if !ok { return Context{}, false @@ -58,6 +58,9 @@ func (f Func) Render(_ Context) UI { } } +// The type represents type +// of current context the processing is happening +// in. type ContextType uint8 const ( NoContextType ContextType = iota @@ -69,7 +72,14 @@ const ( func (c Context) serve() { beh := c.Bot().behaviour c.Run(beh.Init) - beh.Root.Serve(c) + for { + defer func(){ + if err := recover() ; err != nil { + // Need to add some handling later. + } + }() + beh.Root.Serve(c) + } } func (c Context) Arg() any { @@ -85,12 +95,12 @@ func (c Context) Run(a Action) { // Sends to the Sendable object to the session user. func (c Context) Send(v Sendable) (*Message, error) { - config := v.SendConfig(c.SessionId(), c.Bot()) + config := v.SendConfig(c.SessionID(), c.Bot()) if config.Error != nil { return nil, config.Error } - msg, err := c.Bot().Api().Send(config.ToApi()) + msg, err := c.Bot().API().Send(config.ToAPI()) if err != nil { return nil, err } @@ -172,14 +182,16 @@ func (c Context) CallbackUpdate() *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)}) +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, + c.Bot().API().Token, file.FilePath, )) if err != nil { @@ -193,8 +205,8 @@ func (c Context) GetFile(fileId FileId) (io.ReadCloser, string, error) { } // Reads all the content from the specified file. -func (c Context) ReadFile(fileId FileId) ([]byte, string, error) { - file, pth, err := c.GetFile(fileId) +func (c Context) ReadFile(fileID FileID) ([]byte, string, error) { + file, pth, err := c.GetFile(fileID) if err != nil { return nil, "", err } @@ -240,10 +252,9 @@ func (c Context) runWidget(widget Widget, arg any) (*UpdateChan, error) { return nil, EmptyWidgetErr } - pth := c.Path() compos := widget.Render(c.WithArg(arg)) // Leave if changed path or components are empty. - if compos == nil || pth != c.Path() { + if compos == nil { return nil, EmptyCompoErr } chns := make([]*UpdateChan, len(compos)) @@ -289,52 +300,41 @@ func (c Context) runWidget(widget Widget, arg any) (*UpdateChan, error) { } // Simple go without an argument for context. -func (c Context) Go(pth Path) error { +func (c Context) Go(pth Widget) error { return c.GoWithArg(pth, nil) } -// Changes screen of user to the Id one. -// Also gives the arg to the widget calling -// contexts. -func (c Context) GoWithArg(pth Path, arg any) error { +// Go to the specified widget with the +// specificed argument. +func (c Context) GoWithArg(pth Widget, arg any) error { var err error - if pth == "" { - c.session.pathHistory = []Path{} + if pth == nil { + c.session.pathHistory = []Widget{} return nil } + var back bool - if pth == "-" { + if pth == Back { if len(c.session.pathHistory) < 2 { - return c.GoWithArg("", arg) + return c.GoWithArg(nil, arg) } pth = c.session.pathHistory[len(c.session.pathHistory)-2] - c.session.pathHistory = c.session.pathHistory[:len(c.session.pathHistory)-1] - } - // Getting the screen and changing to - // then executing its widget. - if !pth.IsAbs() { - pth = (c.Path() + "/" + pth).Clean() + c.session.pathHistory = + c.session.pathHistory[:len(c.session.pathHistory)-1] + back = true } - if !c.PathExist(pth) { - return ScreenNotExistErr - } - - if !back && c.Path() != pth { + if !back { c.session.pathHistory = append(c.session.pathHistory, pth) } // Stopping the current widget. - screen := c.Bot().behaviour.Screens[pth] c.session.skippedUpdates.Close() - if screen.Widget != nil { - c.session.skippedUpdates, err = c.runWidget(screen.Widget, arg) - if err != nil { - return err - } - } else { - return NoWidgetForScreenErr + // Running the new one. + c.session.skippedUpdates, err = c.runWidget(pth, arg) + if err != nil { + return err } return nil @@ -352,8 +352,8 @@ func (c Context) SessionData() any { return c.session.Data } -func (c Context) SessionId() SessionId { - return c.session.Id +func (c Context) SessionID() SessionID { + return c.session.ID } func (c Context) SessionScope() SessionScope { @@ -372,20 +372,15 @@ func (c Context) Bot() *Bot { return c.session.bot } -// Returns true if the path exists and false otherwise. -func (c Context) PathExist(pth Path) bool { - return c.session.bot.behaviour.PathExist(pth) -} - // Return context's session's path history. -func (c Context) PathHistory() []Path { +func (c Context) PathHistory() []Widget { return c.session.pathHistory } -func (c Context) Path() Path { +func (c Context) Path() Widget { ln := len(c.session.pathHistory) if ln == 0 { - return "" + return nil } return c.session.pathHistory[ln-1] } diff --git a/devel-loop b/devel-loop new file mode 100755 index 0000000..3a4848a --- /dev/null +++ b/devel-loop @@ -0,0 +1,4 @@ +#!/bin/sh + +wgo sh -c './btest && ./exe/test' + diff --git a/file.go b/file.go index c198398..cebd3ce 100644 --- a/file.go +++ b/file.go @@ -10,6 +10,7 @@ import ( "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) +type FileID string type FileConfig = tgbotapi.FileConfig type PhotoConfig = tgbotapi.PhotoConfig type FileType int @@ -107,10 +108,10 @@ func (f *File) SendData() string { } func (f *File) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (SendConfig) { var config SendConfig - cid := sid.ToApi() + cid := sid.ToAPI() switch f.Type() { case PhotoFileType: @@ -119,7 +120,7 @@ func (f *File) SendConfig( config.Chattable = photo case DocumentFileType: - doc := tgbotapi.NewDocument(sid.ToApi(), f) + doc := tgbotapi.NewDocument(sid.ToAPI(), f) doc.Caption = f.caption config.Chattable = doc default: diff --git a/go.go b/go.go index 985c290..d38b96d 100644 --- a/go.go +++ b/go.go @@ -1,22 +1,33 @@ package tg -func Go(pth Path) UI { +func Go(pth Widget) UI { return UI{ - GoWidget(pth), + WidgetGo{ + Path: pth, + }, } } -type GoWidget string -// Implementing the Server interface. -func (widget GoWidget) Serve(c Context) { - c.input.Close() - c.Go(Path(widget)) +// The type implements changing current path to the widget. +type WidgetGo struct { + Path Widget + Arg any } -func (widget GoWidget) Render(c Context) UI { +func (sc WidgetGo) Act(c Context) { + c.GoWithArg(sc.Path, sc.Arg) +} + +// Implementing the Server interface. +func (widget WidgetGo) Serve(c Context) { + c.input.Close() + c.Go(widget) +} + +func (widget WidgetGo) Render(c Context) UI { return UI{widget} } -func (widget GoWidget) Filter(u Update) bool { +func (widget WidgetGo) Filter(u Update) bool { return true } diff --git a/inline.go b/inline.go index 5c11352..128c971 100644 --- a/inline.go +++ b/inline.go @@ -11,7 +11,7 @@ type Inline struct { } // Convert the inline keyboard to markup for the tgbotapi. -func (kbd Inline) ToApi() tgbotapi.InlineKeyboardMarkup { +func (kbd Inline) ToAPI() tgbotapi.InlineKeyboardMarkup { rows := [][]tgbotapi.InlineKeyboardButton{} for _, row := range kbd.Rows { if row == nil { @@ -38,12 +38,12 @@ type InlineCompo struct { // Implementing the Sendable interface. func (compo *InlineCompo) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (SendConfig) { sendConfig := compo.MessageCompo.SendConfig(sid, bot) msg := sendConfig.Chattable.(tgbotapi.MessageConfig) if len(compo.Inline.Rows) > 0 { - msg.ReplyMarkup = compo.Inline.ToApi() + msg.ReplyMarkup = compo.Inline.ToAPI() } sendConfig.Chattable = msg @@ -56,23 +56,23 @@ func (compo *InlineCompo) SendConfig( func (compo *InlineCompo) Update(c Context) error { if compo.Message != nil { var edit tgbotapi.Chattable - markup := compo.Inline.ToApi() + markup := compo.Inline.ToAPI() ln := len(markup.InlineKeyboard) if ln == 0 || compo.Inline.Rows == nil { edit = tgbotapi.NewEditMessageText( - c.SessionId().ToApi(), + c.SessionID().ToAPI(), compo.Message.MessageID, compo.Text, ) } else { edit = tgbotapi.NewEditMessageTextAndMarkup( - c.SessionId().ToApi(), + c.SessionID().ToAPI(), compo.Message.MessageID, compo.Text, markup, ) } - msg, err := c.Bot().Api().Send(edit) + msg, err := c.Bot().API().Send(edit) if err != nil { return err } @@ -113,7 +113,7 @@ func (compo *InlineCompo) OnOneUpdate(c Context, u Update) error { ) data := u.CallbackQuery.Data - _, err := c.Bot().Api().Request(cb) + _, err := c.Bot().API().Request(cb) if err != nil { return err } diff --git a/invoice.go b/invoice.go index 87d8037..8e8e0d1 100644 --- a/invoice.go +++ b/invoice.go @@ -10,7 +10,7 @@ type InvoiceCompo struct { } func (compo *InvoiceCompo) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (*SendConfig) { return nil } diff --git a/location.go b/location.go index 9bca927..6f94e18 100644 --- a/location.go +++ b/location.go @@ -12,9 +12,9 @@ type LocationCompo struct { } func (compo *LocationCompo) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (SendConfig) { - cid := sid.ToApi() + cid := sid.ToAPI() location := tgbotapi.NewLocation( cid, compo.Latitude, diff --git a/message.go b/message.go index d98ccc6..e810081 100644 --- a/message.go +++ b/message.go @@ -34,11 +34,11 @@ func Escape2(str string) string { // Call the function after the message was sent. func (compo *MessageCompo) Update(c Context) error { edit := tgbotapi.NewEditMessageText( - c.Session().Id.ToApi(), + c.Session().ID.ToAPI(), compo.Message.MessageID, compo.Text, ) - msg, err := c.Bot().Api().Send(edit) + msg, err := c.Bot().API().Send(edit) if err != nil { return err } @@ -49,8 +49,8 @@ func (compo *MessageCompo) Update(c Context) error { // Calling the method removes the message on the client side // and sets the Message in the component to nil. func (compo *MessageCompo) Delete(c Context) error { - cfg := tgbotapi.NewDeleteMessage(c.session.Id.ToApi(), compo.Message.MessageID) - _, err := c.Bot().Api().Send(cfg) + cfg := tgbotapi.NewDeleteMessage(c.session.ID.ToAPI(), compo.Message.MessageID) + _, err := c.Bot().API().Send(cfg) if err != nil { return err } @@ -121,7 +121,7 @@ func (msg *MessageCompo) Location( // Implementing the Sendable interface. func (compo *MessageCompo) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (SendConfig) { var ( ret SendConfig @@ -136,7 +136,7 @@ func (compo *MessageCompo) SendConfig( text = compo.Text } - msg := tgbotapi.NewMessage(sid.ToApi(), text) + msg := tgbotapi.NewMessage(sid.ToAPI(), text) msg.ParseMode = compo.ParseMode ret.Chattable = msg diff --git a/reply.go b/reply.go index 1b42ec1..316f256 100644 --- a/reply.go +++ b/reply.go @@ -64,7 +64,7 @@ type ReplyCompo struct { // Implementing the sendable interface. func (compo *ReplyCompo) SendConfig( - sid SessionId, bot *Bot, + sid SessionID, bot *Bot, ) (SendConfig) { sendConfig := compo.MessageCompo.SendConfig(sid, bot) diff --git a/screen.go b/screen.go index 674180a..f8a9787 100644 --- a/screen.go +++ b/screen.go @@ -1,48 +1,32 @@ package tg -import ( - "path" +type WidgetSpecial int +const ( + widgetEmpty WidgetSpecial = iota + widgetBack ) -// The type implements changing screen to the underlying ScreenId -type ScreenGo struct { - Path Path - Arg any + +func (w WidgetSpecial) Render(_ Context) UI { + return nil } -func (sc ScreenGo) Act(c Context) { - c.GoWithArg(sc.Path, sc.Arg) -} +var ( + Back = Widget(widgetBack) +) -// The same as Act. -func (sc ScreenGo) Serve(c Context) { - sc.Act(c) -} - -// Unique identifier for the screen -// and relative paths to the screen. -type Path string +/*// Unique identifier for the screen. +type Path int +const ( + PathEmpty Path = 0 + // Going to the path returns + // a context to the previous screen. + PathBack Path = -1 +) // Returns true if the path is empty. func (p Path) IsEmpty() bool { - return p == "" -} - -// Returns true if the path is absolute. -func (p Path) IsAbs() bool { - if len(p) == 0 { - return false - } - return p[0] == '/' -} - -func (p Path) Dir() Path { - return Path(path.Dir(string(p))) -} - -// Clean the path deleting exceed ., .. and / . -func (p Path) Clean() Path { - return Path(path.Clean(string(p))) + return p == 0 } // Screen statement of the bot. @@ -64,10 +48,10 @@ type Node struct { Path Path Screen *Screen Subs []*Node -} +}*/ // Return new root node with the specified widget in the screen. -func NewRootNode(widget Widget, subs ...*Node) *RootNode { +/*func NewRootNode(widget Widget, subs ...*Node) *RootNode { ret := &RootNode{} ret.Screen = NewScreen(widget) ret.Subs = subs @@ -114,15 +98,13 @@ func (n *Node) ScreenMap(root Path) ScreenMap { } } return m -} +}*/ -// Map structure for the screens. -type ScreenMap map[Path] *Screen // Returns the new screen with specified name and widget. -func NewScreen(widget Widget) *Screen { +/*func NewScreen(widget Widget) *Screen { return &Screen{ Widget: widget, } -} +}*/ diff --git a/send.go b/send.go index 7403286..860f260 100644 --- a/send.go +++ b/send.go @@ -10,7 +10,7 @@ type MessageId int64 // way to define what message will be // sent to the side of a user. type Sendable interface { - SendConfig(SessionId, *Bot) (SendConfig) + SendConfig(SessionID, *Bot) (SendConfig) SetMessage(*Message) } @@ -25,7 +25,7 @@ type SendConfig struct { type MessageMap map[string] *Message // Convert to the bot.Api.Send format. -func (config SendConfig) ToApi() tgbotapi.Chattable { +func (config SendConfig) ToAPI() tgbotapi.Chattable { return config.Chattable } diff --git a/session.go b/session.go index e8476fe..96ada00 100644 --- a/session.go +++ b/session.go @@ -2,12 +2,12 @@ package tg // The type represents map of sessions using // as key. -type SessionMap map[SessionId]*Session +type SessionMap map[SessionID]*Session // Add new empty session by it's ID. func (sm SessionMap) Add( bot *Bot, - sid SessionId, + sid SessionID, scope SessionScope, ) *Session { ret := NewSession(bot, sid, scope) @@ -27,32 +27,32 @@ const ( // Represents unique value to identify chats. // In fact is simply ID of the chat. -type SessionId int64 +type SessionID int64 -// Convert the SessionId to Telegram API's type. -func (si SessionId) ToApi() int64 { +// Convert the SessionID to Telegram API's type. +func (si SessionID) ToAPI() int64 { return int64(si) } // The type represents current state of // user interaction per each of them. type Session struct { - // Id of the chat of the user. - Id SessionId + // ID of the chat of the user. + ID SessionID Scope SessionScope // Custom value for each user. Data any bot *Bot - pathHistory []Path + pathHistory []Widget skippedUpdates *UpdateChan updates *UpdateChan } // Return new empty session. -func NewSession(bot *Bot, id SessionId, scope SessionScope) *Session { +func NewSession(bot *Bot, id SessionID, scope SessionScope) *Session { ret := &Session{} - ret.Id = id + ret.ID = id ret.Scope = scope ret.bot = bot ret.updates = NewUpdateChan() diff --git a/update.go b/update.go index cfd3122..a08809b 100644 --- a/update.go +++ b/update.go @@ -2,7 +2,6 @@ package tg import tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" -type FileId string type Update struct { tgbotapi.Update @@ -64,8 +63,8 @@ func (u Update) HasDocument() bool { u.Message.Document != nil } -func (u Update) DocumentId() FileId { - return FileId(u.Update.Message.Document.FileID) +func (u Update) DocumentID() FileID { + return FileID(u.Update.Message.Document.FileID) } func (u *Update) DocumentName() string { @@ -85,10 +84,10 @@ func (u Update) HasPhotos() bool { len(u.Message.Photo) != 0 } -func (u Update) PhotoIds() []FileId { - ret := make([]FileId, len(u.Message.Photo)) +func (u Update) PhotoIDs() []FileID { + ret := make([]FileID, len(u.Message.Photo)) for i, photo := range u.Message.Photo { - ret[i] = FileId(photo.FileID) + ret[i] = FileID(photo.FileID) } return ret }