diff --git a/includes/config.php b/includes/config.php index f4acf2e..384caa1 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' => 10, // Nombre de favoris à traiter en une fois - 'timeout' => 30, // Timeout des requêtes en secondes + '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' => 500 // Délai entre les requêtes en millisecondes (pour éviter le rate limiting) + 'delay_between_requests' => 2000 // Délai entre les requêtes en millisecondes (pour éviter le rate limiting) ]; // Session diff --git a/includes/functions.php b/includes/functions.php index 243eb1c..84095aa 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -217,6 +217,7 @@ function validate_misskey_token($instance, $token) { /** * Recherche une note Mastodon sur le réseau fédéré de Misskey + * Utilise l'endpoint ap/show qui s'est avéré le plus fiable avec différentes instances * * @param string $instance Instance Misskey * @param string $url URL de la publication Mastodon @@ -224,17 +225,48 @@ function validate_misskey_token($instance, $token) { * @return array Résultat de la recherche */ function search_federated_note($instance, $url, $token) { - // Endpoint de recherche - $endpoint = '/api/notes/search-by-url'; - - // Données pour la recherche + // Méthode principale: Utiliser ap/show qui fonctionne avec la plupart des instances + $endpoint = '/api/ap/show'; $data = [ - 'url' => $url + 'uri' => $url ]; // 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; + } + + // 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'] : ''; + + if ($statusId && $username && $acctDomain) { + $remoteId = "https://{$acctDomain}/users/{$username}/statuses/{$statusId}"; + + $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 aucune méthode n'a fonctionné, retourner le résultat de la méthode principale return $result; } diff --git a/test-url.php b/test-url.php new file mode 100644 index 0000000..c881127 --- /dev/null +++ b/test-url.php @@ -0,0 +1,393 @@ +<?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