2023-08-19 09:12:26 +03:00
|
|
|
package tg
|
2023-07-09 01:28:59 +03:00
|
|
|
|
2024-03-28 10:41:09 +03:00
|
|
|
// The type represents map of sessions using
|
|
|
|
// as key.
|
|
|
|
type SessionMap map[SessionId]*Session
|
|
|
|
|
|
|
|
// Add new empty session by it's ID.
|
|
|
|
func (sm SessionMap) Add(sid SessionId, scope SessionScope) *Session {
|
|
|
|
ret := NewSession(sid, scope)
|
|
|
|
sm[sid] = ret
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2023-09-26 17:13:31 +03:00
|
|
|
// The way to determine where the context is
|
|
|
|
// related to.
|
|
|
|
type SessionScope uint8
|
|
|
|
const (
|
2023-09-27 14:09:49 +03:00
|
|
|
NoSessionScope SessionScope = iota
|
2023-09-26 17:13:31 +03:00
|
|
|
PrivateSessionScope
|
|
|
|
GroupSessionScope
|
|
|
|
ChannelSessionScope
|
|
|
|
)
|
|
|
|
|
2023-07-09 01:28:59 +03:00
|
|
|
// Represents unique value to identify chats.
|
|
|
|
// In fact is simply ID of the chat.
|
2023-10-10 12:42:05 +03:00
|
|
|
type SessionId int64
|
2023-07-09 01:28:59 +03:00
|
|
|
|
2023-08-13 15:37:36 +03:00
|
|
|
// Convert the SessionId to Telegram API's type.
|
2023-08-19 12:47:33 +03:00
|
|
|
func (si SessionId) ToApi() int64 {
|
2023-08-13 15:37:36 +03:00
|
|
|
return int64(si)
|
|
|
|
}
|
|
|
|
|
2023-07-09 01:28:59 +03:00
|
|
|
// The type represents current state of
|
|
|
|
// user interaction per each of them.
|
|
|
|
type Session struct {
|
2023-08-15 16:02:14 +03:00
|
|
|
// Id of the chat of the user.
|
2023-07-09 01:28:59 +03:00
|
|
|
Id SessionId
|
2023-09-26 17:13:31 +03:00
|
|
|
Scope SessionScope
|
2023-08-15 16:02:14 +03:00
|
|
|
// Custom value for each user.
|
2023-09-11 10:23:04 +03:00
|
|
|
Data any
|
2024-03-28 10:41:09 +03:00
|
|
|
|
|
|
|
bot *Bot
|
|
|
|
pathHistory []Path
|
|
|
|
skippedUpdates *UpdateChan
|
|
|
|
updates *UpdateChan
|
2023-07-09 01:28:59 +03:00
|
|
|
}
|
|
|
|
|
2023-08-13 15:37:36 +03:00
|
|
|
// Return new empty session with specified user ID.
|
2023-09-27 14:09:49 +03:00
|
|
|
func NewSession(id SessionId, scope SessionScope) *Session {
|
2023-08-13 15:37:36 +03:00
|
|
|
return &Session{
|
|
|
|
Id: id,
|
2023-09-27 14:09:49 +03:00
|
|
|
Scope: scope,
|
2023-08-13 15:37:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-28 10:41:09 +03:00
|
|
|
// Changes screen of user to the Id one.
|
|
|
|
func (s *Session) go_(pth Path, arg any) error {
|
|
|
|
var err error
|
|
|
|
if pth == "" {
|
|
|
|
s.pathHistory = []Path{}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var back bool
|
|
|
|
if pth == "-" {
|
|
|
|
if len(s.pathHistory) < 2 {
|
|
|
|
return s.Go("")
|
|
|
|
}
|
|
|
|
pth = s.pathHistory[len(s.pathHistory)-2]
|
|
|
|
s.pathHistory = s.pathHistory[:len(s.pathHistory)-1]
|
|
|
|
}
|
|
|
|
// Getting the screen and changing to
|
|
|
|
// then executing its widget.
|
|
|
|
if !pth.IsAbs() {
|
|
|
|
pth = (s.Path() + "/" + pth).Clean()
|
|
|
|
}
|
2023-07-09 01:28:59 +03:00
|
|
|
|
2024-03-28 10:41:09 +03:00
|
|
|
if !s.PathExist(pth) {
|
|
|
|
return ScreenNotExistErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if !back && s.Path() != pth {
|
|
|
|
s.pathHistory = append(s.pathHistory, pth)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stopping the current widget.
|
|
|
|
screen := s.bot.behaviour.Screens[pth]
|
|
|
|
s.skippedUpdates.Close()
|
|
|
|
|
|
|
|
if screen.Widget != nil {
|
|
|
|
s.skippedUpdates, err = s.runWidget(screen.Widget, arg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return NoWidgetForScreenErr
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) runCompo(compo Component, arg any) (*UpdateChan, error) {
|
|
|
|
if compo == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
s, ok := compo.(Sendable)
|
|
|
|
if ok {
|
|
|
|
msg, err := c.Send(s)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
s.SetMessage(msg)
|
|
|
|
}
|
|
|
|
updates := NewUpdateChan()
|
|
|
|
go func() {
|
|
|
|
compo.Serve(
|
|
|
|
c.WithInput(updates).
|
|
|
|
WithArg(arg),
|
|
|
|
)
|
|
|
|
// To let widgets finish themselves before
|
|
|
|
// the channel is closed and close it by themselves.
|
|
|
|
updates.Close()
|
|
|
|
}()
|
|
|
|
return updates, nil
|
2023-08-13 15:37:36 +03:00
|
|
|
}
|
|
|
|
|
2024-03-28 10:41:09 +03:00
|
|
|
// Run widget in background returning the new input channel for it.
|
|
|
|
func (c *Context) runWidget(widget Widget, arg any) (*UpdateChan, error) {
|
|
|
|
var err error
|
|
|
|
if widget == nil {
|
|
|
|
return nil, EmptyWidgetErr
|
|
|
|
}
|
|
|
|
|
|
|
|
pth := c.Path()
|
|
|
|
compos := widget.Render(c.WithArg(c.makeArg(args)))
|
|
|
|
// Leave if changed path or components are empty.
|
|
|
|
if compos == nil || pth != c.Path() {
|
|
|
|
return nil, EmptyCompoErr
|
|
|
|
}
|
|
|
|
chns := make([]*UpdateChan, len(compos))
|
|
|
|
for i, compo := range compos {
|
|
|
|
chns[i], err = c.runCompo(compo, arg)
|
|
|
|
if err != nil {
|
|
|
|
for _, chn := range chns {
|
|
|
|
chn.Close()
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
chn.Close()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
}
|