Fixed naming. We do not need the "Arg" thing.

This commit is contained in:
Andrey Parhomenko 2023-08-18 13:46:10 +03:00
parent 94d8c38dd5
commit 3f26d2f916
9 changed files with 195 additions and 150 deletions

44
airfile Normal file
View file

@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./exe/test.exe"
cmd = "go build -o ./exe/ ./cmd/..."
delay = 0
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

View file

@ -17,12 +17,12 @@ var (
ScreenChange("start") ScreenChange("start")
incDecKeyboard = tx.NewKeyboard("").Row( incDecKeyboard = tx.NewKeyboard("").Row(
tx.NewButton("+").ActionFunc(func(c *tx.A) { tx.NewButton("+").ActionFunc(func(c *tx.Context) {
d := c.V.(*UserData) d := c.V.(*UserData)
d.Counter++ d.Counter++
c.Sendf("%d", d.Counter) c.Sendf("%d", d.Counter)
}), }),
tx.NewButton("-").ActionFunc(func(c *tx.A) { tx.NewButton("-").ActionFunc(func(c *tx.Context) {
d := c.V.(*UserData) d := c.V.(*UserData)
d.Counter-- d.Counter--
c.Sendf("%d", d.Counter) c.Sendf("%d", d.Counter)
@ -46,10 +46,11 @@ var (
Row( Row(
tx.NewButton("Send location"). tx.NewButton("Send location").
WithSendLocation(true). WithSendLocation(true).
ActionFunc(func(c *tx.A) { ActionFunc(func(c *tx.Context) {
var err error var err error
if c.U.Message.Location != nil { u := c.Update
l := c.U.Message.Location if u.Message.Location != nil {
l := u.Message.Location
err = c.Sendf( err = c.Sendf(
"Longitude: %f\n"+ "Longitude: %f\n"+
"Latitude: %f\n"+ "Latitude: %f\n"+
@ -77,7 +78,7 @@ var (
) )
var beh = tx.NewBehaviour(). var beh = tx.NewBehaviour().
WithInitFunc(func(c *tx.A) { WithInitFunc(func(c *tx.Context) {
// The session initialization. // The session initialization.
c.V = &UserData{} c.V = &UserData{}
c.ChangeScreen("start") c.ChangeScreen("start")
@ -108,7 +109,7 @@ var beh = tx.NewBehaviour().
). ).
WithKeyboard(incDecKeyboard). WithKeyboard(incDecKeyboard).
// The function will be called when reaching the screen. // The function will be called when reaching the screen.
ActionFunc(func(c *tx.A) { ActionFunc(func(c *tx.Context) {
d := c.V.(*UserData) d := c.V.(*UserData)
c.Sendf("Current counter value = %d", d.Counter) c.Sendf("Current counter value = %d", d.Counter)
}), }),
@ -130,7 +131,7 @@ var beh = tx.NewBehaviour().
tx.NewKeyboard("").Row( tx.NewKeyboard("").Row(
tx.NewButton("Check"). tx.NewButton("Check").
WithData("check"). WithData("check").
ActionFunc(func(a *tx.A) { ActionFunc(func(a *tx.Context) {
d := a.V.(*UserData) d := a.V.(*UserData)
a.Sendf("Counter = %d", d.Counter) a.Sendf("Counter = %d", d.Counter)
}), }),
@ -139,12 +140,12 @@ var beh = tx.NewBehaviour().
).WithCommands( ).WithCommands(
tx.NewCommand("hello"). tx.NewCommand("hello").
Desc("sends the 'Hello, World!' message back"). Desc("sends the 'Hello, World!' message back").
ActionFunc(func(c *tx.A) { ActionFunc(func(c *tx.Context) {
c.Send("Hello, World!") c.Send("Hello, World!")
}), }),
tx.NewCommand("read"). tx.NewCommand("read").
Desc("reads a string and sends it back"). Desc("reads a string and sends it back").
ActionFunc(func(c *tx.A) { ActionFunc(func(c *tx.Context) {
c.Send("Type some text:") c.Send("Type some text:")
msg, err := c.ReadTextMessage() msg, err := c.ReadTextMessage()
if err != nil { if err != nil {
@ -155,7 +156,7 @@ var beh = tx.NewBehaviour().
) )
func mutateMessage(fn func(string) string) tx.ActionFunc { func mutateMessage(fn func(string) string) tx.ActionFunc {
return func(c *tx.A) { return func(c *tx.Context) {
for { for {
msg, err := c.ReadTextMessage() msg, err := c.ReadTextMessage()
if err == tx.NotAvailableErr { if err == tx.NotAvailableErr {
@ -173,15 +174,15 @@ func mutateMessage(fn func(string) string) tx.ActionFunc {
} }
var gBeh = tx.NewGroupBehaviour(). var gBeh = tx.NewGroupBehaviour().
InitFunc(func(a *tx.GA) { InitFunc(func(c *tx.GC) {
}). }).
WithCommands( WithCommands(
tx.NewGroupCommand("hello").ActionFunc(func(a *tx.GA) { tx.NewGroupCommand("hello").ActionFunc(func(c *tx.GC) {
a.Send("Hello, World!") c.Send("Hello, World!")
}), }),
tx.NewGroupCommand("mycounter").ActionFunc(func(a *tx.GA) { tx.NewGroupCommand("mycounter").ActionFunc(func(c *tx.GC) {
d := a.GetSessionValue().(*UserData) d := c.GetSessionValue().(*UserData)
a.Sendf("Your counter value is %d", d.Counter) c.Sendf("Your counter value is %d", d.Counter)
}), }),
) )

3
mkfile
View file

@ -1,5 +1,8 @@
all: build all: build
run-air:V:
air -c airfile
build:V: build:V:
go build -o exe/ ./cmd/... go build -o exe/ ./cmd/...

View file

@ -22,106 +22,16 @@ func newAction(a Action) *action {
} }
} }
func (a *action) Act(arg *A) { func (a *action) Act(c *Context) {
if a.Action != nil { if a.Action != nil {
a.Action.Act(arg) a.Action.Act(c)
} }
} }
// Customized actions for the bot.
type Action interface {
Act(*Arg)
}
// Customized actions for the
type GroupAction interface {
Act(*GroupArg)
}
type ActionFunc func(*Arg)
func (af ActionFunc) Act(a *Arg) {
af(a)
}
type GroupActionFunc func(*GroupArg)
func (af GroupActionFunc) Act(a *GroupArg) {
af(a)
}
// The type implements changing screen to the underlying ScreenId
type ScreenChange ScreenId
func (sc ScreenChange) Act(c *Arg) {
if !c.B.behaviour.ScreenExist(ScreenId(sc)) {
panic(ScreenNotExistErr)
}
err := c.ChangeScreen(ScreenId(sc))
if err != nil {
panic(err)
}
}
// The argument for handling.
type Arg struct {
// Current context.
*Context
// The update that made the action to be called.
U *Update
}
type A = Arg
// Changes screen of user to the Id one.
func (c *Arg) ChangeScreen(screenId ScreenId) error {
if !c.B.behaviour.ScreenExist(screenId) {
return ScreenNotExistErr
}
// Stop the reading by sending the nil,
// since we change the screen and
// current goroutine needs to be stopped.
if c.readingUpdate {
c.updates <- nil
}
// Getting the screen and changing to
// then executing its action.
screen := c.B.behaviour.Screens[screenId]
c.prevScreen = c.curScreen
c.curScreen = screen
screen.Render(c.Context)
if screen.Action != nil {
c.run(screen.Action, c.U)
}
return nil
}
// The argument for handling in group behaviour.
type GroupArg struct {
*GroupContext
*Update
}
type GA = GroupArg
func (a *GA) SentFromSid() SessionId {
return SessionId(a.SentFrom().ID)
}
func (a *GA) GetSessionValue() any {
v, _ := a.B.GetSessionValueBySid(a.SentFromSid())
return v
}
// The argument for handling in channenl behaviours. // The argument for handling in channenl behaviours.
type ChannelArg struct { type ChannelContext struct {
} }
type CA = ChannelArg type CC = ChannelContext
type ChannelAction struct { type ChannelAction struct {
Act (*ChannelArg) Act (*ChannelContext)
}
type JsonTyper interface {
JsonType() string
} }

View file

@ -128,13 +128,12 @@ func (bot *Bot) handlePrivate(updates chan *Update) {
_, chnOk = chans[sid] _, chnOk = chans[sid]
// Making the bot ignore anything except "start" // Making the bot ignore anything except "start"
// before the session started // before the session started
if u.Message.IsCommand() && if u.Message.IsCommand() && !sessionOk {
(!sessionOk) {
cmdName := CommandName(u.Message.Command()) cmdName := CommandName(u.Message.Command())
if cmdName == "start" { if cmdName == "start" {
session := bot.sessions[sid] session := bot.sessions[sid]
ctx := &Context{ ctx := &context{
B: bot, Bot: bot,
Session: session, Session: session,
updates: make(chan *Update), updates: make(chan *Update),
} }
@ -166,8 +165,8 @@ func (bot *Bot) handleGroup(updates chan *Update) {
if _, ok := bot.groupSessions[sid]; !ok { if _, ok := bot.groupSessions[sid]; !ok {
bot.groupSessions.Add(sid) bot.groupSessions.Add(sid)
session := bot.groupSessions[sid] session := bot.groupSessions[sid]
ctx := &GroupContext{ ctx := &groupContext{
B: bot, Bot: bot,
GroupSession: session, GroupSession: session,
updates: make(chan *Update), updates: make(chan *Update),
} }

View file

@ -6,11 +6,9 @@ import (
apix "github.com/go-telegram-bot-api/telegram-bot-api/v5" apix "github.com/go-telegram-bot-api/telegram-bot-api/v5"
) )
// The type represents way to interact with user in type context struct {
// handling functions. Is provided to Act() function always.
type Context struct {
*Session *Session
B *Bot *Bot
updates chan *Update updates chan *Update
// Is true if currently reading the Update. // Is true if currently reading the Update.
readingUpdate bool readingUpdate bool
@ -18,11 +16,13 @@ type Context struct {
curScreen, prevScreen *Screen curScreen, prevScreen *Screen
} }
// The type represents way to interact with user in
// handling functions. Is provided to Act() function always.
// Goroutie function to handle each user. // Goroutie function to handle each user.
func (c *Context) handleUpdateChan(updates chan *Update) { func (c *context) handleUpdateChan(updates chan *Update) {
var act Action var act Action
bot := c.B beh := c.behaviour
beh := bot.behaviour
if beh.Init != nil { if beh.Init != nil {
c.run(beh.Init, nil) c.run(beh.Init, nil)
@ -73,7 +73,7 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
cb := apix.NewCallback(u.CallbackQuery.ID, u.CallbackQuery.Data) cb := apix.NewCallback(u.CallbackQuery.ID, u.CallbackQuery.Data)
data := u.CallbackQuery.Data data := u.CallbackQuery.Data
_, err := bot.Request(cb) _, err := c.Request(cb)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -103,15 +103,15 @@ func (c *Context) handleUpdateChan(updates chan *Update) {
} }
} }
func (c *Context) run(a Action, u *Update) { func (c *context) run(a Action, u *Update) {
go a.Act(&A{ go a.Act(&Context{
Context: c, context: c,
U: u, Update: u,
}) })
} }
// Returns the next update ignoring current screen. // Returns the next update ignoring current screen.
func (c *Context) ReadUpdate() (*Update, error) { func (c *context) ReadUpdate() (*Update, error) {
c.readingUpdate = true c.readingUpdate = true
u := <-c.updates u := <-c.updates
c.readingUpdate = false c.readingUpdate = false
@ -123,7 +123,7 @@ func (c *Context) ReadUpdate() (*Update, error) {
} }
// Returns the next text message that the user sends. // Returns the next text message that the user sends.
func (c *Context) ReadTextMessage() (string, error) { func (c *context) ReadTextMessage() (string, error) {
u, err := c.ReadUpdate() u, err := c.ReadUpdate()
if err != nil { if err != nil {
return "", err return "", err
@ -136,34 +136,34 @@ func (c *Context) ReadTextMessage() (string, error) {
} }
// Sends to the user specified text. // Sends to the user specified text.
func (c *Context) Send(v ...any) error { func (c *context) Send(v ...any) error {
msg := apix.NewMessage(c.Id.ToTelegram(), fmt.Sprint(v...)) msg := apix.NewMessage(c.Id.ToTelegram(), fmt.Sprint(v...))
_, err := c.B.Send(msg) _, err := c.Bot.Send(msg)
return err return err
} }
// Sends the formatted with fmt.Sprintf message to the user. // Sends the formatted with fmt.Sprintf message to the user.
func (c *Context) Sendf(format string, v ...any) error { func (c *context) Sendf(format string, v ...any) error {
return c.Send(fmt.Sprintf(format, v...)) return c.Send(fmt.Sprintf(format, v...))
} }
// Context for interaction inside groups. // Context for interaction inside groups.
type GroupContext struct { type groupContext struct {
*GroupSession *GroupSession
B *Bot *Bot
updates chan *Update updates chan *Update
} }
func (c *GroupContext) run(a GroupAction, u *Update) { func (c *groupContext) run(a GroupAction, u *Update) {
go a.Act(&GA{ go a.Act(&GroupContext{
GroupContext: c, groupContext: c,
Update: u, Update: u,
}) })
} }
func (c *GroupContext) handleUpdateChan(updates chan *Update) { func (c *groupContext) handleUpdateChan(updates chan *Update) {
var act GroupAction var act GroupAction
beh := c.B.groupBehaviour beh := c.groupBehaviour
for u := range updates { for u := range updates {
if u.Message != nil { if u.Message != nil {
msg := u.Message msg := u.Message
@ -172,7 +172,7 @@ func (c *GroupContext) handleUpdateChan(updates chan *Update) {
// Skipping the commands sent not to us. // Skipping the commands sent not to us.
atName := msg.CommandWithAt()[len(cmdName)+1:] atName := msg.CommandWithAt()[len(cmdName)+1:]
if c.B.Me.UserName != atName { if c.Bot.Me.UserName != atName {
continue continue
} }
cmd, ok := beh.Commands[cmdName] cmd, ok := beh.Commands[cmdName]
@ -189,13 +189,13 @@ func (c *GroupContext) handleUpdateChan(updates chan *Update) {
} }
} }
func (c *GroupContext) Sendf(format string, v ...any) error { func (c *groupContext) Sendf(format string, v ...any) error {
return c.Send(fmt.Sprintf(format, v...)) return c.Send(fmt.Sprintf(format, v...))
} }
// Sends into the chat specified values converted to strings. // Sends into the chat specified values converted to strings.
func (c *GroupContext) Send(v ...any) error { func (c *groupContext) Send(v ...any) error {
msg := apix.NewMessage(c.Id.ToTelegram(), fmt.Sprint(v...)) msg := apix.NewMessage(c.Id.ToTelegram(), fmt.Sprint(v...))
_, err := c.B.Send(msg) _, err := c.Bot.Send(msg)
return err return err
} }

29
src/tx/group.go Normal file
View file

@ -0,0 +1,29 @@
package tx
// Customized actions for the group behaviour.
type GroupAction interface {
Act(*GroupContext)
}
// The handler function type.
type GroupActionFunc func(*GroupContext)
func (af GroupActionFunc) Act(a *GroupContext) {
af(a)
}
type GC = GroupContext
func (c *GroupContext) SentFromSid() SessionId {
return SessionId(c.SentFrom().ID)
}
func (a *GroupContext) GetSessionValue() any {
v, _ := a.Bot.GetSessionValueBySid(a.SentFromSid())
return v
}
type GroupContext struct {
*groupContext
*Update
}

59
src/tx/private.go Normal file
View file

@ -0,0 +1,59 @@
package tx
// Interface to interact with the user.
type Context struct {
*context
*Update
}
// Customized actions for the bot.
type Action interface {
Act(*Context)
}
type ActionFunc func(*Context)
func (af ActionFunc) Act(c *Context) {
af(c)
}
// The type implements changing screen to the underlying ScreenId
type ScreenChange ScreenId
func (sc ScreenChange) Act(c *Context) {
if !c.behaviour.ScreenExist(ScreenId(sc)) {
panic(ScreenNotExistErr)
}
err := c.ChangeScreen(ScreenId(sc))
if err != nil {
panic(err)
}
}
type C = Context
// Changes screen of user to the Id one.
func (c *Context) ChangeScreen(screenId ScreenId) error {
if !c.behaviour.ScreenExist(screenId) {
return ScreenNotExistErr
}
// Stop the reading by sending the nil,
// since we change the screen and
// current goroutine needs to be stopped.
if c.readingUpdate {
c.updates <- nil
}
// Getting the screen and changing to
// then executing its action.
screen := c.behaviour.Screens[screenId]
c.prevScreen = c.curScreen
c.curScreen = screen
screen.Render(c.context)
if screen.Action != nil {
c.run(screen.Action, c.Update)
}
return nil
}

View file

@ -62,7 +62,7 @@ func (s *Screen) ActionFunc(a ActionFunc) *Screen {
} }
// Renders output of the screen only to the side of the user. // Renders output of the screen only to the side of the user.
func (s *Screen) Render(c *Context) error { func (s *Screen) Render(c *context) error {
id := c.Id.ToTelegram() id := c.Id.ToTelegram()
kbd := s.Keyboard kbd := s.Keyboard
iKbd := s.InlineKeyboard iKbd := s.InlineKeyboard
@ -87,13 +87,13 @@ func (s *Screen) Render(c *Context) error {
msg.ReplyMarkup = iKbd.toTelegramInline() msg.ReplyMarkup = iKbd.toTelegramInline()
} else if kbd != nil { } else if kbd != nil {
msg.ReplyMarkup = kbd.toTelegramReply() msg.ReplyMarkup = kbd.toTelegramReply()
if _, err := c.B.Send(msg); err != nil { if _, err := c.Bot.Send(msg); err != nil {
return err return err
} }
return nil return nil
} else { } else {
msg.ReplyMarkup = apix.NewRemoveKeyboard(true) msg.ReplyMarkup = apix.NewRemoveKeyboard(true)
if _, err := c.B.Send(msg); err != nil { if _, err := c.Bot.Send(msg); err != nil {
return err return err
} }
return nil return nil
@ -121,7 +121,7 @@ func (s *Screen) Render(c *Context) error {
for _, m := range ch { for _, m := range ch {
if m != nil { if m != nil {
if _, err := c.B.Send(m); err != nil { if _, err := c.Bot.Send(m); err != nil {
return err return err
} }
} }