Implemented the PreStart handler.

This commit is contained in:
Andrey Parhomenko 2023-09-07 15:45:38 +03:00
parent fc91490c18
commit b2748a8cee
6 changed files with 66 additions and 17 deletions

View file

@ -84,8 +84,10 @@ var beh = tg.NewBehaviour().
WithInitFunc(func(c *tg.Context) { WithInitFunc(func(c *tg.Context) {
// The session initialization. // The session initialization.
c.Session.Value = &UserData{} c.Session.Value = &UserData{}
c.ChangeScreen("start")
}). // On any message update before the bot created session.
WithPreStartFunc(func(c *tg.Context){
c.Send("Please, use the /start command to start the bot")
}).WithScreens( }).WithScreens(
tg.NewScreen("start"). tg.NewScreen("start").
WithText( WithText(
@ -141,6 +143,11 @@ var beh = tg.NewBehaviour().
), ),
), ),
).WithCommands( ).WithCommands(
tg.NewCommand("start").
Desc("start the bot").
ActionFunc(func(c *tg.Context){
c.ChangeScreen("start")
}),
tg.NewCommand("hello"). tg.NewCommand("hello").
Desc("sends the 'Hello, World!' message back"). Desc("sends the 'Hello, World!' message back").
ActionFunc(func(c *tg.Context) { ActionFunc(func(c *tg.Context) {

BIN
media/bot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

View file

@ -5,6 +5,7 @@ package tg
// The type describes behaviour for the bot in personal chats. // The type describes behaviour for the bot in personal chats.
type Behaviour struct { type Behaviour struct {
PreStart *action
Init *action Init *action
Screens ScreenMap Screens ScreenMap
Keyboards KeyboardMap Keyboards KeyboardMap
@ -27,12 +28,27 @@ func (b *Behaviour) WithInit(a Action) *Behaviour {
return b return b
} }
// Alias to WithInit to simplify behaviour definitions.
func (b *Behaviour) WithInitFunc( func (b *Behaviour) WithInitFunc(
fn ActionFunc, fn ActionFunc,
) *Behaviour { ) *Behaviour {
return b.WithInit(fn) return b.WithInit(fn)
} }
// Defines pre-start action.
// E. g. when the user has not type the "/start" command.
// Mostly used to send the "/start" command back
// with some warning.
func (b *Behaviour) WithPreStart(a Action) *Behaviour {
b.PreStart = newAction(a)
return b
}
// Alias for WithPreStart to be used with function inside.
func (b *Behaviour) WithPreStartFunc(fn ActionFunc) *Behaviour {
return b.WithPreStart(fn)
}
// The function sets screens. // The function sets screens.
func (b *Behaviour) WithScreens( func (b *Behaviour) WithScreens(
screens ...*Screen, screens ...*Screen,

View file

@ -168,6 +168,9 @@ func (bot *Bot) handlePrivate(updates chan *Update) {
session, sessionOk := bot.sessions[sid] session, sessionOk := bot.sessions[sid]
chn, chnOk := chans[sid] chn, chnOk := chans[sid]
if sessionOk { if sessionOk {
// Creating new goroutine for
// the session that exists
// but has none.
if !chnOk { if !chnOk {
ctx := &context{ ctx := &context{
Bot: bot, Bot: bot,
@ -178,11 +181,10 @@ func (bot *Bot) handlePrivate(updates chan *Update) {
chans[sid] = chn chans[sid] = chn
go ctx.handleUpdateChan(chn) go ctx.handleUpdateChan(chn)
} }
} else { } else if u.Message != nil {
if u.Message != nil && u.Message.Command() == "start" { // Create session on any message
if !sessionOk { // if we have no one.
bot.sessions.Add(sid) bot.sessions.Add(sid)
}
lsession := bot.sessions[sid] lsession := bot.sessions[sid]
ctx := &context{ ctx := &context{
Bot: bot, Bot: bot,
@ -193,7 +195,6 @@ func (bot *Bot) handlePrivate(updates chan *Update) {
chans[sid] = chn chans[sid] = chn
go ctx.handleUpdateChan(chn) go ctx.handleUpdateChan(chn)
} }
}
chn, ok := chans[sid] chn, ok := chans[sid]
// The bot MUST get the "start" command. // The bot MUST get the "start" command.

View file

@ -23,6 +23,8 @@ type context struct {
func (c *context) handleUpdateChan(updates chan *Update) { func (c *context) handleUpdateChan(updates chan *Update) {
beh := c.Bot.behaviour beh := c.Bot.behaviour
session := c.Session
preStart := beh.PreStart
if beh.Init != nil { if beh.Init != nil {
c.run(beh.Init, nil) c.run(beh.Init, nil)
} }
@ -31,14 +33,34 @@ func (c *context) handleUpdateChan(updates chan *Update) {
screen := c.curScreen screen := c.curScreen
// The part is added to implement custom update handling. // The part is added to implement custom update handling.
if u.Message != nil { if u.Message != nil {
if u.Message.IsCommand() && !c.readingUpdate { if !session.Started {
if u.Message.IsCommand() &&
u.Message.Command() == "start" {
// Special treatment for the "/start"
// command.
session.Started = true
cmdName := CommandName("start")
cmd, ok := beh.Commands[cmdName]
if ok {
act = cmd.Action
} else {
// Some usage.
}
} else {
// Prestart handling.
act = preStart
}
} else if u.Message.IsCommand() {
// Command handling.
cmdName := CommandName(u.Message.Command()) cmdName := CommandName(u.Message.Command())
cmd, ok := beh.Commands[cmdName] cmd, ok := beh.Commands[cmdName]
if ok { if ok {
act = cmd.Action act = cmd.Action
} else { } else {
// Some usage.
} }
} else { } else {
// Simple messages handling.
kbd := screen.Keyboard kbd := screen.Keyboard
if kbd == nil { if kbd == nil {
if c.readingUpdate { if c.readingUpdate {
@ -69,7 +91,7 @@ func (c *context) handleUpdateChan(updates chan *Update) {
act = btn.Action act = btn.Action
} }
} }
} else if u.CallbackQuery != nil { } else if u.CallbackQuery != nil && session.Started {
cb := tgbotapi.NewCallback( cb := tgbotapi.NewCallback(
u.CallbackQuery.ID, u.CallbackQuery.ID,
u.CallbackQuery.Data, u.CallbackQuery.Data,

View file

@ -14,6 +14,9 @@ func (si SessionId) ToApi() int64 {
type Session struct { type Session struct {
// Id of the chat of the user. // Id of the chat of the user.
Id SessionId Id SessionId
// True if the session started.
// (got the '/start' command.
Started bool
// Custom value for each user. // Custom value for each user.
Value any Value any
} }