individual queue for every machine
This commit is contained in:
parent
b13cd05b45
commit
876950f4ce
@ -8,12 +8,10 @@ import (
|
|||||||
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface"
|
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/handler/iface"
|
||||||
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/locale"
|
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/locale"
|
||||||
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/logger"
|
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/internal/logger"
|
||||||
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/types"
|
|
||||||
"github.com/mymmrac/telego"
|
"github.com/mymmrac/telego"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
@ -23,7 +21,6 @@ type App struct {
|
|||||||
Config *config.Config
|
Config *config.Config
|
||||||
Repositories *db.Repositories
|
Repositories *db.Repositories
|
||||||
Handlers map[handler.Type]handler.Handler
|
Handlers map[handler.Type]handler.Handler
|
||||||
updates types.Queue[*telego.Update]
|
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +114,6 @@ func (a *App) initTelegram() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) initHandlers() {
|
func (a *App) initHandlers() {
|
||||||
a.updates = types.NewQueue[*telego.Update]()
|
|
||||||
fsmwizard.PopulateStates(a)
|
fsmwizard.PopulateStates(a)
|
||||||
a.Handlers = map[handler.Type]handler.Handler{
|
a.Handlers = map[handler.Type]handler.Handler{
|
||||||
handler.Noop: handler.NewNoopHandler(a.Logger, a.Config.Debug),
|
handler.Noop: handler.NewNoopHandler(a.Logger, a.Config.Debug),
|
||||||
@ -144,17 +140,6 @@ func (a *App) handler(update telego.Update) handler.Handler {
|
|||||||
return a.Handlers[handler.Noop]
|
return a.Handlers[handler.Noop]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) processUpdates() {
|
|
||||||
for {
|
|
||||||
item := a.updates.Dequeue()
|
|
||||||
if item == nil {
|
|
||||||
time.Sleep(time.Nanosecond * 200)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
a.processUpdate(*item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *App) processUpdate(update telego.Update) {
|
func (a *App) processUpdate(update telego.Update) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -172,11 +157,9 @@ func (a *App) longPoll() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go a.processUpdates()
|
|
||||||
defer a.Telegram.StopLongPolling()
|
defer a.Telegram.StopLongPolling()
|
||||||
for update := range updates {
|
for update := range updates {
|
||||||
update := update
|
go a.processUpdate(update)
|
||||||
a.updates.Enqueue(&update)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -201,10 +184,8 @@ func (a *App) listenWebhook() error {
|
|||||||
defer func() {
|
defer func() {
|
||||||
_ = a.Telegram.StopWebhook()
|
_ = a.Telegram.StopWebhook()
|
||||||
}()
|
}()
|
||||||
go a.processUpdates()
|
|
||||||
for update := range updates {
|
for update := range updates {
|
||||||
update := update
|
go a.processUpdate(update)
|
||||||
a.updates.Enqueue(&update)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,14 @@ func NewChatMemberUpdatedHandler(app iface.App) *ChatMemberUpdatedHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *ChatMemberUpdatedHandler) Handle(wh telego.Update) error {
|
func (h *ChatMemberUpdatedHandler) Handle(wh telego.Update) error {
|
||||||
return fsmwizard.Get(wh.MyChatMember.From.ID).Handle(func(w *fsmwizard.Wizard) *fsmwizard.Wizard {
|
fsmwizard.Get(wh.MyChatMember.From.ID).Enqueue(func(w *fsmwizard.Wizard) *fsmwizard.Wizard {
|
||||||
if w == nil {
|
if w == nil {
|
||||||
return &fsmwizard.Wizard{Data: wh}
|
return &fsmwizard.Wizard{Data: wh}
|
||||||
}
|
}
|
||||||
w.Data = wh
|
w.Data = wh
|
||||||
return w
|
return w
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
// cm := wh.MyChatMember
|
// cm := wh.MyChatMember
|
||||||
// if !cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == h.App.TGProfile().ID &&
|
// if !cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == h.App.TGProfile().ID &&
|
||||||
// cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == h.App.TGProfile().ID {
|
// cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == h.App.TGProfile().ID {
|
||||||
|
@ -83,6 +83,6 @@ func (s State) Move(mc fsm.MachineControls[Wizard], stateID fsm.StateID, pl *Wiz
|
|||||||
s.LogError(mc.Move(stateID, pl))
|
s.LogError(mc.Move(stateID, pl))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s State) MoveForHandle(mc fsm.MachineControls[Wizard], stateID fsm.StateID, pl *Wizard) {
|
func (s State) MoveForHandling(mc fsm.MachineControls[Wizard], stateID fsm.StateID, pl *Wizard) {
|
||||||
s.LogError(mc.MoveForHandle(stateID, pl))
|
s.LogError(mc.MoveForHandling(stateID, pl))
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ func (s *WaitingForMemberWebhookState) Enter(pl *Wizard, mc fsm.MachineControls[
|
|||||||
}
|
}
|
||||||
if !cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == s.App.TGProfile().ID &&
|
if !cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == s.App.TGProfile().ID &&
|
||||||
cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == s.App.TGProfile().ID {
|
cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == s.App.TGProfile().ID {
|
||||||
s.MoveForHandle(mc, AddChatMemberStateID, pl)
|
s.MoveForHandling(mc, AddChatMemberStateID, pl)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == s.App.TGProfile().ID &&
|
if cm.OldChatMember.MemberIsMember() && cm.OldChatMember.MemberUser().ID == s.App.TGProfile().ID &&
|
||||||
!cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == s.App.TGProfile().ID {
|
!cm.NewChatMember.MemberIsMember() && cm.NewChatMember.MemberUser().ID == s.App.TGProfile().ID {
|
||||||
s.MoveForHandle(mc, RemoveChatMemberStateID, pl)
|
s.MoveForHandling(mc, RemoveChatMemberStateID, pl)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,14 @@ func (h *MessageHandler) Handle(wh telego.Update) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fsmwizard.Get(wh.Message.From.ID).Handle(func(w *fsmwizard.Wizard) *fsmwizard.Wizard {
|
fsmwizard.Get(wh.Message.From.ID).Enqueue(func(w *fsmwizard.Wizard) *fsmwizard.Wizard {
|
||||||
if w == nil {
|
if w == nil {
|
||||||
return &fsmwizard.Wizard{Data: wh}
|
return &fsmwizard.Wizard{Data: wh}
|
||||||
}
|
}
|
||||||
w.Data = wh
|
w.Data = wh
|
||||||
return w
|
return w
|
||||||
})
|
})
|
||||||
|
return nil
|
||||||
|
|
||||||
// if util.MatchCommand("start", wh.Message) {
|
// if util.MatchCommand("start", wh.Message) {
|
||||||
// return wizard.NewRegister(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
// return wizard.NewRegister(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package fsm
|
package fsm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/types"
|
"gitea.neur0tx.site/Neur0toxine/vegapokerbot/pkg/types"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -29,15 +32,18 @@ type MachineStateProvider[T any] func(*T) *T
|
|||||||
// - Reset the machine.
|
// - Reset the machine.
|
||||||
type IMachine[T any] interface {
|
type IMachine[T any] interface {
|
||||||
MachineControlsWithState[T]
|
MachineControlsWithState[T]
|
||||||
// Handle the state input. Handle func will accept the current payload and modify it based on user input.
|
// Enqueue the state input. Handle func will accept the current payload and modify it based on user input.
|
||||||
Handle(MachineStateProvider[T]) error
|
Enqueue(MachineStateProvider[T])
|
||||||
// Reset the machine to its initial state.
|
// Reset the machine to its initial state.
|
||||||
Reset()
|
Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Machine is a finite-state machine implementation.
|
// Machine is a finite-state machine implementation.
|
||||||
type Machine[T any] struct {
|
type Machine[T any] struct {
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
payload *T
|
payload *T
|
||||||
|
transitions types.Queue[MachineStateProvider[T]]
|
||||||
stateRouter MachineStateRouter[T]
|
stateRouter MachineStateRouter[T]
|
||||||
state StateID
|
state StateID
|
||||||
initialState StateID
|
initialState StateID
|
||||||
@ -52,13 +58,22 @@ func New[T any](initialState StateID, router MachineStateRouter[T], states []ISt
|
|||||||
for _, state := range states {
|
for _, state := range states {
|
||||||
stateMap.Set(state.ID(), state)
|
stateMap.Set(state.ID(), state)
|
||||||
}
|
}
|
||||||
return &Machine[T]{
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
m := &Machine[T]{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
state: initialState,
|
state: initialState,
|
||||||
|
transitions: types.NewQueue[MachineStateProvider[T]](),
|
||||||
stateRouter: router,
|
stateRouter: router,
|
||||||
initialState: initialState,
|
initialState: initialState,
|
||||||
states: stateMap,
|
states: stateMap,
|
||||||
errHandler: errHandler,
|
errHandler: errHandler,
|
||||||
}
|
}
|
||||||
|
go m.handleQueue()
|
||||||
|
runtime.SetFinalizer(m, func(m *Machine[T]) {
|
||||||
|
m.cancel()
|
||||||
|
})
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to another state.
|
// Move to another state.
|
||||||
@ -90,13 +105,37 @@ func (m *Machine[T]) Move(id StateID, payload *T) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Machine[T]) MoveForHandle(id StateID, payload *T) error {
|
func (m *Machine[T]) MoveForHandling(id StateID, payload *T) error {
|
||||||
m.handleNow = true
|
m.handleNow = true
|
||||||
return m.Move(id, payload)
|
return m.Move(id, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Machine[T]) Enqueue(provider MachineStateProvider[T]) {
|
||||||
|
m.transitions.Enqueue(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Machine[T]) handleQueue() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
go m.handleQueue()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-m.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-time.After(time.Nanosecond * 10):
|
||||||
|
}
|
||||||
|
transition := m.transitions.Dequeue()
|
||||||
|
if transition == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m.handle(transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the input.
|
// Handle the input.
|
||||||
func (m *Machine[T]) Handle(provider MachineStateProvider[T]) error {
|
func (m *Machine[T]) handle(provider MachineStateProvider[T]) {
|
||||||
if provider != nil {
|
if provider != nil {
|
||||||
m.payload = provider(m.payload)
|
m.payload = provider(m.payload)
|
||||||
}
|
}
|
||||||
@ -105,15 +144,15 @@ func (m *Machine[T]) Handle(provider MachineStateProvider[T]) error {
|
|||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
st.Handle(m.payload, m)
|
st.Handle(m.payload, m)
|
||||||
if m.handleNow { // MoveForHandle was called, trying to handle again.
|
if m.handleNow { // MoveForHandling was called, trying to handle again.
|
||||||
m.handleNow = false
|
m.handleNow = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ const NilStateID = StateID("")
|
|||||||
// It may fail with an error which should be handled by the IState implementation.
|
// It may fail with an error which should be handled by the IState implementation.
|
||||||
type MachineControls[T any] interface {
|
type MachineControls[T any] interface {
|
||||||
Move(StateID, *T) error
|
Move(StateID, *T) error
|
||||||
// MoveForHandle is the same as Move but it also triggers Handle immediately for the next state.
|
// MoveForHandling is the same as Move but it also triggers Handle immediately for the next state.
|
||||||
MoveForHandle(StateID, *T) error
|
MoveForHandling(StateID, *T) error
|
||||||
Reset()
|
Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user