From e72947b5e50f2c79c5ad54fd614ef4920437bd1a Mon Sep 17 00:00:00 2001 From: Sina Sadeghi Date: Wed, 14 Aug 2024 16:48:32 +0330 Subject: [PATCH 1/4] add ID,CallbackHandler,CallbackData to Button and update buildKB according to its changes --- dialog/node.go | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/dialog/node.go b/dialog/node.go index c931b64..722d7cb 100644 --- a/dialog/node.go +++ b/dialog/node.go @@ -1,13 +1,17 @@ package dialog import ( + "github.com/go-telegram/bot" "github.com/go-telegram/bot/models" ) type Button struct { - Text string - NodeID string - URL string + ID string + Text string + NodeID string + URL string + CallbackHandler bot.HandlerFunc + CallbackData string } type Node struct { @@ -16,7 +20,7 @@ type Node struct { Keyboard [][]Button } -func (n Node) buildKB(prefix string) models.ReplyMarkup { +func (n Node) buildKB(prefix, nodePrefix, callbackPrefix string) models.ReplyMarkup { if len(n.Keyboard) == 0 { return nil } @@ -29,10 +33,14 @@ func (n Node) buildKB(prefix string) models.ReplyMarkup { b := models.InlineKeyboardButton{ Text: btn.Text, } - if btn.URL != "" { + switch { + case btn.URL != "": b.URL = btn.URL - } else { - b.CallbackData = prefix + btn.NodeID + case btn.CallbackHandler != nil: + b.CallbackData = prefix + callbackPrefix + btn.ID + default: + b.CallbackData = prefix + nodePrefix + btn.NodeID + } kbRow = append(kbRow, b) } From 01c684add57d998808dd869f0b9c39d0da69470a Mon Sep 17 00:00:00 2001 From: Sina Sadeghi Date: Wed, 14 Aug 2024 16:49:56 +0330 Subject: [PATCH 2/4] update dialog.go to handle custom CallbackHandler for dialog --- dialog/dialog.go | 53 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/dialog/dialog.go b/dialog/dialog.go index be7cfb4..0094c35 100644 --- a/dialog/dialog.go +++ b/dialog/dialog.go @@ -13,20 +13,24 @@ import ( type OnErrorHandler func(err error) type Dialog struct { - data []string - prefix string - onError OnErrorHandler - nodes []Node - inline bool + data []string + prefix string + nodePrefix string + callbackPrefix string + onError OnErrorHandler + nodes []Node + inline bool callbackHandlerID string } func New(nodes []Node, opts ...Option) *Dialog { p := &Dialog{ - prefix: bot.RandomString(16), - onError: defaultOnError, - nodes: nodes, + prefix: bot.RandomString(12), + callbackPrefix: bot.RandomString(4), + nodePrefix: bot.RandomString(4), + onError: defaultOnError, + nodes: nodes, } for _, opt := range opts { @@ -50,7 +54,7 @@ func (d *Dialog) showNode(ctx context.Context, b *bot.Bot, chatID any, node Node ChatID: chatID, Text: node.Text, ParseMode: models.ParseModeMarkdown, - ReplyMarkup: node.buildKB(d.prefix), + ReplyMarkup: node.buildKB(d.prefix, d.nodePrefix, d.callbackPrefix), } return b.SendMessage(ctx, params) @@ -76,7 +80,19 @@ func (d *Dialog) callback(ctx context.Context, b *bot.Bot, update *models.Update d.onError(fmt.Errorf("failed to answer callback query")) } - nodeID := strings.TrimPrefix(update.CallbackQuery.Data, d.prefix) + btnID, isCustomCallback := strings.CutPrefix(update.CallbackQuery.Data, d.prefix+d.callbackPrefix) + if isCustomCallback { + btn, ok := d.findButton(btnID) + if !ok { + d.onError(fmt.Errorf("failed to find button with id %s", btnID)) + return + } + update.CallbackQuery.Data = btn.CallbackData + btn.CallbackHandler(ctx, b, update) + return + } + + nodeID := strings.TrimPrefix(update.CallbackQuery.Data, d.prefix+d.nodePrefix) node, ok := d.findNode(nodeID) if !ok { d.onError(fmt.Errorf("failed to find node with id %s", nodeID)) @@ -89,7 +105,7 @@ func (d *Dialog) callback(ctx context.Context, b *bot.Bot, update *models.Update MessageID: update.CallbackQuery.Message.Message.ID, Text: node.Text, ParseMode: models.ParseModeMarkdown, - ReplyMarkup: node.buildKB(d.prefix), + ReplyMarkup: node.buildKB(d.prefix, d.nodePrefix, d.callbackPrefix), }) if errEdit != nil { d.onError(errEdit) @@ -101,7 +117,7 @@ func (d *Dialog) callback(ctx context.Context, b *bot.Bot, update *models.Update ChatID: update.CallbackQuery.Message.Message.Chat.ID, Text: node.Text, ParseMode: models.ParseModeMarkdown, - ReplyMarkup: node.buildKB(d.prefix), + ReplyMarkup: node.buildKB(d.prefix, d.nodePrefix, d.callbackPrefix), }) if errSend != nil { d.onError(errSend) @@ -117,3 +133,16 @@ func (d *Dialog) findNode(id string) (Node, bool) { return Node{}, false } + +func (d *Dialog) findButton(ID string) (Button, bool) { + for _, node := range d.nodes { + for _, row := range node.Keyboard { + for _, btn := range row { + if btn.ID == ID { + return btn, true + } + } + } + } + return Button{}, false +} From 61462c0ce7fec1f6d5c54097397c491c918077fa Mon Sep 17 00:00:00 2001 From: Sina Sadeghi Date: Wed, 14 Aug 2024 16:52:37 +0330 Subject: [PATCH 3/4] add custom CallbackHandler to dialog examples --- examples/dialog.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/dialog.go b/examples/dialog.go index abe684f..63090ef 100644 --- a/examples/dialog.go +++ b/examples/dialog.go @@ -10,10 +10,11 @@ import ( var ( dialogNodes = []dialog.Node{ - {ID: "start", Text: "Start Node", Keyboard: [][]dialog.Button{{{Text: "Go to node 2", NodeID: "2"}, {Text: "Go to node 3", NodeID: "3"}}, {{Text: "Go Telegram UI", URL: "https://github.com/go-telegram/ui"}}}}, + {ID: "start", Text: "Start Node", Keyboard: [][]dialog.Button{{{Text: "Go to node 2", NodeID: "2"}, {Text: "Go to node 3", NodeID: "3"}}, {{Text: "Go Telegram UI", URL: "https://github.com/sinasadeghi83/go-telegram-bot-ui"}}}}, {ID: "2", Text: "node 2 without keyboard"}, {ID: "3", Text: "node 3", Keyboard: [][]dialog.Button{{{Text: "Go to start", NodeID: "start"}, {Text: "Go to node 4", NodeID: "4"}}}}, - {ID: "4", Text: "node 4", Keyboard: [][]dialog.Button{{{Text: "Back to 3", NodeID: "3"}}}}, + {ID: "4", Text: "node 4", Keyboard: [][]dialog.Button{{{Text: "Back to 3", NodeID: "3"}, {Text: "Node 5", NodeID: "5"}}}}, + {ID: "5", Text: "node 5", Keyboard: [][]dialog.Button{{{ID: "1", Text: "Custom Handler", CallbackHandler: handlerDialogCustom, CallbackData: "You choose custom handler"}}}}, } ) @@ -28,3 +29,10 @@ func handlerDialogInline(ctx context.Context, b *bot.Bot, update *models.Update) p.Show(ctx, b, update.Message.Chat.ID, "start") } + +func handlerDialogCustom(ctx context.Context, b *bot.Bot, update *models.Update) { + b.SendMessage(ctx, &bot.SendMessageParams{ + ChatID: update.CallbackQuery.Message.Message.ID, + Text: update.CallbackQuery.Data, + }) +} From 8c00cd011297203a8231036dfa7773dfc3f89f2f Mon Sep 17 00:00:00 2001 From: Sina Sadeghi Date: Wed, 14 Aug 2024 17:19:47 +0330 Subject: [PATCH 4/4] add WithNodePrefix & WithCallbackPrefix to dialog options --- dialog/options.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dialog/options.go b/dialog/options.go index 9e1828b..41b7dde 100644 --- a/dialog/options.go +++ b/dialog/options.go @@ -14,3 +14,17 @@ func WithPrefix(s string) Option { w.prefix = s } } + +// WithCallbackPrefix is a keyboard option that sets a prefix for the widget which uses CallbackHandler +func WithCallbackPrefix(s string) Option { + return func(w *Dialog) { + w.callbackPrefix = s + } +} + +// WithNodePrefix is a keyboard option that sets a prefix for the widget which uses NodeID +func WithNodePrefix(s string) Option { + return func(w *Dialog) { + w.nodePrefix = s + } +}