From 4d50cd3da5c8b6c015fdf5c52e5c2fb8640c4f8c Mon Sep 17 00:00:00 2001 From: root Date: Sun, 7 Dec 2025 17:40:45 +0500 Subject: [PATCH] . --- .../service/FacebookPostingService.java | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/src/main/java/kz/konturai/parser/service/FacebookPostingService.java b/src/main/java/kz/konturai/parser/service/FacebookPostingService.java index 3e0c3b5..5e872b1 100644 --- a/src/main/java/kz/konturai/parser/service/FacebookPostingService.java +++ b/src/main/java/kz/konturai/parser/service/FacebookPostingService.java @@ -98,61 +98,59 @@ public class FacebookPostingService { } /** - * Получает Page Access Token и Page ID из User Access Token - * Вызывает /me/accounts для получения списка страниц, которыми управляет - * пользователь + * Retrieves Page credentials (ID and Token). + * Handles two scenarios: + * 1. The input is a User Token: Calls /me/accounts to find the Page and its + * Page Token. + * 2. The input is ALREADY a Page Token: Detects the error, fetches the Page ID + * directly, and uses the input token. * - * @param userAccessToken User Access Token + * @param accessToken User Access Token or Page Access Token * @return PageCredentials с Page ID и Page Access Token * @throws FacebookTokenExpiredException если токен истек * @throws RuntimeException если не удалось получить credentials * страницы */ - private PageCredentials getPageCredentials(String userAccessToken) { + private PageCredentials getPageCredentials(String accessToken) { try { + // SCENARIO 1: Try to treat it as a User Token and find pages String response = webClient.get() .uri(uriBuilder -> uriBuilder .path("/me/accounts") - .queryParam("access_token", userAccessToken) + .queryParam("access_token", accessToken) .build()) .retrieve() .bodyToMono(String.class) .block(Duration.ofMillis(timeoutMs)); - JsonNode jsonNode = objectMapper.readTree(response); + JsonNode root = objectMapper.readTree(response); + JsonNode data = root.get("data"); - // Проверяем наличие массива data - if (!jsonNode.has("data") || !jsonNode.get("data").isArray()) { - logger.error("Facebook API response does not contain 'data' array"); - throw new RuntimeException("Failed to get page credentials: no pages found in response"); + if (data.isEmpty()) { + throw new RuntimeException("User has no pages available. Please check permissions (pages_show_list)."); } - JsonNode dataArray = jsonNode.get("data"); - if (dataArray.size() == 0) { - logger.error("User has no pages available"); - throw new RuntimeException("Failed to get page credentials: user has no pages"); - } - - // Берем первую страницу из списка - JsonNode firstPage = dataArray.get(0); - String pageId = firstPage.has("id") ? firstPage.get("id").asText() : null; - String pageToken = firstPage.has("access_token") ? firstPage.get("access_token").asText() : null; - - if (pageId == null || pageToken == null) { - logger.error("Page credentials incomplete: pageId={}, pageToken={}", pageId, - pageToken != null ? "present" : "null"); - throw new RuntimeException("Failed to get page credentials: incomplete page data"); - } - - logger.info("Successfully retrieved page credentials for page ID: {}", pageId); - return new PageCredentials(pageId, pageToken); + // Return the first page found + JsonNode firstPage = data.get(0); + return new PageCredentials( + firstPage.get("id").asText(), + firstPage.get("access_token").asText()); } catch (WebClientResponseException e) { + // SCENARIO 2: Check if the token is already a Page Token + // Error code 100 with message containing "node type (Page)" means we are + // already a Page if (isTokenExpiredError(e)) { throw parseFacebookError(e); } - logger.error("Failed to get Facebook page credentials: {} - {}", e.getStatusCode(), - e.getResponseBodyAsString()); + + String responseBody = e.getResponseBodyAsString(); + if (e.getStatusCode().value() == 400 && responseBody != null && responseBody.contains("Page")) { + logger.info("Provided token is already a Page Access Token. Skipping exchange."); + return getPageDetailsDirectly(accessToken); + } + + logger.error("Failed to get Facebook page credentials: {} - {}", e.getStatusCode(), responseBody); throw new RuntimeException("Failed to get page credentials: " + e.getMessage(), e); } catch (JsonProcessingException e) { logger.error("Failed to parse Facebook page credentials response", e); @@ -160,8 +158,43 @@ public class FacebookPostingService { } catch (FacebookTokenExpiredException e) { throw e; } catch (Exception e) { - logger.error("Unexpected error getting page credentials", e); - throw new RuntimeException("Failed to get page credentials", e); + logger.error("Failed to get page credentials", e); + throw new RuntimeException("Failed to get page credentials: " + e.getMessage(), e); + } + } + + /** + * Helper method: When we already have a Page Token, we just need the Page ID. + */ + private PageCredentials getPageDetailsDirectly(String pageAccessToken) { + try { + String response = webClient.get() + .uri(uriBuilder -> uriBuilder + .path("/me") + .queryParam("access_token", pageAccessToken) + .queryParam("fields", "id") + .build()) + .retrieve() + .bodyToMono(String.class) + .block(Duration.ofMillis(timeoutMs)); + + JsonNode root = objectMapper.readTree(response); + String pageId = root.get("id").asText(); + + // Return the ID we found, and reuse the token we already have + return new PageCredentials(pageId, pageAccessToken); + } catch (WebClientResponseException e) { + if (isTokenExpiredError(e)) { + throw parseFacebookError(e); + } + logger.error("Failed to verify Page Access Token: {} - {}", e.getStatusCode(), e.getResponseBodyAsString()); + throw new RuntimeException("Failed to verify Page Access Token: " + e.getMessage(), e); + } catch (JsonProcessingException e) { + logger.error("Failed to parse Page ID response", e); + throw new RuntimeException("Failed to verify Page Access Token: " + e.getMessage(), e); + } catch (Exception e) { + logger.error("Failed to verify Page Access Token", e); + throw new RuntimeException("Failed to verify Page Access Token: " + e.getMessage(), e); } }