change fsm behavior, unknown command state
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Pavel 2024-05-13 21:46:05 +03:00
parent b4ee5f77b8
commit 4fdc64c03b
6 changed files with 78 additions and 10 deletions

View File

@ -1,10 +1,12 @@
package fsmwizard package fsmwizard
import ( import (
"time"
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface"
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/util"
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm" "gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm"
"github.com/maypok86/otter" "github.com/maypok86/otter"
"time"
) )
var ( var (
@ -36,7 +38,7 @@ func Get(userID int64) fsm.IMachine[Wizard] {
} }
func newWizard() fsm.IMachine[Wizard] { func newWizard() fsm.IMachine[Wizard] {
return fsm.New[Wizard](states[0].ID(), Wizard{}, states, errorState) return fsm.New[Wizard](states[0].ID(), Wizard{}, wizardPreHandle, states, errorState)
} }
// PopulateStates will init all state handlers for future use. // PopulateStates will init all state handlers for future use.
@ -48,6 +50,26 @@ func PopulateStates(app iface.App) {
NewKeyboardChooserState(app), NewKeyboardChooserState(app),
NewRemoveChatMemberState(app), NewRemoveChatMemberState(app),
NewHelpState(app), NewHelpState(app),
NewUnknownCommandState(app),
} }
errorState = NewErrorState(app.Log()) errorState = NewErrorState(app.Log())
} }
func wizardPreHandle(w *Wizard, mc fsm.MachineControls[*Wizard]) {
if w.Data.Message != nil {
switch {
case util.MatchCommand("start", w.Data.Message):
mc.Move(RegisterStateID, w)
case util.MatchCommand("help", w.Data.Message):
mc.Move(HelpStateID, w)
case util.HasCommand(w.Data.Message):
mc.Move(UnknownCommandStateID, w)
default:
return
}
}
if w.Data.MyChatMember != nil {
mc.Move(WaitingForMemberWebhookStateID, w)
}
}

View File

@ -18,6 +18,13 @@ func NewRegisterState(app iface.App) fsm.IState[Wizard] {
return &RegisterState{newBase(app)} return &RegisterState{newBase(app)}
} }
func (s *RegisterState) Enter(pl *Wizard, _ fsm.MachineControls[*Wizard]) error {
if pl.Data.Message != nil && pl.Data.Message.Chat.Type != telego.ChatTypePrivate {
return fsm.ErrPreventTransition
}
return nil
}
func (s *RegisterState) Handle(pl *Wizard, mc fsm.MachineControls[*Wizard]) { func (s *RegisterState) Handle(pl *Wizard, mc fsm.MachineControls[*Wizard]) {
loc := s.Localizer(pl.Data.Message.From.LanguageCode) loc := s.Localizer(pl.Data.Message.From.LanguageCode)
userRepo := s.App.DB().ForUser() userRepo := s.App.DB().ForUser()

View File

@ -0,0 +1,32 @@
package fsmwizard
import (
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface"
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/fsm"
"github.com/mymmrac/telego"
tu "github.com/mymmrac/telego/telegoutil"
)
const UnknownCommandStateID fsm.StateID = "unknown_command"
type UnknownCommandState struct {
State
}
func NewUnknownCommandState(app iface.App) fsm.IState[Wizard] {
return &UnknownCommandState{newBase(app)}
}
func (s *UnknownCommandState) Enter(pl *Wizard, _ fsm.MachineControls[*Wizard]) error {
_, err := s.App.TG().SendMessage(&telego.SendMessageParams{
ChatID: tu.ID(s.Payload.Data.Message.Chat.ID),
Text: s.Localizer(s.Payload.Data.Message.From.LanguageCode).Message("unknown_command"),
ParseMode: telego.ModeMarkdown,
})
s.LogError(err)
return fsm.ErrPreventTransition
}
func (s *UnknownCommandState) ID() fsm.StateID {
return UnknownCommandStateID
}

View File

@ -30,10 +30,6 @@ func (h *MessageHandler) Handle(wh telego.Update) error {
return group.NewPoll(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh) return group.NewPoll(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
} }
if util.MatchCommand("start", wh.Message) {
return wizard.NewRegister(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
}
setup, found := store.RedmineSetups.Get(wh.Message.Chat.ID) setup, found := store.RedmineSetups.Get(wh.Message.Chat.ID)
if found { if found {
return wizard.NewRedmineSetup(h.App, wh.Message.From.ID, wh.Message.Chat.ID, setup).Handle(wh) return wizard.NewRedmineSetup(h.App, wh.Message.From.ID, wh.Message.Chat.ID, setup).Handle(wh)

View File

@ -8,3 +8,10 @@ import (
func MatchCommand(command string, msg *telego.Message) bool { func MatchCommand(command string, msg *telego.Message) bool {
return th.CommandEqual(command)(telego.Update{Message: msg}) return th.CommandEqual(command)(telego.Update{Message: msg})
} }
func HasCommand(msg *telego.Message) bool {
if msg == nil {
return false
}
return th.CommandRegexp.MatchString(msg.Text)
}

View File

@ -26,7 +26,7 @@ type MachineHandleInput[T any] func(*T, MachineControls[*T])
type IMachine[T any] interface { type IMachine[T any] interface {
MachineControls[*T] MachineControls[*T]
// Handle the state input. Handle func will accept the current payload and modify it based on user input. // Handle the state input. Handle func will accept the current payload and modify it based on user input.
Handle(MachineHandleInput[T]) error Handle() error
// Reset the machine to its initial state. // Reset the machine to its initial state.
Reset() Reset()
} }
@ -35,6 +35,7 @@ type IMachine[T any] interface {
type Machine[T any] struct { type Machine[T any] struct {
lock sync.Mutex lock sync.Mutex
payload *T payload *T
preHandle MachineHandleInput[T]
state StateID state StateID
initialState StateID initialState StateID
initialPayload T initialPayload T
@ -43,7 +44,7 @@ type Machine[T any] struct {
} }
// New machine. // New machine.
func New[T any](initialState StateID, initialPayload T, states []IState[T], errHandler ErrorState[T]) IMachine[T] { func New[T any](initialState StateID, initialPayload T, preHandle MachineHandleInput[T], states []IState[T], errHandler ErrorState[T]) IMachine[T] {
stateMap := make(map[StateID]IState[T], len(states)) stateMap := make(map[StateID]IState[T], len(states))
for _, state := range states { for _, state := range states {
stateMap[state.ID()] = state stateMap[state.ID()] = state
@ -52,6 +53,7 @@ func New[T any](initialState StateID, initialPayload T, states []IState[T], errH
return &Machine[T]{ return &Machine[T]{
state: initialState, state: initialState,
payload: &pl, payload: &pl,
preHandle: preHandle,
initialState: initialState, initialState: initialState,
initialPayload: initialPayload, initialPayload: initialPayload,
states: stateMap, states: stateMap,
@ -91,10 +93,12 @@ func (m *Machine[T]) Move(id StateID, payload *T) error {
} }
// Handle the input. // Handle the input.
func (m *Machine[T]) Handle(inputFunc MachineHandleInput[T]) error { func (m *Machine[T]) Handle() error {
defer m.lock.Unlock() defer m.lock.Unlock()
m.lock.Lock() m.lock.Lock()
inputFunc(m.payload, m) if m.preHandle != nil {
m.preHandle(m.payload, m)
}
st, err := m.loadState(m.state, m.payload) st, err := m.loadState(m.state, m.payload)
if st == nil || err != nil { if st == nil || err != nil {
return err return err