403 lines
19 KiB
PHP
403 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* FavMasToKey - Traitement des favoris (version améliorée)
|
|
*/
|
|
|
|
// Définir la constante pour inclure les fichiers
|
|
define('FAVMASTOKEY', true);
|
|
|
|
// Forcer les en-têtes JSON dès le début pour éviter tout conflit
|
|
header('Content-Type: application/json');
|
|
|
|
// Activer la capture d'erreurs
|
|
set_error_handler(function($errno, $errstr, $errfile, $errline) {
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => "Erreur PHP: $errstr (ligne $errline dans $errfile)",
|
|
]);
|
|
exit;
|
|
});
|
|
|
|
try {
|
|
// Inclure les fichiers requis
|
|
require_once 'includes/config.php';
|
|
require_once 'includes/functions.php';
|
|
|
|
// Vérifier que la requête est en POST
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['success' => false, 'message' => 'Méthode non autorisée']);
|
|
exit;
|
|
}
|
|
|
|
// Vérifier que l'utilisateur est authentifié
|
|
if (!isset($_SESSION['misskey_token']) || empty($_SESSION['misskey_token'])) {
|
|
http_response_code(401);
|
|
echo json_encode(['success' => false, 'message' => 'Non authentifié']);
|
|
exit;
|
|
}
|
|
|
|
// Récupérer l'instance Misskey
|
|
$misskey_instance = isset($_SESSION['misskey_instance']) ? $_SESSION['misskey_instance'] : '';
|
|
if (empty($misskey_instance)) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'message' => 'Instance Misskey non définie']);
|
|
exit;
|
|
}
|
|
|
|
// Récupérer le token d'accès
|
|
$token = $_SESSION['misskey_token'];
|
|
|
|
// Récupérer les données envoyées
|
|
$input_data = file_get_contents('php://input');
|
|
$input = json_decode($input_data, true);
|
|
|
|
if (!$input || !isset($input['batch']) || !is_array($input['batch'])) {
|
|
http_response_code(400);
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Données invalides',
|
|
'debug' => [
|
|
'received' => $input_data ? substr($input_data, 0, 200) . '...' : 'Aucune donnée reçue'
|
|
]
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
// Journaliser les paramètres reçus
|
|
$receivedParams = [
|
|
'tortoiseMode' => isset($input['tortoiseMode']) ? $input['tortoiseMode'] : 'non défini',
|
|
'slowMode' => isset($input['slowMode']) ? $input['slowMode'] : 'non défini',
|
|
'delaySeconds' => isset($input['delaySeconds']) ? $input['delaySeconds'] : 'non défini',
|
|
'useCachedIds' => isset($input['useCachedIds']) ? $input['useCachedIds'] : false,
|
|
'hasBatchData' => isset($input['batchData']) ? true : false
|
|
];
|
|
error_log("Paramètres reçus dans process.php: " . json_encode($receivedParams));
|
|
|
|
// Récupérer le lot à traiter
|
|
$batch = $input['batch'];
|
|
$currentIndex = isset($input['currentIndex']) ? (int)$input['currentIndex'] : 0;
|
|
$totalItems = isset($input['totalItems']) ? (int)$input['totalItems'] : count($batch);
|
|
$slowMode = isset($input['slowMode']) ? (bool)$input['slowMode'] : false;
|
|
$tortoiseMode = isset($input['tortoiseMode']) ? (bool)$input['tortoiseMode'] : false;
|
|
$delaySeconds = isset($input['delaySeconds']) ? (int)$input['delaySeconds'] : 30;
|
|
$useCachedIds = isset($input['useCachedIds']) ? (bool)$input['useCachedIds'] : false;
|
|
|
|
// AMÉLIORATION 3: Récupérer les données du cache si fournies
|
|
$batchData = isset($input['batchData']) ? $input['batchData'] : null;
|
|
|
|
error_log("Paramètres après conversion: slowMode=" . ($slowMode ? 'true' : 'false') .
|
|
", tortoiseMode=" . ($tortoiseMode ? 'true' : 'false') .
|
|
", delaySeconds=" . $delaySeconds .
|
|
", useCachedIds=" . ($useCachedIds ? 'true' : 'false'));
|
|
|
|
// Ajuster le délai entre les requêtes selon le mode et la valeur personnalisée
|
|
if ($tortoiseMode) {
|
|
$delayMs = $delaySeconds * 1000; // Convertir en millisecondes (délai tortue - 60-300 secondes)
|
|
error_log("Utilisation du mode tortue avec délai: " . $delayMs . "ms");
|
|
} else if ($slowMode) {
|
|
$delayMs = $delaySeconds * 1000; // Convertir en millisecondes (délai lent - 10-60 secondes)
|
|
error_log("Utilisation du mode lent avec délai: " . $delayMs . "ms");
|
|
} else {
|
|
$delayMs = $config['delay_between_requests']; // Délai normal (3000ms par défaut)
|
|
error_log("Utilisation du mode normal avec délai: " . $delayMs . "ms");
|
|
}
|
|
|
|
// Résultats du traitement
|
|
$results = [];
|
|
|
|
// Traiter chaque URL du lot
|
|
foreach ($batch as $index => $url) {
|
|
// Extraire les informations de l'URL
|
|
$urlParts = parse_url($url);
|
|
|
|
// Vérifier que l'URL est valide
|
|
if (!$urlParts || !isset($urlParts['host']) || !isset($urlParts['path'])) {
|
|
$results[$index] = [
|
|
'status' => 'error',
|
|
'message' => "URL invalide: $url",
|
|
'error_type' => 'invalid_url'
|
|
];
|
|
continue;
|
|
}
|
|
|
|
// Ajouter un log pour déboguer
|
|
error_log("Traitement de l'URL: " . $url . " sur l'instance: " . $misskey_instance);
|
|
|
|
// AMÉLIORATION 3: Vérifier si nous avons un ID en cache pour cette URL
|
|
$cachedMisskeyId = null;
|
|
if ($useCachedIds && $batchData) {
|
|
// Vérifier si batchData est un tableau indexé ou associatif
|
|
if (isset($batchData[$index])) {
|
|
// Format indexé
|
|
if ($batchData[$index]['url'] === $url &&
|
|
$batchData[$index]['cached'] === true) {
|
|
|
|
$cachedMisskeyId = $batchData[$index]['cachedId'];
|
|
error_log("ID trouvé en cache pour $url: $cachedMisskeyId");
|
|
}
|
|
} else {
|
|
// Format non-indexé, parcourir et chercher l'URL
|
|
foreach ($batchData as $item) {
|
|
if (isset($item['url']) && $item['url'] === $url &&
|
|
isset($item['cached']) && $item['cached'] === true &&
|
|
isset($item['cachedId'])) {
|
|
|
|
$cachedMisskeyId = $item['cachedId'];
|
|
error_log("ID trouvé en cache pour $url: $cachedMisskeyId (recherche)");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AMÉLIORATION 2: Si nous avons un ID en cache, nous pouvons sauter directement à la vérification du statut de favori
|
|
$noteId = null;
|
|
$searchNeeded = true;
|
|
|
|
if ($cachedMisskeyId) {
|
|
$noteId = $cachedMisskeyId;
|
|
$searchNeeded = false;
|
|
error_log("Utilisation de l'ID en cache, recherche fédérée évitée pour: $url");
|
|
}
|
|
|
|
// Rechercher la note sur Misskey à partir de l'URL si nécessaire
|
|
if ($searchNeeded) {
|
|
$searchResult = search_federated_note($misskey_instance, $url, $token);
|
|
|
|
// Vérifier si la recherche a réussi ET si les données contiennent un ID valide
|
|
if ($searchResult['success'] && isset($searchResult['data']) && !empty($searchResult['data'])) {
|
|
// Vérifier et extraire l'ID de manière sécurisée
|
|
if (isset($searchResult['data']['id']) && !empty($searchResult['data']['id'])) {
|
|
$noteId = $searchResult['data']['id'];
|
|
|
|
// AMÉLIORATION 3: Sauvegarder le résultat dans la réponse pour mise à jour du cache côté client
|
|
$results[$index]['misskey_id'] = $noteId;
|
|
} else {
|
|
// L'ID est manquant ou vide dans la réponse
|
|
$dataKeys = is_array($searchResult['data']) ? array_keys($searchResult['data']) : ['non_array'];
|
|
$results[$index] = [
|
|
'status' => 'error',
|
|
'message' => "Note trouvée mais sans ID valide: $url",
|
|
'error_type' => 'invalid_response',
|
|
'details' => "Clés disponibles: " . implode(', ', $dataKeys)
|
|
];
|
|
|
|
// Log pour déboguer
|
|
error_log("Structure de données reçue: " . json_encode($searchResult['data']));
|
|
continue;
|
|
}
|
|
} else {
|
|
// Note non trouvée ou erreur de recherche
|
|
$errorMessage = isset($searchResult['message']) ? $searchResult['message'] : "Publication introuvable";
|
|
$errorCode = isset($searchResult['error_code']) ? $searchResult['error_code'] : '';
|
|
$httpCode = isset($searchResult['http_code']) ? $searchResult['http_code'] : '';
|
|
|
|
// Déterminer le type d'erreur
|
|
$errorType = 'not_found';
|
|
if (strpos($errorMessage, 'rate limit') !== false || $httpCode == 429 ||
|
|
(isset($searchResult['data']['error']) &&
|
|
(isset($searchResult['data']['error']['code']) && $searchResult['data']['error']['code'] == 'RATE_LIMIT_EXCEEDED'))) {
|
|
$errorType = 'rate_limit';
|
|
if ($tortoiseMode) {
|
|
$errorMessage = "Limite d'API atteinte malgré le mode tortue. Essayez d'augmenter le délai ou attendez quelques minutes.";
|
|
} else if ($slowMode) {
|
|
$errorMessage = "Limite d'API atteinte. Activez le mode tortue ou attendez quelques minutes.";
|
|
} else {
|
|
$errorMessage = "Limite d'API atteinte. Activez le mode ultra-lent ou le mode tortue.";
|
|
}
|
|
} elseif ($httpCode >= 500) {
|
|
$errorType = 'server_error';
|
|
$errorMessage = "L'instance Misskey rencontre des problèmes. Statut HTTP: $httpCode";
|
|
} elseif ($httpCode == 401 || $httpCode == 403) {
|
|
$errorType = 'auth_error';
|
|
$errorMessage = "Problème d'authentification ou de permission. Vérifiez votre token.";
|
|
} elseif ($httpCode == 0) {
|
|
$errorType = 'network_error';
|
|
$errorMessage = "Problème de connexion réseau. Vérifiez votre connectivité.";
|
|
}
|
|
|
|
$results[$index] = [
|
|
'status' => $errorType == 'rate_limit' ? 'warning' : 'error',
|
|
'message' => "Publication non trouvée sur le réseau fédéré: $url ($errorMessage)",
|
|
'error_type' => $errorType,
|
|
'details' => $httpCode ? "HTTP: $httpCode" : ""
|
|
];
|
|
|
|
// Ajouter des infos de debug
|
|
if (isset($searchResult['data']) && is_array($searchResult['data'])) {
|
|
error_log("Données reçues pour URL $url: " . json_encode($searchResult['data']));
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// AMÉLIORATION 2: Vérifier d'abord si la note est déjà favorite pour économiser une requête API
|
|
$favoriteCheckResult = check_if_favorited($misskey_instance, $noteId, $token);
|
|
|
|
if ($favoriteCheckResult['success'] && $favoriteCheckResult['is_favorited']) {
|
|
// La note est déjà dans les favoris
|
|
$results[$index] = [
|
|
'status' => 'info',
|
|
'message' => "Déjà dans vos favoris: $url",
|
|
'error_type' => 'already_favorited',
|
|
'misskey_id' => $noteId // AMÉLIORATION 3: Inclure l'ID même si déjà favori
|
|
];
|
|
|
|
// AMÉLIORATION 2: Ne pas attendre le délai complet pour les notes déjà favorites
|
|
error_log("Note déjà favorite, aucun délai nécessaire: $url");
|
|
continue;
|
|
} else {
|
|
// La note n'est pas encore dans les favoris ou la vérification a échoué
|
|
// Dans le cas d'échec, nous essayons quand même d'ajouter la note
|
|
$favoriteResult = add_to_favorites($misskey_instance, $noteId, $token);
|
|
|
|
if ($favoriteResult['success']) {
|
|
$results[$index] = [
|
|
'status' => 'success',
|
|
'message' => "Ajouté aux favoris: $url",
|
|
'misskey_id' => $noteId // AMÉLIORATION 3: Inclure l'ID pour mise à jour du cache
|
|
];
|
|
} else {
|
|
// Vérifier si c'est une erreur de "déjà ajouté aux favoris"
|
|
$errorMessage = isset($favoriteResult['data']['error']['message'])
|
|
? $favoriteResult['data']['error']['message']
|
|
: (isset($favoriteResult['message']) ? $favoriteResult['message'] : 'Erreur inconnue');
|
|
|
|
// Extraire le code d'erreur si disponible
|
|
$errorCode = isset($favoriteResult['data']['error']['code'])
|
|
? $favoriteResult['data']['error']['code']
|
|
: '';
|
|
|
|
// Déterminer le type d'erreur
|
|
$errorType = 'api_error';
|
|
if (strpos($errorMessage, 'already') !== false) {
|
|
$errorType = 'already_favorited';
|
|
} elseif (strpos($errorMessage, 'rate limit') !== false || strpos($errorMessage, 'limit exceeded') !== false || $errorCode == 'RATE_LIMIT_EXCEEDED') {
|
|
$errorType = 'rate_limit';
|
|
} elseif (strpos($errorMessage, 'permission') !== false || $errorCode == 'NO_PERMISSION') {
|
|
$errorType = 'permission_denied';
|
|
}
|
|
|
|
if ($errorType === 'already_favorited') {
|
|
$results[$index] = [
|
|
'status' => 'info',
|
|
'message' => "Déjà dans vos favoris: $url",
|
|
'error_type' => $errorType,
|
|
'misskey_id' => $noteId // AMÉLIORATION 3: Inclure l'ID même si déjà favori
|
|
];
|
|
} else if ($errorType === 'rate_limit') {
|
|
$message = "Limite d'API atteinte pour: $url. ";
|
|
if ($tortoiseMode) {
|
|
$message .= "Essayez d'augmenter le délai ou attendez quelques minutes.";
|
|
} else if ($slowMode) {
|
|
$message .= "Activez le mode tortue.";
|
|
} else {
|
|
$message .= "Activez le mode ultra-lent ou le mode tortue.";
|
|
}
|
|
|
|
$results[$index] = [
|
|
'status' => 'warning',
|
|
'message' => $message,
|
|
'error_type' => $errorType,
|
|
'details' => $errorMessage,
|
|
'misskey_id' => $noteId // AMÉLIORATION 3: Inclure l'ID pour mise à jour du cache
|
|
];
|
|
} else {
|
|
$results[$index] = [
|
|
'status' => 'error',
|
|
'message' => "Erreur lors de l'ajout aux favoris: $errorMessage",
|
|
'error_type' => $errorType,
|
|
'details' => $errorCode ? "Code: $errorCode" : "",
|
|
'misskey_id' => $noteId // AMÉLIORATION 3: Inclure l'ID pour mise à jour du cache
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
// AMÉLIORATION 2: Pas besoin de pause pour les notes déjà dans les favoris ou si nous avons utilisé l'ID en cache
|
|
// Nous vérifions si l'opération actuelle nécessite un délai
|
|
$requiresDelay = true;
|
|
|
|
// Pas de délai nécessaire si c'est déjà en favoris
|
|
if ($results[$index]['status'] === 'info' && $results[$index]['error_type'] === 'already_favorited') {
|
|
$requiresDelay = false;
|
|
}
|
|
|
|
// Pas de délai nécessaire si nous avons utilisé un ID en cache et l'opération a réussi
|
|
if ($cachedMisskeyId && ($results[$index]['status'] === 'success' ||
|
|
($results[$index]['status'] === 'info' && $results[$index]['error_type'] === 'already_favorited'))) {
|
|
$requiresDelay = false;
|
|
}
|
|
|
|
// Pour les notes qui requièrent vraiment un délai (opérations lourdes d'API)
|
|
if ($requiresDelay) {
|
|
// En mode tortue ou lent, appliquer un délai réduit pour les notes où nous avons fait moins de requêtes API
|
|
if (($tortoiseMode || $slowMode) && !$searchNeeded) {
|
|
// Si nous avons sauté la recherche fédérée, utiliser seulement 10% du délai complet
|
|
$reducedDelay = floor($delayMs * 0.1);
|
|
error_log("Pause réduite de {$reducedDelay}ms pour note avec ID connu: $url");
|
|
usleep($reducedDelay * 1000);
|
|
} else {
|
|
// Délai complet pour les requêtes complètes qui nécessitent une recherche fédérée
|
|
error_log("Pause complète de {$delayMs}ms après traitement complet de: $url");
|
|
usleep($delayMs * 1000);
|
|
}
|
|
} else {
|
|
error_log("Aucune pause nécessaire pour la note: $url");
|
|
}
|
|
}
|
|
|
|
// AMÉLIORATION 2: Vérifier si toutes les notes sont déjà favorites
|
|
$allAlreadyFavorited = true;
|
|
foreach ($results as $result) {
|
|
if (!($result['status'] === 'info' && $result['error_type'] === 'already_favorited')) {
|
|
$allAlreadyFavorited = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Si au moins une erreur de rate limit a été détectée, suggérer le mode approprié
|
|
$hasRateLimitErrors = false;
|
|
foreach ($results as $result) {
|
|
if (isset($result['error_type']) && $result['error_type'] === 'rate_limit') {
|
|
$hasRateLimitErrors = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Déterminer les suggestions à faire à l'utilisateur
|
|
$suggestions = [
|
|
'use_slow_mode' => $hasRateLimitErrors && !$slowMode && !$tortoiseMode,
|
|
'use_tortoise_mode' => $hasRateLimitErrors && $slowMode && !$tortoiseMode,
|
|
'increase_delay' => $hasRateLimitErrors && ($slowMode || $tortoiseMode) && $delaySeconds < ($tortoiseMode ? 300 : 60),
|
|
'all_already_favorited' => $allAlreadyFavorited // AMÉLIORATION 2: Indiquer si toutes les notes sont déjà favorites
|
|
];
|
|
|
|
// Renvoyer les résultats
|
|
echo json_encode([
|
|
'success' => true,
|
|
'results' => $results,
|
|
'progress' => [
|
|
'current' => $currentIndex + count($batch),
|
|
'total' => $totalItems,
|
|
'percentage' => round((($currentIndex + count($batch)) / $totalItems) * 100, 2)
|
|
],
|
|
'suggestions' => $suggestions,
|
|
'config' => [
|
|
'slow_mode' => $slowMode,
|
|
'tortoise_mode' => $tortoiseMode,
|
|
'delay_seconds' => $delaySeconds,
|
|
'all_already_favorited' => $allAlreadyFavorited // AMÉLIORATION 2: Indiquer dans la config si tout est déjà traité
|
|
]
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
// Capturer toutes les exceptions et renvoyer un message d'erreur formaté en JSON
|
|
echo json_encode([
|
|
'success' => false,
|
|
'message' => 'Exception: ' . $e->getMessage(),
|
|
'file' => $e->getFile(),
|
|
'line' => $e->getLine()
|
|
]);
|
|
} |