-
Trouve dans tous les groupes qui se trouvent dans notre base de données les nouveaux articles publiés après l'exécution précédente.
Ce schéma spécifie un plus petit nombre de groupes - uniquement ceux comportant des utilisateurs actifs. À l'époque, cela me paraissait logique, mais maintenant je comprends que, qu'il y ait ou non des utilisateurs actifs abonnés à un groupe spécifique, vous devez toujours maintenir à jour le dernier article traité par le bot. Une situation peut survenir lorsqu'un nouvel utilisateur reçoit immédiatement la totalité des articles publiés depuis la désactivation de ce groupe. Ce n'est pas un comportement attendu, et pour l'éviter, nous devons maintenir à jour les groupes de notre base de données qui n'ont actuellement pas d'utilisateurs actifs. -
S'il y a de nouveaux articles, générez des messages pour tous les utilisateurs activement abonnés à ce groupe. S'il n'y a pas de nouveaux articles, nous terminons simplement le travail.
Rechercher un nouveau service d'article :
package com.github.javarushcommunity.jrtb.service;
/**
* Service for finding new articles.
*/
public interface FindNewArticleService {
/**
* Find new articles and notify subscribers about it.
*/
void findNewArticles();
}
Très simple, non ? C'est son essence, et toute la difficulté sera dans la mise en œuvre :
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);
}
}
Ici, nous traiterons de tout dans l'ordre :
-
En utilisant groupService, nous trouvons tous les groupes présents dans la base de données.
-
Ensuite, nous nous répartissons dans tous les groupes et pour chacun nous appelons le client créé dans le dernier article - javaRushPostClient.findNewPosts .
-
Ensuite, à l'aide de la méthode setNewArticleId , nous mettons à jour l'ID de notre dernier nouvel article afin que notre base de données sache que nous en avons déjà traité de nouveaux.
-
Et en utilisant le fait que GroupSub dispose d'un ensemble d'utilisateurs, nous passons en revue les utilisateurs actifs et envoyons des notifications sur les nouveaux articles.
Créer un travail FindNewArticle
Nous avons déjà parlé de ce qu'est SpringScheduler, mais répétons-le rapidement : il s'agit d'un mécanisme du framework Spring permettant de créer un processus en arrière-plan qui s'exécutera à une heure précise que nous fixons. De quoi avez-vous besoin pour cela ? La première étape consiste à ajouter l' annotation @EnableScheduling à notre classe d'entrée Spring :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);
}
}
La deuxième étape consiste à créer une classe, à l'ajouter à ApplicationContext et à y créer une méthode qui sera exécutée périodiquement. Nous créons un package de tâches au même niveau que le référentiel, le service, etc., et là nous créons la classe 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));
}
}
Pour ajouter cette classe au contexte d'application, j'ai utilisé l' annotation @Component . Et pour que la méthode à l'intérieur de la classe sache qu'elle doit être exécutée périodiquement, j'ai ajouté une annotation à la méthode : @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . Mais nous l'avons défini dans le fichier application.properties :
bot.recountNewArticleFixedRate = 900000
Ici, la valeur est en millisecondes. Ce sera 15 minutes. Dans cette méthode, tout est simple : j'ai ajouté une métrique super simple pour moi-même dans les logs pour calculer la recherche de nouveaux articles, afin de comprendre au moins approximativement à quelle vitesse cela fonctionne.
Tester de nouvelles fonctionnalités
Nous allons maintenant tester sur notre robot de test. Mais comment? Je ne supprimerai pas les articles à chaque fois pour montrer que les notifications sont arrivées ? Bien sûr que non. Nous allons simplement modifier les données dans la base de données et lancer l'application. Je vais le tester sur mon serveur de test. Pour ce faire, abonnez-vous à un groupe. Une fois l'abonnement terminé, le groupe recevra l'identifiant actuel du dernier article. Allons dans la base de données et modifions la valeur de deux articles. En conséquence, nous nous attendons à ce qu’il y ait autant d’articles que nous avons défini lastArticleId plus tôt . Ensuite, nous allons sur le site, trions les articles dans le groupe des projets Java - les nouveaux en premier - et passons au troisième article de la liste : Allons à l'article du bas et depuis la barre d'adresse nous obtenons l'article Id - 3313 : Suivant , allez dans MySQL Workbench et changez la valeur lastArticleId en 3313. Voyons qu'un tel groupe est dans la base de données : Et pour cela nous allons exécuter la commande : Et c'est tout, vous devez maintenant attendre le prochain lancement du travail pour rechercher de nouveaux articles. Nous nous attendons à recevoir deux messages concernant un nouvel article du groupe de projets Java. Comme on dit, le résultat ne s’est pas fait attendre : il s’avère que le bot a fonctionné comme prévu.Fin
Comme toujours, nous mettons à jour la version dans pom.xml et ajoutons une entrée à RELEASE_NOTES afin que l'historique de travail soit enregistré et que vous puissiez toujours revenir en arrière et comprendre ce qui a changé. Par conséquent, nous incrémentons la version d’une unité :<version>0.7.0-SNAPSHOT</version>
Et mettez à jour RELEASE_NOTES :
GO TO FULL VERSION