diff --git a/shortcuts/im/convert_lib/helpers.go b/shortcuts/im/convert_lib/helpers.go index 139499e4f..75368f45f 100644 --- a/shortcuts/im/convert_lib/helpers.go +++ b/shortcuts/im/convert_lib/helpers.go @@ -152,8 +152,9 @@ func ResolveSenderNames(runtime *common.RuntimeContext, messages []map[string]in // This API has lighter permission requirements and works with user identity // even when the target user is not in the app's visible range. // Response uses "users" (not "items") and "user_id" (not "open_id"). +// The basic_batch endpoint caps user_ids at 10 per request. func batchResolveByBasicContact(runtime *common.RuntimeContext, missingIDs []string, nameMap map[string]string) { - const batchSize = 50 + const batchSize = 10 for i := 0; i < len(missingIDs); i += batchSize { end := i + batchSize if end > len(missingIDs) { diff --git a/shortcuts/im/convert_lib/helpers_test.go b/shortcuts/im/convert_lib/helpers_test.go index 557d5ceb3..3220ad6c6 100644 --- a/shortcuts/im/convert_lib/helpers_test.go +++ b/shortcuts/im/convert_lib/helpers_test.go @@ -4,7 +4,9 @@ package convertlib import ( + "encoding/json" "fmt" + "io" "net/http" "reflect" "strings" @@ -170,6 +172,57 @@ func TestResolveSenderNames(t *testing.T) { } } +func TestBatchResolveByBasicContactRespectsAPILimit(t *testing.T) { + // basic_batch allows at most 10 user_ids per request. Given 25 missing IDs, + // expect three requests with sizes 10 / 10 / 5. + var batchSizes []int + runtime := newBotConvertlibRuntime(t, convertlibRoundTripFunc(func(req *http.Request) (*http.Response, error) { + if !strings.Contains(req.URL.Path, "/open-apis/contact/v3/users/basic_batch") { + return nil, fmt.Errorf("unexpected path: %s", req.URL.Path) + } + body, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + var payload map[string]interface{} + if err := json.Unmarshal(body, &payload); err != nil { + return nil, err + } + userIDs, _ := payload["user_ids"].([]interface{}) + if len(userIDs) > 10 { + t.Fatalf("batch exceeded API limit: size = %d", len(userIDs)) + } + batchSizes = append(batchSizes, len(userIDs)) + + users := make([]interface{}, 0, len(userIDs)) + for _, raw := range userIDs { + id, _ := raw.(string) + users = append(users, map[string]interface{}{ + "user_id": id, + "name": "name-" + id, + }) + } + return convertlibJSONResponse(200, map[string]interface{}{ + "code": 0, + "data": map[string]interface{}{"users": users}, + }), nil + })) + + missingIDs := make([]string, 25) + for i := range missingIDs { + missingIDs[i] = fmt.Sprintf("ou_%02d", i) + } + nameMap := map[string]string{} + batchResolveByBasicContact(runtime, missingIDs, nameMap) + + if want := []int{10, 10, 5}; !reflect.DeepEqual(batchSizes, want) { + t.Fatalf("batch sizes = %v, want %v", batchSizes, want) + } + if len(nameMap) != 25 { + t.Fatalf("resolved name count = %d, want 25", len(nameMap)) + } +} + func TestResolveSenderNamesAPIFailure(t *testing.T) { runtime := newBotConvertlibRuntime(t, convertlibRoundTripFunc(func(req *http.Request) (*http.Response, error) { switch {