connector: implement user cache

* Fixes the totally broken UserInfo resolution in guilds.
* Adds support for USER_UPDATE from the gateway.

Design considerations behind the user cache:

* Explicitly handle deleted user IDs by short circuiting the lookup
  logic and returning a singleton.
* The cache map is protected during HTTP requests to the Discord API.
* The nonexistence of a user is cached. This is to prevent excessive
  requests (a user can't suddenly begin existing at a given ID).

The user cache is upserted on READY, incoming messages, backfill, etc.
This commit is contained in:
Skip R
2026-02-06 13:24:26 -08:00
parent c611e8f116
commit d8ca44ecd9
6 changed files with 203 additions and 20 deletions

View File

@@ -189,6 +189,7 @@ func (d *DiscordClient) handleDiscordEvent(rawEvt any) {
switch evt := rawEvt.(type) {
case *discordgo.Ready:
log.Info().Msg("Received READY dispatch from discordgo")
d.userCache.HandleReady(evt)
d.UserLogin.BridgeState.Send(status.BridgeState{
StateEvent: status.StateConnected,
})
@@ -206,8 +207,11 @@ func (d *DiscordClient) handleDiscordEvent(rawEvt any) {
Msg("Dropping message that lacks an author")
return
}
d.userCache.HandleMessage(evt.Message)
wrappedEvt := d.wrapDiscordMessage(evt)
d.UserLogin.Bridge.QueueRemoteEvent(d.UserLogin, &wrappedEvt)
case *discordgo.UserUpdate:
d.userCache.HandleUserUpdate(evt)
case *discordgo.MessageReactionAdd:
wrappedEvt := d.wrapDiscordReaction(evt.MessageReaction, true)
d.UserLogin.Bridge.QueueRemoteEvent(d.UserLogin, &wrappedEvt)