Small fixes to use more intuitive MessageCompo instead of the Compo method for Inline and Reply keyboards.
This commit is contained in:
parent
227dc816fa
commit
47b7c59469
10 changed files with 178 additions and 186 deletions
122
cmd/test/main.go
122
cmd/test/main.go
|
@ -20,13 +20,11 @@ type SessionData struct {
|
|||
}
|
||||
|
||||
type MutateMessageWidget struct {
|
||||
*tg.Compo
|
||||
Mutate func(string) string
|
||||
}
|
||||
|
||||
func NewMutateMessageWidget(fn func(string) string) *MutateMessageWidget {
|
||||
ret := &MutateMessageWidget{}
|
||||
ret.Compo = tg.NewCompo()
|
||||
ret.Mutate = fn
|
||||
return ret
|
||||
}
|
||||
|
@ -104,53 +102,55 @@ WithInitFunc(func(c *tg.Context) {
|
|||
}).WithRootNode(tg.NewRootNode(
|
||||
// The "/" widget.
|
||||
tg.RenderFunc(func(c *tg.Context) tg.UI {return tg.UI {
|
||||
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewMessage(fmt.Sprintf(
|
||||
fmt.Sprint(
|
||||
"Hello, %s!\n",
|
||||
"The testing bot started!\n",
|
||||
"You can see the basics of usage in the ",
|
||||
"cmd/test/main.go file!",
|
||||
),
|
||||
c.SentFrom().UserName,
|
||||
)).Inline(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton("GoT Github page").
|
||||
WithUrl("https://github.com/mojosa-software/got"),
|
||||
).Inline().Compo(
|
||||
fmt.Sprintf(
|
||||
fmt.Sprint(
|
||||
"Hello, %s!\n",
|
||||
"The testing bot started!\n",
|
||||
"You can see the basics of usage in the ",
|
||||
"cmd/test/main.go file!",
|
||||
),
|
||||
c.SentFrom().UserName,
|
||||
),
|
||||
),
|
||||
|
||||
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().Compo(
|
||||
"Choose the point of your interest",
|
||||
),
|
||||
).Inline(),
|
||||
),
|
||||
|
||||
tg.NewMessage("Choose your interest").Reply(
|
||||
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(),
|
||||
),
|
||||
}}),
|
||||
|
||||
tg.NewNode(
|
||||
"mutate-messages", tg.RenderFunc(func(c *tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton("Upper case").Go("upper-case"),
|
||||
tg.NewButton("Lower case").Go("lower-case"),
|
||||
).Row(
|
||||
backButton,
|
||||
).Reply().Compo(
|
||||
tg.NewMessage(
|
||||
"Choose the function to mutate string",
|
||||
).Reply(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton("Upper case").Go("upper-case"),
|
||||
tg.NewButton("Lower case").Go("lower-case"),
|
||||
).Row(
|
||||
backButton,
|
||||
).Reply(),
|
||||
),
|
||||
}
|
||||
}),
|
||||
tg.NewNode(
|
||||
"upper-case", tg.RenderFunc(func(c *tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
backKeyboard.Reply().Compo(
|
||||
"Type a string and the bot will convert it to upper case",
|
||||
),
|
||||
tg.NewMessage(
|
||||
"Type a string and the bot will convert it to upper case",
|
||||
).Reply(
|
||||
backKeyboard.Reply(),
|
||||
),
|
||||
NewMutateMessageWidget(strings.ToUpper),
|
||||
}
|
||||
}),
|
||||
|
@ -158,10 +158,11 @@ WithInitFunc(func(c *tg.Context) {
|
|||
tg.NewNode(
|
||||
"lower-case", tg.RenderFunc(func(c *tg.Context) tg.UI {
|
||||
return tg.UI{
|
||||
backKeyboard.Reply().
|
||||
Compo(
|
||||
"Type a string and the bot will convert it to lower case",
|
||||
),
|
||||
tg.NewMessage(
|
||||
"Type a string and the bot will convert it to lower case",
|
||||
).Reply(
|
||||
backKeyboard.Reply(),
|
||||
),
|
||||
NewMutateMessageWidget(strings.ToLower),
|
||||
}
|
||||
}),
|
||||
|
@ -172,10 +173,12 @@ WithInitFunc(func(c *tg.Context) {
|
|||
"inc-dec", tg.RenderFunc(func(c *tg.Context) tg.UI {
|
||||
d := ExtractSessionData(c)
|
||||
return tg.UI{
|
||||
incDecKeyboard.Reply().Compo(fmt.Sprintf(
|
||||
tg.NewMessage(fmt.Sprintf(
|
||||
"Press the buttons to increment and decrement.\n" +
|
||||
"Current counter value = %d", d.Counter,
|
||||
)),
|
||||
)).Reply(
|
||||
incDecKeyboard.Reply(),
|
||||
),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
@ -183,19 +186,25 @@ WithInitFunc(func(c *tg.Context) {
|
|||
tg.NewNode(
|
||||
"send-location", tg.RenderFunc(func(c *tg.Context) tg.UI {
|
||||
return tg.UI {
|
||||
tg.NewMessage(
|
||||
"Press the button to display your counter",
|
||||
).Inline(
|
||||
tg.NewKeyboard().Row(
|
||||
tg.NewButton(
|
||||
"Check",
|
||||
).WithData(
|
||||
"check",
|
||||
).WithAction(tg.Func(func(c *tg.Context) {
|
||||
d := ExtractSessionData(c)
|
||||
c.Sendf("Counter = %d", d.Counter)
|
||||
})),
|
||||
).Inline().Compo("Press the button to display your counter"),
|
||||
tg.NewButton(
|
||||
"Check",
|
||||
).WithData(
|
||||
"check",
|
||||
).WithAction(tg.Func(func(c *tg.Context) {
|
||||
d := ExtractSessionData(c)
|
||||
c.Sendf("Counter = %d", d.Counter)
|
||||
})),
|
||||
).Inline(),
|
||||
),
|
||||
|
||||
sendLocationKeyboard.Compo(
|
||||
tg.NewMessage(
|
||||
"Press the button to send your location!",
|
||||
).Reply(
|
||||
sendLocationKeyboard,
|
||||
),
|
||||
}
|
||||
}),
|
||||
|
@ -243,19 +252,6 @@ WithPreStart(tg.Func(func(c *tg.Context){
|
|||
})),
|
||||
))
|
||||
|
||||
var gBeh = tg.NewGroupBehaviour().
|
||||
InitFunc(func(c *tg.GC) {
|
||||
}).
|
||||
WithCommands(
|
||||
tg.NewGroupCommand("hello").ActionFunc(func(c *tg.GC) {
|
||||
c.Sendf("Hello, World!")
|
||||
}),
|
||||
tg.NewGroupCommand("mycounter").ActionFunc(func(c *tg.GC) {
|
||||
d := c.Session().Data.(*SessionData)
|
||||
c.Sendf("Your counter value is %d", d.Counter)
|
||||
}),
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println(beh.Screens)
|
||||
token := os.Getenv("BOT_TOKEN")
|
||||
|
|
15
tg/bot.go
15
tg/bot.go
|
@ -49,7 +49,8 @@ func (bot *Bot) Debug(debug bool) *Bot {
|
|||
}
|
||||
|
||||
// Send the Renderable to the specified session client side.
|
||||
// Can be used for both group and private sessions.
|
||||
// Can be used for both group and private sessions because
|
||||
// SessionId represents both for chat IDs.
|
||||
func (bot *Bot) Send(
|
||||
sid SessionId, v Sendable, args ...any,
|
||||
) (*Message, error) {
|
||||
|
@ -61,17 +62,7 @@ func (bot *Bot) Send(
|
|||
c := &Context{
|
||||
context: ctx,
|
||||
}
|
||||
|
||||
config := v.SendConfig(c.WithArg(c.MakeArg(args)))
|
||||
if config.Error != nil {
|
||||
return nil, config.Error
|
||||
}
|
||||
|
||||
msg, err := bot.Api.Send(config.ToApi())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg, nil
|
||||
return c.Bot.Send(c.Session.Id, v)
|
||||
}
|
||||
|
||||
/*func (bot *Bot) Render(
|
||||
|
|
|
@ -107,14 +107,17 @@ type CommandCompo struct {
|
|||
}
|
||||
|
||||
// Returns new empty CommandCompo.
|
||||
func NewCommandCompo() *CommandCompo {
|
||||
ret := &CommandCompo{}
|
||||
ret.Commands = make(CommandMap)
|
||||
func NewCommandCompo(cmds ...*Command) *CommandCompo {
|
||||
ret := (&CommandCompo{}).WithCommands(cmds...)
|
||||
//ret.Commands = make(CommandMap)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Set the commands to handle.
|
||||
func (w *CommandCompo) WithCommands(cmds ...*Command) *CommandCompo {
|
||||
if w.Commands == nil {
|
||||
w.Commands = make(CommandMap)
|
||||
}
|
||||
for _, cmd := range cmds {
|
||||
if cmd.Name == "" {
|
||||
panic("empty command name")
|
||||
|
|
16
tg/compo.go
16
tg/compo.go
|
@ -29,19 +29,3 @@ type Component interface {
|
|||
Server
|
||||
}
|
||||
|
||||
// The type to embed into potential components.
|
||||
// Implements empty versions of interfaces.
|
||||
type Compo struct{
|
||||
*Message
|
||||
}
|
||||
|
||||
func NewCompo() *Compo {
|
||||
return &Compo{}
|
||||
}
|
||||
|
||||
// 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) bool {return false}
|
||||
|
||||
|
|
|
@ -81,18 +81,30 @@ func (c *Context) Skip(u *Update) {
|
|||
|
||||
// Sends to the Sendable object.
|
||||
func (c *Context) Send(v Sendable) (*Message, error) {
|
||||
return c.Bot.Send(c.Session.Id, v)
|
||||
config := v.SendConfig(c)
|
||||
if config.Error != nil {
|
||||
return nil, config.Error
|
||||
}
|
||||
|
||||
msg, err := c.Bot.Api.Send(config.ToApi())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// Sends the formatted with fmt.Sprintf message to the user.
|
||||
// Sends the formatted with fmt.Sprintf message to the user
|
||||
// using default Markdown parsing format.
|
||||
func (c *Context) Sendf(format string, v ...any) (*Message, error) {
|
||||
return c.Send(NewMessage(fmt.Sprintf(format, v...)))
|
||||
}
|
||||
|
||||
// Same as Sendf but uses Markdown 2 format for parsing.
|
||||
func (c *Context) Sendf2(format string, v ...any) (*Message, error) {
|
||||
return c.Send(NewMessage(fmt.Sprintf(format, v...)).MD2())
|
||||
}
|
||||
|
||||
// Same as Sendf but uses HTML format for parsing.
|
||||
func (c *Context) SendfHTML(format string, v ...any) (*Message, error) {
|
||||
return c.Send(NewMessage(fmt.Sprintf(format, v...)).HTML())
|
||||
}
|
||||
|
@ -227,7 +239,7 @@ func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
|||
pth := c.Path()
|
||||
compos := widget.Render(c.WithArg(c.MakeArg(args)))
|
||||
// Leave if changed path.
|
||||
if pth != c.Path() {
|
||||
if compos == nil || pth != c.Path() {
|
||||
return nil
|
||||
}
|
||||
chns := make([]*UpdateChan, len(compos))
|
||||
|
@ -237,13 +249,27 @@ func (c *Context) RunWidget(widget Widget, args ...any) *UpdateChan {
|
|||
|
||||
ret := NewUpdateChan()
|
||||
go func() {
|
||||
ln := len(compos)
|
||||
UPDATE:
|
||||
for u := range ret.Chan() {
|
||||
if u == nil {
|
||||
break
|
||||
}
|
||||
cnt := 0
|
||||
for i, compo := range compos {
|
||||
chn := chns[i]
|
||||
if chn.Closed() {
|
||||
cnt++
|
||||
continue
|
||||
}
|
||||
if !compo.Filter(u) {
|
||||
chn.Send(u)
|
||||
continue UPDATE
|
||||
}
|
||||
}
|
||||
if cnt == ln {
|
||||
break
|
||||
}
|
||||
}
|
||||
ret.Close()
|
||||
for _, chn := range chns {
|
||||
|
|
11
tg/file.go
11
tg/file.go
|
@ -23,17 +23,18 @@ var (
|
|||
)
|
||||
|
||||
type File struct {
|
||||
*Compo
|
||||
*MessageCompo
|
||||
path string
|
||||
typ FileType
|
||||
caption string
|
||||
}
|
||||
|
||||
func NewFile(path string) *File {
|
||||
ret := &File{
|
||||
path: path,
|
||||
}
|
||||
ret.Compo = NewCompo()
|
||||
ret := &File{}
|
||||
|
||||
ret.MessageCompo = NewMessage("")
|
||||
ret.path = path
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
|
57
tg/inline.go
57
tg/inline.go
|
@ -23,49 +23,41 @@ func (kbd *Inline) ToApi() tgbotapi.InlineKeyboardMarkup {
|
|||
return tgbotapi.NewInlineKeyboardMarkup(rows...)
|
||||
}
|
||||
|
||||
// Transform the keyboard to widget with the specified text.
|
||||
func (kbd *Inline) Compo(text string) *InlineCompo {
|
||||
ret := &InlineCompo{}
|
||||
ret.Inline = kbd
|
||||
ret.Text = text
|
||||
ret.Compo = NewCompo()
|
||||
return ret
|
||||
}
|
||||
|
||||
// The type implements message with an inline keyboard.
|
||||
type InlineCompo struct {
|
||||
*Compo
|
||||
Text string
|
||||
*MessageCompo
|
||||
*Inline
|
||||
}
|
||||
|
||||
// Implementing the Sendable interface.
|
||||
func (widget *InlineCompo) SendConfig(
|
||||
func (compo *InlineCompo) SendConfig(
|
||||
c *Context,
|
||||
) (*SendConfig) {
|
||||
var text string
|
||||
if widget.Text != "" {
|
||||
text = widget.Text
|
||||
} else {
|
||||
text = ">"
|
||||
|
||||
sendConfig := compo.MessageCompo.SendConfig(c)
|
||||
sendConfig.Message.ReplyMarkup = compo.Inline.ToApi()
|
||||
|
||||
return sendConfig
|
||||
}
|
||||
|
||||
// Implementing the Filterer interface.
|
||||
func (compo *InlineCompo) Filter(u *Update) bool {
|
||||
if compo == nil || u.CallbackQuery == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
sid := c.Session.Id.ToApi()
|
||||
msgConfig := tgbotapi.NewMessage(sid, text)
|
||||
msgConfig.ReplyMarkup = widget.ToApi()
|
||||
if u.CallbackQuery.Message.MessageID !=
|
||||
compo.Message.MessageID {
|
||||
return true
|
||||
}
|
||||
|
||||
ret := &SendConfig{}
|
||||
ret.Message = &msgConfig
|
||||
return ret
|
||||
return false
|
||||
}
|
||||
|
||||
// Implementing the Server interface.
|
||||
func (widget *InlineCompo) 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,
|
||||
|
@ -92,17 +84,4 @@ func (widget *InlineCompo) Serve(c *Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// Implementing the Filterer interface.
|
||||
func (compo *InlineCompo) Filter(u *Update) bool {
|
||||
if compo == nil || u.CallbackQuery == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if u.CallbackQuery.Message.MessageID !=
|
||||
compo.Message.MessageID {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -5,45 +5,87 @@ import (
|
|||
)
|
||||
|
||||
// Simple text message type.
|
||||
type MessageConfig struct {
|
||||
Compo
|
||||
type MessageCompo struct {
|
||||
Message *Message
|
||||
ParseMode string
|
||||
Text string
|
||||
}
|
||||
|
||||
func (compo *MessageCompo) SetMessage(msg *Message) {
|
||||
compo.Message = msg
|
||||
}
|
||||
|
||||
// Return new message with the specified text.
|
||||
func NewMessage(text string) *MessageConfig {
|
||||
ret := &MessageConfig{}
|
||||
func NewMessage(text string) *MessageCompo {
|
||||
ret := &MessageCompo{}
|
||||
ret.Text = text
|
||||
ret.ParseMode = tgbotapi.ModeMarkdown
|
||||
return ret
|
||||
}
|
||||
|
||||
func (msg *MessageConfig) withParseMode(mode string) *MessageConfig{
|
||||
func (msg *MessageCompo) withParseMode(mode string) *MessageCompo {
|
||||
msg.ParseMode = mode
|
||||
return msg
|
||||
}
|
||||
|
||||
// Set the default Markdown parsing mode.
|
||||
func (msg *MessageConfig) MD() *MessageConfig {
|
||||
func (msg *MessageCompo) MD() *MessageCompo {
|
||||
return msg.withParseMode(tgbotapi.ModeMarkdown)
|
||||
}
|
||||
|
||||
func (msg *MessageConfig) MD2() *MessageConfig {
|
||||
// Set the Markdown 2 parsing mode.
|
||||
func (msg *MessageCompo) MD2() *MessageCompo {
|
||||
return msg.withParseMode(tgbotapi.ModeMarkdownV2)
|
||||
}
|
||||
|
||||
|
||||
func (msg *MessageConfig) HTML() *MessageConfig {
|
||||
// Set the HTML parsing mode.
|
||||
func (msg *MessageCompo) HTML() *MessageCompo {
|
||||
return msg.withParseMode(tgbotapi.ModeHTML)
|
||||
}
|
||||
|
||||
func (config *MessageConfig) SendConfig(
|
||||
// Transform the message component into one with reply keyboard.
|
||||
func (msg *MessageCompo) Inline(inline *Inline) *InlineCompo {
|
||||
return &InlineCompo{
|
||||
Inline: inline,
|
||||
MessageCompo: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the message component into one with reply keyboard.
|
||||
func (msg *MessageCompo) Reply(reply *Reply) *ReplyCompo {
|
||||
return &ReplyCompo{
|
||||
Reply: reply,
|
||||
MessageCompo: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func (config *MessageCompo) SendConfig(
|
||||
c *Context,
|
||||
) (*SendConfig) {
|
||||
var ret SendConfig
|
||||
msg := tgbotapi.NewMessage(c.Session.Id.ToApi(), config.Text)
|
||||
var (
|
||||
ret SendConfig
|
||||
text string
|
||||
)
|
||||
|
||||
if config.Text == "" {
|
||||
text = ">"
|
||||
} else {
|
||||
text = config.Text
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(c.Session.Id.ToApi(), text)
|
||||
ret.Message = &msg
|
||||
ret.Message.ParseMode = config.ParseMode
|
||||
|
||||
return &ret
|
||||
}
|
||||
|
||||
// Empty serving to use messages in rendering.
|
||||
func (compo *MessageCompo) Serve(c *Context) {
|
||||
}
|
||||
|
||||
func (compo *MessageCompo) Filter(_ *Update) bool {
|
||||
// Skip everything
|
||||
return true
|
||||
}
|
||||
|
|
37
tg/reply.go
37
tg/reply.go
|
@ -50,19 +50,9 @@ func (kbd *Reply) ToApi() any {
|
|||
return tgbotapi.NewReplyKeyboard(rows...)
|
||||
}
|
||||
|
||||
// Transform the keyboard to widget with the specified text.
|
||||
func (kbd *Reply) Compo(text string) *ReplyCompo {
|
||||
ret := &ReplyCompo{}
|
||||
ret.Reply = kbd
|
||||
ret.Text = text
|
||||
ret.Compo = NewCompo()
|
||||
return ret
|
||||
}
|
||||
|
||||
// The type implements reply keyboard widget.
|
||||
type ReplyCompo struct {
|
||||
*Compo
|
||||
Text string
|
||||
*MessageCompo
|
||||
*Reply
|
||||
}
|
||||
|
||||
|
@ -70,27 +60,9 @@ type ReplyCompo struct {
|
|||
func (compo *ReplyCompo) SendConfig(
|
||||
c *Context,
|
||||
) (*SendConfig) {
|
||||
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 compo.Text != "" {
|
||||
text = compo.Text
|
||||
} else {
|
||||
text = ">"
|
||||
}
|
||||
|
||||
msgConfig := tgbotapi.NewMessage(sid, text)
|
||||
msgConfig.ReplyMarkup = compo.ToApi()
|
||||
|
||||
ret := &SendConfig{}
|
||||
ret.Message = &msgConfig
|
||||
return ret
|
||||
sendConfig := compo.MessageCompo.SendConfig(c)
|
||||
sendConfig.Message.ReplyMarkup = compo.Reply.ToApi()
|
||||
return sendConfig
|
||||
}
|
||||
|
||||
func (compo *ReplyCompo) Filter(
|
||||
|
@ -134,4 +106,3 @@ func (compo *ReplyCompo) Serve(c *Context) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ type MessageId int64
|
|||
type Sendable interface {
|
||||
SendConfig(*Context) (*SendConfig)
|
||||
SetMessage(*Message)
|
||||
GetMessage() *Message
|
||||
}
|
||||
|
||||
type Errorer interface {
|
||||
|
|
Loading…
Reference in a new issue