Got rid off map of keyboards.
This commit is contained in:
parent
c2562cc54c
commit
772adb7b8b
9 changed files with 228 additions and 183 deletions
128
cmd/test/main.go
128
cmd/test/main.go
|
@ -12,20 +12,11 @@ type UserData struct {
|
||||||
Counter int
|
Counter int
|
||||||
}
|
}
|
||||||
|
|
||||||
var startScreenButton = tx.NewButton("🏠 To the start screen").
|
var (
|
||||||
ScreenChange("start")
|
startScreenButton = tx.NewButton("🏠 To the start screen").
|
||||||
|
ScreenChange("start")
|
||||||
|
|
||||||
var beh = tx.NewBehaviour().
|
incDecKeyboard = tx.NewKeyboard("").Row(
|
||||||
|
|
||||||
// The function will be called every time
|
|
||||||
// the bot is started.
|
|
||||||
OnStartFunc(func(c *tx.A) {
|
|
||||||
c.V = &UserData{}
|
|
||||||
c.ChangeScreen("start")
|
|
||||||
}).WithKeyboards(
|
|
||||||
|
|
||||||
// Increment/decrement keyboard.
|
|
||||||
tx.NewKeyboard("inc/dec").Row(
|
|
||||||
tx.NewButton("+").ActionFunc(func(c *tx.A) {
|
tx.NewButton("+").ActionFunc(func(c *tx.A) {
|
||||||
d := c.V.(*UserData)
|
d := c.V.(*UserData)
|
||||||
d.Counter++
|
d.Counter++
|
||||||
|
@ -38,49 +29,60 @@ var beh = tx.NewBehaviour().
|
||||||
}),
|
}),
|
||||||
).Row(
|
).Row(
|
||||||
startScreenButton,
|
startScreenButton,
|
||||||
),
|
)
|
||||||
|
|
||||||
// The navigational keyboard.
|
navKeyboard = tx.NewKeyboard("Choose your interest").
|
||||||
tx.NewKeyboard("nav").Row(
|
WithOneTime(true).
|
||||||
tx.NewButton("Inc/Dec").ScreenChange("inc/dec"),
|
Row(
|
||||||
).Row(
|
tx.NewButton("Inc/Dec").ScreenChange("inc/dec"),
|
||||||
|
).Row(
|
||||||
tx.NewButton("Upper case").ScreenChange("upper-case"),
|
tx.NewButton("Upper case").ScreenChange("upper-case"),
|
||||||
tx.NewButton("Lower case").ScreenChange("lower-case"),
|
tx.NewButton("Lower case").ScreenChange("lower-case"),
|
||||||
).Row(
|
).Row(
|
||||||
tx.NewButton("Send location").
|
tx.NewButton("Send location").ScreenChange("send-location"),
|
||||||
WithSendLocation(true).
|
)
|
||||||
ActionFunc(func(c *tx.A) {
|
|
||||||
var err error
|
|
||||||
if c.U.Message.Location != nil {
|
|
||||||
l := c.U.Message.Location
|
|
||||||
err = c.Sendf(
|
|
||||||
"Longitude: %f\n"+
|
|
||||||
"Latitude: %f\n"+
|
|
||||||
"Heading: %d"+
|
|
||||||
"",
|
|
||||||
l.Longitude,
|
|
||||||
l.Latitude,
|
|
||||||
l.Heading,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
err = c.Send("Somehow wrong location was sent")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
c.Send(err)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
|
|
||||||
tx.NewKeyboard("istart").Row(
|
sendLocationKeyboard = tx.NewKeyboard("Press the button to send your location").
|
||||||
tx.NewButton("GoT Github page").
|
Row(
|
||||||
WithUrl("https://github.com/mojosa-software/got"),
|
tx.NewButton("Send location").
|
||||||
),
|
WithSendLocation(true).
|
||||||
|
ActionFunc(func(c *tx.A) {
|
||||||
|
var err error
|
||||||
|
if c.U.Message.Location != nil {
|
||||||
|
l := c.U.Message.Location
|
||||||
|
err = c.Sendf(
|
||||||
|
"Longitude: %f\n"+
|
||||||
|
"Latitude: %f\n"+
|
||||||
|
"Heading: %d"+
|
||||||
|
"",
|
||||||
|
l.Longitude,
|
||||||
|
l.Latitude,
|
||||||
|
l.Heading,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
err = c.Send("Somehow wrong location was sent")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
c.Send(err)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
).Row(
|
||||||
|
startScreenButton,
|
||||||
|
)
|
||||||
|
|
||||||
// The keyboard to return to the start screen.
|
// The keyboard to return to the start screen.
|
||||||
tx.NewKeyboard("nav-start").Row(
|
navToStartKeyboard = tx.NewKeyboard("").Row(
|
||||||
startScreenButton,
|
startScreenButton,
|
||||||
),
|
)
|
||||||
).WithScreens(
|
)
|
||||||
|
|
||||||
|
var beh = tx.NewBehaviour().
|
||||||
|
OnStartFunc(func(c *tx.A) {
|
||||||
|
// The function will be called every time
|
||||||
|
// the bot is started.
|
||||||
|
c.V = &UserData{}
|
||||||
|
c.ChangeScreen("start")
|
||||||
|
}).WithScreens(
|
||||||
tx.NewScreen("start").
|
tx.NewScreen("start").
|
||||||
WithText(
|
WithText(
|
||||||
"The bot started!"+
|
"The bot started!"+
|
||||||
|
@ -88,8 +90,14 @@ var beh = tx.NewBehaviour().
|
||||||
" understand of how the API works, so just"+
|
" understand of how the API works, so just"+
|
||||||
" horse around a bit to guess everything out"+
|
" horse around a bit to guess everything out"+
|
||||||
" by yourself!",
|
" by yourself!",
|
||||||
).Keyboard("nav").
|
).WithKeyboard(navKeyboard).
|
||||||
IKeyboard("istart"),
|
// The inline keyboard with link to GitHub page.
|
||||||
|
WithIKeyboard(
|
||||||
|
tx.NewKeyboard("istart").Row(
|
||||||
|
tx.NewButton("GoT Github page").
|
||||||
|
WithUrl("https://github.com/mojosa-software/got"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
tx.NewScreen("inc/dec").
|
tx.NewScreen("inc/dec").
|
||||||
WithText(
|
WithText(
|
||||||
|
@ -98,7 +106,7 @@ var beh = tx.NewBehaviour().
|
||||||
"by saving the counter for each of users "+
|
"by saving the counter for each of users "+
|
||||||
"separately. ",
|
"separately. ",
|
||||||
).
|
).
|
||||||
Keyboard("inc/dec").
|
WithKeyboard(incDecKeyboard).
|
||||||
// The function will be called when reaching the screen.
|
// The function will be called when reaching the screen.
|
||||||
ActionFunc(func(c *tx.A) {
|
ActionFunc(func(c *tx.A) {
|
||||||
d := c.V.(*UserData)
|
d := c.V.(*UserData)
|
||||||
|
@ -107,13 +115,27 @@ var beh = tx.NewBehaviour().
|
||||||
|
|
||||||
tx.NewScreen("upper-case").
|
tx.NewScreen("upper-case").
|
||||||
WithText("Type text and the bot will send you the upper case version to you").
|
WithText("Type text and the bot will send you the upper case version to you").
|
||||||
Keyboard("nav-start").
|
WithKeyboard(navToStartKeyboard).
|
||||||
ActionFunc(mutateMessage(strings.ToUpper)),
|
ActionFunc(mutateMessage(strings.ToUpper)),
|
||||||
|
|
||||||
tx.NewScreen("lower-case").
|
tx.NewScreen("lower-case").
|
||||||
WithText("Type text and the bot will send you the lower case version").
|
WithText("Type text and the bot will send you the lower case version").
|
||||||
Keyboard("nav-start").
|
WithKeyboard(navToStartKeyboard).
|
||||||
ActionFunc(mutateMessage(strings.ToLower)),
|
ActionFunc(mutateMessage(strings.ToLower)),
|
||||||
|
|
||||||
|
tx.NewScreen("send-location").
|
||||||
|
WithText("Send your location and I will tell where you are!").
|
||||||
|
WithKeyboard(sendLocationKeyboard).
|
||||||
|
WithIKeyboard(
|
||||||
|
tx.NewKeyboard("").Row(
|
||||||
|
tx.NewButton("Check").
|
||||||
|
WithData("check").
|
||||||
|
ActionFunc(func(a *tx.A) {
|
||||||
|
d := a.V.(*UserData)
|
||||||
|
a.Sendf("Counter = %d", d.Counter)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
).WithCommands(
|
).WithCommands(
|
||||||
tx.NewCommand("hello").
|
tx.NewCommand("hello").
|
||||||
Desc("sends the 'Hello, World!' message back").
|
Desc("sends the 'Hello, World!' message back").
|
||||||
|
|
|
@ -48,26 +48,23 @@ type A = Arg
|
||||||
|
|
||||||
// Changes screen of user to the Id one.
|
// Changes screen of user to the Id one.
|
||||||
func (c *Arg) ChangeScreen(screenId ScreenId) error {
|
func (c *Arg) ChangeScreen(screenId ScreenId) error {
|
||||||
// Return if it will not change anything.
|
|
||||||
if c.CurrentScreenId == screenId {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.B.behaviour.ScreenExist(screenId) {
|
if !c.B.behaviour.ScreenExist(screenId) {
|
||||||
return ScreenNotExistErr
|
return ScreenNotExistErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the reading by sending the nil.
|
// Stop the reading by sending the nil,
|
||||||
|
// since we change the screen and
|
||||||
|
// current goroutine needs to be stopped.
|
||||||
if c.readingUpdate {
|
if c.readingUpdate {
|
||||||
c.updates <- nil
|
c.updates <- nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getting the screen and changing to
|
||||||
|
// then executing its action.
|
||||||
screen := c.B.behaviour.Screens[screenId]
|
screen := c.B.behaviour.Screens[screenId]
|
||||||
|
c.prevScreen = c.curScreen
|
||||||
|
c.curScreen = screen
|
||||||
screen.Render(c.Context)
|
screen.Render(c.Context)
|
||||||
|
|
||||||
c.Session.ChangeScreen(screenId)
|
|
||||||
c.KeyboardId = screen.KeyboardId
|
|
||||||
|
|
||||||
if screen.Action != nil {
|
if screen.Action != nil {
|
||||||
c.run(screen.Action, c.U)
|
c.run(screen.Action, c.U)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@ package tx
|
||||||
// The package implements
|
// The package implements
|
||||||
// behaviour for the Telegram bots.
|
// behaviour for the Telegram bots.
|
||||||
|
|
||||||
// The type describes behaviour for the bot in channels.
|
|
||||||
type ChannelBehaviour struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type describes behaviour for the bot in personal chats.
|
// The type describes behaviour for the bot in personal chats.
|
||||||
type Behaviour struct {
|
type Behaviour struct {
|
||||||
Start Action
|
Start Action
|
||||||
|
@ -35,29 +31,6 @@ func (b *Behaviour) OnStartFunc(
|
||||||
return b.WithStart(fn)
|
return b.WithStart(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Behaviour) OnStartChangeScreen(
|
|
||||||
id ScreenId,
|
|
||||||
) *Behaviour {
|
|
||||||
return b.WithStart(ScreenChange(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The function sets keyboards.
|
|
||||||
func (b *Behaviour) WithKeyboards(
|
|
||||||
kbds ...*Keyboard,
|
|
||||||
) *Behaviour {
|
|
||||||
for _, kbd := range kbds {
|
|
||||||
if kbd.Id == "" {
|
|
||||||
panic("empty keyboard ID")
|
|
||||||
}
|
|
||||||
_, ok := b.Keyboards[kbd.Id]
|
|
||||||
if ok {
|
|
||||||
panic("duplicate keyboard IDs")
|
|
||||||
}
|
|
||||||
b.Keyboards[kbd.Id] = kbd
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// The function sets screens.
|
// The function sets screens.
|
||||||
func (b *Behaviour) WithScreens(
|
func (b *Behaviour) WithScreens(
|
||||||
screens ...*Screen,
|
screens ...*Screen,
|
||||||
|
@ -90,21 +63,6 @@ func (b *Behaviour) WithCommands(cmds ...*Command) *Behaviour {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// The function sets group commands.
|
|
||||||
/*func (b *Behaviour) WithGroupCommands(cmds ...*Command) *Behaviour {
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
if cmd.Name == "" {
|
|
||||||
panic("empty group command name")
|
|
||||||
}
|
|
||||||
_, ok := b.GroupCommands[cmd.Name]
|
|
||||||
if ok {
|
|
||||||
panic("duplicate group command definition")
|
|
||||||
}
|
|
||||||
b.GroupCommands[cmd.Name] = cmd
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Check whether the screen exists in the behaviour.
|
// Check whether the screen exists in the behaviour.
|
||||||
func (beh *Behaviour) ScreenExist(id ScreenId) bool {
|
func (beh *Behaviour) ScreenExist(id ScreenId) bool {
|
||||||
_, ok := beh.Screens[id]
|
_, ok := beh.Screens[id]
|
||||||
|
@ -128,21 +86,27 @@ type GroupBehaviour struct {
|
||||||
Commands GroupCommandMap
|
Commands GroupCommandMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns new empty group behaviour object.
|
||||||
func NewGroupBehaviour() *GroupBehaviour {
|
func NewGroupBehaviour() *GroupBehaviour {
|
||||||
return &GroupBehaviour{
|
return &GroupBehaviour{
|
||||||
Commands: make(GroupCommandMap),
|
Commands: make(GroupCommandMap),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets an Action for initialization on each group connected to the
|
||||||
|
// group bot.
|
||||||
func (b *GroupBehaviour) WithInitAction(a GroupAction) *GroupBehaviour {
|
func (b *GroupBehaviour) WithInitAction(a GroupAction) *GroupBehaviour {
|
||||||
b.Init = a
|
b.Init = a
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The method reciveies a function to be called on initialization of the
|
||||||
|
// bot group bot.
|
||||||
func (b *GroupBehaviour) InitFunc(fn GroupActionFunc) *GroupBehaviour {
|
func (b *GroupBehaviour) InitFunc(fn GroupActionFunc) *GroupBehaviour {
|
||||||
return b.WithInitAction(fn)
|
return b.WithInitAction(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The method sets group commands.
|
||||||
func (b *GroupBehaviour) WithCommands(
|
func (b *GroupBehaviour) WithCommands(
|
||||||
cmds ...*GroupCommand,
|
cmds ...*GroupCommand,
|
||||||
) *GroupBehaviour {
|
) *GroupBehaviour {
|
||||||
|
@ -158,3 +122,7 @@ func (b *GroupBehaviour) WithCommands(
|
||||||
}
|
}
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The type describes behaviour for the bot in channels.
|
||||||
|
type ChannelBehaviour struct {
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,11 @@ func (btn *Button) WithAction(a Action) *Button {
|
||||||
return btn
|
return btn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (btn *Button) WithData(dat string) *Button {
|
||||||
|
btn.Data = dat
|
||||||
|
return btn
|
||||||
|
}
|
||||||
|
|
||||||
// Sets whether the button must send owner's location.
|
// Sets whether the button must send owner's location.
|
||||||
func (btn *Button) WithSendLocation(ok bool) *Button {
|
func (btn *Button) WithSendLocation(ok bool) *Button {
|
||||||
btn.SendLocation = ok
|
btn.SendLocation = ok
|
||||||
|
|
|
@ -12,20 +12,20 @@ type Context struct {
|
||||||
*Session
|
*Session
|
||||||
B *Bot
|
B *Bot
|
||||||
updates chan *Update
|
updates chan *Update
|
||||||
|
|
||||||
// Is true if currently reading the Update.
|
// Is true if currently reading the Update.
|
||||||
readingUpdate bool
|
readingUpdate bool
|
||||||
|
|
||||||
|
curScreen, prevScreen *Screen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goroutie function to handle each user.
|
// Goroutie function to handle each user.
|
||||||
func (c *Context) handleUpdateChan(updates chan *Update) {
|
func (c *Context) handleUpdateChan(updates chan *Update) {
|
||||||
var act Action
|
var act Action
|
||||||
bot := c.B
|
bot := c.B
|
||||||
session := c.Session
|
|
||||||
beh := bot.behaviour
|
beh := bot.behaviour
|
||||||
c.run(beh.Start, nil)
|
c.run(beh.Start, nil)
|
||||||
for u := range updates {
|
for u := range updates {
|
||||||
screen := bot.behaviour.Screens[session.CurrentScreenId]
|
screen := c.curScreen
|
||||||
// The part is added to implement custom update handling.
|
// The part is added to implement custom update handling.
|
||||||
if u.Message != nil {
|
if u.Message != nil {
|
||||||
if u.Message.IsCommand() && !c.readingUpdate {
|
if u.Message.IsCommand() && !c.readingUpdate {
|
||||||
|
@ -36,7 +36,13 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
kbd := beh.Keyboards[screen.KeyboardId]
|
kbd := screen.Keyboard
|
||||||
|
if kbd == nil {
|
||||||
|
if c.readingUpdate {
|
||||||
|
c.updates <- u
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
btns := kbd.buttonMap()
|
btns := kbd.buttonMap()
|
||||||
text := u.Message.Text
|
text := u.Message.Text
|
||||||
btn, ok := btns[text]
|
btn, ok := btns[text]
|
||||||
|
@ -68,13 +74,24 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
kbd := beh.Keyboards[screen.InlineKeyboardId]
|
kbd := screen.InlineKeyboard
|
||||||
|
if kbd == nil {
|
||||||
|
if c.readingUpdate {
|
||||||
|
c.updates <- u
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
btns := kbd.buttonMap()
|
btns := kbd.buttonMap()
|
||||||
btn, ok := btns[data]
|
btn, ok := btns[data]
|
||||||
if !ok && c.readingUpdate {
|
if !ok && c.readingUpdate {
|
||||||
c.updates <- u
|
c.updates <- u
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
c.Sendf("%q", btns)
|
||||||
|
continue
|
||||||
|
}
|
||||||
act = btn.Action
|
act = btn.Action
|
||||||
}
|
}
|
||||||
if act != nil {
|
if act != nil {
|
||||||
|
|
|
@ -10,10 +10,11 @@ type WrongUpdateType struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
KeyboardNotExistErr = errors.New("keyboard does not exist")
|
||||||
NotAvailableErr = errors.New("the context is not available")
|
NotAvailableErr = errors.New("the context is not available")
|
||||||
|
EmptyKeyboardTextErr = errors.New("got empty text for a keyboard")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (wut WrongUpdateType) Error() string {
|
func (wut WrongUpdateType) Error() string {
|
||||||
|
|
|
@ -23,19 +23,33 @@ 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 {
|
||||||
Id KeyboardId
|
// Text to be displayed with the keyboard.
|
||||||
|
Text string
|
||||||
|
// Rows to be displayed once the
|
||||||
|
// keyboard is sent.
|
||||||
Rows []ButtonRow
|
Rows []ButtonRow
|
||||||
|
|
||||||
|
OneTime bool
|
||||||
|
Inline bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type KeyboardMap map[KeyboardId]*Keyboard
|
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(id KeyboardId) *Keyboard {
|
func NewKeyboard(text string) *Keyboard {
|
||||||
return &Keyboard{
|
return &Keyboard{
|
||||||
Id: id,
|
Text: text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kbd *Keyboard) TelegramMarkup() any {
|
||||||
|
if kbd.Inline {
|
||||||
|
return kbd.toTelegramInline()
|
||||||
|
}
|
||||||
|
|
||||||
|
return kbd.toTelegramReply()
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a new button row to the current keyboard.
|
// Adds a new button row to the current keyboard.
|
||||||
func (kbd *Keyboard) Row(btns ...*Button) *Keyboard {
|
func (kbd *Keyboard) Row(btns ...*Button) *Keyboard {
|
||||||
// For empty row. We do not need that.
|
// For empty row. We do not need that.
|
||||||
|
@ -46,8 +60,8 @@ func (kbd *Keyboard) Row(btns ...*Button) *Keyboard {
|
||||||
return kbd
|
return kbd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the Keyboard to the Telegram API type.
|
// Convert the Keyboard to the Telegram API type of reply keyboard.
|
||||||
func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup {
|
func (kbd *Keyboard) toTelegramReply() apix.ReplyKeyboardMarkup {
|
||||||
rows := [][]apix.KeyboardButton{}
|
rows := [][]apix.KeyboardButton{}
|
||||||
for _, row := range kbd.Rows {
|
for _, row := range kbd.Rows {
|
||||||
buttons := []apix.KeyboardButton{}
|
buttons := []apix.KeyboardButton{}
|
||||||
|
@ -57,10 +71,14 @@ func (kbd *Keyboard) ToTelegram() apix.ReplyKeyboardMarkup {
|
||||||
rows = append(rows, buttons)
|
rows = append(rows, buttons)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kbd.OneTime {
|
||||||
|
return apix.NewOneTimeReplyKeyboard(rows...)
|
||||||
|
}
|
||||||
|
|
||||||
return apix.NewReplyKeyboard(rows...)
|
return apix.NewReplyKeyboard(rows...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kbd *Keyboard) ToTelegramInline() apix.InlineKeyboardMarkup {
|
func (kbd *Keyboard) toTelegramInline() apix.InlineKeyboardMarkup {
|
||||||
rows := [][]apix.InlineKeyboardButton{}
|
rows := [][]apix.InlineKeyboardButton{}
|
||||||
for _, row := range kbd.Rows {
|
for _, row := range kbd.Rows {
|
||||||
buttons := []apix.InlineKeyboardButton{}
|
buttons := []apix.InlineKeyboardButton{}
|
||||||
|
@ -73,6 +91,16 @@ func (kbd *Keyboard) ToTelegramInline() apix.InlineKeyboardMarkup {
|
||||||
return apix.NewInlineKeyboardMarkup(rows...)
|
return apix.NewInlineKeyboardMarkup(rows...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kbd *Keyboard) WithOneTime(oneTime bool) *Keyboard {
|
||||||
|
kbd.OneTime = oneTime
|
||||||
|
return kbd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kbd *Keyboard) WithInline(inline bool) *Keyboard {
|
||||||
|
kbd.Inline = inline
|
||||||
|
return kbd
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
|
|
117
src/tx/screen.go
117
src/tx/screen.go
|
@ -7,24 +7,17 @@ import (
|
||||||
// Unique identifier for the screen.
|
// Unique identifier for the screen.
|
||||||
type ScreenId string
|
type ScreenId string
|
||||||
|
|
||||||
// Should be replaced with something that can be
|
|
||||||
// dinamicaly rendered. (WIP)
|
|
||||||
type ScreenText string
|
|
||||||
|
|
||||||
// Screen statement of the bot.
|
// Screen statement of the bot.
|
||||||
// Mostly what buttons to show.
|
// Mostly what buttons to show.
|
||||||
type Screen struct {
|
type Screen struct {
|
||||||
Id ScreenId
|
Id ScreenId
|
||||||
|
// The text to be displayed when the screen is
|
||||||
// Text to be sent to the user when changing to the screen.
|
// reached.
|
||||||
Text ScreenText
|
Text string
|
||||||
|
|
||||||
// The keyboard to be sent in the message part.
|
// The keyboard to be sent in the message part.
|
||||||
InlineKeyboardId KeyboardId
|
InlineKeyboard *Keyboard
|
||||||
|
|
||||||
// Keyboard to be displayed on the screen.
|
// Keyboard to be displayed on the screen.
|
||||||
KeyboardId KeyboardId
|
Keyboard *Keyboard
|
||||||
|
|
||||||
// Action called on the reaching the screen.
|
// Action called on the reaching the screen.
|
||||||
Action Action
|
Action Action
|
||||||
}
|
}
|
||||||
|
@ -40,18 +33,22 @@ func NewScreen(id ScreenId) *Screen {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the screen with specified text printing on appearing.
|
// Returns the screen with specified text printing on appearing.
|
||||||
func (s *Screen) WithText(text ScreenText) *Screen {
|
func (s *Screen) WithText(text string) *Screen {
|
||||||
s.Text = text
|
s.Text = text
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Screen) IKeyboard(kbdId KeyboardId) *Screen {
|
func (s *Screen) WithInlineKeyboard(ikbd *Keyboard) *Screen {
|
||||||
s.InlineKeyboardId = kbdId
|
s.InlineKeyboard = ikbd
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Screen) Keyboard(kbdId KeyboardId) *Screen {
|
func (s *Screen) WithIKeyboard(ikbd *Keyboard) *Screen {
|
||||||
s.KeyboardId = kbdId
|
return s.WithInlineKeyboard(ikbd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Screen) WithKeyboard(kbd *Keyboard) *Screen {
|
||||||
|
s.Keyboard = kbd
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,49 +61,69 @@ func (s *Screen) ActionFunc(a ActionFunc) *Screen {
|
||||||
return s.WithAction(a)
|
return s.WithAction(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rendering the screen text to string to be sent or printed.
|
|
||||||
func (st ScreenText) String() string {
|
|
||||||
return string(st)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renders output of the screen only to the side of the user.
|
// Renders output of the screen only to the side of the user.
|
||||||
func (s *Screen) Render(c *Context) error {
|
func (s *Screen) Render(c *Context) error {
|
||||||
id := c.Id.ToTelegram()
|
id := c.Id.ToTelegram()
|
||||||
|
kbd := s.Keyboard
|
||||||
|
iKbd := s.InlineKeyboard
|
||||||
|
|
||||||
msg := apix.NewMessage(id, s.Text.String())
|
var ch [2]apix.Chattable
|
||||||
|
var txt string
|
||||||
|
|
||||||
if s.InlineKeyboardId != "" {
|
// Screen text and inline keyboard.
|
||||||
kbd, ok := c.B.behaviour.Keyboards[s.InlineKeyboardId]
|
if s.Text != "" {
|
||||||
if !ok {
|
txt = s.Text
|
||||||
return KeyboardNotExistErr
|
} else if iKbd != nil {
|
||||||
|
if iKbd.Text != "" {
|
||||||
|
txt = iKbd.Text
|
||||||
|
} else {
|
||||||
|
// Default to send the keyboard.
|
||||||
|
txt = ">"
|
||||||
}
|
}
|
||||||
msg.ReplyMarkup = kbd.ToTelegramInline()
|
|
||||||
}
|
}
|
||||||
|
if txt != "" {
|
||||||
_, err := c.B.Send(msg)
|
msg := apix.NewMessage(id, txt)
|
||||||
if err != nil {
|
if iKbd != nil {
|
||||||
return err
|
msg.ReplyMarkup = iKbd.toTelegramInline()
|
||||||
}
|
} else if kbd != nil {
|
||||||
|
msg.ReplyMarkup = kbd.toTelegramReply()
|
||||||
msg = apix.NewMessage(id, ">")
|
if _, err := c.B.Send(msg); err != nil {
|
||||||
// Checking if we need to resend the keyboard.
|
return err
|
||||||
if s.KeyboardId != c.KeyboardId {
|
|
||||||
// Remove keyboard by default.
|
|
||||||
var tkbd any
|
|
||||||
tkbd = apix.NewRemoveKeyboard(true)
|
|
||||||
|
|
||||||
// Replace keyboard with the new one.
|
|
||||||
if s.KeyboardId != "" {
|
|
||||||
kbd, ok := c.B.behaviour.Keyboards[s.KeyboardId]
|
|
||||||
if !ok {
|
|
||||||
return KeyboardNotExistErr
|
|
||||||
}
|
}
|
||||||
tkbd = kbd.ToTelegram()
|
return nil
|
||||||
|
} else {
|
||||||
|
msg.ReplyMarkup = apix.NewRemoveKeyboard(true)
|
||||||
|
if _, err := c.B.Send(msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
ch[0] = msg
|
||||||
|
}
|
||||||
|
|
||||||
msg.ReplyMarkup = tkbd
|
// Screen text and reply keyboard.
|
||||||
if _, err := c.B.Send(msg); err != nil {
|
txt = ""
|
||||||
return err
|
if kbd != nil {
|
||||||
|
if kbd.Text != "" {
|
||||||
|
txt = kbd.Text
|
||||||
|
} else {
|
||||||
|
txt = ">"
|
||||||
|
}
|
||||||
|
msg := apix.NewMessage(id, txt)
|
||||||
|
msg.ReplyMarkup = kbd.toTelegramReply()
|
||||||
|
ch[1] = msg
|
||||||
|
} else {
|
||||||
|
// Removing keyboard if there is none.
|
||||||
|
msg := apix.NewMessage(id, ">")
|
||||||
|
msg.ReplyMarkup = apix.NewRemoveKeyboard(true)
|
||||||
|
ch[1] = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range ch {
|
||||||
|
if m != nil {
|
||||||
|
if _, err := c.B.Send(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,10 @@ func (si SessionId) ToTelegram() int64 {
|
||||||
// The type represents current state of
|
// The type represents current state of
|
||||||
// user interaction per each of them.
|
// user interaction per each of them.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
|
// Id of the chat of the user.
|
||||||
Id SessionId
|
Id SessionId
|
||||||
// Current screen identifier.
|
// Custom value for each user.
|
||||||
CurrentScreenId ScreenId
|
V any
|
||||||
// ID of the previous screen.
|
|
||||||
PreviousScreenId ScreenId
|
|
||||||
// The currently showed on display keyboard inside Action.
|
|
||||||
KeyboardId KeyboardId
|
|
||||||
V any
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new empty session with specified user ID.
|
// Return new empty session with specified user ID.
|
||||||
|
@ -30,12 +26,6 @@ func NewSession(id SessionId) *Session {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes screen of user to the Id one for the session.
|
|
||||||
func (c *Session) ChangeScreen(screenId ScreenId) {
|
|
||||||
c.PreviousScreenId = c.CurrentScreenId
|
|
||||||
c.CurrentScreenId = screenId
|
|
||||||
}
|
|
||||||
|
|
||||||
// The type represents map of sessions using
|
// The type represents map of sessions using
|
||||||
// as key.
|
// as key.
|
||||||
type SessionMap map[SessionId]*Session
|
type SessionMap map[SessionId]*Session
|
||||||
|
|
Loading…
Reference in a new issue