From f6b78c029a1e9e4f09b600131e99bbc13c65f30a Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Mon, 9 Jan 2023 09:45:12 +0300 Subject: Mastodon API: statuses --- src/main/java/com/juick/ActivityPubManager.java | 37 ++++++++++++++++------ src/main/java/com/juick/www/api/Mastodon.java | 31 +++++++++++++++--- .../java/com/juick/www/api/activity/Profile.java | 10 ++---- .../api/activity/helpers/ProfileUriBuilder.java | 9 +++++- 4 files changed, 66 insertions(+), 21 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java index b62be93a..e9a04197 100644 --- a/src/main/java/com/juick/ActivityPubManager.java +++ b/src/main/java/com/juick/ActivityPubManager.java @@ -31,6 +31,7 @@ import com.juick.service.component.NotificationListener; import com.juick.service.component.PingEvent; import com.juick.service.component.SystemEvent; import com.juick.util.HttpBadRequestException; +import com.juick.util.HttpNotFoundException; import com.juick.util.HttpUtils; import com.juick.util.MessageUtils; import com.juick.util.formatters.PlainTextFormatter; @@ -149,11 +150,11 @@ public class ActivityPubManager implements ActivityListener, NotificationListene UriComponents uriComponents = UriComponentsBuilder.fromUriString(event.getMessageUri()).build(); List segments = uriComponents.getPathSegments(); if (segments.get(0).equals("n")) { - String[] ids = segments.get(1).split("-", 2); - if (ids.length == 2 && Integer.parseInt(ids[1]) == 0) { + var message = findMessage(segments.get(1)); + if (message != null && !MessageUtils.isReply(message)) { // only messages - logger.info("{} recommends {}", event.getActorUri(), Integer.valueOf(ids[0])); - messagesService.likeMessage(Integer.parseInt(ids[0]), 0, Reaction.LIKE, event.getActorUri()); + logger.info("{} recommends {}", event.getActorUri(), message.getMid()); + messagesService.likeMessage(message.getMid(), 0, Reaction.LIKE, event.getActorUri()); } } } @@ -163,11 +164,11 @@ public class ActivityPubManager implements ActivityListener, NotificationListene UriComponents uriComponents = UriComponentsBuilder.fromUriString(event.getMessageUri()).build(); List segments = uriComponents.getPathSegments(); if (segments.get(0).equals("n")) { - String[] ids = segments.get(1).split("-", 2); - if (ids.length == 2 && Integer.parseInt(ids[1]) == 0) { + var message = findMessage(segments.get(1)); + if (message != null && !MessageUtils.isReply(message)) { // only messages - logger.info("{} stop recommending {}", event.getActorUri(), Integer.valueOf(ids[0])); - messagesService.likeMessage(Integer.parseInt(ids[0]), 0, null, event.getActorUri()); + logger.info("{} stop recommending {}", event.getActorUri(), message.getMid()); + messagesService.likeMessage(message.getMid(), 0, null, event.getActorUri()); } } } @@ -382,7 +383,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene activityToFollowers(user, me, announce); } - public User actorToUser(URI uri) throws HttpBadRequestException, JsonProcessingException { + public User actorToUser(URI uri) throws HttpBadRequestException { var context = activityPubService.get(uri); if (context.isPresent() && context.get() instanceof Actor actor) { User user = new User(); @@ -397,4 +398,22 @@ public class ActivityPubManager implements ActivityListener, NotificationListene throw new HttpBadRequestException(); } } + public Message findMessage(String statusId) throws HttpNotFoundException { + String[]ids = statusId.split("-", 2); + try { + if (ids.length == 2) { + return findMessage(Integer.parseInt(ids[0]), Integer.parseInt(ids[1])); + } + } catch (Exception e) { + logger.warn("{}: message not found, error: {}", statusId, e.getMessage()); + } + throw new HttpNotFoundException(); + } + public Message findMessage(int mid, int rid) throws HttpNotFoundException { + try { + return rid > 0 ? messagesService.getReply(mid, rid) : messagesService.getMessage(mid).get(); + } catch (NoSuchElementException e) { + throw new HttpNotFoundException(); + } + } } diff --git a/src/main/java/com/juick/www/api/Mastodon.java b/src/main/java/com/juick/www/api/Mastodon.java index af1c5ef7..5982209e 100644 --- a/src/main/java/com/juick/www/api/Mastodon.java +++ b/src/main/java/com/juick/www/api/Mastodon.java @@ -17,6 +17,7 @@ package com.juick.www.api; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; @@ -298,11 +299,11 @@ public class Mastodon { )) : List.of(); return new Status( - String.valueOf(message.getMid()), + ProfileUriBuilder.messageId(message), message.getCreated(), toAccount(message.getUser()), - null, - null, + message.getReplyto() > 0 ? ProfileUriBuilder.messageId(message.getMid(), message.getReplyto()) : null, + message.getTo().getUid() > 0 ? String.valueOf(message.getTo().getUid()) : null, MessageUtils.isSensitive(message), "", "public", @@ -314,9 +315,16 @@ public class Mastodon { @GetMapping("/api/v1/timelines/{timeline}") public List publicTimeline(@ModelAttribute User visitor, @PathVariable String timeline, - @RequestParam(value = "max_id", required = false, defaultValue = "0") int before, + @RequestParam(value = "max_id", required = false) String maxId, @RequestParam(value = "only_media", required = false, defaultValue = "false") Boolean media) { List mids = List.of(); + int before = 0; + if (maxId != null) { + var lastMessage = activityPubManager.findMessage(maxId); + if (lastMessage != null) { + before = lastMessage.getMid(); + } + } if (timeline.equals("public")) { if (media) { mids = messagesService.getPhotos(visitor.getUid(), before); @@ -333,4 +341,19 @@ public class Mastodon { }) .collect(Collectors.toList()); } + @GetMapping("/api/v1/statuses/{mid}-{rid}") + public Status status(@PathVariable int mid, @PathVariable int rid) { + return toStatus(activityPubManager.findMessage(mid, rid)); + } + public record Context(@JsonInclude List ancestors, @JsonInclude List descendants) { + + } + @GetMapping("/api/v1/statuses/{mid}-{rid}/context") + public Context thread(@ModelAttribute User visitor, @PathVariable int mid, @PathVariable int rid) { + var thread = messagesService.getReplies(visitor, mid).stream() + .filter(m -> m.getRid() > rid) + .peek(msg -> msg.getUser().setAvatar(webApp.getAvatarUrl(msg.getUser()))) + .map(this::toStatus).toList(); + return new Context(List.of(), thread); + } } diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java index b0a39a76..bf4bda25 100644 --- a/src/main/java/com/juick/www/api/activity/Profile.java +++ b/src/main/java/com/juick/www/api/activity/Profile.java @@ -256,13 +256,9 @@ public class Profile { @GetMapping(value = "/n/{mid}-{rid}", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE, MediaType.APPLICATION_JSON_VALUE }) public Context showNote(@PathVariable int mid, @PathVariable int rid) { - try { - Message message = rid > 0 ? messagesService.getReply(mid, rid) : messagesService.getMessage(mid).get(); - if (message != null) { - return Context.build(activityPubManager.makeNote(message)); - } - } catch (NoSuchElementException e) { - throw new HttpNotFoundException(); + var message = activityPubManager.findMessage(mid, rid); + if (message != null) { + return Context.build(activityPubManager.makeNote(message)); } throw new HttpNotFoundException(); } diff --git a/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java b/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java index e10c3078..3f48502b 100644 --- a/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java +++ b/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java @@ -61,9 +61,16 @@ public record ProfileUriBuilder(String baseUri) { return messageUri(msg.getMid(), msg.getRid()); } + public static String messageId(Message msg) { + return messageId(msg.getMid(), msg.getRid()); + } + public static String messageId(int mid, int rid) { + return String.format("%d-%d", mid, rid); + } + public String messageUri(int mid, int rid) { UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - uri.replacePath(String.format("/n/%d-%d", mid, rid)); + uri.replacePath("/n/" + messageId(mid, rid)); return uri.toUriString(); } -- cgit v1.2.3