connector: fix UserLogin lifecycle during provisioning

Bridge provisioning would crash because we wouldn't thread the necessary
database models through.
This commit is contained in:
Skip R
2025-11-25 14:25:17 -08:00
parent 063b9d00dd
commit ab68fae8dd
2 changed files with 50 additions and 28 deletions

View File

@@ -37,6 +37,7 @@ type DiscordClient struct {
usersFromReady map[string]*discordgo.User
UserLogin *bridgev2.UserLogin
Session *discordgo.Session
hasBegunSyncing bool
}
func (d *DiscordConnector) LoadUserLogin(ctx context.Context, login *bridgev2.UserLogin) error {
@@ -116,23 +117,6 @@ func (cl *DiscordClient) connect(ctx context.Context) error {
user := cl.Session.State.User
log.Info().Str("user_id", user.ID).Str("user_username", user.Username).Msg("Connected to Discord")
if cl.UserLogin != nil {
// Feels a bit hacky to check for this here, but it should be true when
// logging in initially. The UserLogin is only ever created if we know
// that we connected successfully. We _do_ know that by now here, but we're
// not tasked with creating the UserLogin; the login code is. Alas.
// FIXME(skip): Avatar.
cl.UserLogin.RemoteProfile = status.RemoteProfile{
Email: user.Email,
Phone: user.Phone,
Name: user.String(),
}
if err := cl.UserLogin.Save(ctx); err != nil {
log.Err(err).Msg("Couldn't save UserLogin after connecting")
}
}
// Stash all of the users we received in READY so we can perform quick lookups
// keyed by user ID.
cl.usersFromReady = make(map[string]*discordgo.User)
@@ -140,7 +124,11 @@ func (cl *DiscordClient) connect(ctx context.Context) error {
cl.usersFromReady[user.ID] = user
}
go cl.syncChannels(ctx)
// We won't have a UserLogin during provisioning, because the UserLogin can
// only be properly constructed once we know what the Discord user ID is
// (i.e. we have returned from this function). Thus, rely on the login
// process calling this method manually.
cl.BeginSyncingIfUserLoginPresent(ctx)
return nil
}
@@ -160,6 +148,33 @@ func (d *DiscordClient) LogoutRemote(ctx context.Context) {
d.Disconnect()
}
func (cl *DiscordClient) BeginSyncingIfUserLoginPresent(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 {
cl.connector.bridge.Log.Warn().Msg("Not beginning sync more than once")
return
}
cl.hasBegunSyncing = true
log := cl.UserLogin.Log
user := cl.Session.State.User
// FIXME(skip): Avatar.
cl.UserLogin.RemoteProfile = status.RemoteProfile{
Email: user.Email,
Phone: user.Phone,
Name: user.String(),
}
if err := cl.UserLogin.Save(ctx); err != nil {
log.Err(err).Msg("Couldn't save UserLogin after connecting")
}
go cl.syncChannels(ctx)
}
func (d *DiscordClient) syncChannels(ctx context.Context) {
for _, dm := range d.Session.State.PrivateChannels {
d.UserLogin.Log.Debug().Str("channel_id", dm.ID).Msg("Syncing private channel")

View File

@@ -48,10 +48,11 @@ func (d *DiscordConnector) CreateLogin(ctx context.Context, user *bridgev2.User,
return nil, fmt.Errorf("unknown login flow ID")
}
return &DiscordLogin{User: user}, nil
return &DiscordLogin{connector: d, User: user}, nil
}
type DiscordLogin struct {
connector *DiscordConnector
User *bridgev2.User
Token string
Session *discordgo.Session
@@ -104,6 +105,7 @@ func (dl *DiscordLogin) SubmitUserInput(ctx context.Context, input map[string]st
}
client := DiscordClient{
connector: dl.connector,
Session: session,
}
err = client.connect(ctx)
@@ -123,9 +125,14 @@ func (dl *DiscordLogin) SubmitUserInput(ctx context.Context, input map[string]st
HeartbeatSession: session.HeartbeatSession,
},
}, &bridgev2.NewLoginParams{
// We already have a Session; call this instead of the connector's main LoadUserLogin method and thread the Session through.
// We already have a Session; let's call this instead of the connector's
// main LoadUserLogin method, and thread the Session through.
LoadUserLogin: func(ctx context.Context, login *bridgev2.UserLogin) error {
login.Client = &client
client.UserLogin = login
// Only now that we have a UserLogin can we begin syncing.
client.BeginSyncingIfUserLoginPresent(ctx)
return nil
},
DeleteOnConflict: true,