From 6dd954e8ad7047e9030f5de90cb76cc296a72610 Mon Sep 17 00:00:00 2001 From: surdeus Date: Wed, 12 Jul 2023 02:02:33 +0300 Subject: [PATCH] Implemented rendering also for the inline keyboards. --- src/behx/beh.go | 10 +++++++ src/behx/bot.go | 6 +++- src/behx/button.go | 40 +++++++++++++++++++++++-- src/behx/context.go | 1 - src/behx/errors.go | 2 ++ src/behx/keyboard.go | 19 +++++++++++- src/behx/screen.go | 54 ++++++++++++++++++++++----------- src/behx/session.go | 1 + src/cmd/test/main.go | 71 ++++++++++++++++++++++++++++++-------------- 9 files changed, 159 insertions(+), 45 deletions(-) diff --git a/src/behx/beh.go b/src/behx/beh.go index 16da3b8..53577f1 100644 --- a/src/behx/beh.go +++ b/src/behx/beh.go @@ -8,8 +8,18 @@ package behx type Behaviour struct { Start Action Screens ScreenMap + Keyboards KeyboardMap } +func NewBehaviour( + start Action, + screens ScreenMap, + keyboards KeyboardMap, +) *Behaviour { + return &Behaviour{ + start, screens, keyboards, + } +} // Check whether the screen exists in the behaviour. func (beh *Behaviour) ScreenExists(id ScreenId) bool { diff --git a/src/behx/bot.go b/src/behx/bot.go index b6f79d9..91244b3 100644 --- a/src/behx/bot.go +++ b/src/behx/bot.go @@ -2,6 +2,7 @@ package behx import ( apix "github.com/go-telegram-bot-api/telegram-bot-api/v5" + "log" ) // The wrapper around Telegram API. @@ -66,7 +67,10 @@ func (bot *Bot) Run() error { } screen := bot.Screens[session.CurrentScreenId] - screen.Render(ctx) + err := screen.Render(ctx) + if err != nil { + log.Println("screen rendering:", err) + } } return nil diff --git a/src/behx/button.go b/src/behx/button.go index e60389a..cce3a33 100644 --- a/src/behx/button.go +++ b/src/behx/button.go @@ -6,7 +6,9 @@ import ( // The type wraps Telegram API's button to provide Action functionality. type Button struct { - apix.KeyboardButton + Text string + Data string + Url string Action Action } @@ -18,11 +20,45 @@ type ButtonRow []*Button // Returns new button with specified text and action. func NewButton(text string, action Action) *Button { return &Button{ - KeyboardButton: apix.NewKeyboardButton(text), + Text: text, Action: action, } } +func NewButtonData(text string, data string, action Action) *Button { + return &Button{ + Text: text, + Data: data, + Action: action, + } +} + +func NewButtonUrl(text string, url string, action Action) *Button { + return &Button{ + Text: text, + Url: url, + Action: action, + } +} + +func (btn *Button) ToTelegram() apix.KeyboardButton { + return apix.NewKeyboardButton(btn.Text) +} + +func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton { + if btn.Data != "" { + return apix.NewInlineKeyboardButtonData(btn.Text, btn.Data) + } + + if btn.Url != "" { + return apix.NewInlineKeyboardButtonURL(btn.Text, btn.Url) + } + + // If no match then return the data one with data the same as the text. + return apix.NewInlineKeyboardButtonData(btn.Text, btn.Text) +} + + func NewButtonRow(btns ...*Button) ButtonRow { return btns } diff --git a/src/behx/context.go b/src/behx/context.go index 7c836b4..04daa61 100644 --- a/src/behx/context.go +++ b/src/behx/context.go @@ -28,7 +28,6 @@ func (c *Context) ChangeScreen(screenId ScreenId) error { // Sends to the user specified text. func (c *Context) Send(text string) error { - return nil } diff --git a/src/behx/errors.go b/src/behx/errors.go index d06cf04..47bb6b8 100644 --- a/src/behx/errors.go +++ b/src/behx/errors.go @@ -7,5 +7,7 @@ import ( var ( ScreenNotExistErr = errors.New("screen does not exist") SessionNotExistErr = errors.New("session does not exist") + KeyboardNotExistErr = errors.New("keyboard does not exist") ) + diff --git a/src/behx/keyboard.go b/src/behx/keyboard.go index f528d27..4e58c9e 100644 --- a/src/behx/keyboard.go +++ b/src/behx/keyboard.go @@ -17,12 +17,16 @@ var otherKeyboard = tgbotapi.NewReplyKeyboard( ), )*/ +type KeyboardId string + // The type represents reply keyboard which // is supposed to be showed on a Screen. type Keyboard struct { Rows []ButtonRow } +type KeyboardMap map[KeyboardId] *Keyboard + // Return the new reply keyboard with rows as specified. func NewKeyboard(rows ...ButtonRow) *Keyboard { return &Keyboard{ @@ -36,7 +40,7 @@ func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup { for _, row := range kbd.Rows { buttons := []apix.KeyboardButton{} for _, button := range row { - buttons = append(buttons, apix.NewKeyboardButton(button.Text)) + buttons = append(buttons, button.ToTelegram()) } rows = append(rows, buttons) } @@ -44,6 +48,19 @@ func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup { return apix.NewReplyKeyboard(rows...) } +func (kbd *Keyboard) ToTelegramInline() apix.InlineKeyboardMarkup { + rows := [][]apix.InlineKeyboardButton{} + for _, row := range kbd.Rows { + buttons := []apix.InlineKeyboardButton{} + for _, button := range row { + buttons = append(buttons, button.ToTelegramInline()) + } + rows = append(rows, buttons) + } + + return apix.NewInlineKeyboardMarkup(rows...) +} + // Returns the map of buttons. Used to define the Action. func (kbd *Keyboard) buttonMap() ButtonMap { ret := make(ButtonMap) diff --git a/src/behx/screen.go b/src/behx/screen.go index 8bd8f7e..fc6b90f 100644 --- a/src/behx/screen.go +++ b/src/behx/screen.go @@ -16,20 +16,23 @@ type ScreenText string type Screen struct { // Text to be sent to the user when changing to the screen. Text ScreenText - // Keyboard to be displayed on the screen. - Keyboard *Keyboard + // The keyboard to be sent in the message part. - InlineKeyboard *Keyboard + InlineKeyboardId KeyboardId + + // Keyboard to be displayed on the screen. + KeyboardId KeyboardId } // Map structure for the screens. type ScreenMap map[ScreenId] *Screen // Returns the new screen with specified Text and Keyboard. -func NewScreen(text ScreenText, kbd *Keyboard) *Screen { +func NewScreen(text ScreenText, ikbd KeyboardId, kbd KeyboardId) *Screen { return &Screen { Text: text, - Keyboard: kbd, + InlineKeyboardId: ikbd, + KeyboardId: kbd, } } @@ -39,25 +42,40 @@ func (st ScreenText) String() string { } // Renders output of the screen to the side of the user. -func (s *Screen) Render(c *Context) { +func (s *Screen) Render(c *Context) error { id := c.S.Id.ToTelegram() + msg := apix.NewMessage(id, s.Text.String()) - // First sending the inline keyboard. - if s.InlineKeyboard != nil { - msg.ReplyMarkup = s.InlineKeyboard.ToTelegram() - if _, err := c.B.Send(msg) ; err != nil { - panic(err) + if s.InlineKeyboardId != "" { + kbd, ok := c.B.Keyboards[s.InlineKeyboardId] + if !ok { + return KeyboardNotExistErr } + msg.ReplyMarkup = kbd.ToTelegramInline() } - // Then sending the screen one. - if s.Keyboard != nil { - msg = apix.NewMessage(id, "check") - msg.ReplyMarkup = s.Keyboard.ToTelegram() - if _, err := c.B.Send(msg) ; err != nil { - panic(err) - } + _, err := c.B.Send(msg) + if err != nil { + return err } + + if s.KeyboardId != "" { + msg = apix.NewMessage(id, ">") + + kbd, ok := c.B.Keyboards[s.KeyboardId] + if !ok { + return KeyboardNotExistErr + } + + msg.ReplyMarkup = kbd.ToTelegram() + if _, err := c.B.Send(msg) ; err != nil { + return err + } + + } + + + return nil } diff --git a/src/behx/session.go b/src/behx/session.go index 11d8090..b73be5a 100644 --- a/src/behx/session.go +++ b/src/behx/session.go @@ -10,6 +10,7 @@ type Session struct { Id SessionId CurrentScreenId ScreenId PreviousScreenId ScreenId + KeyboardId KeyboardId } // The type represents map of sessions using diff --git a/src/cmd/test/main.go b/src/cmd/test/main.go index ea36673..56029f0 100644 --- a/src/cmd/test/main.go +++ b/src/cmd/test/main.go @@ -8,34 +8,61 @@ import ( "boteval/src/behx" ) -var startScreen = behx.NewScreen( - "Hello, World!", - behx.NewKeyboard( - behx.NewButtonRow( - behx.NewButton( - "PRESS ME", - behx.NewCustomAction(func(c *behx.Context){ - log.Println("pressed the button!") - }), - ), - behx.NewButton("PRESS ME 2", behx.NewCustomAction(func(c *behx.Context){ - log.Println("pressed another button!") - })), - ), - behx.NewButtonRow( - behx.NewButton("PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){ - log.Println("pressed third button!") - })), +var rootKbd = behx.NewKeyboard( + behx.NewButtonRow( + behx.NewButton( + "PRESS ME", + behx.NewCustomAction(func(c *behx.Context){ + log.Println("pressed the button!") + }), ), + behx.NewButton("PRESS ME 2", behx.NewCustomAction(func(c *behx.Context){ + log.Println("pressed another button!") + })), + ), + behx.NewButtonRow( + behx.NewButton("PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){ + log.Println("pressed third button!") + })), ), ) -var behaviour = &behx.Behaviour{ - Start: behx.NewScreenChange("start"), - Screens: behx.ScreenMap{ +var inlineKbd = behx.NewKeyboard( + behx.NewButtonRow( + behx.NewButton( + "INLINE PRESS ME", + behx.NewCustomAction(func(c *behx.Context){ + log.Println("INLINE pressed the button!") + }), + ), + behx.NewButton("INLINE PRESS ME 2", behx.NewCustomAction(func(c *behx.Context){ + log.Println("INLINE pressed another button!") + })), + ), + behx.NewButtonRow( + behx.NewButton("INLINE PRESS ME 3", behx.NewCustomAction(func(c *behx.Context){ + log.Println("INLINE pressed third button!") + })), + ), +) + + +var startScreen = behx.NewScreen( + "Hello, World!", + "inline", + "root", +) + +var behaviour = behx.NewBehaviour( + behx.NewScreenChange("start"), + behx.ScreenMap{ "start": startScreen, }, -} + behx.KeyboardMap{ + "root": rootKbd, + "inline": inlineKbd, + }, +) func main() { token := os.Getenv("BOT_TOKEN")