«1. Обзор

Публикация на Reddit — это дерьмо. Один пост может преуспеть и привлечь много внимания, в то время как другой, может быть, лучший пост вообще не получит любви. Как насчет того, чтобы следить за этими сообщениями на раннем этапе и, если они не получают достаточного внимания, быстро удалять их и повторно отправлять.

В этой короткой статье мы продолжаем пример Reddit, реализуя интересную функцию — удаление и повторная отправка сообщения, если оно сразу же не привлекает достаточного внимания.

Простая цель состоит в том, чтобы позволить пользователю настроить количество голосов на Reddit, достаточное для того, чтобы считать, что пост получает достаточную поддержку, чтобы оставить его — в пределах определенного временного интервала.

2. Дополнительные разрешения Reddit

Во-первых, нам нужно запросить дополнительные разрешения у API Reddit, в частности, нам нужно редактировать сообщения.

Итак, мы добавим область «редактирования» в наш ресурс Reddit:

@Bean
public OAuth2ProtectedResourceDetails reddit() {
    AuthorizationCodeResourceDetails details = 
      new AuthorizationCodeResourceDetails();
    details.setScope(Arrays.asList("identity", "read", "submit", "edit"));
    ...
}

3. Сущность и репозиторий

Теперь добавим дополнительную информацию в нашу сущность Post: ~~ ~

@Entity
public class Post {
    ...
    private String redditID;
    private int noOfAttempts;
    private int timeInterval;
    private int minScoreRequired;
}

Поля:

    redditID: Идентификатор сообщения на Reddit, который будет использоваться при проверке оценки и при удалении сообщения. noOfAttempts: Максимальное количество попыток повторной отправки (удалить сообщение и отправить его повторно) пост набирает обороты minScoreRequired: минимальный балл, необходимый для того, чтобы считать его достаточно успешным, чтобы оставить его

Далее — давайте добавим несколько новых операций в наш интерфейс PostRepository — для того, чтобы легко извлекать сообщения, когда нам нужно их проверить. их:

public interface PostRepository extends JpaRepository<Post, Long> {

    List<Post> findBySubmissionDateBeforeAndIsSent(Date date, boolean sent);

    List<Post> findByUser(User user);

    List<Post> findByRedditIDNotNullAndNoOfAttemptsGreaterThan(int attempts);
}

4. Новая запланированная задача

Теперь — давайте определим новую задачу — задачу повторной отправки — в планировщик:

@Scheduled(fixedRate = 3 * 60 * 1000)
public void checkAndReSubmitPosts() {
    List<Post> submitted = 
      postReopsitory.findByRedditIDNotNullAndNoOfAttemptsGreaterThan(0);
    for (Post post : submitted) {
        checkAndReSubmit(post);
    }
}

Каждые несколько минут , это просто повторение постов, которые все еще находятся в игре, и, если они не получают достаточного внимания, он удаляет их и снова отправляет.

А вот и метод checkAndReSubmit():

private void checkAndReSubmit(Post post) {
    try {
        checkAndReSubmitInternal(post);
    } catch (final Exception e) {
        logger.error("Error occurred while check post " + post.toString(), e);
    }
}
private void checkAndReSubmitInternal(Post post) {
    if (didIntervalPassed(post.getSubmissionDate(), post.getTimeInterval())) {
        int score = getPostScore(post.getRedditID());
        if (score < post.getMinScoreRequired()) {
            deletePost(post.getRedditID());
            resetPost(post);
        } else {
            post.setNoOfAttempts(0);
            postReopsitory.save(post);
        }
    }
}
private boolean didIntervalPassed(Date submissonDate, int postInterval) {
    long currentTime = new Date().getTime();
    long interval = currentTime - submissonDate.getTime();
    long intervalInMinutes = TimeUnit.MINUTES.convert(interval, TimeUnit.MILLISECONDS);
    return intervalInMinutes > postInterval;
}
private void resetPost(Post post) {
    long time = new Date().getTime();
    time += TimeUnit.MILLISECONDS.convert(post.getTimeInterval(), TimeUnit.MINUTES);
    post.setRedditID(null);
    post.setSubmissionDate(new Date(time));
    post.setSent(false);
    post.setSubmissionResponse("Not sent yet");
    postReopsitory.save(post);
}

Нам также нужно отслеживать идентификатор redditID при первой отправке его в Reddit:

private void submitPostInternal(Post post) {
    ...
    JsonNode node = redditRestTemplate.postForObject(
      "https://oauth.reddit.com/api/submit", param, JsonNode.class);
    JsonNode errorNode = node.get("json").get("errors").get(0);
    if (errorNode == null) {
        post.setRedditID(node.get("json").get("data").get("id").asText());
        post.setNoOfAttempts(post.getNoOfAttempts() - 1);
        ...
}

Логика здесь также довольно проста — «Мы просто сохраняем идентификатор и уменьшаем количество попыток.

5. Получите оценку поста Reddit

Теперь давайте посмотрим, как мы можем получить текущую оценку публикации от Reddit:

private int getPostScore(String redditId) {
    JsonNode node = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/api/info?id=t3_" + redditId, JsonNode.class);
    int score = node.get("data").get("children").get(0).get("data").get("score").asInt();
    return score;
}

Обратите внимание, что:

    Нам нужно «прочитано». область действия при получении информации о сообщении из Reddit Мы добавляем «t3_» к идентификатору Reddit, чтобы получить полное имя сообщения

6. Удаляем сообщение Reddit

Далее — давайте посмотрим, как удалить сообщение Reddit, используя его идентификатор :

private void deletePost(String redditId) {
    MultiValueMap<String, String> param = new LinkedMultiValueMap<String, String>();
    param.add("id", "t3_" + redditId);
    redditRestTemplate.postForObject(
      "https://oauth.reddit.com/api/del.json", param, JsonNode.class);
}

7. RedditController

Теперь — давайте добавим новую информацию в контроллер:

@RequestMapping(value = "/schedule", method = RequestMethod.POST)
public String schedule(Model model, 
  @RequestParam Map<String, String> formParams) throws ParseException {
    Post post = new Post();
    post.setTitle(formParams.get("title"));
    post.setSubreddit(formParams.get("sr"));
    post.setUrl(formParams.get("url"));
    post.setNoOfAttempts(Integer.parseInt(formParams.get("attempt")));
    post.setTimeInterval(Integer.parseInt(formParams.get("interval")));
    post.setMinScoreRequired(Integer.parseInt(formParams.get("score")));
    ....
}

8. Пользовательский интерфейс — настройка правил

Наконец — давайте изменим нашу очень простую форму расписания, чтобы добавить повторную отправку новых настроек:

<label class="col-sm-3">Resubmit Settings</label>

<label>Number of Attempts</label> 
<select name="attempt">
    <option value="0" selected>None</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
</select>

<label>Time interval</label>
<select name="interval">
    <option value="0" selected >None</option>
    <option value="45">45 minutes</option>
    <option value="60">1 hour</option>
    <option value="120">2 hours</option>
</select>

<label>Min score</label>
<input type="number"value="0" name="score" required/>

9. Заключение

Мы продолжаем улучшать возможности этого простого приложения — теперь мы можем публиковать сообщения в Reddit и — «Если публикация не набирает достаточной популярности быстро, мы можем попросить систему удалить ее и опубликовать повторно, чтобы увеличить шансы на успех.