Implemented reading in commands as widget.
This commit is contained in:
parent
6c6b041133
commit
36fa167549
4 changed files with 237 additions and 202 deletions
|
@ -16,6 +16,7 @@ type Command struct {
|
||||||
Name CommandName
|
Name CommandName
|
||||||
Description string
|
Description string
|
||||||
Action *action
|
Action *action
|
||||||
|
Widget Widget
|
||||||
}
|
}
|
||||||
type CommandMap map[CommandName]*Command
|
type CommandMap map[CommandName]*Command
|
||||||
|
|
||||||
|
@ -34,6 +35,15 @@ func (c *Command) ActionFunc(af ActionFunc) *Command {
|
||||||
return c.WithAction(af)
|
return c.WithAction(af)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Command) WithWidget(w Widget) *Command {
|
||||||
|
c.Widget = w
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Command) WidgetFunc(fn WidgetFunc) *Command {
|
||||||
|
return c.WithWidget(fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Command) ToApi() tgbotapi.BotCommand {
|
func (c *Command) ToApi() tgbotapi.BotCommand {
|
||||||
ret := tgbotapi.BotCommand{}
|
ret := tgbotapi.BotCommand{}
|
||||||
ret.Command = string(c.Name)
|
ret.Command = string(c.Name)
|
||||||
|
|
202
tg/page.go
Normal file
202
tg/page.go
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
package tg
|
||||||
|
|
||||||
|
import (
|
||||||
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The basic widget to provide keyboard functionality
|
||||||
|
// without implementing much.
|
||||||
|
type Page struct {
|
||||||
|
Text string
|
||||||
|
SubWidget Widget
|
||||||
|
Inline *InlineKeyboard
|
||||||
|
Reply *ReplyKeyboard
|
||||||
|
Action Action
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return new page with the specified text.
|
||||||
|
func NewPage(text string) *Page {
|
||||||
|
ret := &Page{}
|
||||||
|
ret.Text = text
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the inline keyboard.
|
||||||
|
func (p *Page) WithInline(inline *InlineKeyboard) *Page {
|
||||||
|
p.Inline = inline
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the reply keyboard.
|
||||||
|
func (p *Page) WithReply(reply *ReplyKeyboard) *Page {
|
||||||
|
p.Reply = reply
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the action to be run before serving.
|
||||||
|
func (p *Page) WithAction(a Action) *Page {
|
||||||
|
p.Action = a
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alias to with action to simpler define actions.
|
||||||
|
func (p *Page) ActionFunc(fn ActionFunc) *Page {
|
||||||
|
return p.WithAction(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the sub widget that will get the skipped
|
||||||
|
// updates.
|
||||||
|
func (p *Page) WithSub(sub Widget) *Page {
|
||||||
|
p.SubWidget = sub
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Page) Serve(
|
||||||
|
c *Context, updates chan *Update,
|
||||||
|
) error {
|
||||||
|
msgs, err := c.Render(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The inline message is always returned
|
||||||
|
// and the reply one is useless in our case.
|
||||||
|
inlineMsg := msgs[0]
|
||||||
|
|
||||||
|
if p.Action != nil {
|
||||||
|
c.run(p.Action, c.Update)
|
||||||
|
}
|
||||||
|
var subUpdates chan *Update
|
||||||
|
if p.SubWidget != nil {
|
||||||
|
subUpdates = make(chan *Update)
|
||||||
|
go p.SubWidget.Serve(c, subUpdates)
|
||||||
|
defer close(subUpdates)
|
||||||
|
}
|
||||||
|
for u := range updates {
|
||||||
|
var act Action
|
||||||
|
if u.Message != nil {
|
||||||
|
text := u.Message.Text
|
||||||
|
kbd := p.Reply
|
||||||
|
if kbd == nil {
|
||||||
|
if subUpdates != nil {
|
||||||
|
subUpdates <- u
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
btns := kbd.ButtonMap()
|
||||||
|
btn, ok := btns[text]
|
||||||
|
if !ok {
|
||||||
|
if u.Message.Location != nil {
|
||||||
|
for _, b := range btns {
|
||||||
|
if b.SendLocation {
|
||||||
|
btn = b
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if subUpdates != nil {
|
||||||
|
subUpdates <- u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if btn != nil {
|
||||||
|
act = btn.Action
|
||||||
|
} else if kbd.Action != nil {
|
||||||
|
act = kbd.Action
|
||||||
|
}
|
||||||
|
} else if u.CallbackQuery != nil {
|
||||||
|
if u.CallbackQuery.Message.MessageID != inlineMsg.MessageID {
|
||||||
|
if subUpdates != nil {
|
||||||
|
subUpdates <- u
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
kbd := p.Inline
|
||||||
|
if kbd == nil {
|
||||||
|
if subUpdates != nil {
|
||||||
|
subUpdates <- u
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
btns := kbd.ButtonMap()
|
||||||
|
btn, ok := btns[data]
|
||||||
|
if !ok {
|
||||||
|
if subUpdates != nil {
|
||||||
|
subUpdates <- u
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if btn != nil {
|
||||||
|
act = btn.Action
|
||||||
|
} else if kbd.Action != nil {
|
||||||
|
act = kbd.Action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if act != nil {
|
||||||
|
c.run(act, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Page) Render(
|
||||||
|
sid SessionId, bot *Bot,
|
||||||
|
) ([]*SendConfig, error) {
|
||||||
|
cid := sid.ToApi()
|
||||||
|
reply := s.Reply
|
||||||
|
inline := s.Inline
|
||||||
|
ret := []*SendConfig{}
|
||||||
|
var txt string
|
||||||
|
// Screen text and inline keyboard.
|
||||||
|
if s.Text != "" {
|
||||||
|
txt = s.Text
|
||||||
|
} else if inline != nil {
|
||||||
|
// Default to send the keyboard.
|
||||||
|
txt = ">"
|
||||||
|
}
|
||||||
|
if txt != "" {
|
||||||
|
msgConfig := tgbotapi.NewMessage(cid, txt)
|
||||||
|
if inline != nil {
|
||||||
|
msgConfig.ReplyMarkup = inline.ToApi()
|
||||||
|
} else if reply != nil {
|
||||||
|
msgConfig.ReplyMarkup = reply.ToApi()
|
||||||
|
ret = append(ret, &SendConfig{Message: &msgConfig})
|
||||||
|
return ret, nil
|
||||||
|
} else {
|
||||||
|
msgConfig.ReplyMarkup = NewReply().
|
||||||
|
WithRemove(true).
|
||||||
|
ToApi()
|
||||||
|
ret = append(ret, &SendConfig{Message: &msgConfig})
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
ret = append(ret, &SendConfig{Message: &msgConfig})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Screen text and reply keyboard.
|
||||||
|
if reply != nil {
|
||||||
|
msgConfig := tgbotapi.NewMessage(cid, ">")
|
||||||
|
msgConfig.ReplyMarkup = reply.ToApi()
|
||||||
|
ret = append(ret, &SendConfig{
|
||||||
|
Message: &msgConfig,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Removing keyboard if there is none.
|
||||||
|
msgConfig := tgbotapi.NewMessage(cid, ">")
|
||||||
|
msgConfig.ReplyMarkup = NewReply().
|
||||||
|
WithRemove(true).
|
||||||
|
ToApi()
|
||||||
|
ret = append(ret, &SendConfig{Message: &msgConfig})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ func (c *context) handleUpdateChan(updates chan *Update) {
|
||||||
if beh.Init != nil {
|
if beh.Init != nil {
|
||||||
c.run(beh.Init, nil)
|
c.run(beh.Init, nil)
|
||||||
}
|
}
|
||||||
|
var cmdUpdates chan *Update
|
||||||
for u := range updates {
|
for u := range updates {
|
||||||
// The part is added to implement custom update handling.
|
// The part is added to implement custom update handling.
|
||||||
if !session.started {
|
if !session.started {
|
||||||
|
@ -59,6 +60,20 @@ func (c *context) handleUpdateChan(updates chan *Update) {
|
||||||
if cmd.Action != nil {
|
if cmd.Action != nil {
|
||||||
c.run(cmd.Action, u)
|
c.run(cmd.Action, u)
|
||||||
}
|
}
|
||||||
|
if cmd.Widget != nil {
|
||||||
|
if cmdUpdates != nil {
|
||||||
|
close(cmdUpdates)
|
||||||
|
}
|
||||||
|
cmdUpdates = make(chan *Update)
|
||||||
|
go func() {
|
||||||
|
cmd.Widget.Serve(
|
||||||
|
&Context{context: c, Update: u},
|
||||||
|
cmdUpdates,
|
||||||
|
)
|
||||||
|
close(cmdUpdates)
|
||||||
|
cmdUpdates = nil
|
||||||
|
}()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Some usage.
|
// Some usage.
|
||||||
}
|
}
|
||||||
|
@ -66,8 +81,12 @@ func (c *context) handleUpdateChan(updates chan *Update) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The standard thing - send messages to widgets.
|
// The standard thing - send messages to widgets.
|
||||||
|
if cmdUpdates != nil {
|
||||||
|
cmdUpdates <- u
|
||||||
|
} else {
|
||||||
c.widgetUpdates <- u
|
c.widgetUpdates <- u
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *context) run(a Action, u *Update) {
|
func (c *context) run(a Action, u *Update) {
|
||||||
|
|
204
tg/widget.go
204
tg/widget.go
|
@ -1,7 +1,7 @@
|
||||||
package tg
|
package tg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
//tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implementing the interface provides
|
// Implementing the interface provides
|
||||||
|
@ -22,205 +22,9 @@ type DynamicWidget interface {
|
||||||
|
|
||||||
// The function that implements the Widget
|
// The function that implements the Widget
|
||||||
// interface.
|
// interface.
|
||||||
type WidgetFunc func(*Context, chan *Update)
|
type WidgetFunc func(*Context, chan *Update) error
|
||||||
|
|
||||||
func (wf WidgetFunc) Serve(c *Context, updates chan *Update){
|
func (wf WidgetFunc) Serve(c *Context, updates chan *Update) error {
|
||||||
wf(c, updates)
|
return wf(c, updates)
|
||||||
}
|
|
||||||
|
|
||||||
// The basic widget to provide keyboard functionality
|
|
||||||
// without implementing much.
|
|
||||||
type Page struct {
|
|
||||||
Text string
|
|
||||||
SubWidget Widget
|
|
||||||
Inline *InlineKeyboard
|
|
||||||
Reply *ReplyKeyboard
|
|
||||||
Action Action
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return new page with the specified text.
|
|
||||||
func NewPage(text string) *Page {
|
|
||||||
ret := &Page{}
|
|
||||||
ret.Text = text
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the inline keyboard.
|
|
||||||
func (p *Page) WithInline(inline *InlineKeyboard) *Page {
|
|
||||||
p.Inline = inline
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the reply keyboard.
|
|
||||||
func (p *Page) WithReply(reply *ReplyKeyboard) *Page {
|
|
||||||
p.Reply = reply
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the action to be run before serving.
|
|
||||||
func (p *Page) WithAction(a Action) *Page {
|
|
||||||
p.Action = a
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alias to with action to simpler define actions.
|
|
||||||
func (p *Page) ActionFunc(fn ActionFunc) *Page {
|
|
||||||
return p.WithAction(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the sub widget that will get the skipped
|
|
||||||
// updates.
|
|
||||||
func (p *Page) WithSub(sub Widget) *Page {
|
|
||||||
p.SubWidget = sub
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Page) Serve(
|
|
||||||
c *Context, updates chan *Update,
|
|
||||||
) error {
|
|
||||||
msgs, err := c.Render(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The inline message is always returned
|
|
||||||
// and the reply one is useless in our case.
|
|
||||||
inlineMsg := msgs[0]
|
|
||||||
|
|
||||||
if p.Action != nil {
|
|
||||||
c.run(p.Action, c.Update)
|
|
||||||
}
|
|
||||||
var subUpdates chan *Update
|
|
||||||
if p.SubWidget != nil {
|
|
||||||
subUpdates = make(chan *Update)
|
|
||||||
go p.SubWidget.Serve(c, subUpdates)
|
|
||||||
defer close(subUpdates)
|
|
||||||
}
|
|
||||||
for u := range updates {
|
|
||||||
var act Action
|
|
||||||
if u.Message != nil {
|
|
||||||
text := u.Message.Text
|
|
||||||
kbd := p.Reply
|
|
||||||
if kbd == nil {
|
|
||||||
if subUpdates != nil {
|
|
||||||
subUpdates <- u
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
btns := kbd.ButtonMap()
|
|
||||||
btn, ok := btns[text]
|
|
||||||
if !ok {
|
|
||||||
if u.Message.Location != nil {
|
|
||||||
for _, b := range btns {
|
|
||||||
if b.SendLocation {
|
|
||||||
btn = b
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if subUpdates != nil {
|
|
||||||
subUpdates <- u
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if btn != nil {
|
|
||||||
act = btn.Action
|
|
||||||
} else if kbd.Action != nil {
|
|
||||||
act = kbd.Action
|
|
||||||
}
|
|
||||||
} else if u.CallbackQuery != nil {
|
|
||||||
if u.CallbackQuery.Message.MessageID != inlineMsg.MessageID {
|
|
||||||
if subUpdates != nil {
|
|
||||||
subUpdates <- u
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
kbd := p.Inline
|
|
||||||
if kbd == nil {
|
|
||||||
if subUpdates != nil {
|
|
||||||
subUpdates <- u
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
btns := kbd.ButtonMap()
|
|
||||||
btn, ok := btns[data]
|
|
||||||
if !ok {
|
|
||||||
if subUpdates != nil {
|
|
||||||
subUpdates <- u
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if btn != nil {
|
|
||||||
act = btn.Action
|
|
||||||
} else if kbd.Action != nil {
|
|
||||||
act = kbd.Action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if act != nil {
|
|
||||||
c.run(act, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Page) Render(
|
|
||||||
sid SessionId, bot *Bot,
|
|
||||||
) ([]*SendConfig, error) {
|
|
||||||
cid := sid.ToApi()
|
|
||||||
reply := s.Reply
|
|
||||||
inline := s.Inline
|
|
||||||
ret := []*SendConfig{}
|
|
||||||
var txt string
|
|
||||||
// Screen text and inline keyboard.
|
|
||||||
if s.Text != "" {
|
|
||||||
txt = s.Text
|
|
||||||
} else if inline != nil {
|
|
||||||
// Default to send the keyboard.
|
|
||||||
txt = ">"
|
|
||||||
}
|
|
||||||
if txt != "" {
|
|
||||||
msgConfig := tgbotapi.NewMessage(cid, txt)
|
|
||||||
if inline != nil {
|
|
||||||
msgConfig.ReplyMarkup = inline.ToApi()
|
|
||||||
} else if reply != nil {
|
|
||||||
msgConfig.ReplyMarkup = reply.ToApi()
|
|
||||||
ret = append(ret, &SendConfig{Message: &msgConfig})
|
|
||||||
return ret, nil
|
|
||||||
} else {
|
|
||||||
msgConfig.ReplyMarkup = NewReply().
|
|
||||||
WithRemove(true).
|
|
||||||
ToApi()
|
|
||||||
ret = append(ret, &SendConfig{Message: &msgConfig})
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
ret = append(ret, &SendConfig{Message: &msgConfig})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Screen text and reply keyboard.
|
|
||||||
if reply != nil {
|
|
||||||
msgConfig := tgbotapi.NewMessage(cid, ">")
|
|
||||||
msgConfig.ReplyMarkup = reply.ToApi()
|
|
||||||
ret = append(ret, &SendConfig{
|
|
||||||
Message: &msgConfig,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Removing keyboard if there is none.
|
|
||||||
msgConfig := tgbotapi.NewMessage(cid, ">")
|
|
||||||
msgConfig.ReplyMarkup = NewReply().
|
|
||||||
WithRemove(true).
|
|
||||||
ToApi()
|
|
||||||
ret = append(ret, &SendConfig{Message: &msgConfig})
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue