mentionedNonUsers(); if ($handles === []) { return null; } $known = Profile::query() ->where('type', ProfileType::Organization) ->whereIn(DB::raw('LOWER(handle)'), $handles) ->get(); /** @var list $lookups */ $lookups = collect($handles) ->diff($known->map(fn (Profile $organization): string => Str::lower($organization->handle))) ->reject(fn (string $handle): bool => Cache::has("github-organizations:not-found:{$handle}")) ->map(fn (string $handle): array => [ "users/{$handle}", fn (): bool => Cache::put("github-organizations:not-found:{$handle}", true, now()->addDays(self::NotFoundDays)), ]) ->merge($known ->filter(fn (Profile $organization): bool => $organization->github_synced_at?->addHours(Profile::FRESH_FOR_HOURS)->isPast() ?? true) ->map(fn (Profile $organization): array => [ "user/{$organization->github_id}", function () use ($organization): void { $this->deleteOrganization->handle($organization); }, ])) ->take(self::MaxLookups) ->values() ->all(); $retryAt = null; foreach ($lookups as [$path, $notFound]) { $pausedUntil = Cache::integer('github-organizations:paused', 0); if ($pausedUntil > 0) { $retryAt = CarbonImmutable::createFromTimestamp($pausedUntil); break; } $response = $this->github->request()->get($path); $remaining = $response->header('X-RateLimit-Remaining'); if ($remaining !== '' && (int) $remaining < self::RateLimitFloor) { $resetsAt = CarbonImmutable::createFromTimestamp((int) $response->header('X-RateLimit-Reset')); Cache::put('github-organizations:paused', $resetsAt->getTimestamp(), $resetsAt); } if ($response->notFound() || $response->throw()->json('type') !== 'Organization') { $notFound(); continue; } /** @var array $account */ $account = $response->json(); $this->updateOrCreateOrganization->handle($account); } $this->createCommitTopics->handle($commit, $this->mentionedOrganizations->get($commit->body, $commit->mentions ?? [])); return $retryAt; } }