Implemented rendering also for the inline keyboards.

This commit is contained in:
Andrey Parhomenko 2023-07-12 02:02:33 +03:00
parent c537ccdec1
commit 6dd954e8ad
9 changed files with 159 additions and 45 deletions

View file

@ -8,8 +8,18 @@ package behx
type Behaviour struct { type Behaviour struct {
Start Action Start Action
Screens ScreenMap 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. // Check whether the screen exists in the behaviour.
func (beh *Behaviour) ScreenExists(id ScreenId) bool { func (beh *Behaviour) ScreenExists(id ScreenId) bool {

View file

@ -2,6 +2,7 @@ package behx
import ( import (
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5" apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"log"
) )
// The wrapper around Telegram API. // The wrapper around Telegram API.
@ -66,7 +67,10 @@ func (bot *Bot) Run() error {
} }
screen := bot.Screens[session.CurrentScreenId] screen := bot.Screens[session.CurrentScreenId]
screen.Render(ctx) err := screen.Render(ctx)
if err != nil {
log.Println("screen rendering:", err)
}
} }
return nil return nil

View file

@ -6,7 +6,9 @@ import (
// The type wraps Telegram API's button to provide Action functionality. // The type wraps Telegram API's button to provide Action functionality.
type Button struct { type Button struct {
apix.KeyboardButton Text string
Data string
Url string
Action Action Action Action
} }
@ -18,11 +20,45 @@ type ButtonRow []*Button
// Returns new button with specified text and action. // Returns new button with specified text and action.
func NewButton(text string, action Action) *Button { func NewButton(text string, action Action) *Button {
return &Button{ return &Button{
KeyboardButton: apix.NewKeyboardButton(text), Text: text,
Action: action, 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 { func NewButtonRow(btns ...*Button) ButtonRow {
return btns return btns
} }

View file

@ -28,7 +28,6 @@ func (c *Context) ChangeScreen(screenId ScreenId) error {
// Sends to the user specified text. // Sends to the user specified text.
func (c *Context) Send(text string) error { func (c *Context) Send(text string) error {
return nil return nil
} }

View file

@ -7,5 +7,7 @@ import (
var ( var (
ScreenNotExistErr = errors.New("screen does not exist") ScreenNotExistErr = errors.New("screen does not exist")
SessionNotExistErr = errors.New("session does not exist") SessionNotExistErr = errors.New("session does not exist")
KeyboardNotExistErr = errors.New("keyboard does not exist")
) )

View file

@ -17,12 +17,16 @@ var otherKeyboard = tgbotapi.NewReplyKeyboard(
), ),
)*/ )*/
type KeyboardId string
// The type represents reply keyboard which // The type represents reply keyboard which
// is supposed to be showed on a Screen. // is supposed to be showed on a Screen.
type Keyboard struct { type Keyboard struct {
Rows []ButtonRow Rows []ButtonRow
} }
type KeyboardMap map[KeyboardId] *Keyboard
// Return the new reply keyboard with rows as specified. // Return the new reply keyboard with rows as specified.
func NewKeyboard(rows ...ButtonRow) *Keyboard { func NewKeyboard(rows ...ButtonRow) *Keyboard {
return &Keyboard{ return &Keyboard{
@ -36,7 +40,7 @@ func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup {
for _, row := range kbd.Rows { for _, row := range kbd.Rows {
buttons := []apix.KeyboardButton{} buttons := []apix.KeyboardButton{}
for _, button := range row { for _, button := range row {
buttons = append(buttons, apix.NewKeyboardButton(button.Text)) buttons = append(buttons, button.ToTelegram())
} }
rows = append(rows, buttons) rows = append(rows, buttons)
} }
@ -44,6 +48,19 @@ func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup {
return apix.NewReplyKeyboard(rows...) 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. // Returns the map of buttons. Used to define the Action.
func (kbd *Keyboard) buttonMap() ButtonMap { func (kbd *Keyboard) buttonMap() ButtonMap {
ret := make(ButtonMap) ret := make(ButtonMap)

View file

@ -16,20 +16,23 @@ type ScreenText string
type Screen struct { type Screen struct {
// Text to be sent to the user when changing to the screen. // Text to be sent to the user when changing to the screen.
Text ScreenText Text ScreenText
// Keyboard to be displayed on the screen.
Keyboard *Keyboard
// The keyboard to be sent in the message part. // 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. // Map structure for the screens.
type ScreenMap map[ScreenId] *Screen type ScreenMap map[ScreenId] *Screen
// Returns the new screen with specified Text and Keyboard. // 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 { return &Screen {
Text: text, 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. // 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() id := c.S.Id.ToTelegram()
msg := apix.NewMessage(id, s.Text.String()) msg := apix.NewMessage(id, s.Text.String())
// First sending the inline keyboard. if s.InlineKeyboardId != "" {
if s.InlineKeyboard != nil { kbd, ok := c.B.Keyboards[s.InlineKeyboardId]
msg.ReplyMarkup = s.InlineKeyboard.ToTelegram() if !ok {
if _, err := c.B.Send(msg) ; err != nil { return KeyboardNotExistErr
panic(err)
} }
msg.ReplyMarkup = kbd.ToTelegramInline()
} }
// Then sending the screen one. _, err := c.B.Send(msg)
if s.Keyboard != nil { if err != nil {
msg = apix.NewMessage(id, "check") return err
msg.ReplyMarkup = s.Keyboard.ToTelegram()
if _, err := c.B.Send(msg) ; err != nil {
panic(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
} }

View file

@ -10,6 +10,7 @@ type Session struct {
Id SessionId Id SessionId
CurrentScreenId ScreenId CurrentScreenId ScreenId
PreviousScreenId ScreenId PreviousScreenId ScreenId
KeyboardId KeyboardId
} }
// The type represents map of sessions using // The type represents map of sessions using

View file

@ -8,34 +8,61 @@ import (
"boteval/src/behx" "boteval/src/behx"
) )
var startScreen = behx.NewScreen( var rootKbd = behx.NewKeyboard(
"Hello, World!", behx.NewButtonRow(
behx.NewKeyboard( behx.NewButton(
behx.NewButtonRow( "PRESS ME",
behx.NewButton( behx.NewCustomAction(func(c *behx.Context){
"PRESS ME", log.Println("pressed the button!")
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!")
})),
), ),
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{ var inlineKbd = behx.NewKeyboard(
Start: behx.NewScreenChange("start"), behx.NewButtonRow(
Screens: behx.ScreenMap{ 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, "start": startScreen,
}, },
} behx.KeyboardMap{
"root": rootKbd,
"inline": inlineKbd,
},
)
func main() { func main() {
token := os.Getenv("BOT_TOKEN") token := os.Getenv("BOT_TOKEN")