// Package state provides a shared state instance for other packages to use. package state import ( "context" "log" "github.com/diamondburned/arikawa/v2/discord" "github.com/diamondburned/arikawa/v2/session" "github.com/diamondburned/arikawa/v2/state" "github.com/diamondburned/arikawa/v2/state/store/defaultstore" "github.com/diamondburned/arikawa/v2/utils/httputil/httpdriver" "github.com/diamondburned/cchat" "github.com/diamondburned/cchat-discord/internal/discord/state/labels" "github.com/diamondburned/cchat-discord/internal/discord/state/nonce" "github.com/diamondburned/ningen/v2" "github.com/pkg/errors" ) type Instance struct { *ningen.State Nonces *nonce.Map Labels *labels.Repository // UserID is a constant user ID of the current user. It is guaranteed to be // valid. UserID discord.UserID } var _ cchat.SessionSaver = (*Instance)(nil) // ErrInvalidSession is returned if SessionRestore is given a bad session. var ErrInvalidSession = errors.New("invalid session") func NewFromData(data map[string]string) (*Instance, error) { tk, ok := data["token"] if !ok { return nil, ErrInvalidSession } return NewFromToken(tk) } func NewFromToken(token string) (*Instance, error) { s, err := state.New(token) if err != nil { return nil, err } return New(s) } func Login(email, password, mfa string) (*Instance, error) { session, err := session.Login(email, password, mfa) if err != nil { return nil, err } cabinet := defaultstore.New() cabinet.MessageStore = defaultstore.NewMessage(50) return New(state.NewFromSession(session, cabinet)) } func New(s *state.State) (*Instance, error) { n, err := ningen.FromState(s) if err != nil { return nil, errors.Wrap(err, "failed to create a state wrapper") } n.Client.OnRequest = append(n.Client.OnRequest, func(r httpdriver.Request) error { log.Println("[Discord] Request", r.GetPath()) return nil }, ) if err := n.Open(); err != nil { return nil, err } // Prefetch user. u, err := s.Me() if err != nil { return nil, errors.Wrap(err, "failed to get current user") } return &Instance{ UserID: u.ID, State: n, Nonces: new(nonce.Map), Labels: labels.NewRepository(n), }, nil } // Permissions queries for the permission without hitting the REST API. func (s *Instance) Permissions( chID discord.ChannelID, uID discord.UserID) (discord.Permissions, error) { return s.StateOnly().Permissions(chID, uID) } var deadCtx = expiredContext() // StateOnly returns a shallow copy of *State with an already-expired context. func (s *Instance) StateOnly() *state.State { return s.WithContext(deadCtx) } func (s *Instance) SaveSession() map[string]string { return map[string]string{ "token": s.Token, } } func expiredContext() context.Context { ctx, cancel := context.WithCancel(context.Background()) cancel() return ctx }