-
Trova in tutti i gruppi presenti nel nostro database i nuovi articoli pubblicati dopo l'esecuzione precedente.
Questo schema specifica un numero minore di gruppi, solo quelli con utenti attivi. Allora mi sembrava logico, ma ora capisco che, indipendentemente dal fatto che ci siano utenti attivi iscritti a un gruppo specifico o meno, è comunque necessario mantenere aggiornato l'ultimo articolo elaborato dal bot. Può verificarsi una situazione in cui un nuovo utente riceve immediatamente l'intero numero di articoli pubblicati dalla disattivazione di questo gruppo. Questo non è un comportamento previsto e, per evitarlo, dobbiamo mantenere aggiornati i gruppi del nostro database che attualmente non hanno utenti attivi. -
Se sono presenti nuovi articoli, genera messaggi per tutti gli utenti che sono attivamente iscritti a questo gruppo. Se non ci sono nuovi articoli, completiamo semplicemente il lavoro.
TrovaNuovoArticoloServizio:
package com.github.javarushcommunity.jrtb.service;
/**
* Service for finding new articles.
*/
public interface FindNewArticleService {
/**
* Find new articles and notify subscribers about it.
*/
void findNewArticles();
}
Molto semplice, vero? Questa è la sua essenza e tutta la difficoltà sarà nell'implementazione:
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);
}
}
Qui tratteremo tutto in ordine:
-
Utilizzando groupService troviamo tutti i gruppi presenti nel database.
-
Quindi ci disperdiamo in tutti i gruppi e per ciascuno chiamiamo il client creato nell'ultimo articolo: javaRushPostClient.findNewPosts .
-
Successivamente, utilizzando il metodo setNewArticleId , aggiorniamo l'ID dell'articolo del nostro ultimo nuovo articolo in modo che il nostro database sappia che ne abbiamo già elaborati di nuovi.
-
E sfruttando il fatto che GroupSub ha una raccolta di utenti, esaminiamo quelli attivi e inviamo notifiche sui nuovi articoli.
Crea TrovaNuovoArticoloLavoro
Abbiamo già parlato di cos'è SpringScheduler, ma ripetiamolo velocemente: è un meccanismo del framework Spring per creare un processo in background che verrà eseguito in un momento specifico da noi impostato. Di cosa hai bisogno per questo? Il primo passo è aggiungere l' annotazione @EnableScheduling alla nostra classe di input 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);
}
}
Il secondo passaggio consiste nel creare una classe, aggiungerla all'ApplicationContext e creare al suo interno un metodo che verrà eseguito periodicamente. Creiamo un pacchetto di lavoro allo stesso livello di repository, servizio e così via, e lì creiamo 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));
}
}
Per aggiungere questa classe al contesto dell'applicazione ho utilizzato l' annotazione @Component . E affinché il metodo all'interno della classe sappia che deve essere eseguito periodicamente, ho aggiunto un'annotazione al metodo: @Scheduled(fixedRateString = "${bot.recountNewArticleFixedRate}") . Ma lo impostiamo nel file application.properties:
bot.recountNewArticleFixedRate = 900000
Qui il valore è in millisecondi. Saranno 15 minuti. Con questo metodo tutto è semplice: ho aggiunto nei log una metrica semplicissima per calcolare la ricerca di nuovi articoli, in modo da capire almeno approssimativamente quanto velocemente funziona.
Testare nuove funzionalità
Ora testeremo sul nostro bot di prova. Ma come? Non eliminerò ogni volta gli articoli per dimostrare che sono arrivate le notifiche? Ovviamente no. Modificheremo semplicemente i dati nel database e avvieremo l'applicazione. Lo proverò sul mio server di prova. Per fare ciò, iscriviamoci a qualche gruppo. Una volta completata l'iscrizione, al gruppo verrà assegnato l'ID corrente dell'ultimo articolo. Andiamo al database e modifichiamo il valore due articoli indietro. Di conseguenza, prevediamo che ci saranno tanti articoli quanti ne abbiamo impostati lastArticleId in precedenza . Successivamente, andiamo al sito, ordiniamo gli articoli nel gruppo dei progetti Java - prima quelli nuovi - e andiamo al terzo articolo dall'elenco: Andiamo all'articolo in fondo e dalla barra degli indirizzi otteniamo l'ID articolo - 3313: Avanti , vai su MySQL Workbench e modifica il valore lastArticleId in 3313. Vediamo che un gruppo di questo tipo è nel database: E per questo eseguiremo il comando: E questo è tutto, ora devi aspettare fino al prossimo avvio del lavoro per cercare nuovi articoli. Ci aspettiamo di ricevere due messaggi su un nuovo articolo dal gruppo dei progetti Java. Come si suol dire, il risultato non si è fatto attendere: il bot ha funzionato come ci aspettavamo.Fine
Come sempre, aggiorniamo la versione in pom.xml e aggiungiamo una voce a RELEASE_NOTES in modo che la cronologia del lavoro venga salvata e tu possa sempre tornare indietro e capire cosa è cambiato. Pertanto, incrementiamo la versione di un'unità:<version>0.7.0-SNAPSHOT</version>
E aggiorna RELEASE_NOTES:
GO TO FULL VERSION