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 {
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 {

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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")
)

View file

@ -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)

View file

@ -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
}

View file

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

View file

@ -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")