Im ersten Mastodon Beitrag wurden einige Grundlagen zur API erläutert. In diesem Beitrag soll ein eigner Bot erstellt werden. Die Bots auf Mastodon sind spezielle Accounts die autmatisierte Aufgaben erfüllen. Dies können regelmäßige Info-Tröts sein, die Analyse des Nutzerverhaltens, gelegentliche Umfragen oder, oder oder. Ein Mastodon Bot wird wie ein normaler Account erstellt, jedoch wird ein zusätzliches Häkchen gesetzt.
In diesem Fall also ein Bot der auf nrw.social
regelmäßig englischsprachige Zitate publizieren möchte.
Damit der Bot auch etwas unternehmen kann, benötigt er eine oder mehrere Anwendungen. In diesem Beispiel heißt auch die Anwendung Quick Quotes und hat die Erlaubnis zu tröten. Wichtig ist bei der Anwendung das Zugangs-Token.
Hier im Screenshot natürlich ausgeblendet, damit niemand anderes den Bot verwenden kann.
Damit der Bot wie gewünscht arbeiten kann, muss er vier Dinge beherrschen. Er muss erstens in regelmäßigen Abständen Zitate verarbeiten können, zweitens muss er tröten können, drittens die Zitate passend darstellen und viertens natürlich die Zitate beschaffen.
Die erste Aufgabe ist für unsere Spring Boot Anwendung eine leichte Sache. Dazu muss nur das Scheduling aktiviert werden und die entsprechende Methode mit der @Scheduled
Annotation versehen werden.
@Scheduled(cron = "0 8,16 * * 1-5 ?") public void sendQuote() { Quotation quotation = getQuotation(); log.info("quotation: {}", quotation); if (quotation == null) { return; } client.status(template.process(Map.of("quotation", quotation)), Locale.ENGLISH, "public"); }
Die Methode sendQuote
wird von Montag bis Freitag um 8 Uhr und um 16 Uhr aufgerufen. Damit produziert der Bot zu Beginn und Ende der üblichen Arbeitszeiten zwei Zitate, die hoffentlich ein wenig inspirieren.
Als nächste muss der schon bestehenden MastodonClient
Komponente das Tröten beigebracht werden.
public void status(String content, Locale locale, Visibility visibility) { Objects.requireNonNull(locale.getLanguage()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); MultiValueMap<String, Object> theMultipartRequest = new LinkedMultiValueMap<>(); theMultipartRequest.put("status", List.of(content)); theMultipartRequest.put("language", List.of(locale.getLanguage())); theMultipartRequest.put("visibility", List.of(visibility)); ResponseEntity<String> response = restTemplate.exchange("/api/v1/statuses", HttpMethod.POST, new HttpEntity<>(theMultipartRequest, headers), String.class); log.info("status: {}", response.getBody()); }
Zum tröten wird ein Request an den Endpunkt /api/v1/statuses
gesendet, der Request enthält dabei den Text der Statusmeldung als String
, die Sprache als Locale
mit gesetztem Language Attribute und die Sichtbarkeit als ein Enum.
Zur Darstellung der Zitate nutzt der Bot die Template-Engine FreshMarker. Das notwendige FreshMarker Template
wird als Bean bereitgestellt.
@Bean public Template template() throws IOException, ParseException { org.freshmarker.Configuration configuration = new org.freshmarker.Configuration(); StringTemplateLoader templateLoader = new StringTemplateLoader(); templateLoader.putTemplate("quotation", """ “${quotation.content}” ― ${quotation.author} ${quotation.link!''} #Quote <#list quotation.tags as tag, l>#${tag}</#list> """ ); configuration.registerTemplateLoader(templateLoader); return configuration.getTemplate("quotation"); }
Die Status Meldung besteht aus dem Zitat in Anführungsstrichen, der Autorenangabe mit vorangestelltem Em-Dash. Einem Link auf Autoreninformationen und einer Liste von Links. Der Aufruf des Templates wurde schon in der send
Methode weiter oben gezeigt und enthält eine Map
mit einem Quotation Wert.
Als letztes wird noch das Zitat benötigt, dass auf Mastodon präsentiert werden soll. Erfreulicherweise gibt es im Internet eine große Zahl frei verfügbarer API, die mit allerlei Informationen aufwarten. In diesem Fall wurde die https://github.com/lukePeavey/quotable genutzt. Mit dem Aufruf https://api.quotable.io/random erhält man ein zufällig ausgewähltes Zitat mit Autorenangabe geliefert.
private Quotation getQuotation() { Quotation quotation = randomQuotes.getForObject("/random", Quotation.class); if (quotation == null) { return null; } if (quotation.getAuthorSlug() == null) { return null; } Authors authors = randomQuotes.getForObject("/authors?slug={slug}", Authors.class, quotation.getAuthorSlug()); Stream.ofNullable(authors) .map(Authors::getResults).filter(Objects::nonNull).flatMap(List::stream).map(Author::getLink).findFirst() .ifPresent(quotation::setLink); quotation.setTags(quotation.getTags().stream().map(this::toCamelCase).toList()); return quotation; }
Die Methode getQuotation
holt das Zitat und die zusätzlichen Autoreninformationen und bereiten die Daten noch ein wenig auf.
Damit ist der Bot auch schon fertig implementiert und kann seine Aufgabe erfüllen und Zitate in das Fediverse tröten.