connector: fetch @me to create login before creating client
Creating the client before the actual UserLogin is bad form.
This commit is contained in:
@@ -54,7 +54,6 @@ func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.Us
|
|||||||
meta := login.Metadata.(*discordid.UserLoginMetadata)
|
meta := login.Metadata.(*discordid.UserLoginMetadata)
|
||||||
|
|
||||||
session, err := NewDiscordSession(ctx, meta.Token)
|
session, err := NewDiscordSession(ctx, meta.Token)
|
||||||
login.Save(ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -65,7 +64,6 @@ func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.Us
|
|||||||
UserLogin: login,
|
UserLogin: login,
|
||||||
Session: session,
|
Session: session,
|
||||||
}
|
}
|
||||||
cl.SetUp(ctx, meta)
|
|
||||||
|
|
||||||
login.Client = &cl
|
login.Client = &cl
|
||||||
|
|
||||||
@@ -74,46 +72,31 @@ func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.Us
|
|||||||
|
|
||||||
var _ bridgev2.NetworkAPI = (*DiscordClient)(nil)
|
var _ bridgev2.NetworkAPI = (*DiscordClient)(nil)
|
||||||
|
|
||||||
// SetUp performs basic bookkeeping and initialization that should be done
|
|
||||||
// immediately after a DiscordClient has been created.
|
|
||||||
//
|
|
||||||
// nil may be passed for meta, especially during provisioning where we need to
|
|
||||||
// connect to the Discord gateway, but don't have a UserLogin yet.
|
|
||||||
func (d *DiscordClient) SetUp(ctx context.Context, meta *discordid.UserLoginMetadata) {
|
|
||||||
// TODO: Turn this into a factory function like `NewDiscordClient`.
|
|
||||||
log := zerolog.Ctx(ctx)
|
|
||||||
|
|
||||||
// We'll have UserLogin metadata if this UserLogin is being loaded from the
|
|
||||||
// database, i.e. it hasn't just been provisioned.
|
|
||||||
if meta != nil {
|
|
||||||
if meta.HeartbeatSession.IsExpired() {
|
|
||||||
log.Info().Msg("Heartbeat session expired, creating a new one")
|
|
||||||
meta.HeartbeatSession = discordgo.NewHeartbeatSession()
|
|
||||||
}
|
|
||||||
meta.HeartbeatSession.BumpLastUsed()
|
|
||||||
d.Session.HeartbeatSession = meta.HeartbeatSession
|
|
||||||
}
|
|
||||||
|
|
||||||
d.markedOpened = make(map[string]time.Time)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DiscordClient) Connect(ctx context.Context) {
|
func (d *DiscordClient) Connect(ctx context.Context) {
|
||||||
log := zerolog.Ctx(ctx)
|
log := zerolog.Ctx(ctx)
|
||||||
|
|
||||||
if d.Session == nil {
|
meta := d.UserLogin.Metadata.(*discordid.UserLoginMetadata)
|
||||||
log.Error().Msg("No session present")
|
|
||||||
d.UserLogin.BridgeState.Send(status.BridgeState{
|
|
||||||
StateEvent: status.StateBadCredentials,
|
|
||||||
Error: "discord-not-logged-in",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if meta.HeartbeatSession.IsExpired() {
|
||||||
|
log.Info().Msg("Heartbeat session expired, creating a new one")
|
||||||
|
meta.HeartbeatSession = discordgo.NewHeartbeatSession()
|
||||||
|
}
|
||||||
|
meta.HeartbeatSession.BumpLastUsed()
|
||||||
|
d.Session.HeartbeatSession = meta.HeartbeatSession
|
||||||
|
|
||||||
|
d.markedOpened = make(map[string]time.Time)
|
||||||
|
|
||||||
|
log.Debug().Msg("Connecting to Discord")
|
||||||
d.UserLogin.BridgeState.Send(status.BridgeState{
|
d.UserLogin.BridgeState.Send(status.BridgeState{
|
||||||
StateEvent: status.StateConnecting,
|
StateEvent: status.StateConnecting,
|
||||||
})
|
})
|
||||||
if err := d.connect(ctx); err != nil {
|
if err := d.connect(ctx); err != nil {
|
||||||
log.Err(err).Msg("Couldn't connect to Discord")
|
log.Err(err).Msg("Couldn't connect to Discord")
|
||||||
|
d.UserLogin.BridgeState.Send(status.BridgeState{
|
||||||
|
StateEvent: status.StateUnknownError,
|
||||||
|
Error: "discord-connect-error",
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,11 +135,7 @@ func (cl *DiscordClient) connect(ctx context.Context) error {
|
|||||||
cl.usersFromReady[user.ID] = user
|
cl.usersFromReady[user.ID] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: We won't have a UserLogin during provisioning, because the UserLogin
|
cl.BeginSyncing(ctx)
|
||||||
// can only be properly constructed once we know what the Discord user ID is
|
|
||||||
// (i.e. we have returned from this function). We'll rely on the login
|
|
||||||
// process calling this method manually instead.
|
|
||||||
cl.BeginSyncingIfUserLoginPresent(ctx)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -175,11 +154,7 @@ func (d *DiscordClient) LogoutRemote(ctx context.Context) {
|
|||||||
d.Disconnect()
|
d.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cl *DiscordClient) BeginSyncingIfUserLoginPresent(ctx context.Context) {
|
func (cl *DiscordClient) BeginSyncing(ctx context.Context) {
|
||||||
if cl.UserLogin == nil {
|
|
||||||
cl.connector.Bridge.Log.Warn().Msg("Not syncing just yet as we don't have a UserLogin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cl.hasBegunSyncing {
|
if cl.hasBegunSyncing {
|
||||||
cl.connector.Bridge.Log.Warn().Msg("Not beginning sync more than once")
|
cl.connector.Bridge.Log.Warn().Msg("Not beginning sync more than once")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -164,23 +164,6 @@ func (d *DiscordClient) wrapDiscordReaction(reaction *discordgo.MessageReaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DiscordClient) handleDiscordEvent(rawEvt any) {
|
func (d *DiscordClient) handleDiscordEvent(rawEvt any) {
|
||||||
if d.UserLogin == nil {
|
|
||||||
// Our event handlers are able to assume that a UserLogin is available.
|
|
||||||
// We respond to special events like READY outside of this function,
|
|
||||||
// by virtue of methods like Session.Open only returning control flow
|
|
||||||
// after RESUME or READY.
|
|
||||||
log := zerolog.Ctx(context.TODO())
|
|
||||||
log.Trace().Msg("Dropping Discord event received before UserLogin creation")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.Session == nil || d.Session.State == nil || d.Session.State.User == nil {
|
|
||||||
// Our event handlers are able to assume that we've fully connected to the
|
|
||||||
// gateway.
|
|
||||||
d.UserLogin.Log.Debug().Msg("Dropping Discord event received before READY or RESUMED")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
err := recover()
|
err := recover()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -47,55 +47,47 @@ type DiscordGenericLogin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dl *DiscordGenericLogin) FinalizeCreatingLogin(ctx context.Context, token string) (*bridgev2.UserLogin, error) {
|
func (dl *DiscordGenericLogin) FinalizeCreatingLogin(ctx context.Context, token string) (*bridgev2.UserLogin, error) {
|
||||||
|
log := zerolog.Ctx(ctx).With().Str("action", "finalize login").Logger()
|
||||||
|
|
||||||
|
log.Info().Msg("Creating session with provided token")
|
||||||
session, err := NewDiscordSession(ctx, token)
|
session, err := NewDiscordSession(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't create discord session: %w", err)
|
return nil, fmt.Errorf("couldn't create discord session: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := DiscordClient{
|
|
||||||
connector: dl.connector,
|
|
||||||
Session: session,
|
|
||||||
}
|
|
||||||
client.SetUp(ctx, nil)
|
|
||||||
|
|
||||||
err = client.connect(ctx)
|
|
||||||
if err != nil {
|
|
||||||
dl.Cancel()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// At this point we've opened a WebSocket connection to the gateway, received
|
|
||||||
// a READY packet, and know who we are.
|
|
||||||
user := session.State.User
|
|
||||||
dl.DiscordUser = user
|
|
||||||
|
|
||||||
dl.Session = session
|
dl.Session = session
|
||||||
|
|
||||||
|
log.Info().Msg("Requesting @me with provided token")
|
||||||
|
self, err := session.User("@me")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't request self user (bad credentials?): %w", err)
|
||||||
|
}
|
||||||
|
dl.DiscordUser = self
|
||||||
|
|
||||||
|
log.Info().Msg("Fetched @me")
|
||||||
ul, err := dl.User.NewLogin(ctx, &database.UserLogin{
|
ul, err := dl.User.NewLogin(ctx, &database.UserLogin{
|
||||||
ID: discordid.MakeUserLoginID(user.ID),
|
ID: discordid.MakeUserLoginID(self.ID),
|
||||||
Metadata: &discordid.UserLoginMetadata{
|
Metadata: &discordid.UserLoginMetadata{
|
||||||
Token: token,
|
Token: token,
|
||||||
HeartbeatSession: session.HeartbeatSession,
|
HeartbeatSession: session.HeartbeatSession,
|
||||||
},
|
},
|
||||||
}, &bridgev2.NewLoginParams{
|
}, &bridgev2.NewLoginParams{
|
||||||
LoadUserLogin: func(ctx context.Context, login *bridgev2.UserLogin) error {
|
LoadUserLogin: func(ctx context.Context, login *bridgev2.UserLogin) error {
|
||||||
login.Client = &client
|
// (mautrix will instead call `LoadUserLogin` on the connector if we don't provide this.)
|
||||||
client.UserLogin = login
|
login.Client = &DiscordClient{
|
||||||
|
connector: dl.connector,
|
||||||
// Only now that we have a UserLogin can we begin syncing.
|
UserLogin: login,
|
||||||
client.BeginSyncingIfUserLoginPresent(ctx)
|
Session: session,
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
DeleteOnConflict: true,
|
DeleteOnConflict: true,
|
||||||
DontReuseExisting: false,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dl.Cancel()
|
dl.Cancel()
|
||||||
return nil, fmt.Errorf("couldn't create login: %w", err)
|
return nil, fmt.Errorf("couldn't create login during finalization: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
zerolog.Ctx(ctx).Info().
|
(ul.Client.(*DiscordClient)).Connect(ctx)
|
||||||
Str("user_id", user.ID).
|
|
||||||
Str("user_username", user.Username).
|
|
||||||
Msg("Logged in to Discord")
|
|
||||||
|
|
||||||
return ul, nil
|
return ul, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user