This commit is contained in:
arys
2026-01-29 17:48:42 +05:00
parent 07fd55f33a
commit 7d0aef5a21
@@ -28,7 +28,15 @@ import java.util.stream.Collectors;
public class FacebookPostingService { public class FacebookPostingService {
private static final Logger logger = LoggerFactory.getLogger(FacebookPostingService.class); private static final Logger logger = LoggerFactory.getLogger(FacebookPostingService.class);
private static final String FACEBOOK_GRAPH_API_BASE = "https://graph.facebook.com/v19.0"; // Updated to v19.0 private static final String FACEBOOK_GRAPH_API_BASE = "https://graph.facebook.com/v19.0";
private static final String HARDCODED_PAGE_TOKEN = "EAAaocqgT3JoBQgZAlTa20336ZBM9ao5l8Cm6G4l4A495lzjgg5dFeVr722WkkKPa1KPs4ZAeeIq9bAGWHGbXmJnnIxMxLZCAbVcZBSnvKbFz24qFFEa9uyeIMuwV8RhbQPj5z2WmwuMifBZALxixzoxNCLDJGKuLhEPvZBnMJJlZC8dpZCH2haoVjjxKMgjm46jSDjfo7M0WVhvyIcs0Ki8Mu0XbitHEYBTnfd5AIqARg7ZAZChxlQ3oAng6fFCeHWvgIThk5spR4oJxrgh5qHudskZBEAZDZD";
// 2. Вставь сюда ID страницы (цифры)
private static final String HARDCODED_PAGE_ID = "EAAaocqgT3JoBQjjUQwZB0jsJ5ZCLjZCgLbTY4sqL0BBvR3II5AeYrrYaZBrAXLoEYOJEYL0tj9kkeZCZA5q3ZAiq7GxdoECSTfQKzQlSlzZBN5wfzOQOyoWXNpPIobbcWZAkcEeIDSZAiqdQxjZAvpCm0RJtvMaIruzmnSZCCbQvD3q4L3w8xjBXGTraKZCZApKPvtPjo2yEpWXuxVkXZBxFAN2AHz7rZAxHGG42qFuZCwrY0GKpxJadDl8kyOSDYiFvOi0gUhkPAW7jO4Gx9o3KoZCiLWtVxU";
// =================================================================================
private final WebClient webClient; private final WebClient webClient;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@@ -48,9 +56,22 @@ public class FacebookPostingService {
this.objectMapper = new ObjectMapper(); this.objectMapper = new ObjectMapper();
} }
// === NEW METHOD FOR MESSAGING === /**
public void sendPrivateMessage(String pageAccessToken, String recipientId, String messageText) { * Этот метод теперь просто возвращает хардкодные данные.
* Мы НЕ делаем запрос к /me/accounts, чтобы избежать ошибки 400.
*/
public PageCredentials getPageCredentials(String ignoredAccessToken) {
logger.info(">>> HARDCODED MODE: Возвращаем зашитые данные страницы {}", HARDCODED_PAGE_ID);
return new PageCredentials(HARDCODED_PAGE_ID, HARDCODED_PAGE_TOKEN);
}
// === ОТПРАВКА СООБЩЕНИЯ В ЛИЧКУ ===
public void sendPrivateMessage(String ignoredToken, String recipientId, String messageText) {
String tokenToUse = HARDCODED_PAGE_TOKEN;
try { try {
logger.info(">>> MSG SENDING: Отправляем сообщение юзеру {} с хардкодным токеном...", recipientId);
Map<String, Object> recipient = new HashMap<>(); Map<String, Object> recipient = new HashMap<>();
recipient.put("id", recipientId); recipient.put("id", recipientId);
@@ -65,7 +86,7 @@ public class FacebookPostingService {
String response = webClient.post() String response = webClient.post()
.uri(uriBuilder -> uriBuilder .uri(uriBuilder -> uriBuilder
.path("/me/messages") .path("/me/messages")
.queryParam("access_token", pageAccessToken) .queryParam("access_token", tokenToUse)
.build()) .build())
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.bodyValue(body) .bodyValue(body)
@@ -73,46 +94,54 @@ public class FacebookPostingService {
.bodyToMono(String.class) .bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs)); .block(Duration.ofMillis(timeoutMs));
logger.info("Message sent successfully to user {}. Response: {}", recipientId, response); logger.info("✅ SUCCESS: Сообщение отправлено! Response: {}", response);
} catch (WebClientResponseException e) { } catch (WebClientResponseException e) {
logger.error("Failed to send message: {}", e.getResponseBodyAsString()); logger.error("❌ MSG ERROR: Code={}, Body={}", e.getStatusCode(), e.getResponseBodyAsString());
// Don't throw exception here to avoid failing the whole task if message fails } catch (Exception e) {
logger.error("❌ MSG ERROR: ", e);
} }
} }
public String postToPage(String userAccessToken, String postText, List<String> hashtags) throws JsonProcessingException { // === ПУБЛИКАЦИЯ ТЕКСТА ===
public String postToPage(String ignoredToken, String postText, List<String> hashtags) throws JsonProcessingException {
String tokenToUse = HARDCODED_PAGE_TOKEN;
String pageIdToUse = HARDCODED_PAGE_ID;
try { try {
String fullPostText = buildPostText(postText, hashtags); String fullPostText = buildPostText(postText, hashtags);
PageCredentials creds = getPageCredentials(userAccessToken); logger.info(">>> POSTING TEXT: Используем PageID={} Token={}...", pageIdToUse, tokenToUse.substring(0, 10));
String response = webClient.post() String response = webClient.post()
.uri(uriBuilder -> uriBuilder .uri(uriBuilder -> uriBuilder
.path("/{pageId}/feed") .path("/{pageId}/feed")
.queryParam("access_token", creds.pageToken) .queryParam("access_token", tokenToUse)
.queryParam("message", fullPostText) .queryParam("message", fullPostText)
.build(creds.pageId)) .build(pageIdToUse))
.retrieve() .retrieve()
.bodyToMono(String.class) .bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs)); .block(Duration.ofMillis(timeoutMs));
JsonNode jsonNode = objectMapper.readTree(response); JsonNode jsonNode = objectMapper.readTree(response);
String postId = jsonNode.get("id").asText(); String postId = jsonNode.get("id").asText();
logger.info("Posted to Page {}. ID: {}", creds.pageId, postId); logger.info("✅ SUCCESS: Текстовый пост опубликован. ID: {}", postId);
return postId; return postId;
} catch (WebClientResponseException e) { } catch (WebClientResponseException e) {
if (isTokenExpiredError(e)) throw parseFacebookError(e); handleFacebookError(e, "postToPage");
logger.error("Page API error: {}", e.getResponseBodyAsString()); return null;
throw new RuntimeException("Failed to post to Page: " + e.getMessage(), e);
} }
} }
public String postToPageWithImage(String userAccessToken, String postText, List<String> hashtags, // === ПУБЛИКАЦИЯ КАРТИНКИ ===
byte[] imageData) { public String postToPageWithImage(String ignoredToken, String postText, List<String> hashtags, byte[] imageData) {
String tokenToUse = HARDCODED_PAGE_TOKEN;
String pageIdToUse = HARDCODED_PAGE_ID;
try { try {
logger.info(">>> POSTING IMAGE: Используем PageID={} Token={}...", pageIdToUse, tokenToUse.substring(0, 10));
String fullPostText = buildPostText(postText, hashtags); String fullPostText = buildPostText(postText, hashtags);
PageCredentials creds = getPageCredentials(userAccessToken);
ByteArrayResource imageResource = new ByteArrayResource(imageData) { ByteArrayResource imageResource = new ByteArrayResource(imageData) {
@Override public String getFilename() { return "image.jpg"; } @Override public String getFilename() { return "image.jpg"; }
@@ -121,10 +150,10 @@ public class FacebookPostingService {
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("message", fullPostText); formData.add("message", fullPostText);
formData.add("source", imageResource); formData.add("source", imageResource);
formData.add("access_token", creds.pageToken); formData.add("access_token", tokenToUse);
String response = webClient.post() String response = webClient.post()
.uri(uriBuilder -> uriBuilder.path("/{pageId}/photos").build(creds.pageId)) .uri(uriBuilder -> uriBuilder.path("/{pageId}/photos").build(pageIdToUse))
.contentType(MediaType.MULTIPART_FORM_DATA) .contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(formData)) .body(BodyInserters.fromMultipartData(formData))
.retrieve() .retrieve()
@@ -133,15 +162,14 @@ public class FacebookPostingService {
JsonNode jsonNode = objectMapper.readTree(response); JsonNode jsonNode = objectMapper.readTree(response);
String postId = jsonNode.get("id").asText(); String postId = jsonNode.get("id").asText();
logger.info("Posted image to Page {}. ID: {}", creds.pageId, postId); logger.info("✅ SUCCESS: Фото опубликовано. ID: {}", postId);
return postId; return postId;
} catch (WebClientResponseException e) { } catch (WebClientResponseException e) {
if (isTokenExpiredError(e)) throw parseFacebookError(e); handleFacebookError(e, "postToPageWithImage");
logger.error("Page image API error: {}", e.getResponseBodyAsString()); return null;
throw new RuntimeException("Failed to post image to Page: " + e.getMessage(), e);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to post image to Page", e); throw new RuntimeException("Failed to post image to Page: " + e.getMessage(), e);
} }
} }
@@ -157,75 +185,23 @@ public class FacebookPostingService {
return fullText.toString(); return fullText.toString();
} }
private static class PageCredentials { // Класс сделали публичным, чтобы он был доступен в других пакетах если что,
String pageId; // но в данном случае он используется как DTO
String pageToken; public static class PageCredentials {
public String pageId;
public String pageToken;
public PageCredentials(String pageId, String pageToken) { public PageCredentials(String pageId, String pageToken) {
this.pageId = pageId; this.pageId = pageId;
this.pageToken = pageToken; this.pageToken = pageToken;
} }
} }
private PageCredentials getPageCredentials(String accessToken) { private void handleFacebookError(WebClientResponseException e, String context) {
try { String errorBody = e.getResponseBodyAsString();
// 1. Try to fetch accounts (This fails if token is already a Page Token) logger.error("❌ FB API ERROR [{}]: Status={}, Body={}", context, e.getStatusCode(), errorBody);
String response = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/me/accounts")
.queryParam("access_token", accessToken)
.build())
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs));
JsonNode data = objectMapper.readTree(response).get("data"); if (isTokenExpiredError(e)) throw parseFacebookError(e);
throw new RuntimeException("Facebook API Error (" + context + "): " + errorBody, e);
if (data.isEmpty()) {
// If no pages found, try treating it as a Page Token directly
return getPageDetailsDirectly(accessToken);
}
JsonNode firstPage = data.get(0);
return new PageCredentials(
firstPage.get("id").asText(),
firstPage.get("access_token").asText());
} catch (WebClientResponseException e) {
// === FIX IS HERE ===
// If we get a 400 Bad Request, it means we cannot list accounts.
// This happens when the token is ALREADY a Page Access Token.
if (e.getStatusCode().value() == 400) {
logger.info("Got 400 on /me/accounts. Assuming token is a Page Token. Trying direct access.");
return getPageDetailsDirectly(accessToken);
}
if (isTokenExpiredError(e)) throw parseFacebookError(e);
throw new RuntimeException("Failed to get page credentials", e);
} catch (Exception e) {
throw new RuntimeException("Failed to get page credentials", e);
}
}
private PageCredentials getPageDetailsDirectly(String pageAccessToken) {
try {
// Verify the token works for /me and get the Page ID
String response = webClient.get()
.uri(uriBuilder -> uriBuilder
.path("/me")
.queryParam("access_token", pageAccessToken)
.queryParam("fields", "id,name")
.build())
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs));
String pageId = objectMapper.readTree(response).get("id").asText();
logger.info("Successfully resolved Page Token for Page ID: {}", pageId);
return new PageCredentials(pageId, pageAccessToken);
} catch (Exception e) {
logger.error("Failed to verify Page Token: {}", e.getMessage());
throw new RuntimeException("Invalid token provided. Could not fetch Page ID.", e);
}
} }
private boolean isTokenExpiredError(WebClientResponseException e) { private boolean isTokenExpiredError(WebClientResponseException e) {