From 0abd04a9ea2cc8bb7cd5944093fa694d321ca9d9 Mon Sep 17 00:00:00 2001 From: surdeus Date: Mon, 25 Sep 2023 19:52:57 +0300 Subject: [PATCH] A bit of refactoring to make more sense for the user and the developer sides. --- tg/inline.go | 112 +++++++++++++++++++++++ tg/keyboard.go | 93 ++----------------- tg/page.go | 10 +-- tg/reply.go | 143 +++++++++++++++++++++++++++++ tg/update.go | 51 +++++++++++ tg/widget.go | 239 +------------------------------------------------ 6 files changed, 318 insertions(+), 330 deletions(-) create mode 100644 tg/inline.go create mode 100644 tg/reply.go create mode 100644 tg/update.go diff --git a/tg/inline.go b/tg/inline.go new file mode 100644 index 0000000..08a7e1d --- /dev/null +++ b/tg/inline.go @@ -0,0 +1,112 @@ +package tg + +import ( + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) + +// The type represents keyboard to be emdedded into the messages (inline in Telegram terms). +type Inline struct { + *Keyboard +} + +// Transform the keyboard to widget with the specified text. +func (kbd *Inline) Widget(text string) *InlineWidget { + ret := &InlineWidget{} + ret.Inline = kbd + ret.Text = text + return ret +} + +// Convert the inline keyboard to markup for the tgbotapi. +func (kbd *Inline) ToApi() tgbotapi.InlineKeyboardMarkup { + rows := [][]tgbotapi.InlineKeyboardButton{} + for _, row := range kbd.Rows { + buttons := []tgbotapi.InlineKeyboardButton{} + for _, button := range row { + buttons = append(buttons, button.ToTelegramInline()) + } + rows = append(rows, buttons) + } + + return tgbotapi.NewInlineKeyboardMarkup(rows...) +} + +// The type implements message with an inline keyboard. +type InlineWidget struct { + Text string + *Inline +} + +// Implementing the Sendable interface. +func (widget *InlineWidget) SendConfig( + sid SessionId, + bot *Bot, +) (*SendConfig) { + var text string + if widget.Text != "" { + text = widget.Text + } else { + text = ">" + } + + msgConfig := tgbotapi.NewMessage(sid.ToApi(), text) + msgConfig.ReplyMarkup = widget.ToApi() + + ret := &SendConfig{} + ret.Message = &msgConfig + return ret +} + +// Implementing the Widget interface. +func (widget *InlineWidget) Serve(c *Context) { + for u := range c.Input() { + var act Action + if u.CallbackQuery == nil { + continue + } + cb := tgbotapi.NewCallback( + u.CallbackQuery.ID, + u.CallbackQuery.Data, + ) + data := u.CallbackQuery.Data + + _, err := c.Bot.Api.Request(cb) + if err != nil { + //return err + continue + } + + btns := widget.ButtonMap() + btn, ok := btns[data] + if !ok { + continue + } + if btn != nil { + act = btn.Action + } else if widget.Action != nil { + act = widget.Action + } + c.Run(act, u) + } +} + +func (widget *InlineWidget) Filter( + u *Update, + msgs MessageMap, +) bool { + if widget == nil || u.CallbackQuery == nil { + return true + } + + inlineMsg, inlineOk := msgs[""] + if !inlineOk { + return true + } + if u.CallbackQuery.Message.MessageID != + inlineMsg.MessageID { + return true + } + + return false +} + diff --git a/tg/keyboard.go b/tg/keyboard.go index fcc9d9a..21e50c5 100644 --- a/tg/keyboard.go +++ b/tg/keyboard.go @@ -1,7 +1,7 @@ package tg import ( - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + //tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) // The general keyboard type used both in Reply and Inline. @@ -13,15 +13,6 @@ type Keyboard struct { buttonMap ButtonMap } -// The type represents reply keyboards. -type ReplyKeyboard struct { - *Keyboard - // If true will be removed after one press. - OneTime bool - // If true will remove the keyboard on send. - Remove bool -} - // Returns the new keyboard with specified rows. func NewKeyboard(rows ...ButtonRow) *Keyboard { ret := &Keyboard{} @@ -69,88 +60,16 @@ func (kbd Keyboard) ButtonMap() ButtonMap { } // Convert the keyboard to the more specific inline one. -func (kbd *Keyboard) Inline() *InlineKeyboard { - ret := &InlineKeyboard{} +func (kbd *Keyboard) Inline() *Inline { + ret := &Inline{} ret.Keyboard = kbd return ret } -func (kbd *Keyboard) Reply() *ReplyKeyboard { - ret := &ReplyKeyboard{} +// Convert the keyboard to the more specific reply one. +func (kbd *Keyboard) Reply() *Reply { + ret := &Reply{} ret.Keyboard = kbd return ret } -// The type represents keyboard to be emdedded into the messages. -type InlineKeyboard struct { - *Keyboard -} - -// Transform the keyboard to widget with the specified text. -func (kbd *InlineKeyboard) Widget(text string) *InlineKeyboardWidget { - ret := &InlineKeyboardWidget{} - ret.InlineKeyboard = kbd - ret.Text = text - return ret -} - -// Transform the keyboard to widget with the specified text. -func (kbd *ReplyKeyboard) Widget(text string) *ReplyKeyboardWidget { - ret := &ReplyKeyboardWidget{} - ret.ReplyKeyboard = kbd - ret.Text = text - return ret -} - -// Convert the Keyboard to the Telegram API type of reply keyboard. -func (kbd *ReplyKeyboard) ToApi() any { - // Shades everything. - if kbd.Remove { - return tgbotapi.NewRemoveKeyboard(true) - } - - rows := [][]tgbotapi.KeyboardButton{} - for _, row := range kbd.Rows { - buttons := []tgbotapi.KeyboardButton{} - for _, button := range row { - buttons = append(buttons, button.ToTelegram()) - } - rows = append(rows, buttons) - } - - if kbd.OneTime { - return tgbotapi.NewOneTimeReplyKeyboard(rows...) - } - - return tgbotapi.NewReplyKeyboard(rows...) -} - -// Convert the inline keyboard to markup for the tgbotapi. -func (kbd *InlineKeyboard) ToApi() tgbotapi.InlineKeyboardMarkup { - rows := [][]tgbotapi.InlineKeyboardButton{} - for _, row := range kbd.Rows { - buttons := []tgbotapi.InlineKeyboardButton{} - for _, button := range row { - buttons = append(buttons, button.ToTelegramInline()) - } - rows = append(rows, buttons) - } - - return tgbotapi.NewInlineKeyboardMarkup(rows...) -} - -// Set if we should remove current keyboard on the user side -// when sending the keyboard. -func (kbd *ReplyKeyboard) WithRemove(remove bool) *ReplyKeyboard { - kbd.Remove = remove - return kbd -} - -// Set if the keyboard should be hidden after -// one of buttons is pressede. -func (kbd *ReplyKeyboard) WithOneTime(oneTime bool) *ReplyKeyboard { - kbd.OneTime = oneTime - return kbd -} - - diff --git a/tg/page.go b/tg/page.go index ea8acef..2eedf50 100644 --- a/tg/page.go +++ b/tg/page.go @@ -7,11 +7,11 @@ import ( // The basic widget to provide keyboard functionality // without implementing much. type Page struct { + Action Action Text string SubWidget Widget - Inline *InlineKeyboardWidget - Action Action - Reply *ReplyKeyboardWidget + Inline *InlineWidget + Reply *ReplyWidget } // Return new page with the specified text. @@ -26,13 +26,13 @@ func (p *Page) WithText(text string) *Page { } // Set the inline keyboard. -func (p *Page) WithInline(inline *InlineKeyboardWidget) *Page { +func (p *Page) WithInline(inline *InlineWidget) *Page { p.Inline = inline return p } // Set the reply keyboard. -func (p *Page) WithReply(reply *ReplyKeyboardWidget) *Page { +func (p *Page) WithReply(reply *ReplyWidget) *Page { p.Reply = reply return p } diff --git a/tg/reply.go b/tg/reply.go new file mode 100644 index 0000000..da235c6 --- /dev/null +++ b/tg/reply.go @@ -0,0 +1,143 @@ +package tg + +import ( + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) + +// The type represents reply keyboards. +type Reply struct { + *Keyboard + // If true will be removed after one press. + OneTime bool + // If true will remove the keyboard on send. + Remove bool +} + +// Set if we should remove current keyboard on the user side +// when sending the keyboard. +func (kbd *Reply) WithRemove(remove bool) *Reply { + kbd.Remove = remove + return kbd +} + +// Set if the keyboard should be hidden after +// one of buttons is pressede. +func (kbd *Reply) WithOneTime(oneTime bool) *Reply{ + kbd.OneTime = oneTime + return kbd +} + +// Convert the Keyboard to the Telegram API type of reply keyboard. +func (kbd *Reply) ToApi() any { + // Shades everything. + if kbd.Remove { + return tgbotapi.NewRemoveKeyboard(true) + } + + rows := [][]tgbotapi.KeyboardButton{} + for _, row := range kbd.Rows { + buttons := []tgbotapi.KeyboardButton{} + for _, button := range row { + buttons = append(buttons, button.ToTelegram()) + } + rows = append(rows, buttons) + } + + if kbd.OneTime { + return tgbotapi.NewOneTimeReplyKeyboard(rows...) + } + + return tgbotapi.NewReplyKeyboard(rows...) +} + +// Transform the keyboard to widget with the specified text. +func (kbd *Reply) Widget(text string) *ReplyWidget { + ret := &ReplyWidget{} + ret.Reply = kbd + ret.Text = text + return ret +} + +// The type implements reply keyboard widget. +type ReplyWidget struct { + Text string + *Reply +} + +// Implementing the sendable interface. +func (widget *ReplyWidget) SendConfig( + sid SessionId, + bot *Bot, +) (*SendConfig) { + if widget == nil { + msgConfig := tgbotapi.NewMessage(sid.ToApi(), ">") + msgConfig.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true) + return &SendConfig{ + Message: &msgConfig, + } + } + var text string + if widget.Text != "" { + text = widget.Text + } else { + text = ">" + } + + msgConfig := tgbotapi.NewMessage(sid.ToApi(), text) + msgConfig.ReplyMarkup = widget.ToApi() + + ret := &SendConfig{} + ret.Message = &msgConfig + return ret +} + +func (widget *ReplyWidget) Filter( + u *Update, + msgs MessageMap, +) bool { + if widget == nil { + return true + } + + if u.Message == nil { + return true + } + + + _, ok := widget.ButtonMap()[u.Message.Text] + if !ok { + if u.Message.Location != nil { + locBtn := widget.ButtonMap().LocationButton() + if locBtn == nil { + return true + } + } else { + return true + } + } + return false +} + +// Implementing the Widget interface. +func (widget *ReplyWidget) Serve(c *Context) { + for u := range c.Input() { + if u.Message == nil || u.Message.Text == "" { + continue + } + var btn *Button + text := u.Message.Text + btns := widget.ButtonMap() + + btn, ok := btns[text] + if !ok { + if u.Message.Location != nil { + btn = btns.LocationButton() + } + } + + if btn != nil { + c.Run(btn.Action, u) + } + } +} + diff --git a/tg/update.go b/tg/update.go new file mode 100644 index 0000000..60439da --- /dev/null +++ b/tg/update.go @@ -0,0 +1,51 @@ +package tg + +// The type represents general update channel. +type UpdateChan struct { + chn chan *Update +} + +// Return new update channel. +func NewUpdateChan() *UpdateChan { + ret := &UpdateChan{} + ret.chn = make(chan *Update) + return ret +} + + +func (updates *UpdateChan) Chan() chan *Update { + return updates.chn +} + +// Send an update to the channel. +// Returns true if the update was sent. +func (updates *UpdateChan) Send(u *Update) bool { + if updates == nil || updates.chn == nil { + return false + } + updates.chn <- u + return true +} + +// Read an update from the channel. +func (updates *UpdateChan) Read() *Update { + if updates == nil || updates.chn == nil { + return nil + } + return <-updates.chn +} + +// Returns true if the channel is closed. +func (updates *UpdateChan) Closed() bool { + return updates==nil || updates.chn == nil +} + +// Close the channel. Used in defers. +func (updates *UpdateChan) Close() { + if updates == nil || updates.chn == nil { + return + } + close(updates.chn) + updates.chn = nil +} + diff --git a/tg/widget.go b/tg/widget.go index 9a4cfd2..7d5923a 100644 --- a/tg/widget.go +++ b/tg/widget.go @@ -1,7 +1,7 @@ package tg import ( - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + //tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" //"fmt" ) @@ -52,60 +52,6 @@ func (f Func) Serve(c *Context) { f(c) } -// The type represents general update channel. -type UpdateChan struct { - chn chan *Update -} - -// Return new update channel. -func NewUpdateChan() *UpdateChan { - ret := &UpdateChan{} - ret.chn = make(chan *Update) - return ret -} - - -func (updates *UpdateChan) Chan() chan *Update { - return updates.chn -} - -// Send an update to the channel. -// Returns true if the update was sent. -func (updates *UpdateChan) Send(u *Update) bool { - if updates == nil || updates.chn == nil { - return false - } - updates.chn <- u - return true -} - -// Read an update from the channel. -func (updates *UpdateChan) Read() *Update { - if updates == nil || updates.chn == nil { - return nil - } - return <-updates.chn -} - -// Returns true if the channel is closed. -func (updates *UpdateChan) Closed() bool { - return updates==nil || updates.chn == nil -} - -// Close the channel. Used in defers. -func (updates *UpdateChan) Close() { - if updates == nil || updates.chn == nil { - return - } - close(updates.chn) - updates.chn = nil -} - - -// Implementing the interface provides -type DynamicWidget interface { - MakeWidget() Widget -} // The function that implements the Widget // interface. @@ -115,187 +61,4 @@ func (wf WidgetFunc) Serve(c *Context) { wf(c) } -func (wf WidgetFunc) Filter( - u *Update, - msgs ...*Message, -) bool { - return false -} - -// The type implements message with an inline keyboard. -type InlineKeyboardWidget struct { - Text string - *InlineKeyboard -} - -// The type implements dynamic inline keyboard widget. -// Aka message with inline keyboard. -func NewInlineKeyboardWidget( - text string, - inline *InlineKeyboard, -) *InlineKeyboardWidget { - ret := &InlineKeyboardWidget{} - ret.Text = text - ret.InlineKeyboard = inline - return ret -} -func (widget *InlineKeyboardWidget) SendConfig( - sid SessionId, - bot *Bot, -) (*SendConfig) { - var text string - if widget.Text != "" { - text = widget.Text - } else { - text = ">" - } - - msgConfig := tgbotapi.NewMessage(sid.ToApi(), text) - msgConfig.ReplyMarkup = widget.ToApi() - - ret := &SendConfig{} - ret.Message = &msgConfig - return ret -} - -func (widget *InlineKeyboardWidget) Serve(c *Context) { - for u := range c.Input() { - var act Action - if u.CallbackQuery == nil { - continue - } - cb := tgbotapi.NewCallback( - u.CallbackQuery.ID, - u.CallbackQuery.Data, - ) - data := u.CallbackQuery.Data - - _, err := c.Bot.Api.Request(cb) - if err != nil { - //return err - continue - } - - btns := widget.ButtonMap() - btn, ok := btns[data] - if !ok { - continue - } - if btn != nil { - act = btn.Action - } else if widget.Action != nil { - act = widget.Action - } - c.Run(act, u) - } -} - -func (widget *InlineKeyboardWidget) Filter( - u *Update, - msgs MessageMap, -) bool { - if widget == nil || u.CallbackQuery == nil { - return true - } - - inlineMsg, inlineOk := msgs[""] - if !inlineOk { - return true - } - if u.CallbackQuery.Message.MessageID != - inlineMsg.MessageID { - return true - } - - return false -} - -// The type implements dynamic reply keyboard widget. -type ReplyKeyboardWidget struct { - Text string - *ReplyKeyboard -} - -// Returns new empty reply keyboard widget. -func NewReplyKeyboardWidget( - text string, - reply *ReplyKeyboard, -) *ReplyKeyboardWidget { - ret := &ReplyKeyboardWidget{} - ret.Text = text - ret.ReplyKeyboard = reply - return ret -} - -func (widget *ReplyKeyboardWidget) SendConfig( - sid SessionId, - bot *Bot, -) (*SendConfig) { - if widget == nil { - msgConfig := tgbotapi.NewMessage(sid.ToApi(), ">") - msgConfig.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true) - return &SendConfig{ - Message: &msgConfig, - } - } - var text string - if widget.Text != "" { - text = widget.Text - } else { - text = ">" - } - - msgConfig := tgbotapi.NewMessage(sid.ToApi(), text) - msgConfig.ReplyMarkup = widget.ToApi() - - ret := &SendConfig{} - ret.Message = &msgConfig - return ret -} - -func (widget *ReplyKeyboardWidget) Filter( - u *Update, - msgs MessageMap, -) bool { - if widget == nil { - return true - } - - if u.Message == nil { - return true - } - - - _, ok := widget.ButtonMap()[u.Message.Text] - if !ok { - if u.Message.Location != nil { - locBtn := widget.ButtonMap().LocationButton() - if locBtn == nil { - return true - } - } else { - return true - } - } - return false -} - -func (widget *ReplyKeyboardWidget) Serve(c *Context) { - for u := range c.Input() { - var btn *Button - text := u.Message.Text - btns := widget.ButtonMap() - - btn, ok := btns[text] - if !ok { - if u.Message.Location != nil { - btn = btns.LocationButton() - } - } - - if btn != nil { - c.Run(btn.Action, u) - } - } -}