-
Naghahanap sa lahat ng mga grupo na nasa aming database ng mga bagong artikulo na na-publish pagkatapos ng nakaraang pagpapatupad.
Tinutukoy ng scheme na ito ang isang mas maliit na bilang ng mga pangkat - ang mga may aktibong user lamang. Sa oras na iyon, tila lohikal ito sa akin, ngunit ngayon naiintindihan ko na kahit na may mga aktibong user na naka-subscribe sa isang partikular na grupo o wala, kailangan mo pa ring panatilihing napapanahon ang pinakabagong artikulo na naproseso ng bot. Maaaring lumitaw ang isang sitwasyon kapag natanggap kaagad ng isang bagong user ang buong bilang ng mga artikulong nai-publish mula nang i-deactivate ang pangkat na ito. Hindi ito inaasahang pag-uugali, at upang maiwasan ito, kailangan nating panatilihin ang mga pangkat na iyon mula sa aming database na kasalukuyang walang kasalukuyang mga aktibong user. -
Kung may mga bagong artikulo, bumuo ng mga mensahe para sa lahat ng mga user na aktibong naka-subscribe sa grupong ito. Kung walang mga bagong artikulo, kumpletuhin lang namin ang gawain.
FindNewArticleService:
package com.github.javarushcommunity.jrtb.service;
/**
* Service for finding new articles.
*/
public interface FindNewArticleService {
/**
* Find new articles and notify subscribers about it.
*/
void findNewArticles();
}
Napakasimple, tama? Ito ang kakanyahan nito, at ang lahat ng kahirapan ay nasa pagpapatupad:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.javarushclient.JavaRushPostClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.PostInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class FindNewArticleServiceImpl implements FindNewArticleService {
public static final String JAVARUSH_WEB_POST_FORMAT = "https://javarush.com/groups/posts/%s";
private final GroupSubService groupSubService;
private final JavaRushPostClient javaRushPostClient;
private final SendBotMessageService sendMessageService;
@Autowired
public FindNewArticleServiceImpl(GroupSubService groupSubService,
JavaRushPostClient javaRushPostClient,
SendBotMessageService sendMessageService) {
this.groupSubService = groupSubService;
this.javaRushPostClient = javaRushPostClient;
this.sendMessageService = sendMessageService;
}
@Override
public void findNewArticles() {
groupSubService.findAll().forEach(gSub -> {
List<PostInfo> newPosts = javaRushPostClient.findNewPosts(gSub.getId(), gSub.getLastArticleId());
setNewLastArticleId(gSub, newPosts);
notifySubscribersAboutNewArticles(gSub, newPosts);
});
}
private void notifySubscribersAboutNewArticles(GroupSub gSub, List<PostInfo> newPosts) {
Collections.reverse(newPosts);
List<String> messagesWithNewArticles = newPosts.stream()
.map(post -> String.format("✨Вышла новая статья <b>%s</b> в группе <b>%s</b>.✨\n\n" +
"<b>Описание:</b> %s\n\n" +
"<b>Ссылка:</b> %s\n",
post.getTitle(), gSub.getTitle(), post.getDescription(), getPostUrl(post.getKey())))
.collect(Collectors.toList());
gSub.getUsers().stream()
.filter(TelegramUser::isActive)
.forEach(it -> sendMessageService.sendMessage(it.getChatId(), messagesWithNewArticles));
}
private void setNewLastArticleId(GroupSub gSub, List<PostInfo> newPosts) {
newPosts.stream().mapToInt(PostInfo::getId).max()
.ifPresent(id -> {
gSub.setLastArticleId(id);
groupSubService.save(gSub);
});
}
private String getPostUrl(String key) {
return String.format(JAVARUSH_WEB_POST_FORMAT, key);
}
}
Dito ay haharapin natin ang lahat sa pagkakasunud-sunod:
-
Gamit ang groupService nakita namin ang lahat ng mga grupo na nasa database.
-
Pagkatapos ay kumalat kami sa lahat ng mga grupo at para sa bawat isa ay tinawag namin ang kliyente na nilikha sa huling artikulo - javaRushPostClient.findNewPosts .
-
Susunod, gamit ang setNewArticleId method , ina-update namin ang article ID ng aming pinakabagong bagong artikulo para malaman ng aming database na naproseso na namin ang mga bago.
-
At gamit ang katotohanan na ang GroupSub ay may koleksyon ng mga user, dumaan kami sa mga aktibo at nagpapadala ng mga abiso tungkol sa mga bagong artikulo.
Lumikha ng FindNewArticleJob
Мы уже говорor о том, что такое SpringScheduler, но повторим еще раз быстро: это механизм в Spring фреймворке для создания фонового процесса, который будет выполняться в определенное время, задаваемого нами. What нужно для этого? Первый этап — добавить аннотацию @EnableScheduling к нашему входному классу для спринга:package com.github.javarushcommunity.jrtb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
@SpringBootApplication
public class JavarushTelegramBotApplication {
public static void main(String[] args) {
SpringApplication.run(JavarushTelegramBotApplication.class, args);
}
}
Второй этап — создать класс, добавить его в ApplicationContext и создать в нем метод, который будет запускаться периодически. Создаем пакет job на одном уровне с repository, service и так далее и там создаем класс FindNewArticleJob:
package com.github.javarushcommunity.jrtb.job;
import com.github.javarushcommunity.jrtb.service.FindNewArticleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
/**
* Job for finding new articles.
*/
@Slf4j
@Component
public class FindNewArticlesJob {
private final FindNewArticleService findNewArticleService;
@Autowired
public FindNewArticlesJob(FindNewArticleService findNewArticleService) {
this.findNewArticleService = findNewArticleService;
}
@Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}")
public void findNewArticles() {
LocalDateTime start = LocalDateTime.now();
log.info("Find new article job started.");
findNewArticleService.findNewArticles();
LocalDateTime end = LocalDateTime.now();
log.info("Find new articles job finished. Took seconds: {}",
end.toEpochSecond(ZoneOffset.UTC) - start.toEpochSecond(ZoneOffset.UTC));
}
}
Whatбы добавить этот класс в Application Context, я использовал аннотацию @Component. А чтобы метод внутри класса знал, что ему нужно запускаться периодически, я добавил к методу аннотацию: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}"). А вот Howое meaning будет — его мы задаем уже в application.properties файле:
bot.recountNewArticleFixedRate = 900000
Здесь meaning указано в миллисекундах. Это будет 15 minutes. В этом методе все просто: я для себя в логах добавил супер простую метрику для подсчета поиска новых статей, чтобы хоть примерно представлять насколько быстро работает.
Тестируем новый функционал
Теперь будем тестировать на нашем тестовом боте. Но How? Не буду же я удалять каждый раз статьи, чтобы показать, что уведомления пришли? Нет, конечно. Просто будем править данные в БД и запускать приложение. Тестировать я буду на своем тестовом енве. Для этого подпишемся на Howую-то группу. Когда подписка будет оформлена, группе будет поставлен актуальный ID последней статьи. Пойдем в базу и поменяем meaning на две статьи назад. В итоге ожидаем, что будет столько статей, на сколько раньше поставим lastArticleId.Далее идем на сайт, сортируем статьи в группе Java-проекты — сначала новые — и заходим в третью статью из списка:Зайдем в нижнюю статью и из addressной строки получим article Id — 3313:Далее идем в MySQL Workbench и меняем meaning lastArticleId на 3313. Посмотрим, что такая группа есть в базе:И для нее выполним команду:И все, теперь нужно подождать до следующего запуска джобы по поиску новых статей. Ожидаем, что придет два messages о новой статье из группы Java-проекты. Как говорится, результат не заставил себя ждать:Получается, что бот отработал так, How мы и ожидали.Окончание
Как и всегда — обновляем версию в pom.xml и добавляем запись в RELEASE_NOTES, чтобы история работы сохранилась и всегда можно было вернуться и понять, что изменилось. Поэтому инкрементируем на одну единицу версию:<version>0.7.0-SNAPSHOT</version>
И обновляем RELEASE_NOTES:
GO TO FULL VERSION