Small fixes to use more intuitive MessageCompo instead of the Compo method for Inline and Reply keyboards.

This commit is contained in:
Andrey Parhomenko 2023-09-29 13:36:37 +03:00
parent 227dc816fa
commit 47b7c59469
10 changed files with 178 additions and 186 deletions

View file

@ -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")

View file

@ -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(

View file

@ -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")

View file

@ -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}

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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) {
}
}

View file

@ -12,7 +12,6 @@ type MessageId int64
type Sendable interface {
SendConfig(*Context) (*SendConfig)
SetMessage(*Message)
GetMessage() *Message
}
type Errorer interface {