This commit is contained in:
arys
2026-01-29 21:33:17 +05:00
parent 1eba543d7d
commit 19c7213ebb
2 changed files with 76 additions and 117 deletions
@@ -30,12 +30,9 @@ public class FacebookPostingService {
private static final Logger logger = LoggerFactory.getLogger(FacebookPostingService.class);
private static final String FACEBOOK_GRAPH_API_BASE = "https://graph.facebook.com/v19.0";
private static final String HARDCODED_PAGE_TOKEN = "EAAaocqgT3JoBQor7WTel65wL4q11uOZBvIkjbuZAU06mBmmEUJUP27ep6oKM3gSNkx6DYFg7NWQfXkKiI0ZByfVCMgqrsBTcshWK8OROp5l5kwZBOZCfuWhmRrxXeisu1WWh2dfadqoRGq8G83iFR82UqPhZCZANxH8kyDAlDLR5TIjZB0HE3y4rbawUzRflruzQoQfKhGUxSGWSQdLy5s3IGsBNmZClVjfdQu6XZA6QyCPZBsL";
private static final String HARDCODED_PAGE_TOKEN = "EAAaocqgT3JoBQjjUQwZB0jsJ5ZCLjZCgLbTY4sqL0BBvR3II5AeYrrYaZBrAXLoEYOJEYL0tj9kkeZCZA5q3ZAiq7GxdoECSTfQKzQlSlzZBN5wfzOQOyoWXNpPIobbcWZAkcEeIDSZAiqdQxjZAvpCm0RJtvMaIruzmnSZCCbQvD3q4L3w8xjBXGTraKZCZApKPvtPjo2yEpWXuxVkXZBxFAN2AHz7rZAxHGG42qFuZCwrY0GKpxJadDl8kyOSDYiFvOi0gUhkPAW7jO4Gx9o3KoZCiLWtVxU";
private static final String HARDCODED_PAGE_ID = "918835061309575";
private final WebClient webClient;
private final ObjectMapper objectMapper;
@@ -54,22 +51,12 @@ public class FacebookPostingService {
this.objectMapper = new ObjectMapper();
}
/**
* Этот метод теперь просто возвращает хардкодные данные.
* Мы НЕ делаем запрос к /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 {
logger.info(">>> MSG SENDING: Отправляем сообщение юзеру {} с хардкодным токеном...", recipientId);
Map<String, Object> recipient = new HashMap<>();
recipient.put("id", recipientId);
@@ -81,78 +68,80 @@ public class FacebookPostingService {
body.put("message", messageContent);
body.put("messaging_type", "RESPONSE");
String response = webClient.post()
.uri(uriBuilder -> uriBuilder
.path("/me/messages")
.queryParam("access_token", tokenToUse)
.build())
webClient.post()
.uri(uriBuilder -> uriBuilder.path("/me/messages")
.queryParam("access_token", HARDCODED_PAGE_TOKEN).build())
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(body)
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs));
logger.info("✅ SUCCESS: Сообщение отправлено! Response: {}", response);
} catch (WebClientResponseException e) {
logger.error("❌ MSG ERROR: Code={}, Body={}", e.getStatusCode(), e.getResponseBodyAsString());
logger.info("Message sent to user {}", recipientId);
} catch (Exception e) {
logger.error("❌ MSG ERROR: ", e);
logger.error("Failed to send text message: ", e);
}
}
// === ПУБЛИКАЦИЯ ТЕКСТА ===
public String postToPage(String ignoredToken, String postText, List<String> hashtags) throws JsonProcessingException {
String tokenToUse = HARDCODED_PAGE_TOKEN;
String pageIdToUse = HARDCODED_PAGE_ID;
public void sendPrivateImageMessage(String ignoredToken, String recipientId, byte[] imageData) {
try {
String fullPostText = buildPostText(postText, hashtags);
logger.info(">>> POSTING TEXT: Используем PageID={} Token={}...", pageIdToUse, tokenToUse.substring(0, 10));
String response = webClient.post()
.uri(uriBuilder -> uriBuilder
.path("/{pageId}/feed")
.queryParam("access_token", tokenToUse)
.queryParam("message", fullPostText)
.build(pageIdToUse))
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs));
JsonNode jsonNode = objectMapper.readTree(response);
String postId = jsonNode.get("id").asText();
logger.info("✅ SUCCESS: Текстовый пост опубликован. ID: {}", postId);
return postId;
} catch (WebClientResponseException e) {
handleFacebookError(e, "postToPage");
return null;
}
}
// === ПУБЛИКАЦИЯ КАРТИНКИ ===
public String postToPageWithImage(String ignoredToken, String postText, List<String> hashtags, byte[] imageData) {
String tokenToUse = HARDCODED_PAGE_TOKEN;
String pageIdToUse = HARDCODED_PAGE_ID;
try {
logger.info(">>> POSTING IMAGE: Используем PageID={} Token={}...", pageIdToUse, tokenToUse.substring(0, 10));
String fullPostText = buildPostText(postText, hashtags);
ByteArrayResource imageResource = new ByteArrayResource(imageData) {
@Override public String getFilename() { return "image.jpg"; }
};
String recipientJson = "{\"id\":\"" + recipientId + "\"}";
String messageJson = "{\"attachment\":{\"type\":\"image\", \"payload\":{\"is_reusable\":true}}}";
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("recipient", recipientJson);
formData.add("message", messageJson);
formData.add("filedata", imageResource);
formData.add("access_token", HARDCODED_PAGE_TOKEN);
webClient.post()
.uri(uriBuilder -> uriBuilder.path("/me/messages").build())
.contentType(MediaType.MULTIPART_FORM_DATA)
.body(BodyInserters.fromMultipartData(formData))
.retrieve()
.bodyToMono(String.class)
.block(Duration.ofMillis(timeoutMs * 2));
logger.info("Image message sent to user {}", recipientId);
} catch (Exception e) {
logger.error("Failed to send image message: ", e);
}
}
public String postToPage(String ignoredToken, String postText, List<String> hashtags) throws JsonProcessingException {
return internalPost(postText, hashtags, null);
}
public String postToPageWithImage(String ignoredToken, String postText, List<String> hashtags, byte[] imageData) {
return internalPost(postText, hashtags, imageData);
}
private String internalPost(String postText, List<String> hashtags, byte[] imageData) {
try {
String fullPostText = buildPostText(postText, hashtags);
MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
formData.add("access_token", HARDCODED_PAGE_TOKEN);
formData.add("message", fullPostText);
formData.add("source", imageResource);
formData.add("access_token", tokenToUse);
String endpoint;
if (imageData != null) {
endpoint = "/{pageId}/photos";
formData.add("source", new ByteArrayResource(imageData) {
@Override public String getFilename() { return "post.jpg"; }
});
} else {
endpoint = "/{pageId}/feed";
}
String response = webClient.post()
.uri(uriBuilder -> uriBuilder.path("/{pageId}/photos").build(pageIdToUse))
.contentType(MediaType.MULTIPART_FORM_DATA)
.uri(uriBuilder -> uriBuilder.path(endpoint).build(HARDCODED_PAGE_ID))
.contentType(imageData != null ? MediaType.MULTIPART_FORM_DATA : MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromMultipartData(formData))
.retrieve()
.bodyToMono(String.class)
@@ -160,14 +149,14 @@ public class FacebookPostingService {
JsonNode jsonNode = objectMapper.readTree(response);
String postId = jsonNode.get("id").asText();
logger.info("✅ SUCCESS: Фото опубликовано. ID: {}", postId);
logger.info("Published to Facebook. ID: {}", postId);
return postId;
} catch (WebClientResponseException e) {
handleFacebookError(e, "postToPageWithImage");
handleFacebookError(e, "internalPost");
return null;
} catch (Exception e) {
throw new RuntimeException("Failed to post image to Page: " + e.getMessage(), e);
throw new RuntimeException("Error posting: " + e.getMessage(), e);
}
}
@@ -183,44 +172,22 @@ public class FacebookPostingService {
return fullText.toString();
}
// Класс сделали публичным, чтобы он был доступен в других пакетах если что,
// но в данном случае он используется как DTO
public static class PageCredentials {
public String pageId;
public String pageToken;
public PageCredentials(String pageId, String pageToken) {
this.pageId = pageId;
this.pageToken = pageToken;
}
public String pageId; public String pageToken;
public PageCredentials(String pageId, String pageToken) { this.pageId = pageId; this.pageToken = pageToken; }
}
private void handleFacebookError(WebClientResponseException e, String context) {
String errorBody = e.getResponseBodyAsString();
logger.error("❌ FB API ERROR [{}]: Status={}, Body={}", context, e.getStatusCode(), errorBody);
logger.error("FB API Error [{}]: Body={}", context, e.getResponseBodyAsString());
if (isTokenExpiredError(e)) throw parseFacebookError(e);
throw new RuntimeException("Facebook API Error (" + context + "): " + errorBody, e);
throw new RuntimeException("Facebook API Error: " + e.getResponseBodyAsString(), e);
}
private boolean isTokenExpiredError(WebClientResponseException e) {
if (e.getStatusCode().value() != 400) return false;
try {
String body = e.getResponseBodyAsString();
if (body == null) return false;
JsonNode error = objectMapper.readTree(body).get("error");
if (error == null) return false;
int code = error.has("code") ? error.get("code").asInt() : 0;
int subcode = error.has("error_subcode") ? error.get("error_subcode").asInt() : 0;
return code == 190 && subcode == 463;
} catch (Exception ex) {
return false;
}
return e.getStatusCode().value() == 400 || e.getStatusCode().value() == 401;
}
private FacebookTokenExpiredException parseFacebookError(WebClientResponseException e) {
return new FacebookTokenExpiredException(
"Facebook token expired. Please re-login.",
"Token Expired", 190, 463, e);
return new FacebookTokenExpiredException("Token Expired", "Token Expired", 190, 463, e);
}
}
@@ -23,7 +23,6 @@ import java.util.Optional;
public class PostingTaskService {
private static final Logger logger = LoggerFactory.getLogger(PostingTaskService.class);
private static final String TEST_RECIPIENT_ID = "25769470256007187";
private final PostingTaskRepository taskRepository;
@@ -146,33 +145,26 @@ public class PostingTaskService {
if ("facebook".equalsIgnoreCase(task.getPlatform())) {
// 1. ПУБЛИКУЕМ ПОСТ НА СТРАНИЦУ
if (imageData != null && imageData.length > 0) {
postId = facebookPostingService.postToPageWithImage(
credentials,
task.getPostText(),
task.getHashtags(),
imageData);
postId = facebookPostingService.postToPageWithImage(credentials, task.getPostText(), task.getHashtags(), imageData);
} else {
postId = facebookPostingService.postToPage(
credentials,
task.getPostText(),
task.getHashtags());
postId = facebookPostingService.postToPage(credentials, task.getPostText(), task.getHashtags());
}
// 2. ОТПРАВЛЯЕМ ЛИЧНОЕ СООБЩЕНИЕ (ЕСЛИ ПОСТ УДАЛСЯ)
if (postId != null) {
logger.info("Пост успешно опубликован (ID: {}). Пытаемся отправить уведомление...", postId);
try {
String message = "Ваш пост опубликован на Странице!\nID поста: " + postId + "\nВремя: " + LocalDateTime.now();
String reportText = "НОВЫЙ ПОСТ ОПУБЛИКОВАН!\n\n" +
"📜 Текст поста:\n" + task.getPostText() + "\n\n" +
"🔗 Ссылка: https://facebook.com/" + postId + "\n" +
"🕒 Время: " + LocalDateTime.now();
// Отправляем сообщение тебе
facebookPostingService.sendPrivateMessage(credentials, TEST_RECIPIENT_ID, message);
facebookPostingService.sendPrivateMessage(credentials, TEST_RECIPIENT_ID, reportText);
logger.info(">>> SUCCESS: Уведомление отправлено пользователю {}", TEST_RECIPIENT_ID);
if (imageData != null && imageData.length > 0) {
facebookPostingService.sendPrivateImageMessage(credentials, TEST_RECIPIENT_ID, imageData);
}
} catch (Exception msgEx) {
logger.warn("Пост опубликован, но не удалось отправить сообщение в личку: {}", msgEx.getMessage());
logger.warn("Post published but failed to send private notification: {}", msgEx.getMessage());
}
}