2023-11-06 10:38:26 +01:00
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
|
|
|
|
class ApiManager {
|
|
|
|
String baseUrl;
|
|
|
|
Future<void> ongoingRequest = Future.value();
|
|
|
|
int consecutiveRateLimitExceeded = 0;
|
|
|
|
Duration rateLimitTimeout = Duration.zero;
|
|
|
|
DateTime? lastRequest;
|
|
|
|
|
|
|
|
ApiManager(this.baseUrl);
|
|
|
|
|
|
|
|
Future<http.Response> get(String path) async {
|
|
|
|
Future<void> waitingForRequest = ongoingRequest;
|
|
|
|
Completer completer = Completer();
|
|
|
|
try {
|
|
|
|
ongoingRequest = completer.future;
|
|
|
|
await waitingForRequest;
|
|
|
|
|
|
|
|
DateTime? lastRequestLocal = lastRequest;
|
|
|
|
if (consecutiveRateLimitExceeded > 0 &&
|
|
|
|
lastRequestLocal != null &&
|
|
|
|
DateTime.now().isBefore(lastRequestLocal.add(rateLimitTimeout))) {
|
|
|
|
throw Exception("Too many requests");
|
|
|
|
}
|
|
|
|
|
|
|
|
http.Response response = await http.get(Uri.parse(baseUrl + path));
|
|
|
|
lastRequest = DateTime.now();
|
|
|
|
if (response.statusCode == 429) {
|
|
|
|
consecutiveRateLimitExceeded++;
|
|
|
|
if (consecutiveRateLimitExceeded == 1) {
|
|
|
|
rateLimitTimeout = const Duration(seconds: 1);
|
|
|
|
} else {
|
|
|
|
rateLimitTimeout *= 2;
|
|
|
|
}
|
|
|
|
String? retryAfter = response.headers["Retry-After"];
|
|
|
|
if (retryAfter != null) {
|
|
|
|
int? retryAfterSeconds = int.tryParse(retryAfter);
|
|
|
|
if (retryAfterSeconds != null) {
|
|
|
|
Duration retryAfterDuration = Duration(seconds: retryAfterSeconds);
|
|
|
|
if (retryAfterDuration > rateLimitTimeout) {
|
|
|
|
rateLimitTimeout = retryAfterDuration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw Exception("Too many requests");
|
|
|
|
} else {
|
|
|
|
consecutiveRateLimitExceeded = 0;
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
} finally {
|
|
|
|
completer.complete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-11-16 13:12:55 +01:00
|
|
|
|
|
|
|
class RateLimitStatus {
|
|
|
|
int consecutiveCount = 0;
|
|
|
|
Duration timeout = const Duration(seconds: 1);
|
|
|
|
}
|