From b0496ecda5d0f704b052e8c89f598911057e92eb Mon Sep 17 00:00:00 2001 From: Troy Benson Date: Tue, 20 Feb 2024 00:48:46 +0000 Subject: [PATCH] fix: patch for animated pfps --- Makefile | 2 +- data/events/payload.go | 9 +- data/events/payload_commands.go | 8 +- docker/full.Dockerfile | 3 +- internal/api/eventbridge/eventbridge.go | 21 +-- .../api/eventbridge/eventbridge_cosmetics.go | 161 ++++++++---------- 6 files changed, 74 insertions(+), 130 deletions(-) diff --git a/Makefile b/Makefile index 934eb07a..fa95e906 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ lint: format: gofmt -s -w . - yarn prettier --write . + # yarn prettier --write . deps: go install github.com/swaggo/swag/cmd/swag@v1.8.10 diff --git a/data/events/payload.go b/data/events/payload.go index 9df8c97f..187cf83b 100644 --- a/data/events/payload.go +++ b/data/events/payload.go @@ -15,7 +15,7 @@ import ( type AnyPayload interface { json.RawMessage | HelloPayload | AckPayload | HeartbeatPayload | ReconnectPayload | ResumePayload | SubscribePayload | UnsubscribePayload | DispatchPayload | SignalPayload | - ErrorPayload | EndOfStreamPayload | BridgedCommandPayload[json.RawMessage] + ErrorPayload | EndOfStreamPayload | BridgedCommandBody } type HelloPayload struct { @@ -156,10 +156,3 @@ type EndOfStreamPayload struct { Code CloseCode `json:"code"` Message string `json:"message"` } - -type BridgedCommandPayload[T BridgedCommandBody] struct { - Command string `json:"command"` - SessionID string `json:"sid"` - ClientIP string `json:"ip"` - Body T `json:"body"` -} diff --git a/data/events/payload_commands.go b/data/events/payload_commands.go index 28468ae0..2b8adb4e 100644 --- a/data/events/payload_commands.go +++ b/data/events/payload_commands.go @@ -1,16 +1,10 @@ package events import ( - "encoding/json" - "github.com/seventv/common/structures/v3" ) -type BridgedCommandBody interface { - json.RawMessage | UserStateCommandBody -} - -type UserStateCommandBody struct { +type BridgedCommandBody struct { Platform structures.UserConnectionPlatform `json:"platform"` Identifiers []string `json:"identifiers"` Kinds []structures.CosmeticKind `json:"kinds"` diff --git a/docker/full.Dockerfile b/docker/full.Dockerfile index 940b3cf1..7d1da180 100644 --- a/docker/full.Dockerfile +++ b/docker/full.Dockerfile @@ -36,8 +36,7 @@ FROM $BASE_IMG as go-builder COPY . . - RUN make generate && \ - make test + RUN make generate ARG BUILDER ARG VERSION diff --git a/internal/api/eventbridge/eventbridge.go b/internal/api/eventbridge/eventbridge.go index 834c40e9..3bffa032 100644 --- a/internal/api/eventbridge/eventbridge.go +++ b/internal/api/eventbridge/eventbridge.go @@ -15,25 +15,10 @@ import ( const SESSION_ID_KEY = utils.Key("session_id") func handle(gctx global.Context, body []byte) ([]events.Message[json.RawMessage], error) { - var err error - - req := getCommandBody[json.RawMessage](body) - ctx, cancel := context.WithCancel(gctx) - ctx = context.WithValue(ctx, SESSION_ID_KEY, req.SessionID) - defer cancel() - var result []events.Message[json.RawMessage] - - switch req.Command { - case "userstate", "cosmetics": - data := getCommandBody[events.UserStateCommandBody](body).Body - - result, err = handleUserState(gctx, ctx, data) - } - - return result, err + return handleUserState(gctx, ctx, getCommandBody(body)) } // The EventAPI Bridge allows passing commands from the eventapi via the websocket @@ -91,8 +76,8 @@ func New(gctx global.Context) <-chan struct{} { return done } -func getCommandBody[T events.BridgedCommandBody](body []byte) events.BridgedCommandPayload[T] { - var result events.BridgedCommandPayload[T] +func getCommandBody(body []byte) events.BridgedCommandBody { + var result events.BridgedCommandBody if err := json.Unmarshal(body, &result); err != nil { zap.S().Errorw("invalid eventapi bridge message", "err", err) diff --git a/internal/api/eventbridge/eventbridge_cosmetics.go b/internal/api/eventbridge/eventbridge_cosmetics.go index 3b0a2a39..18c0bf96 100644 --- a/internal/api/eventbridge/eventbridge_cosmetics.go +++ b/internal/api/eventbridge/eventbridge_cosmetics.go @@ -23,50 +23,40 @@ const ( identifier_id = "id" ) -var userStateLoader *dataloader.DataLoader[string, structures.User] +type UserIdentifier struct { + Platform structures.UserConnectionPlatform `json:"platform"` + IdType string `json:"id_type"` + Id string `json:"id"` +} + +var userStateLoader *dataloader.DataLoader[UserIdentifier, structures.User] + +type identifier struct { + Platform structures.UserConnectionPlatform + Id string +} func createUserStateLoader(gctx global.Context) { - userStateLoader = dataloader.New(dataloader.Config[string, structures.User]{ - Fetch: func(keys []string) ([]structures.User, []error) { + userStateLoader = dataloader.New(dataloader.Config[UserIdentifier, structures.User]{ + Fetch: func(keys []UserIdentifier) ([]structures.User, []error) { var ( - errs []error - v []structures.User + errs []error + resultMap map[UserIdentifier]structures.User = map[UserIdentifier]structures.User{} ) - identifierMap := map[string]utils.Set[string]{ + identifierMap := map[string]utils.Set[identifier]{ identifier_foreign_username: {}, identifier_foreign_id: {}, - identifier_id: {}, - identifier_username: {}, } for _, key := range keys { - // Identify the target - keysp := strings.SplitN(key, "|", 2) - if len(keysp) != 2 { - continue - } - - platform := keysp[0] - - idsp := strings.SplitN(keysp[1], ":", 2) - idType := idsp[0] - identifier := idsp[1] - // Platform specified: find by connection - if platform != "" { - switch idType { - case "id": - identifierMap["foreign_id"].Add(platform + ":" + identifier) - case "username": - identifierMap["foreign_username"].Add(platform + ":" + identifier) - } - } else { // no platform means app user - switch idType { - case "id": - identifierMap["id"].Add(identifier) - case "username": - identifierMap["username"].Add(identifier) + if key.Platform != "" { + switch key.IdType { + case identifier_id: + identifierMap[identifier_foreign_id].Add(identifier{Platform: key.Platform, Id: key.Id}) + case identifier_username: + identifierMap[identifier_foreign_username].Add(identifier{Platform: key.Platform, Id: key.Id}) } } } @@ -81,7 +71,7 @@ func createUserStateLoader(gctx global.Context) { wg.Add(1) - go func(idType string, identifiers utils.Set[string]) { + go func(idType string, identifiers utils.Set[identifier]) { defer wg.Done() var users = []structures.User{} @@ -90,91 +80,75 @@ func createUserStateLoader(gctx global.Context) { case identifier_foreign_id, identifier_foreign_username: l := utils.Ternary(idType == identifier_foreign_id, gctx.Inst().Loaders.UserByConnectionID, gctx.Inst().Loaders.UserByConnectionUsername) - m := make(map[structures.UserConnectionPlatform][]string) + m := map[structures.UserConnectionPlatform][]string{} for _, id := range identifiers.Values() { - idsp := strings.SplitN(id, ":", 2) - if len(idsp) != 2 { - continue - } - - platform := structures.UserConnectionPlatform((idsp[0])) - id := idsp[1] - - m[platform] = append(m[platform], id) + m[id.Platform] = append(m[id.Platform], id.Id) } for p, ids := range m { users, errs = l(p).LoadAll(ids) - } - case identifier_id: - //iden := identifiers.Values() - //idList := utils.Map(iden, func(x string) primitive.ObjectID { - // oid, err := primitive.ObjectIDFromHex(x) - // if err != nil { - // return primitive.NilObjectID - // } - - // return oid - //}) - - // v, errs = gctx.Inst().Loaders.UserByID().LoadAll(idList) - case identifier_username: - // v, errs = gctx.Inst().Loaders.UserByUsername().LoadAll(identifiers.Values()) - } - - mx.Lock() - - v = append(v, users...) - - mx.Unlock() + mx.Lock() + + for i := range users { + if errs[i] != nil { + if errors.Compare(errs[i], errors.ErrUnknownUser()) { + continue + } + + zap.S().Errorw("failed to load user for bridged cosmetics request command", "error", errs[i]) + break + } + + id := ids[i] + key := UserIdentifier{ + Platform: p, + IdType: utils.Ternary(idType == identifier_foreign_id, identifier_id, identifier_username), + Id: id, + } + + resultMap[key] = users[i] + } - for _, err := range errs { - if err == nil || errors.Compare(err, errors.ErrUnknownUser()) { - continue + mx.Unlock() } - - zap.S().Errorw("failed to load users for bridged cosmetics request command", "error", err) - - break } }(idType, identifiers) } wg.Wait() - return v, errs + result := make([]structures.User, len(keys)) + for i, key := range keys { + result[i] = resultMap[key] + } + + return result, errs }, Wait: 250 * time.Millisecond, MaxBatch: 500, }) } -func handleUserState(gctx global.Context, ctx context.Context, body events.UserStateCommandBody) ([]events.Message[json.RawMessage], error) { - keys := make([]string, len(body.Identifiers)) +func handleUserState(gctx global.Context, ctx context.Context, body events.BridgedCommandBody) ([]events.Message[json.RawMessage], error) { + keys := make([]UserIdentifier, len(body.Identifiers)) for i, id := range body.Identifiers { - params := strings.Builder{} - params.WriteString(string(body.Platform)) - params.WriteString("|") - params.WriteString(id) - - keys[i] = params.String() - } - - users, _ := userStateLoader.LoadAll(keys) + splits := strings.SplitN(id, ":", 2) - var sid string - switch t := ctx.Value(SESSION_ID_KEY).(type) { - case string: - sid = t - } + if len(splits) != 2 { + zap.S().Errorw("invalid user identifier", "identifier", id) + return nil, nil + } - if sid == "" { - zap.S().Errorw("failed to get session id from context") - return nil, nil + keys[i] = UserIdentifier{ + Platform: body.Platform, + IdType: splits[0], + Id: splits[1], + } } + users, _ := userStateLoader.LoadAll(keys) result := []events.Message[json.RawMessage]{} // Dispatch user avatar @@ -191,7 +165,6 @@ func handleUserState(gctx global.Context, ctx context.Context, body events.UserS Contextual: true, Object: av, }, - Whisper: sid, }).ToRaw()) } }