From fe761771ce3cb65b00dc3a826c51b9f20ff6c72d Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 5 Oct 2018 01:38:04 +0300 Subject: TopEvent: Announce in Fediverse, like in XMPP/Telegram --- .../service/component/NotificationListener.java | 3 ++ .../java/com/juick/service/component/TopEvent.java | 21 ++++++++++++ .../java/com/juick/components/APNSManager.java | 24 ++++++++++++++ .../java/com/juick/components/FirebaseManager.java | 5 +++ .../java/com/juick/components/MPNSManager.java | 5 +++ .../java/com/juick/server/ActivityPubManager.java | 38 ++++++++++++++++++++-- .../main/java/com/juick/server/EmailManager.java | 5 +++ .../java/com/juick/server/MessengerManager.java | 18 ++++++++++ .../main/java/com/juick/server/ServerManager.java | 5 +++ .../java/com/juick/server/TelegramBotManager.java | 12 +++++-- .../src/main/java/com/juick/server/TopManager.java | 28 +++------------- .../main/java/com/juick/server/TwitterManager.java | 22 +++++++++++++ .../main/java/com/juick/server/XMPPConnection.java | 15 +++++++++ .../juick/server/api/activity/model/Context.java | 1 + .../api/activity/model/activities/Announce.java | 6 ++++ 15 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 juick-common/src/main/java/com/juick/service/component/TopEvent.java create mode 100644 juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java diff --git a/juick-common/src/main/java/com/juick/service/component/NotificationListener.java b/juick-common/src/main/java/com/juick/service/component/NotificationListener.java index 8092e258..38d0490a 100644 --- a/juick-common/src/main/java/com/juick/service/component/NotificationListener.java +++ b/juick-common/src/main/java/com/juick/service/component/NotificationListener.java @@ -19,4 +19,7 @@ public interface NotificationListener { @Async @EventListener void processMessageReadEvent(MessageReadEvent messageReadEvent); + @Async + @EventListener + void processTopEvent(TopEvent topEvent); } diff --git a/juick-common/src/main/java/com/juick/service/component/TopEvent.java b/juick-common/src/main/java/com/juick/service/component/TopEvent.java new file mode 100644 index 00000000..b7e24957 --- /dev/null +++ b/juick-common/src/main/java/com/juick/service/component/TopEvent.java @@ -0,0 +1,21 @@ +package com.juick.service.component; + +import com.juick.Message; +import org.springframework.context.ApplicationEvent; + +public class TopEvent extends ApplicationEvent { + private Message message; + /** + * Create a new ApplicationEvent. + * + * @param source the object on which the event initially occurred (never {@code null}) + */ + public TopEvent(Object source, Message message) { + super(source); + this.message = message; + } + + public Message getMessage() { + return message; + } +} diff --git a/juick-notifications/src/main/java/com/juick/components/APNSManager.java b/juick-notifications/src/main/java/com/juick/components/APNSManager.java index c0380847..3eaf3c93 100644 --- a/juick-notifications/src/main/java/com/juick/components/APNSManager.java +++ b/juick-notifications/src/main/java/com/juick/components/APNSManager.java @@ -1,6 +1,7 @@ package com.juick.components; import com.juick.ExternalToken; +import com.juick.Message; import com.juick.User; import com.juick.formatters.PlainTextFormatter; import com.juick.service.component.*; @@ -113,6 +114,29 @@ public class APNSManager implements NotificationListener { }); }); } + + @Override + public void processTopEvent(TopEvent topEvent) { + Message message = topEvent.getMessage(); + ApnsPayloadBuilder apnsPayloadBuilder = new ApnsPayloadBuilder(); + message.getUser().getTokens().stream().filter(t -> t.getType().equals("apns")) + .map(ExternalToken::getToken).forEach( token -> { + String payload = apnsPayloadBuilder.setAlertTitle("Top").setAlertBody("Your message became popular!") + .addCustomProperty("mid", message.getMid()) + .addCustomProperty("uname", message.getUser().getName()) + .buildWithDefaultMaximumLength(); + Future> notification = apns.sendNotification( + new SimpleApnsPushNotification(token, topic, payload)); + notification.addListener((PushNotificationResponseListener) future -> { + if (future.isSuccess()) { + processAPNSResponse(token, future.getNow()); + } else { + logger.warn("APNS error ", future.cause()); + } + }); + }); + } + @PreDestroy public void close() { apns.close(); diff --git a/juick-notifications/src/main/java/com/juick/components/FirebaseManager.java b/juick-notifications/src/main/java/com/juick/components/FirebaseManager.java index 54e7c97a..24bb43d1 100644 --- a/juick-notifications/src/main/java/com/juick/components/FirebaseManager.java +++ b/juick-notifications/src/main/java/com/juick/components/FirebaseManager.java @@ -87,4 +87,9 @@ public class FirebaseManager implements NotificationListener { public void processMessageReadEvent(MessageReadEvent messageReadEvent) { } + + @Override + public void processTopEvent(TopEvent topEvent) { + + } } diff --git a/juick-notifications/src/main/java/com/juick/components/MPNSManager.java b/juick-notifications/src/main/java/com/juick/components/MPNSManager.java index 56c2df8d..d6c99b0b 100644 --- a/juick-notifications/src/main/java/com/juick/components/MPNSManager.java +++ b/juick-notifications/src/main/java/com/juick/components/MPNSManager.java @@ -160,4 +160,9 @@ public class MPNSManager implements NotificationListener { public void processMessageReadEvent(MessageReadEvent messageReadEvent) { } + + @Override + public void processTopEvent(TopEvent topEvent) { + + } } diff --git a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java b/juick-server/src/main/java/com/juick/server/ActivityPubManager.java index 2e511738..f08754c6 100644 --- a/juick-server/src/main/java/com/juick/server/ActivityPubManager.java +++ b/juick-server/src/main/java/com/juick/server/ActivityPubManager.java @@ -6,9 +6,11 @@ import com.juick.server.api.activity.model.Link; import com.juick.server.api.activity.model.Note; import com.juick.server.api.activity.model.Person; import com.juick.server.api.activity.model.activities.Accept; +import com.juick.server.api.activity.model.activities.Announce; import com.juick.server.api.activity.model.activities.Create; import com.juick.server.api.activity.model.activities.Delete; import com.juick.service.SocialService; +import com.juick.service.UserService; import com.juick.service.activities.*; import com.juick.service.component.*; import com.juick.util.MessageUtils; @@ -20,6 +22,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Nonnull; +import javax.annotation.PostConstruct; import javax.inject.Inject; import java.io.IOException; import java.net.URI; @@ -29,11 +32,22 @@ import java.util.Collections; public class ActivityPubManager implements ActivityListener, NotificationListener { private static final Logger logger = LoggerFactory.getLogger(ActivityPubManager.class); @Inject - SignatureManager signatureManager; + private SignatureManager signatureManager; @Inject - SocialService socialService; + private SocialService socialService; + @Inject + private UserService userService; @Value("${ap_base_uri:http://localhost:8080/}") private String baseUri; + @Value("${service_user:juick}") + private String serviceUsername; + + private User serviceUser; + + @PostConstruct + public void init() { + serviceUser = userService.getUserByName(serviceUsername); + } @Override public void processFollowEvent(@Nonnull FollowEvent followEvent) { @@ -204,4 +218,24 @@ public class ActivityPubManager implements ActivityListener, NotificationListene public void processMessageReadEvent(MessageReadEvent messageReadEvent) { } + + @Override + public void processTopEvent(TopEvent topEvent) { + Message message = topEvent.getMessage(); + Note note = makeNote(message); + Announce announce = new Announce(); + announce.setId(note.getId() + "#top"); + announce.setActor(personUri(serviceUser)); + announce.setObject(note); + Person me = (Person) signatureManager.getContext(URI.create(announce.getActor())).get(); + socialService.getFollowers(serviceUser).forEach(acct -> { + Person follower = (Person) signatureManager.getContext(URI.create(acct)).get(); + try { + logger.info("Announcing top: {}", message.getMid()); + signatureManager.post(me, follower, announce); + } catch (IOException e) { + logger.warn("activitypub exception", e); + } + }); + } } diff --git a/juick-server/src/main/java/com/juick/server/EmailManager.java b/juick-server/src/main/java/com/juick/server/EmailManager.java index 534de35b..726da4b9 100644 --- a/juick-server/src/main/java/com/juick/server/EmailManager.java +++ b/juick-server/src/main/java/com/juick/server/EmailManager.java @@ -88,6 +88,11 @@ public class EmailManager implements NotificationListener { } + @Override + public void processTopEvent(TopEvent topEvent) { + + } + private void emailNotify(String email, String subject, Message msg) { Map headers = new HashMap<>(); if (!MessageUtils.isPM(msg)) { diff --git a/juick-server/src/main/java/com/juick/server/MessengerManager.java b/juick-server/src/main/java/com/juick/server/MessengerManager.java index 90c58f03..96320b3b 100644 --- a/juick-server/src/main/java/com/juick/server/MessengerManager.java +++ b/juick-server/src/main/java/com/juick/server/MessengerManager.java @@ -27,7 +27,9 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; @@ -54,6 +56,8 @@ public class MessengerManager implements NotificationListener { private MessengerService messengerService; @Inject private CommandsManager commandsManager; + @Inject + private FacebookPageManager facebookPageManager; @Value("${fb_page_access_token:12345678}") private String facebookPageAccessToken; @@ -145,6 +149,20 @@ public class MessengerManager implements NotificationListener { } + @Override + public void processTopEvent(TopEvent topEvent) { + Message jmsg = topEvent.getMessage(); + String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText()); + String link = "https://juick.com/m/" + jmsg.getMid(); + try { + facebookPageManager.post(status, link); + } catch (HttpClientErrorException ex) { + HttpStatus statusCode = ex.getStatusCode(); + String responseString = ex.getResponseBodyAsString(); + logger.warn("facebook error {}: {}", statusCode.value(), responseString); + } + } + private void messengerNotify(String messengerUser, String text, String url) { try { if (!StringUtils.isEmpty(url)) { diff --git a/juick-server/src/main/java/com/juick/server/ServerManager.java b/juick-server/src/main/java/com/juick/server/ServerManager.java index 8a9d224b..81703492 100644 --- a/juick-server/src/main/java/com/juick/server/ServerManager.java +++ b/juick-server/src/main/java/com/juick/server/ServerManager.java @@ -207,4 +207,9 @@ public class ServerManager implements NotificationListener { }); }); } + + @Override + public void processTopEvent(TopEvent topEvent) { + + } } diff --git a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java index ab1675f5..12f195aa 100644 --- a/juick-server/src/main/java/com/juick/server/TelegramBotManager.java +++ b/juick-server/src/main/java/com/juick/server/TelegramBotManager.java @@ -253,7 +253,7 @@ public class TelegramBotManager implements NotificationListener { telegramMessage.replyToMessageId(replyTo); } telegramMessage.parseMode(ParseMode.Markdown).disableWebPagePreview(true); - bot.execute(telegramMessage, new Callback() { + bot.execute(telegramMessage, new Callback<>() { @Override public void onResponse(SendMessage request, SendResponse response) { processTelegramResponse(chatId, response, source); @@ -272,7 +272,7 @@ public class TelegramBotManager implements NotificationListener { telegramPhoto.replyToMessageId(replyTo); } telegramPhoto.parseMode(ParseMode.Markdown); - bot.execute(telegramPhoto, new Callback() { + bot.execute(telegramPhoto, new Callback<>() { @Override public void onResponse(SendPhoto request, SendResponse response) { processTelegramResponse(chatId, response, source); @@ -392,6 +392,14 @@ public class TelegramBotManager implements NotificationListener { } + @Override + public void processTopEvent(TopEvent topEvent) { + com.juick.Message message = topEvent.getMessage(); + telegramService.getTelegramIdentifiers(Collections.singletonList(message.getUser())) + .forEach(c -> telegramNotify(c, String.format("Your [post](%s) became popular!", + formatUrl(message)), new com.juick.Message())); + } + @Override public void processSubscribeEvent(SubscribeEvent subscribeEvent) { User subscriber = subscribeEvent.getUser(); diff --git a/juick-server/src/main/java/com/juick/server/TopManager.java b/juick-server/src/main/java/com/juick/server/TopManager.java index e6c22533..a9157841 100644 --- a/juick-server/src/main/java/com/juick/server/TopManager.java +++ b/juick-server/src/main/java/com/juick/server/TopManager.java @@ -22,11 +22,13 @@ import com.juick.Tag; import com.juick.User; import com.juick.service.MessagesService; import com.juick.service.UserService; +import com.juick.service.component.TopEvent; import com.juick.util.MessageUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -43,21 +45,9 @@ public class TopManager { @Inject private MessagesService messagesService; @Inject - private FacebookPageManager facebookPageManager; + private ApplicationEventPublisher applicationEventPublisher; @Inject private TwitterManager twitterManager; - @Inject - private UserService userService; - - @Value("${service_user:juick}") - private String serviceUsername; - - private User serviceUser; - - @PostConstruct - public void init() { - serviceUser = userService.getUserByName(serviceUsername); - } @Scheduled(fixedRate = 3600000) public void updateTop() { @@ -67,17 +57,7 @@ public class TopManager { messagesService.setMessagePopular(m, 1); List tags = jmsg.getTags().stream().map(Tag::getName).map(String::toLowerCase).collect(Collectors.toList()); if (!tags.contains("juick")) { - String status = MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText()); - String link = "https://juick.com/m/" + jmsg.getMid(); - try { - facebookPageManager.post(status, link); - } catch (HttpClientErrorException ex) { - HttpStatus statusCode = ex.getStatusCode(); - String responseString = ex.getResponseBodyAsString(); - logger.warn("facebook error {}: {}", statusCode.value(), responseString); - } - jmsg.setUser(serviceUser); - twitterManager.twitterPost(jmsg); + applicationEventPublisher.publishEvent(new TopEvent(this, jmsg)); } }); } diff --git a/juick-server/src/main/java/com/juick/server/TwitterManager.java b/juick-server/src/main/java/com/juick/server/TwitterManager.java index eeef2b91..613594e6 100644 --- a/juick-server/src/main/java/com/juick/server/TwitterManager.java +++ b/juick-server/src/main/java/com/juick/server/TwitterManager.java @@ -17,6 +17,8 @@ package com.juick.server; import com.juick.Message; +import com.juick.User; +import com.juick.service.UserService; import com.juick.service.component.*; import com.juick.service.CrosspostService; import com.juick.util.MessageUtils; @@ -28,6 +30,7 @@ import org.springframework.stereotype.Component; import twitter4j.TwitterFactory; import twitter4j.conf.ConfigurationBuilder; +import javax.annotation.PostConstruct; import javax.inject.Inject; /** @@ -45,6 +48,18 @@ public class TwitterManager implements NotificationListener { private String twitter_consumer_key; @Value("${twitter_consumer_secret:secret}") private String twitter_consumer_secret; + @Inject + private UserService userService; + + @Value("${service_user:juick}") + private String serviceUsername; + + private User serviceUser; + + @PostConstruct + public void init() { + serviceUser = userService.getUserByName(serviceUsername); + } void twitterPost(final com.juick.Message jmsg) { crosspostService.getTwitterToken(jmsg.getUser().getUid()).ifPresent(t -> { @@ -100,4 +115,11 @@ public class TwitterManager implements NotificationListener { public void processMessageReadEvent(MessageReadEvent messageReadEvent) { } + + @Override + public void processTopEvent(TopEvent topEvent) { + Message jmsg = topEvent.getMessage(); + jmsg.setUser(serviceUser); + twitterPost(jmsg); + } } diff --git a/juick-server/src/main/java/com/juick/server/XMPPConnection.java b/juick-server/src/main/java/com/juick/server/XMPPConnection.java index fdbb5fe6..f405e384 100644 --- a/juick-server/src/main/java/com/juick/server/XMPPConnection.java +++ b/juick-server/src/main/java/com/juick/server/XMPPConnection.java @@ -119,6 +119,10 @@ public class XMPPConnection implements StanzaListener, NotificationListener { private ExecutorService service; @Inject private ApplicationEventPublisher applicationEventPublisher; + @Value("${service_user:juick}") + private String serviceUsername; + + private User serviceUser; @PostConstruct public void init() { @@ -267,6 +271,7 @@ public class XMPPConnection implements StanzaListener, NotificationListener { logger.warn("xmpp exception", e); } }); + serviceUser = userService.getUserByName(serviceUsername); } private String stanzaToString(Stanza stanza) throws XMLStreamException, JAXBException { @@ -474,6 +479,16 @@ public class XMPPConnection implements StanzaListener, NotificationListener { } + @Override + public void processTopEvent(TopEvent topEvent) { + com.juick.Message message = topEvent.getMessage(); + try { + commandsManager.processCommand(serviceUser, String.format("! #%d", message.getMid()), URI.create(StringUtils.EMPTY)); + } catch (Exception e) { + logger.warn("XMPP error", e); + } + } + private void incomingPresence(Presence p) { final String username = p.getTo().getLocal(); final boolean toJuick = username.equals(jid.getLocal()); diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java index a645d746..9eb03c6f 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java @@ -19,6 +19,7 @@ import java.util.List; @JsonSubTypes.Type(value = Accept.class, name = "Accept"), @JsonSubTypes.Type(value = Undo.class, name = "Undo"), @JsonSubTypes.Type(value = Like.class, name = "Like"), + @JsonSubTypes.Type(value = Activity.class, name = "Activity"), @JsonSubTypes.Type(value = Image.class, name = "Image"), @JsonSubTypes.Type(value = Key.class, name = "Key"), @JsonSubTypes.Type(value = Link.class, name = "Link"), diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java new file mode 100644 index 00000000..f2859404 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Announce.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Announce extends Activity { +} -- cgit v1.2.3