KEEP IMPLEMENTING THE NEW COMPONENT SYSTEM INSTEAD OF OLD WIDGETS.
This commit is contained in:
parent
185c8fc1f8
commit
1f52474082
12 changed files with 192 additions and 151 deletions
|
@ -7,6 +7,8 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/mojosa-software/got/tg"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type BotData struct {
|
||||
|
@ -73,13 +75,7 @@ var (
|
|||
backButton,
|
||||
)
|
||||
|
||||
navKeyboard = tg.NewKeyboard().Row(
|
||||
tg.NewButton("Inc/Dec").Go("/inc-dec"),
|
||||
).Row(
|
||||
tg.NewButton("Mutate messages").Go("/mutate-messages"),
|
||||
).Row(
|
||||
tg.NewButton("Send location").Go("/send-location"),
|
||||
).Reply()
|
||||
navKeyboard =
|
||||
|
||||
sendLocationKeyboard = tg.NewKeyboard().Row(
|
||||
tg.NewButton("Send location").
|
||||
|
@ -107,21 +103,34 @@ WithInitFunc(func(c *tg.Context) {
|
|||
c.Session.Data = &SessionData{}
|
||||
}).WithRootNode(tg.NewRootNode(
|
||||
// The "/" widget.
|
||||
tg.NewPage().
|
||||
WithInline(
|
||||
tg.WidgetFunc(func(c *tg.Context) tg.UIs {
|
||||
return tg.UIs{
|
||||
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton("GoT Github page").
|
||||
WithUrl("https://github.com/mojosa-software/got"),
|
||||
).Inline().Widget(
|
||||
fmt.Sprint(
|
||||
"The testing bot started!\n",
|
||||
"You can see the basics of usage in the ",
|
||||
"cmd/test/main.go file!",
|
||||
tg.NewButton("GoT Github page").
|
||||
WithUrl("https://github.com/mojosa-software/got"),
|
||||
).Inline().Widget(
|
||||
fmt.Sprintf(
|
||||
"Hello, %s"
|
||||
"The testing bot started!\n",
|
||||
"You can see the basics of usage in the ",
|
||||
"cmd/test/main.go file!",
|
||||
c.SentFrom().UserName,
|
||||
),
|
||||
),
|
||||
),
|
||||
).WithReply(
|
||||
navKeyboard.Widget("Choose what you are interested in"),
|
||||
),
|
||||
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton("Inc/Dec").Go("/inc-dec"),
|
||||
).Row(
|
||||
tg.NewButton("Mutate messages").Go("/mutate-messages"),
|
||||
).Row(
|
||||
tg.NewButton("Send location").Go("/send-location"),
|
||||
).Reply().Widget(
|
||||
"Choose the point of your interest",
|
||||
),
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
tg.NewNode(
|
||||
"mutate-messages", tg.NewPage().WithReply(
|
||||
|
@ -216,6 +225,32 @@ WithInitFunc(func(c *tg.Context) {
|
|||
bd := c.Bot.Data.(*BotData)
|
||||
c.Sendf("My name is %q", bd.Name)
|
||||
}),
|
||||
tg.NewCommand("dynamic").
|
||||
Desc("check of the dynamic work").
|
||||
WidgetFunc(func(c *tg.Context){
|
||||
nRow, nBtn := rand.Int()%10, rand.Int()%5
|
||||
rows := []tg.ButtonRow{}
|
||||
for i:=0 ; i<nRow ; i++ {
|
||||
row := []*tg.Button{}
|
||||
for j:=0 ; j<nBtn ; j++ {
|
||||
row = append(row, tg.NewButton(
|
||||
strconv.Itoa(i) + " " + strconv.Itoa(j),
|
||||
))
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
kbd := tg.NewKeyboard(rows...).ActionFunc(func(c *tg.Context){
|
||||
c.Sendf(u.)
|
||||
})Inline().Widget("sample text")
|
||||
c.Send(kbd)
|
||||
kbdChn := c.RunWidget(kbd)
|
||||
for u := range c.Input() {
|
||||
if kbd.Filter(u, nil) {
|
||||
continue
|
||||
}
|
||||
kbdChn.Send(u)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
var gBeh = tg.NewGroupBehaviour().
|
||||
|
|
|
@ -47,10 +47,12 @@ func (bot *Bot) Debug(debug bool) *Bot {
|
|||
return bot
|
||||
}
|
||||
|
||||
// Send the Renderable to the specified session client side.
|
||||
// Can be used for both group and private sessions.
|
||||
func (bot *Bot) Send(
|
||||
sid SessionId, v Sendable,
|
||||
sid SessionId, v Renderable,
|
||||
) (*Message, error) {
|
||||
config := v.SendConfig(sid, bot)
|
||||
config := v.Render(sid, bot)
|
||||
if config.Error != nil {
|
||||
return nil, config.Error
|
||||
}
|
||||
|
|
42
tg/compo.go
Normal file
42
tg/compo.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package tg
|
||||
|
||||
type UIs []UI
|
||||
|
||||
// The type describes dynamic screen widget.
|
||||
type Widget interface {
|
||||
UIs(*Context) UIs
|
||||
}
|
||||
|
||||
// The way to describe custom function based Widgets.
|
||||
type WidgetFunc func(c *Context) UIs
|
||||
func (fn WidgetFunc) UIs(c *Context) UIs {
|
||||
return fn(c)
|
||||
}
|
||||
|
||||
// The type describes interfaces
|
||||
// needed to be implemented to be endpoint handlers.
|
||||
type UI interface {
|
||||
Renderable
|
||||
|
||||
SetMessage(*Message)
|
||||
GetMessage() *Message
|
||||
Filterer
|
||||
|
||||
Server
|
||||
}
|
||||
|
||||
type UiFunc func()
|
||||
|
||||
// The type to embed into potential components.
|
||||
// Implements empty versions of interfaces
|
||||
// and contains
|
||||
type Compo struct{
|
||||
*Message
|
||||
}
|
||||
|
||||
// Defalut setting message
|
||||
func (compo Compo) SetMessage(msg *Message) { compo.Message = msg }
|
||||
func (compo Compo) GetMessage() *Message { return compo.Message }
|
||||
// Default non filtering filter. Always returns false.
|
||||
func (compo Compo) Filter(_ *Update, _ *Message) bool {return false}
|
||||
|
|
@ -7,6 +7,15 @@ import (
|
|||
//"path"
|
||||
)
|
||||
|
||||
// General type function for faster typing.
|
||||
type Func func(*Context)
|
||||
func (f Func) Act(c *Context) {
|
||||
f(c)
|
||||
}
|
||||
func (f Func) Serve(c *Context) {
|
||||
f(c)
|
||||
}
|
||||
|
||||
// The way to determine where the context is
|
||||
// related to.
|
||||
type ContextScope uint8
|
||||
|
@ -185,7 +194,7 @@ func (c *Context) PathExist(pth Path) bool {
|
|||
}
|
||||
|
||||
// Run widget in background returning the new input channel for it.
|
||||
func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
||||
func (c *Context) runWidget(widget Widget, args ...any) {
|
||||
if widget == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -197,19 +206,32 @@ func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
|||
arg = args
|
||||
}
|
||||
|
||||
updates := NewUpdateChan()
|
||||
go func() {
|
||||
widget.Serve(
|
||||
c.Copy().
|
||||
WithInput(updates).
|
||||
WithArg(arg),
|
||||
)
|
||||
// To let widgets finish themselves before
|
||||
// the channel is closed.
|
||||
updates.Close()
|
||||
}()
|
||||
uis := widget.UI()
|
||||
chns := make(map[UI] *UpdateChan)
|
||||
for _, ui := range uis {
|
||||
msg := c.Send(ui.Render(c))
|
||||
ui.SetMessage(msg)
|
||||
updates := NewUpdateChan()
|
||||
go func() {
|
||||
ui.Serve(
|
||||
c.Copy().
|
||||
WithInput(updates).
|
||||
WithArg(arg),
|
||||
)
|
||||
// To let widgets finish themselves before
|
||||
// the channel is closed.
|
||||
updates.Close()
|
||||
}()
|
||||
chns[ui] = updates
|
||||
}
|
||||
|
||||
return updates
|
||||
for u := range c.skippedUpdates.Chan() {
|
||||
for ui := range uis {
|
||||
if !ui.Filter() {
|
||||
chns[ui] <- u
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple way to read strings for widgets.
|
||||
|
|
18
tg/filter.go
Normal file
18
tg/filter.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package tg
|
||||
|
||||
|
||||
// Implementing the interface provides way
|
||||
// to know exactly what kind of updates
|
||||
// the widget needs.
|
||||
type Filterer interface {
|
||||
// Return true if should filter the update
|
||||
// and not send it inside the widget.
|
||||
Filter(*Update) bool
|
||||
}
|
||||
|
||||
type FilterFunc func(*Update, MessageMap) bool
|
||||
func (f FilterFunc) Filter(
|
||||
u *Update, msgs MessageMap,
|
||||
) bool {
|
||||
return f(u, msgs)
|
||||
}
|
22
tg/inline.go
22
tg/inline.go
|
@ -32,15 +32,15 @@ func (kbd *Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
|||
}
|
||||
|
||||
// The type implements message with an inline keyboard.
|
||||
type InlineWidget struct {
|
||||
type InlineCompo struct {
|
||||
Compo
|
||||
Text string
|
||||
*Inline
|
||||
}
|
||||
|
||||
// Implementing the Sendable interface.
|
||||
func (widget *InlineWidget) SendConfig(
|
||||
sid SessionId,
|
||||
bot *Bot,
|
||||
c *Context,
|
||||
) (*SendConfig) {
|
||||
var text string
|
||||
if widget.Text != "" {
|
||||
|
@ -49,7 +49,8 @@ func (widget *InlineWidget) SendConfig(
|
|||
text = ">"
|
||||
}
|
||||
|
||||
msgConfig := tgbotapi.NewMessage(sid.ToApi(), text)
|
||||
sid := c.Session.Id.ToApi()
|
||||
msgConfig := tgbotapi.NewMessage(sid, text)
|
||||
msgConfig.ReplyMarkup = widget.ToApi()
|
||||
|
||||
ret := &SendConfig{}
|
||||
|
@ -58,7 +59,7 @@ func (widget *InlineWidget) SendConfig(
|
|||
}
|
||||
|
||||
// Implementing the Widget interface.
|
||||
func (widget *InlineWidget) Serve(c *Context) {
|
||||
func (widget *InlineCompo) Serve(c *Context) {
|
||||
for u := range c.Input() {
|
||||
var act Action
|
||||
if u.CallbackQuery == nil {
|
||||
|
@ -90,20 +91,13 @@ func (widget *InlineWidget) Serve(c *Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (widget *InlineWidget) Filter(
|
||||
u *Update,
|
||||
msgs MessageMap,
|
||||
) bool {
|
||||
func (compo *InlineCompo) Filter(u *Update) bool {
|
||||
if widget == nil || u.CallbackQuery == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
inlineMsg, inlineOk := msgs[""]
|
||||
if !inlineOk {
|
||||
return true
|
||||
}
|
||||
if u.CallbackQuery.Message.MessageID !=
|
||||
inlineMsg.MessageID {
|
||||
compo.Message.MessageID {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
2
tg/make.go
Normal file
2
tg/make.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
package tg
|
||||
|
|
@ -3,9 +3,7 @@ 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 {
|
||||
Action Action
|
||||
Text string
|
||||
|
@ -129,5 +127,5 @@ func (p *Page) Serve(c *Context) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
|
35
tg/reply.go
35
tg/reply.go
|
@ -59,51 +59,45 @@ func (kbd *Reply) Widget(text string) *ReplyWidget {
|
|||
}
|
||||
|
||||
// The type implements reply keyboard widget.
|
||||
type ReplyWidget struct {
|
||||
type ReplyCompo struct {
|
||||
Text string
|
||||
*Reply
|
||||
}
|
||||
|
||||
// Implementing the sendable interface.
|
||||
func (widget *ReplyWidget) SendConfig(
|
||||
sid SessionId,
|
||||
bot *Bot,
|
||||
func (compo *ReplyCompo) Render(
|
||||
c *Context,
|
||||
) (*SendConfig) {
|
||||
if widget == nil {
|
||||
msgConfig := tgbotapi.NewMessage(sid.ToApi(), ">")
|
||||
sid := c.Session.Id.ToApi()
|
||||
if compo == nil {
|
||||
msgConfig := tgbotapi.NewMessage(sid, ">")
|
||||
msgConfig.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
|
||||
return &SendConfig{
|
||||
Message: &msgConfig,
|
||||
}
|
||||
}
|
||||
var text string
|
||||
if widget.Text != "" {
|
||||
text = widget.Text
|
||||
if compo.Text != "" {
|
||||
text = compo.Text
|
||||
} else {
|
||||
text = ">"
|
||||
}
|
||||
|
||||
msgConfig := tgbotapi.NewMessage(sid.ToApi(), text)
|
||||
msgConfig.ReplyMarkup = widget.ToApi()
|
||||
msgConfig := tgbotapi.NewMessage(sid, text)
|
||||
msgConfig.ReplyMarkup = compo.ToApi()
|
||||
|
||||
ret := &SendConfig{}
|
||||
ret.Message = &msgConfig
|
||||
return ret
|
||||
}
|
||||
|
||||
func (widget *ReplyWidget) Filter(
|
||||
func (compo *ReplyCompo) Filter(
|
||||
u *Update,
|
||||
msgs MessageMap,
|
||||
) bool {
|
||||
if widget == nil {
|
||||
if compo == nil || u.Message == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.Message == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
_, ok := widget.ButtonMap()[u.Message.Text]
|
||||
if !ok {
|
||||
if u.Message.Location != nil {
|
||||
|
@ -119,11 +113,8 @@ func (widget *ReplyWidget) Filter(
|
|||
}
|
||||
|
||||
// Implementing the Widget interface.
|
||||
func (widget *ReplyWidget) Serve(c *Context) {
|
||||
func (compo *ReplyCompo) 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()
|
||||
|
|
12
tg/send.go
12
tg/send.go
|
@ -5,16 +5,12 @@ import (
|
|||
)
|
||||
|
||||
type MessageId int64
|
||||
type Image any
|
||||
|
||||
// Implementing the interface lets the
|
||||
// value to be sent.
|
||||
type Sendable interface {
|
||||
SendConfig(SessionId, *Bot) *SendConfig
|
||||
}
|
||||
|
||||
// Implementing the interface provides
|
||||
// way to define what message will be
|
||||
// sent to the side of a user.
|
||||
type Renderable interface {
|
||||
Render(SessionId, *Bot) ([]*SendConfig)
|
||||
Render(*Context) (*SendConfig)
|
||||
}
|
||||
|
||||
type Errorer interface {
|
||||
|
|
8
tg/server.go
Normal file
8
tg/server.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package tg
|
||||
|
||||
// Implementing the interface provides
|
||||
// the way to define how to handle updates.
|
||||
type Server interface {
|
||||
Serve(*Context)
|
||||
}
|
||||
|
69
tg/widget.go
69
tg/widget.go
|
@ -1,77 +1,10 @@
|
|||
package tg
|
||||
|
||||
import (
|
||||
//tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
//"fmt"
|
||||
)
|
||||
|
||||
type Maker[V any] interface {
|
||||
Make(*Context) V
|
||||
}
|
||||
|
||||
type MakeFunc[V any] func(*Context) V
|
||||
func (fn MakeFunc[V]) Make(c *Context) V {
|
||||
return fn(c)
|
||||
}
|
||||
|
||||
type ArgMap = map[string] any
|
||||
type ArgSlice = []any
|
||||
type ArgList[V any] []V
|
||||
|
||||
// Implementing the interface provides
|
||||
// ability to build your own widgets,
|
||||
// aka components.
|
||||
type Widget interface {
|
||||
// When the update channel is closed
|
||||
// widget MUST end its work.
|
||||
// Mostly made by looping over the
|
||||
// updates range.
|
||||
Serve(*Context)
|
||||
}
|
||||
|
||||
type DynamicWidget[W Widget] interface {
|
||||
Maker[W]
|
||||
}
|
||||
|
||||
// Implementing the interface provides ability to
|
||||
// be used as the root widget for contexts.
|
||||
type RootWidget interface {
|
||||
Widget
|
||||
SetSub(Widget)
|
||||
}
|
||||
|
||||
// Implementing the interface provides way
|
||||
// to know exactly what kind of updates
|
||||
// the widget needs.
|
||||
type Filterer interface {
|
||||
// Return true if should filter the update
|
||||
// and not send it inside the widget.
|
||||
Filter(*Update, MessageMap) bool
|
||||
}
|
||||
|
||||
type FilterFunc func(*Update, MessageMap) bool
|
||||
func (f FilterFunc) Filter(
|
||||
u *Update, msgs MessageMap,
|
||||
) bool {
|
||||
return f(u, msgs)
|
||||
}
|
||||
|
||||
// General type function for faster typing.
|
||||
type Func func(*Context)
|
||||
func (f Func) Act(c *Context) {
|
||||
f(c)
|
||||
}
|
||||
func (f Func) Serve(c *Context) {
|
||||
f(c)
|
||||
}
|
||||
|
||||
|
||||
// The function that implements the Widget
|
||||
// interface.
|
||||
type WidgetFunc func(*Context)
|
||||
|
||||
func (wf WidgetFunc) Serve(c *Context) {
|
||||
wf(c)
|
||||
type RootHandler interface {
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue