-
Encuentra en todos los grupos que se encuentran en nuestra base de datos nuevos artículos publicados después de la ejecución anterior.
Este esquema especifica un número menor de grupos, solo aquellos con usuarios activos. En ese momento me pareció lógico, pero ahora entiendo que independientemente de si hay usuarios activos suscritos a un grupo específico o no, aún es necesario mantener actualizado el último artículo que procesó el bot. Puede surgir una situación en la que un nuevo usuario reciba inmediatamente la cantidad total de artículos publicados desde la desactivación de este grupo. Este no es un comportamiento esperado y, para evitarlo, debemos mantener actualizados aquellos grupos de nuestra base de datos que actualmente no tienen usuarios activos. -
Si hay artículos nuevos, genere mensajes para todos los usuarios que estén suscritos activamente a este grupo. Si no hay artículos nuevos, simplemente completamos el trabajo.
Buscar nuevo artículo servicio:
package com.github.javarushcommunity.jrtb.service;
/**
* Service for finding new articles.
*/
public interface FindNewArticleService {
/**
* Find new articles and notify subscribers about it.
*/
void findNewArticles();
}
Muy sencillo, ¿verdad? Ésta es su esencia, y toda la dificultad estará en la implementación:
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);
}
}
Aquí nos ocuparemos de todo en orden:
-
Usando groupService encontramos todos los grupos que hay en la base de datos.
-
Luego nos dispersamos en todos los grupos y para cada uno llamamos al cliente creado en el último artículo: javaRushPostClient.findNewPosts .
-
A continuación, utilizando el método setNewArticleId , actualizamos el ID del artículo nuevo más reciente para que nuestra base de datos sepa que ya hemos procesado otros nuevos.
-
Y aprovechando el hecho de que GroupSub tiene una colección de usuarios, revisamos los activos y enviamos notificaciones sobre nuevos artículos.
Crear BuscarNuevoArtículoTrabajo
Ya hemos hablado de qué es SpringScheduler, pero repitámoslo rápidamente: es un mecanismo en el marco de Spring para crear un proceso en segundo plano que se ejecutará en un momento específico que establezcamos. ¿Qué necesitas para esto? El primer paso es agregar la anotación @EnableScheduling a nuestra clase de entrada de primavera: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);
}
}
El segundo paso es crear una clase, agregarla a ApplicationContext y crear un método en ella que se ejecutará periódicamente. Creamos un paquete de trabajo al mismo nivel que repositorio, servicio, etc., y allí creamos la clase 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));
}
}
Para agregar esta clase al contexto de la aplicación utilicé la anotación @Component . Y para que el método dentro de la clase sepa que debe ejecutarse periódicamente, agregué una anotación al método: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . Pero lo configuramos en el archivo application.properties:
bot.recountNewArticleFixedRate = 900000
Aquí el valor está en milisegundos. Serán 15 minutos. En este método, todo es simple: agregué una métrica súper simple en los registros para calcular la búsqueda de nuevos artículos, para poder comprender al menos aproximadamente qué tan rápido funciona.
Probando nueva funcionalidad
Ahora realizaremos la prueba en nuestro bot de prueba. ¿Pero cómo? ¿No eliminaré artículos cada vez para mostrar que han llegado notificaciones? Por supuesto que no. Simplemente editaremos los datos en la base de datos y ejecutaremos la aplicación. Lo probaré en mi servidor de prueba. Para hacer esto, suscribámonos a algún grupo. Cuando se complete la suscripción, el grupo recibirá la identificación actual del último artículo. Vayamos a la base de datos y cambiemos el valor dos artículos atrás. Como resultado, esperamos que haya tantos artículos como establecimos en lastArticleId anteriormente . A continuación, vamos al sitio, clasificamos los artículos en el grupo de proyectos Java ( los nuevos primero ) y vamos al tercer artículo de la lista: Vayamos al artículo inferior y desde la barra de direcciones obtenemos el Id. del artículo: 3313: Siguiente , vaya a MySQL Workbench y cambie el valor de lastArticleId a 3313. Veamos que dicho grupo está en la base de datos: Y para ello ejecutaremos el comando: Y listo, ahora debe esperar hasta el próximo lanzamiento del trabajo para buscar nuevos artículos. Esperamos recibir dos mensajes sobre un nuevo artículo del grupo de proyectos Java. Como dicen, el resultado no se hizo esperar: resulta que el bot funcionó como esperábamos.Finalizando
Como siempre, actualizamos la versión en pom.xml y agregamos una entrada a RELEASE_NOTES para que se guarde el historial de trabajo y usted siempre pueda regresar y comprender qué ha cambiado. Por tanto, incrementamos la versión en una unidad:<version>0.7.0-SNAPSHOT</version>
Y actualice RELEASE_NOTES:
GO TO FULL VERSION