<?php /** * FavMasToKey - Traitement des favoris (version multi-tokens) */ // 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; } // 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['url'])) { 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 l'instance Misskey (devrait être la même pour tous les tokens) $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 l'URL à traiter $url = $input['url']; // Récupérer l'ID du token à utiliser $token_id = isset($input['token_id']) ? $input['token_id'] : 'primary'; // Récupérer le token d'accès selon l'ID fourni if ($token_id === 'primary') { // Utiliser le token principal if (!isset($_SESSION['misskey_token']) || empty($_SESSION['misskey_token'])) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'Token principal non disponible']); exit; } $token = $_SESSION['misskey_token']; } else { // Utiliser un token supplémentaire if (!isset($_SESSION['additional_tokens']) || !isset($_SESSION['additional_tokens'][$token_id])) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'Token supplémentaire non trouvé: ' . $token_id]); exit; } $token = $_SESSION['additional_tokens'][$token_id]['token']; // Mettre à jour les statistiques d'utilisation du token $_SESSION['additional_tokens'][$token_id]['last_used'] = time(); $_SESSION['additional_tokens'][$token_id]['usage_count'] = (isset($_SESSION['additional_tokens'][$token_id]['usage_count']) ? $_SESSION['additional_tokens'][$token_id]['usage_count'] : 0) + 1; } // Journaliser le début du traitement error_log("Traitement de l'URL: " . $url . " avec token ID: " . $token_id); // Traitement de l'URL // 1. Vérifier si nous avons un ID en cache pour cette URL $cachedMisskeyId = null; $federatedCache = get_federated_cache(); if (isset($federatedCache[$url])) { $cachedMisskeyId = $federatedCache[$url]['id']; error_log("ID trouvé en cache pour $url: $cachedMisskeyId"); } // 2. Recherche de la note sur le réseau fédéré $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"); } if ($searchNeeded) { $searchResult = search_federated_note($misskey_instance, $url, $token); if ($searchResult['success'] && isset($searchResult['data']) && !empty($searchResult['data'])) { if (isset($searchResult['data']['id']) && !empty($searchResult['data']['id'])) { $noteId = $searchResult['data']['id']; // Sauvegarder dans le cache update_federated_cache($url, $noteId); error_log("Note trouvée et mise en cache: $url -> $noteId"); } else { // L'ID est manquant dans la réponse $dataKeys = is_array($searchResult['data']) ? array_keys($searchResult['data']) : ['non_array']; echo json_encode([ 'success' => true, 'result' => [ 'status' => 'error', 'message' => "Note trouvée mais sans ID valide: $url", 'error_type' => 'invalid_response', 'details' => "Clés disponibles: " . implode(', ', $dataKeys) ] ]); exit; } } 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'; $errorMessage = "Limite d'API atteinte. Ce jeton doit se reposer."; } 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 avec ce jeton."; } elseif ($httpCode == 0) { $errorType = 'network_error'; $errorMessage = "Problème de connexion réseau. Vérifiez votre connectivité."; } echo json_encode([ 'success' => true, 'result' => [ '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" : "" ] ]); exit; } } // 3. Vérifier si la note est déjà favorite $favoriteCheckResult = check_if_favorited($misskey_instance, $noteId, $token); if ($favoriteCheckResult['success'] && $favoriteCheckResult['is_favorited']) { // La note est déjà dans les favoris echo json_encode([ 'success' => true, 'result' => [ 'status' => 'info', 'message' => "Déjà dans vos favoris: $url", 'error_type' => 'already_favorited', 'misskey_id' => $noteId ] ]); exit; } // 4. Ajouter la note aux favoris $favoriteResult = add_to_favorites($misskey_instance, $noteId, $token); if ($favoriteResult['success']) { echo json_encode([ 'success' => true, 'result' => [ 'status' => 'success', 'message' => "Ajouté aux favoris: $url", 'misskey_id' => $noteId ] ]); } else { // Déterminer le type d'erreur $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'; echo json_encode([ 'success' => true, 'result' => [ 'status' => 'info', 'message' => "Déjà dans vos favoris: $url", 'error_type' => $errorType, 'misskey_id' => $noteId ] ]); exit; } elseif (strpos($errorMessage, 'rate limit') !== false || strpos($errorMessage, 'limit exceeded') !== false || $errorCode == 'RATE_LIMIT_EXCEEDED') { $errorType = 'rate_limit'; $message = "Limite d'API atteinte pour: $url. Ce jeton doit se reposer."; echo json_encode([ 'success' => true, 'result' => [ 'status' => 'warning', 'message' => $message, 'error_type' => $errorType, 'details' => $errorMessage, 'misskey_id' => $noteId ] ]); exit; } elseif (strpos($errorMessage, 'permission') !== false || $errorCode == 'NO_PERMISSION') { $errorType = 'permission_denied'; } echo json_encode([ 'success' => true, 'result' => [ 'status' => 'error', 'message' => "Erreur lors de l'ajout aux favoris: $errorMessage", 'error_type' => $errorType, 'details' => $errorCode ? "Code: $errorCode" : "", 'misskey_id' => $noteId ] ]); } } 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() ]); } /** * Récupère le cache de fédération depuis la session * * @return array Cache de fédération */ function get_federated_cache() { if (!isset($_SESSION['federated_cache'])) { $_SESSION['federated_cache'] = []; } return $_SESSION['federated_cache']; } /** * Met à jour le cache de fédération dans la session * * @param string $url URL de la publication Mastodon * @param string $misskey_id ID de la publication sur Misskey */ function update_federated_cache($url, $misskey_id) { if (!isset($_SESSION['federated_cache'])) { $_SESSION['federated_cache'] = []; } $_SESSION['federated_cache'][$url] = [ 'id' => $misskey_id, 'timestamp' => time() ]; // Nettoyer le cache si nécessaire (plus de 1000 entrées) if (count($_SESSION['federated_cache']) > 1000) { // Trier par timestamp et ne garder que les 500 plus récentes uasort($_SESSION['federated_cache'], function($a, $b) { return $b['timestamp'] - $a['timestamp']; }); $_SESSION['federated_cache'] = array_slice($_SESSION['federated_cache'], 0, 500, true); } }