From 2277d181725e61691595f1ceb2d9757d39c51ff9 Mon Sep 17 00:00:00 2001 From: Haw Loeung Date: Thu, 20 Aug 2026 06:55:00 +1000 Subject: [PATCH] Guard against passing UserID to GetUserByUserID --- channels.go | 2 -- matterclient.go | 2 ++ users.go | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/channels.go b/channels.go index 3862df8..245aa01 100644 --- a/channels.go +++ b/channels.go @@ -340,8 +340,6 @@ func (m *Client) GetDMChannelName(userID1 string, userID2 string) string { return model.GetDMNameFromIds(userID1, userID2) } -const mattermostIDLen = 26 - // GetDMUserIDs extracts the two user IDs from a direct message channel name (userID1__userID2). // It returns ok as false if the channel name does not conform to the DM naming format. func (m *Client) GetDMUserIDs(channelName string) (string, string, bool) { diff --git a/matterclient.go b/matterclient.go index 5b01e10..fe6684d 100644 --- a/matterclient.go +++ b/matterclient.go @@ -167,6 +167,8 @@ const ( schemeHTTP = "http://" ) +const mattermostIDLen = 26 + // Mattermost has a hardcoded `PerPageMaximum = 200` & `LimitMaximum = 200` // See https://github.com/mattermost/mattermost/blob/master/server/channels/web/params.go const mattermostPerPageMax = 200 diff --git a/users.go b/users.go index f863b48..22f3883 100644 --- a/users.go +++ b/users.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "strconv" "strings" "time" @@ -240,6 +241,13 @@ func (m *Client) GetUserByUserID(ctx context.Context, userID string) *model.User func (m *Client) GetUserByUsername(ctx context.Context, username string) *model.User { m.Users.mu.RLock() + // Fast path: check if caller passed an already-cached User ID + if u, ok := m.Users.users[username]; ok { + m.Users.mu.RUnlock() + return u + } + + // Scan cached users by username for _, u := range m.Users.users { if u.Username == username { m.Users.mu.RUnlock() @@ -251,6 +259,7 @@ func (m *Client) GetUserByUsername(ctx context.Context, username string) *model. var mmuser *model.User + // Query API by Username (handles genuine 26-character usernames) retryCount := 0 for { m.apiLogger.Warnf("GetUserByUsername: User: %s #%d", username, retryCount) @@ -261,12 +270,23 @@ func (m *Client) GetUserByUsername(ctx context.Context, username string) *model. break } + // If user not found and the string looks like an ID, cascade to GetUser + if resp != nil && resp.StatusCode == http.StatusNotFound && model.IsValidId(username) { + m.logger.Debugf("GetUserByUsername: %s not found as username, attempting lookup by User ID", username) + return m.GetUser(ctx, username) + } + shouldRetry, hErr := m.HandleRetry(ctx, "GetUserByUsername", err, retryCount, 10, resp) if hErr == nil && shouldRetry { retryCount++ continue } + // Fallback attempt by ID if error was not retryable + if model.IsValidId(username) { + return m.GetUser(ctx, username) + } + m.logger.Debugf("GetUserByUsername failed to fetch missing user %s: %v", username, err) return nil