Simplified the NewButton call and implemented location sending handling.

This commit is contained in:
Andrey Parhomenko 2023-08-12 15:54:05 +03:00
parent 44185e92af
commit bbe076f29a
5 changed files with 111 additions and 52 deletions

View file

@ -12,27 +12,26 @@ type UserData struct {
Counter int Counter int
} }
var startScreenButton = tx.NewButton(). var startScreenButton = tx.NewButton("🏠 To the start screen").
WithText("🏠 To the start screen").
ScreenChange("start") ScreenChange("start")
var beh = tx.NewBehaviour(). var beh = tx.NewBehaviour().
// The function will be called every time // The function will be called every time
// the bot is started. // the bot is started.
OnStartFunc(func(c *tx.Context) { OnStartFunc(func(c *tx.A) {
c.V = &UserData{} c.V = &UserData{}
c.ChangeScreen("start") c.ChangeScreen("start")
}).WithKeyboards( }).WithKeyboards(
// Increment/decrement keyboard. // Increment/decrement keyboard.
tx.NewKeyboard("inc/dec").Row( tx.NewKeyboard("inc/dec").Row(
tx.NewButton().WithText("+").ActionFunc(func(c *tx.Context) { tx.NewButton("+").ActionFunc(func(c *tx.A) {
d := c.V.(*UserData) d := c.V.(*UserData)
d.Counter++ d.Counter++
c.Sendf("%d", d.Counter) c.Sendf("%d", d.Counter)
}), }),
tx.NewButton().WithText("-").ActionFunc(func(c *tx.Context) { tx.NewButton("-").ActionFunc(func(c *tx.A) {
d := c.V.(*UserData) d := c.V.(*UserData)
d.Counter-- d.Counter--
c.Sendf("%d", d.Counter) c.Sendf("%d", d.Counter)
@ -43,14 +42,37 @@ var beh = tx.NewBehaviour().
// The navigational keyboard. // The navigational keyboard.
tx.NewKeyboard("nav").Row( tx.NewKeyboard("nav").Row(
tx.NewButton().WithText("Inc/Dec").ScreenChange("inc/dec"), tx.NewButton("Inc/Dec").ScreenChange("inc/dec"),
).Row( ).Row(
tx.NewButton().WithText("Upper case").ScreenChange("upper-case"), tx.NewButton("Upper case").ScreenChange("upper-case"),
tx.NewButton().WithText("Lower case").ScreenChange("lower-case"), tx.NewButton("Lower case").ScreenChange("lower-case"),
).Row(
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)
}
}),
), ),
tx.NewKeyboard("istart").Row( tx.NewKeyboard("istart").Row(
tx.NewButton().WithText("GoT Github page"). tx.NewButton("GoT Github page").
WithUrl("https://github.com/mojosa-software/got"), WithUrl("https://github.com/mojosa-software/got"),
), ),
@ -78,7 +100,7 @@ var beh = tx.NewBehaviour().
). ).
Keyboard("inc/dec"). Keyboard("inc/dec").
// The function will be called when reaching the screen. // The function will be called when reaching the screen.
ActionFunc(func(c *tx.Context) { ActionFunc(func(c *tx.A) {
d := c.V.(*UserData) d := c.V.(*UserData)
c.Sendf("Current counter value = %d", d.Counter) c.Sendf("Current counter value = %d", d.Counter)
}), }),
@ -95,12 +117,12 @@ var beh = tx.NewBehaviour().
).WithCommands( ).WithCommands(
tx.NewCommand("hello"). tx.NewCommand("hello").
Desc("sends the 'Hello, World!' message back"). Desc("sends the 'Hello, World!' message back").
ActionFunc(func(c *tx.Context) { ActionFunc(func(c *tx.A) {
c.Send("Hello, World!") c.Send("Hello, World!")
}), }),
tx.NewCommand("read"). tx.NewCommand("read").
Desc("reads a string and sends it back"). Desc("reads a string and sends it back").
ActionFunc(func(c *tx.Context) { ActionFunc(func(c *tx.A) {
c.Send("Type some text:") c.Send("Type some text:")
msg, err := c.ReadTextMessage() msg, err := c.ReadTextMessage()
if err != nil { if err != nil {
@ -111,7 +133,7 @@ var beh = tx.NewBehaviour().
) )
func mutateMessage(fn func(string) string) tx.ActionFunc { func mutateMessage(fn func(string) string) tx.ActionFunc {
return func(c *tx.Context) { return func(c *tx.A) {
for { for {
msg, err := c.ReadTextMessage() msg, err := c.ReadTextMessage()
if err == tx.NotAvailableErr { if err == tx.NotAvailableErr {

View file

@ -1,18 +1,37 @@
package tx package tx
// Implementing the intereface lets you import (
// provide behaviour for the buttons etc. apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type Update = apix.Update
// The argument for handling.
type Arg struct {
// Current context.
*Context
// The update that made the action to be called.
U *Update
}
type A = Arg
type GroupArg struct {
GroupArg *GroupContext
U *Update
}
type GA = GroupArg
type Action interface { type Action interface {
Act(*Context) Act(*Arg)
} }
// Customized action for the bot. // Customized action for the bot.
type ActionFunc func(*Context) type ActionFunc func(*Arg)
// The type implements changing screen to the underlying ScreenId // The type implements changing screen to the underlying ScreenId
type ScreenChange ScreenId type ScreenChange ScreenId
func (sc ScreenChange) Act(c *Context) { func (sc ScreenChange) Act(c *Arg) {
if !c.B.ScreenExist(ScreenId(sc)) { if !c.B.ScreenExist(ScreenId(sc)) {
panic(ScreenNotExistErr) panic(ScreenNotExistErr)
} }
@ -22,6 +41,6 @@ func (sc ScreenChange) Act(c *Context) {
} }
} }
func (af ActionFunc) Act(c *Context) { func (af ActionFunc) Act(c *Arg) {
af(c) af(c)
} }

View file

@ -9,6 +9,7 @@ type Button struct {
Text string Text string
Data string Data string
Url string Url string
SendLocation bool
Action Action Action Action
} }
@ -17,26 +18,32 @@ type ButtonMap map[string]*Button
// Represents the reply button row. // Represents the reply button row.
type ButtonRow []*Button type ButtonRow []*Button
// Returns new button with specified text and action. // Returns new button with the specified text and no action.
func NewButton() *Button { func NewButton(text string) *Button {
return &Button{} return &Button{
} Text: text,
}
func (btn *Button) WithText(text string) *Button {
btn.Text = text
return btn
} }
// Set the URL for the button. Only for inline buttons.
func (btn *Button) WithUrl(url string) *Button { func (btn *Button) WithUrl(url string) *Button {
btn.Url = url btn.Url = url
return btn return btn
} }
// Set the action when pressing the button.
// By default is nil and does nothing.
func (btn *Button) WithAction(a Action) *Button { func (btn *Button) WithAction(a Action) *Button {
btn.Action = a btn.Action = a
return btn return btn
} }
// Sets whether the button must send owner's location.
func (btn *Button) WithSendLocation(ok bool) *Button {
btn.SendLocation = ok
return btn
}
func (btn *Button) ActionFunc(fn ActionFunc) *Button { func (btn *Button) ActionFunc(fn ActionFunc) *Button {
return btn.WithAction(fn) return btn.WithAction(fn)
} }
@ -46,7 +53,11 @@ func (btn *Button) ScreenChange(sc ScreenChange) *Button {
} }
func (btn *Button) ToTelegram() apix.KeyboardButton { func (btn *Button) ToTelegram() apix.KeyboardButton {
return apix.NewKeyboardButton(btn.Text) ret := apix.NewKeyboardButton(btn.Text)
if btn.SendLocation {
ret.RequestLocation = true
}
return ret
} }
func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton { func (btn *Button) ToTelegramInline() apix.InlineKeyboardButton {

View file

@ -27,7 +27,7 @@ type GroupContext struct {
func (c *Context) handleUpdateChan(updates chan *Update) { func (c *Context) handleUpdateChan(updates chan *Update) {
bot := c.B bot := c.B
session := c.Session session := c.Session
bot.Start.Act(c) c.run(bot.Start, nil)
for u := range updates { for u := range updates {
screen := bot.Screens[session.CurrentScreenId] screen := bot.Screens[session.CurrentScreenId]
// The part is added to implement custom update handling. // The part is added to implement custom update handling.
@ -45,12 +45,21 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
btns := kbd.buttonMap() btns := kbd.buttonMap()
text := u.Message.Text text := u.Message.Text
btn, ok := btns[text] btn, ok := btns[text]
// Sending wrong messages to if !ok {
// the currently reading goroutine. if u.Message.Location != nil {
if !ok && c.readingUpdate { for _, b := range btns {
if b.SendLocation {
btn = b
ok = true
}
}
} else if c.readingUpdate {
// Skipping the update sending it to
// the reading goroutine.
c.updates <- u c.updates <- u
continue continue
} }
}
if ok { if ok {
act = btn.Action act = btn.Action
@ -58,7 +67,7 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
} }
if act != nil { if act != nil {
c.run(act) c.run(act, u)
} }
} else if u.CallbackQuery != nil { } else if u.CallbackQuery != nil {
cb := apix.NewCallback(u.CallbackQuery.ID, u.CallbackQuery.Data) cb := apix.NewCallback(u.CallbackQuery.ID, u.CallbackQuery.Data)
@ -70,18 +79,22 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
} }
kbd := bot.Keyboards[screen.InlineKeyboardId] kbd := bot.Keyboards[screen.InlineKeyboardId]
btns := kbd.buttonMap() btns := kbd.buttonMap()
btn := btns[data] btn, ok := btns[data]
btn.Action.Act(c) if !ok && c.readingUpdate {
c.updates <- u
continue
}
c.run(btn.Action, u)
} }
} }
} }
func (c *Context) run(a Action) { func (c *Context) run(a Action, u *Update) {
go a.Act(c) go a.Act(&A{c, u})
} }
// Changes screen of user to the Id one. // Changes screen of user to the Id one.
func (c *Context) ChangeScreen(screenId ScreenId) error { func (c *Arg) ChangeScreen(screenId ScreenId) error {
// Return if it will not change anything. // Return if it will not change anything.
if c.CurrentScreenId == screenId { if c.CurrentScreenId == screenId {
return nil return nil
@ -97,13 +110,13 @@ func (c *Context) ChangeScreen(screenId ScreenId) error {
} }
screen := c.B.Screens[screenId] screen := c.B.Screens[screenId]
screen.Render(c) screen.Render(c.Context)
c.Session.ChangeScreen(screenId) c.Session.ChangeScreen(screenId)
c.KeyboardId = screen.KeyboardId c.KeyboardId = screen.KeyboardId
if screen.Action != nil { if screen.Action != nil {
c.run(screen.Action) c.run(screen.Action, c.U)
} }
return nil return nil

View file

@ -1,11 +1,5 @@
package tx package tx
import (
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type Update = apix.Update
// Represents unique value to identify chats. // Represents unique value to identify chats.
// In fact is simply ID of the chat. // In fact is simply ID of the chat.
type SessionId int64 type SessionId int64