diff --git a/includes/config.php b/includes/config.php index 384caa1..402e7f4 100644 --- a/includes/config.php +++ b/includes/config.php @@ -38,10 +38,10 @@ $config = [ 'misskey_api_endpoint' => '/api/notes/favorites/create', // Paramètres pour le traitement - 'batch_size' => 5, // Nombre de favoris à traiter en une fois - 'timeout' => 45, // Timeout des requêtes en secondes - 'max_retries' => 3, // Nombre maximal de tentatives par favori - 'delay_between_requests' => 2000 // Délai entre les requêtes en millisecondes (pour éviter le rate limiting) + 'batch_size' => 2, + 'timeout' => 90, + 'max_retries' => 3, + 'delay_between_requests' => 3000 ]; // Session diff --git a/includes/functions.php b/includes/functions.php index 84095aa..fa9ebc9 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -225,49 +225,127 @@ function validate_misskey_token($instance, $token) { * @return array Résultat de la recherche */ function search_federated_note($instance, $url, $token) { + // Nettoyer l'URL (enlever les éventuels paramètres) + $cleanUrl = strtok($url, '?'); + + // Journal de débogage + error_log("Recherche fédérée pour: " . $cleanUrl); + // Méthode principale: Utiliser ap/show qui fonctionne avec la plupart des instances $endpoint = '/api/ap/show'; $data = [ - 'uri' => $url + 'uri' => $cleanUrl ]; // Effectuer la requête $result = misskey_api_request($instance, $endpoint, $data, $token); - // Si la méthode principale a réussi, retourner le résultat - if ($result['success'] && isset($result['data']) && !empty($result['data'])) { - return $result; + // Journal pour le format de la réponse + if ($result['success'] && isset($result['data'])) { + error_log("Format de réponse ap/show: " . json_encode(array_keys($result['data']))); } - // Méthode de secours 1: Essayer search-by-url (ancienne méthode) - $fallback_result = misskey_api_request($instance, '/api/notes/search-by-url', ['url' => $url], $token); - - if ($fallback_result['success'] && isset($fallback_result['data']) && !empty($fallback_result['data'])) { - return $fallback_result; - } - - // Méthode de secours 2: Extraction et recherche par ID distant - $urlParts = parse_url($url); - $pathParts = isset($urlParts['path']) ? explode('/', trim($urlParts['path'], '/')) : []; - - if (count($pathParts) >= 4 && $pathParts[count($pathParts) - 2] === 'statuses') { - $statusId = end($pathParts); - $username = $pathParts[count($pathParts) - 3]; - $acctDomain = isset($urlParts['host']) ? $urlParts['host'] : ''; + // Si la méthode principale a réussi et renvoie un ID, retourner le résultat + if ($result['success'] && isset($result['data'])) { + // Vérifier si l'ID existe directement + if (isset($result['data']['id'])) { + return $result; + } - if ($statusId && $username && $acctDomain) { - $remoteId = "https://{$acctDomain}/users/{$username}/statuses/{$statusId}"; + // Certaines instances peuvent avoir l'ID dans 'note' + if (isset($result['data']['note']) && isset($result['data']['note']['id'])) { + // Remonter l'ID au niveau principal + $result['data']['id'] = $result['data']['note']['id']; + return $result; + } + + // Pour les instances plus récentes qui utilisent un format différent + if (!empty($result['data'])) { + // Rechercher un champ qui pourrait contenir l'ID + foreach (['id', 'noteId', 'objectId', 'originalId'] as $possibleIdField) { + if (isset($result['data'][$possibleIdField])) { + $result['data']['id'] = $result['data'][$possibleIdField]; + return $result; + } + } - $remote_result = misskey_api_request($instance, '/api/notes/show', ['uri' => $remoteId], $token); - - if ($remote_result['success'] && isset($remote_result['data']) && !empty($remote_result['data'])) { - return $remote_result; + // Si toujours pas d'ID, examiner la structure pour le trouver + foreach ($result['data'] as $key => $value) { + if (is_array($value) && isset($value['id'])) { + $result['data']['id'] = $value['id']; + return $result; + } } } } - // Si aucune méthode n'a fonctionné, retourner le résultat de la méthode principale - return $result; + // Méthode de secours 1: Essayer notes/search-by-url (parfois utilisé dans les anciennes versions) + $fallback_result = misskey_api_request($instance, '/api/notes/search-by-url', ['url' => $cleanUrl], $token); + + if ($fallback_result['success'] && isset($fallback_result['data'])) { + error_log("Format de réponse search-by-url: " . json_encode(array_keys($fallback_result['data']))); + + // Vérifier si nous avons un résultat avec un ID + if (isset($fallback_result['data']['id'])) { + return $fallback_result; + } + + // Si le résultat est un tableau (certaines instances renvoient un tableau) + if (is_array($fallback_result['data']) && !isset($fallback_result['data']['id'])) { + // Chercher le premier élément avec un ID + foreach ($fallback_result['data'] as $item) { + if (is_array($item) && isset($item['id'])) { + $fallback_result['data'] = $item; // Utiliser cet élément comme résultat + return $fallback_result; + } + } + } + } + + // Méthode de secours 2: Extraction et recherche par ID distant + $urlParts = parse_url($cleanUrl); + if (isset($urlParts['path'])) { + $pathParts = explode('/', trim($urlParts['path'], '/')); + + if (count($pathParts) >= 4 && $pathParts[count($pathParts) - 2] === 'statuses') { + $statusId = end($pathParts); + $username = $pathParts[count($pathParts) - 3]; + $acctDomain = isset($urlParts['host']) ? $urlParts['host'] : ''; + + if ($statusId && $username && $acctDomain) { + $remoteId = "https://{$acctDomain}/users/{$username}/statuses/{$statusId}"; + + // Essayer d'abord avec /api/notes/show + $remote_result = misskey_api_request($instance, '/api/notes/show', ['uri' => $remoteId], $token); + + if ($remote_result['success'] && isset($remote_result['data']['id'])) { + return $remote_result; + } + + // Dernier recours: essayer renotes/search + $renote_result = misskey_api_request($instance, '/api/notes/search', [ + 'query' => "@{$username}@{$acctDomain} {$statusId}", + 'limit' => 10 + ], $token); + + if ($renote_result['success'] && !empty($renote_result['data'])) { + // Parcourir les résultats pour trouver une correspondance + foreach ($renote_result['data'] as $note) { + if (isset($note['id'])) { + $renote_result['data'] = $note; + return $renote_result; + } + } + } + } + } + } + + // Si aucune méthode n'a fonctionné, retourner une erreur + return [ + 'success' => false, + 'message' => "Impossible de trouver la publication sur le réseau fédéré après plusieurs tentatives" + ]; } /** diff --git a/js/app.js b/js/app.js index a885213..f5ba176 100644 --- a/js/app.js +++ b/js/app.js @@ -366,7 +366,7 @@ document.addEventListener('DOMContentLoaded', function() { } // Nombre d'éléments à traiter dans ce lot - const batchSize = 5; + const batchSize = 2; // Réduit de 5 à 2 pour limiter les risques de timeout const endIndex = Math.min(currentIndex + batchSize, totalItems); // Préparer les éléments du lot @@ -376,73 +376,112 @@ document.addEventListener('DOMContentLoaded', function() { currentProgress.classList.add('active'); updateProgress(); - // Simuler le traitement (à remplacer par l'appel API réel) + // Ajouter une entrée dans le journal addLogEntry(`Traitement du lot ${currentIndex + 1} à ${endIndex}...`, 'info'); - // Envoyer la requête au serveur - fetch('process.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - batch: batch, - currentIndex: currentIndex, - totalItems: totalItems + // Compteur de tentatives pour cette requête + let retryAttempt = 0; + const maxRetries = 3; + + // Fonction pour envoyer la requête avec retry automatique + function sendRequest() { + fetch('process.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + batch: batch, + currentIndex: currentIndex, + totalItems: totalItems + }), + // Augmenter le timeout pour éviter les erreurs de limite de temps + timeout: 60000 }) - }) - .then(response => response.json()) - .then(data => { - if (data.success) { - // Traiter les résultats - if (data.results && data.results.length) { - data.results.forEach(result => { - addLogEntry(result.message, result.status); - - // Mettre à jour les compteurs - if (result.status === 'success') { - successCount++; - } else if (result.status === 'error') { - errorCount++; - } else if (result.status === 'info') { - skippedCount++; - } + .then(response => { + // Vérifier si la réponse est au format JSON + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + // Si ce n'est pas du JSON, récupérer le texte pour déboguer + return response.text().then(text => { + throw new Error(`Réponse non-JSON reçue: ${text.substring(0, 100)}${text.length > 100 ? '...' : ''}`); }); } + return response.json(); + }) + .then(data => { + if (data.success) { + // Traiter les résultats + if (data.results && data.results.length) { + data.results.forEach(result => { + addLogEntry(result.message, result.status); + + // Mettre à jour les compteurs + if (result.status === 'success') { + successCount++; + } else if (result.status === 'error') { + errorCount++; + } else if (result.status === 'info') { + skippedCount++; + } + }); + } + + // Mettre à jour l'index + currentIndex = endIndex; + + // Mettre à jour la progression + updateProgress(data.progress.percentage); + + // Mettre à jour les données de migration + updateMigrationData('in_progress', data.progress); + + // Traiter le lot suivant après un court délai + setTimeout(processBatch, 1000); + } else { + // Gérer l'erreur + addLogEntry('Erreur: ' + data.message, 'error'); + + // Pause en cas d'erreur + isPaused = true; + pauseMigration.textContent = 'Reprendre'; + currentProgress.classList.remove('active'); + + // Mettre à jour le statut de la migration + updateMigrationData('error'); + } + }) + .catch(error => { + // Afficher l'erreur détaillée dans la console pour le débogage + console.error('Erreur complète:', error); - // Mettre à jour l'index - currentIndex = endIndex; + // Ajouter l'erreur au journal + addLogEntry('Erreur de connexion: ' + error.message, 'error'); - // Mettre à jour la progression - updateProgress(data.progress.percentage); - - // Mettre à jour les données de migration - updateMigrationData('in_progress', data.progress); - - // Traiter le lot suivant après un court délai - setTimeout(processBatch, 1000); - } else { - // Gérer l'erreur - addLogEntry('Erreur: ' + data.message, 'error'); - - // Pause en cas d'erreur - isPaused = true; - pauseMigration.textContent = 'Reprendre'; - currentProgress.classList.remove('active'); - - // Mettre à jour le statut de la migration - updateMigrationData('error'); - } - }) - .catch(error => { - // Gérer l'erreur réseau - addLogEntry('Erreur de connexion: ' + error.message, 'error'); - - // Pause en cas d'erreur - isPaused = true; - pauseMigration.textContent = 'Reprendre'; - currentProgress.classList.remove('active'); - }); + // Retenter si nous n'avons pas atteint le nombre maximum de tentatives + if (retryAttempt < maxRetries) { + retryAttempt++; + const waitTime = Math.pow(2, retryAttempt) * 1000; // Backoff exponentiel + + addLogEntry(`Nouvelle tentative (${retryAttempt}/${maxRetries}) dans ${waitTime/1000} secondes...`, 'warning'); + + setTimeout(sendRequest, waitTime); + } else { + // Pause après plusieurs échecs + isPaused = true; + pauseMigration.textContent = 'Reprendre'; + currentProgress.classList.remove('active'); + + // Mettre à jour le statut de la migration + updateMigrationData('error'); + + addLogEntry(`Échec après ${maxRetries} tentatives. Veuillez reprendre manuellement.`, 'error'); + } + }); + } + + // Lancer la requête + sendRequest(); } /** diff --git a/process.php b/process.php index 70386b8..6118c23 100644 --- a/process.php +++ b/process.php @@ -6,119 +6,171 @@ // Définir la constante pour inclure les fichiers define('FAVMASTOKEY', true); -// Inclure les fichiers requis -require_once 'includes/config.php'; -require_once 'includes/functions.php'; +// Forcer les en-têtes JSON dès le début pour éviter tout conflit +header('Content-Type: application/json'); -// 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']); +// 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; -} +}); -// 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; -} +try { + // Inclure les fichiers requis + require_once 'includes/config.php'; + require_once 'includes/functions.php'; -// 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 = json_decode(file_get_contents('php://input'), true); -if (!$input || !isset($input['batch']) || !is_array($input['batch'])) { - http_response_code(400); - echo json_encode(['success' => false, 'message' => 'Données invalides']); - exit; -} - -// 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); - -// 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[] = [ - 'status' => 'error', - 'message' => "URL invalide: $url" - ]; - continue; + // 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); - // Rechercher la note sur Misskey à partir de l'URL - $searchResult = search_federated_note($misskey_instance, $url, $token); - - if ($searchResult['success'] && isset($searchResult['data']) && !empty($searchResult['data'])) { - // Note trouvée, récupérer son ID - $noteId = $searchResult['data']['id']; + 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; + } + + // 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); + + // 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); - // Tenter d'ajouter la note aux favoris - $favoriteResult = add_to_favorites($misskey_instance, $noteId, $token); - - if ($favoriteResult['success']) { + // Vérifier que l'URL est valide + if (!$urlParts || !isset($urlParts['host']) || !isset($urlParts['path'])) { $results[] = [ - 'status' => 'success', - 'message' => "Ajouté aux favoris: $url" + 'status' => 'error', + 'message' => "URL invalide: $url" ]; - } else { - // Vérifier si c'est une erreur de "déjà ajouté aux favoris" - $errorMessage = isset($favoriteResult['data']['error']['message']) - ? $favoriteResult['data']['error']['message'] - : $favoriteResult['message']; - - if (strpos($errorMessage, 'already') !== false) { - $results[] = [ - 'status' => 'info', - 'message' => "Déjà dans vos favoris: $url" - ]; + continue; + } + + // Ajouter un log pour déboguer + error_log("Recherche de l'URL: " . $url . " sur l'instance: " . $misskey_instance); + + // Rechercher la note sur Misskey à partir de l'URL + $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']; + + // Tenter d'ajouter la note aux favoris + $favoriteResult = add_to_favorites($misskey_instance, $noteId, $token); + + if ($favoriteResult['success']) { + $results[] = [ + 'status' => 'success', + 'message' => "Ajouté aux favoris: $url" + ]; + } 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'); + + if (strpos($errorMessage, 'already') !== false) { + $results[] = [ + 'status' => 'info', + 'message' => "Déjà dans vos favoris: $url" + ]; + } else { + $results[] = [ + 'status' => 'error', + 'message' => "Erreur lors de l'ajout aux favoris: $errorMessage" + ]; + } + } } else { + // L'ID est manquant ou vide dans la réponse + $dataKeys = is_array($searchResult['data']) ? array_keys($searchResult['data']) : ['non_array']; $results[] = [ 'status' => 'error', - 'message' => "Erreur lors de l'ajout aux favoris: $errorMessage" + 'message' => "Note trouvée mais sans ID valide: $url. Clés disponibles: " . implode(', ', $dataKeys) ]; + + // Log pour déboguer + error_log("Structure de données reçue: " . json_encode($searchResult['data'])); + } + } else { + // Note non trouvée ou erreur de recherche + $errorMessage = isset($searchResult['message']) ? $searchResult['message'] : "Publication introuvable"; + + $results[] = [ + 'status' => 'error', + 'message' => "Publication non trouvée sur le réseau fédéré: $url ($errorMessage)" + ]; + + // 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'])); } } - } else { - // Note non trouvée - $errorMessage = isset($searchResult['message']) ? $searchResult['message'] : "Publication introuvable"; - $results[] = [ - 'status' => 'error', - 'message' => "Publication non trouvée sur le réseau fédéré: $url ($errorMessage)" - ]; + // Pause pour éviter le rate limiting + usleep($config['delay_between_requests'] * 1000); } - - // Pause pour éviter le rate limiting - usleep($config['delay_between_requests'] * 1000); -} -// 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) - ] -]); \ No newline at end of file + // 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) + ] + ]); + +} 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() + ]); +} \ No newline at end of file diff --git a/test-url.php b/test-url.php deleted file mode 100644 index c881127..0000000 --- a/test-url.php +++ /dev/null @@ -1,393 +0,0 @@ -<?php -/** - * FavMasToKey - Test d'URL - * Ce script permet de tester manuellement une URL pour voir comment Misskey la traite - */ - -// Définir la constante pour inclure les fichiers -define('FAVMASTOKEY', true); - -// Inclure les fichiers requis -require_once 'includes/config.php'; -require_once 'includes/functions.php'; - -// Initialiser les variables -$url = ''; -$test_results = []; -$detailed_results = []; -$misskey_instance = isset($_SESSION['misskey_instance']) ? $_SESSION['misskey_instance'] : ''; -$token = isset($_SESSION['misskey_token']) ? $_SESSION['misskey_token'] : ''; -$connected = !empty($misskey_instance) && !empty($token); - -// Si non connecté, permettre de saisir les informations d'authentification -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'connect') { - if (isset($_POST['misskey_instance']) && isset($_POST['misskey_token'])) { - $misskey_instance = trim($_POST['misskey_instance']); - $token = trim($_POST['misskey_token']); - - // Valider le format de l'instance - $misskey_instance = preg_replace('/^https?:\/\//', '', $misskey_instance); - $misskey_instance = rtrim($misskey_instance, '/'); - - if (!empty($misskey_instance) && !empty($token)) { - // Vérifier la validité du token en effectuant une requête test - $validate_result = validate_misskey_token($misskey_instance, $token); - - if ($validate_result['success']) { - // Connecté avec succès - $connected = true; - - // Stockage temporaire pour cette session uniquement - $_SESSION['misskey_token_temp'] = $token; - $_SESSION['misskey_instance_temp'] = $misskey_instance; - - $success_message = "Connecté temporairement à {$misskey_instance} pour les tests."; - } else { - $error_message = "Impossible de se connecter à l'instance avec ce token."; - } - } else { - $error_message = "Veuillez renseigner à la fois l'instance et le token."; - } - } -} - -// Récupérer les valeurs temporaires si disponibles -if (!$connected && isset($_SESSION['misskey_token_temp']) && isset($_SESSION['misskey_instance_temp'])) { - $token = $_SESSION['misskey_token_temp']; - $misskey_instance = $_SESSION['misskey_instance_temp']; - $connected = true; -} - -// Traiter le test d'URL -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'test' && $connected) { - if (isset($_POST['url'])) { - $url = trim($_POST['url']); - - if (!empty($url)) { - // Méthode 1: search-by-url - $test_results[] = [ - 'method' => 'search-by-url', - 'endpoint' => '/api/notes/search-by-url', - 'result' => misskey_api_request($misskey_instance, '/api/notes/search-by-url', ['url' => $url], $token) - ]; - - // Méthode 2: ap/show - $test_results[] = [ - 'method' => 'ap/show', - 'endpoint' => '/api/ap/show', - 'result' => misskey_api_request($misskey_instance, '/api/ap/show', ['uri' => $url], $token) - ]; - - // Méthode 3: Extraction et recherche par ID distant - $urlParts = parse_url($url); - $pathParts = isset($urlParts['path']) ? explode('/', trim($urlParts['path'], '/')) : []; - - if (count($pathParts) >= 4 && $pathParts[count($pathParts) - 2] === 'statuses') { - $statusId = end($pathParts); - $username = $pathParts[count($pathParts) - 3]; - $acctDomain = isset($urlParts['host']) ? $urlParts['host'] : ''; - - if ($statusId && $username && $acctDomain) { - $remoteId = "https://{$acctDomain}/users/{$username}/statuses/{$statusId}"; - - $test_results[] = [ - 'method' => 'notes/show (uri)', - 'endpoint' => '/api/notes/show', - 'params' => ['uri' => $remoteId], - 'result' => misskey_api_request($misskey_instance, '/api/notes/show', ['uri' => $remoteId], $token) - ]; - } - } - - // Méthode 4: Forcer la fédération - if (curl_init()) { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Accept: application/activity+json' - ]); - curl_setopt($ch, CURLOPT_TIMEOUT, 5); - $response = curl_exec($ch); - $info = curl_getinfo($ch); - curl_close($ch); - - $detailed_results['direct_request'] = [ - 'url' => $url, - 'http_code' => $info['http_code'], - 'content_type' => $info['content_type'], - 'response_size' => strlen($response), - 'response_preview' => substr($response, 0, 500) . (strlen($response) > 500 ? '...' : '') - ]; - - // Attendre un moment pour que l'instance ait le temps de fédérer - sleep(2); - - // Réessayer avec search-by-url après avoir forcé la fédération - $test_results[] = [ - 'method' => 'search-by-url (après tentative de fédération)', - 'endpoint' => '/api/notes/search-by-url', - 'result' => misskey_api_request($misskey_instance, '/api/notes/search-by-url', ['url' => $url], $token) - ]; - } - - // Essayer la recherche par contenu - // Cette méthode est moins précise mais peut aider dans certains cas - if (curl_init()) { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HEADER, false); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - $html = curl_exec($ch); - curl_close($ch); - - // Extraction simple du contenu (très basique) - $content = ''; - if (preg_match('/<article[^>]*>(.*?)<\/article>/is', $html, $matches)) { - $content = strip_tags($matches[1]); - $content = preg_replace('/\s+/', ' ', $content); - $content = trim($content); - - if (strlen($content) > 5) { - $detailed_results['content_search'] = [ - 'extracted_content' => substr($content, 0, 200) . (strlen($content) > 200 ? '...' : ''), - 'content_length' => strlen($content) - ]; - - // Rechercher par les premiers mots du contenu - $searchWords = implode(' ', array_slice(explode(' ', $content), 0, 10)); - - $test_results[] = [ - 'method' => 'notes/search (par contenu)', - 'endpoint' => '/api/notes/search', - 'params' => ['query' => $searchWords, 'limit' => 5], - 'result' => misskey_api_request($misskey_instance, '/api/notes/search', ['query' => $searchWords, 'limit' => 5], $token) - ]; - } - } - } - } else { - $error_message = "Veuillez entrer une URL à tester."; - } - } -} -?> -<!DOCTYPE html> -<html lang="fr" data-bs-theme="dark"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <meta name="theme-color" content="#7e57c2"> - <link rel="icon" href="images/favicon.svg" type="image/svg+xml"> - <title>Test d'URL - FavMasToKey</title> - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"> - <link rel="stylesheet" href="css/styles.css"> -</head> -<body> - <div class="container py-5"> - <header class="text-center mb-4"> - <h1>FavMasToKey - Test d'URL</h1> - <p class="lead">Outil de diagnostic pour tester la résolution d'URL Mastodon</p> - <p><a href="index.php" class="btn btn-primary">Retour à l'application</a></p> - </header> - - <?php if (isset($error_message)): ?> - <div class="alert alert-danger alert-dismissible fade show" role="alert"> - <?php echo $error_message; ?> - <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fermer"></button> - </div> - <?php endif; ?> - - <?php if (isset($success_message)): ?> - <div class="alert alert-success alert-dismissible fade show" role="alert"> - <?php echo $success_message; ?> - <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fermer"></button> - </div> - <?php endif; ?> - - <?php if (!$connected): ?> - <div class="card mb-4"> - <div class="card-header"> - <h2 class="card-title h5 mb-0">Connexion à Misskey pour les tests</h2> - </div> - <div class="card-body"> - <div class="alert alert-info"> - <strong>Information:</strong> Vous devez vous connecter à votre instance Misskey pour tester les URLs. - <p class="mb-0">Cette connexion est temporaire et utilisée uniquement pour cette page de test.</p> - </div> - - <form method="post" action=""> - <input type="hidden" name="action" value="connect"> - <div class="mb-3"> - <label for="misskey-instance" class="form-label">Instance Misskey</label> - <input type="text" class="form-control" id="misskey-instance" name="misskey_instance" - placeholder="misskey.io" required> - <div class="form-text">Entrez le nom de domaine de votre instance Misskey (ex: misskey.io)</div> - </div> - <div class="mb-3"> - <label for="misskey-token" class="form-label">Jeton d'accès</label> - <input type="password" class="form-control" id="misskey-token" name="misskey_token" - placeholder="Votre jeton d'accès Misskey" required> - <div class="form-text">Collez le jeton d'accès généré dans les paramètres de votre compte Misskey</div> - </div> - <button type="submit" class="btn btn-primary">Se connecter pour les tests</button> - </form> - </div> - </div> - <?php else: ?> - <div class="card mb-4"> - <div class="card-header d-flex justify-content-between align-items-center"> - <h2 class="card-title h5 mb-0">Tester une URL</h2> - <span class="badge bg-success">Connecté à <?php echo htmlspecialchars($misskey_instance); ?></span> - </div> - <div class="card-body"> - <form method="post" action=""> - <input type="hidden" name="action" value="test"> - <div class="mb-3"> - <label for="url" class="form-label">URL d'une publication Mastodon</label> - <input type="url" class="form-control" id="url" name="url" - placeholder="https://instance.tld/users/username/statuses/123456" - value="<?php echo htmlspecialchars($url); ?>" required> - <div class="form-text"> - Entrez l'URL complète d'une publication Mastodon que vous souhaitez tester. - </div> - </div> - <button type="submit" class="btn btn-primary">Tester cette URL</button> - </form> - </div> - </div> - - <?php if (!empty($test_results)): ?> - <div class="card mb-4"> - <div class="card-header"> - <h2 class="card-title h5 mb-0">Résultats de la recherche pour <?php echo htmlspecialchars($url); ?></h2> - </div> - <div class="card-body"> - <div class="mb-4"> - <h3 class="h6">Structure de l'URL</h3> - <?php - $urlParts = parse_url($url); - $pathParts = isset($urlParts['path']) ? explode('/', trim($urlParts['path'], '/')) : []; - ?> - <pre class="bg-dark text-light p-3"><?php - echo "Schema: " . (isset($urlParts['scheme']) ? $urlParts['scheme'] : 'non défini') . "\n"; - echo "Host: " . (isset($urlParts['host']) ? $urlParts['host'] : 'non défini') . "\n"; - echo "Path: " . (isset($urlParts['path']) ? $urlParts['path'] : 'non défini') . "\n"; - - if (count($pathParts) >= 4 && $pathParts[count($pathParts) - 2] === 'statuses') { - $statusId = end($pathParts); - $username = $pathParts[count($pathParts) - 3]; - echo "\nFormat reconnu comme URL Mastodon: Oui\n"; - echo "Username: {$username}\n"; - echo "Status ID: {$statusId}\n"; - echo "ActivityPub URI: https://{$urlParts['host']}/users/{$username}/statuses/{$statusId}\n"; - } else { - echo "\nFormat reconnu comme URL Mastodon: Non\n"; - echo "Segments du chemin: " . implode(', ', $pathParts); - } - ?></pre> - </div> - - <?php foreach ($test_results as $index => $result): ?> - <div class="mb-4"> - <h3 class="h6 mb-2">Méthode <?php echo $index + 1; ?>: <?php echo htmlspecialchars($result['method']); ?></h3> - <div class="card bg-dark"> - <div class="card-header d-flex justify-content-between align-items-center"> - <span>Endpoint: <?php echo htmlspecialchars($result['endpoint']); ?></span> - <span class="badge <?php echo $result['result']['success'] ? 'bg-success' : 'bg-danger'; ?>"> - <?php echo $result['result']['success'] ? 'Succès' : 'Échec'; ?> - </span> - </div> - <div class="card-body"> - <?php if (isset($result['params'])): ?> - <h4 class="h6">Paramètres</h4> - <pre class="text-light"><?php print_r($result['params']); ?></pre> - <?php endif; ?> - - <h4 class="h6">Code HTTP: <?php echo $result['result']['http_code']; ?></h4> - - <?php if ($result['result']['success'] && isset($result['result']['data']) && !empty($result['result']['data'])): ?> - <h4 class="h6 text-success">Publication trouvée!</h4> - <pre class="text-light"><?php print_r($result['result']['data']); ?></pre> - <?php else: ?> - <h4 class="h6 text-danger">Publication non trouvée</h4> - <pre class="text-light"><?php - if (isset($result['result']['message'])) { - echo "Message: " . print_r($result['result']['message'], true); - } - if (isset($result['result']['data'])) { - echo "\nDonnées: " . print_r($result['result']['data'], true); - } - ?></pre> - <?php endif; ?> - </div> - </div> - </div> - <?php endforeach; ?> - - <?php if (!empty($detailed_results)): ?> - <div class="mb-4"> - <h3 class="h6">Détails supplémentaires</h3> - <pre class="bg-dark text-light p-3"><?php print_r($detailed_results); ?></pre> - </div> - <?php endif; ?> - - <div class="mt-4"> - <h3 class="h6">Recommandations</h3> - <ul> - <?php - $successFound = false; - $bestMethod = ''; - - foreach ($test_results as $result) { - if ($result['result']['success'] && isset($result['result']['data']) && !empty($result['result']['data'])) { - $successFound = true; - $bestMethod = $result['method']; - break; - } - } - - if ($successFound): - ?> - <li class="text-success"> - <strong>Bonne nouvelle!</strong> Publication trouvée avec la méthode "<?php echo htmlspecialchars($bestMethod); ?>". - <p class="mt-2">Vous devriez modifier la fonction <code>search_federated_note</code> pour utiliser cette méthode en priorité.</p> - <div class="alert alert-info"> - <p class="mb-1"><strong>Suggestion pour la fonction search_federated_note:</strong></p> - <pre class="text-dark mb-0"> -function search_federated_note($instance, $url, $token) { - // Pour cette publication spécifique, utiliser la méthode qui fonctionne - if ($url === "<?php echo htmlspecialchars($url); ?>") { - return misskey_api_request($instance, "<?php echo $result['endpoint']; ?>", - <?php echo isset($result['params']) - ? var_export($result['params'], true) - : "['url' => \$url]"; ?>, $token); - } - - // Pour les autres publications, essayer les différentes méthodes... - // [reste du code actuel] -}</pre> - </div> - </li> - <?php else: ?> - <li class="text-warning"> - <strong>Publication non trouvée</strong> avec aucune des méthodes testées. Voici quelques suggestions: - <ul class="mt-2"> - <li>Vérifiez que la publication existe toujours et est publique</li> - <li>Assurez-vous que votre instance Misskey peut se fédérer avec l'instance d'origine</li> - <li>Visitez manuellement l'URL depuis votre instance Misskey pour forcer la fédération</li> - <li>Si c'est un problème récurrent, ajustez les délais entre les requêtes dans config.php</li> - </ul> - </li> - <?php endif; ?> - </ul> - </div> - </div> - </div> - <?php endif; ?> - <?php endif; ?> - </div> - - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> -</body> -</html> \ No newline at end of file