diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java index 06f2cdd8cfa1a15acf871e277564f4ba13f9c5b7..5b02435947e36f13f768df22eaa7795b18208e62 100644 --- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java +++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/ModelUtils.java @@ -145,16 +145,20 @@ public class ModelUtils { return pubkey.substring(0, 8); } - public static String joinPubkeys(Collection<String> pubkeys, boolean minify, String separator) { - Preconditions.checkArgument(CollectionUtils.isNotEmpty(pubkeys)); - Preconditions.checkNotNull(separator); + public static String joinPubkeys(Set<String> pubkeys, String separator, boolean minify) { + Preconditions.checkNotNull(pubkeys); + Preconditions.checkArgument(pubkeys.size()>0); + if (pubkeys.size() == 1) { + String pubkey = pubkeys.iterator().next(); + return (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); + } StringBuilder sb = new StringBuilder(); - for (String pubkey : pubkeys) { - sb.append(separator) - .append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); - } + pubkeys.stream().forEach((pubkey)-> { + sb.append(separator); + sb.append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); + }); - return sb.toString().substring(separator.length()); + return sb.substring(separator.length()); } } diff --git a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java index dce12461e98206e121c5d4ea4f05ffe09b341be7..852643dd1e453ee1a986fdee64d583bb5c4ab981 100644 --- a/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java +++ b/duniter4j-es-core/src/main/java/org/duniter/elasticsearch/service/AbstractService.java @@ -233,7 +233,7 @@ public abstract class AbstractService implements Bean { protected String getIssuer(JsonNode actualObj) { - return actualObj.get(Record.PROPERTY_ISSUER).asText(); + return getMandatoryField(actualObj, Record.PROPERTY_ISSUER).asText(); } protected JsonNode getMandatoryField(JsonNode actualObj, String fieldName) { diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java index e1a5053e4e7c490f9c89d71b62a9dd5aa39157b0..7f7ac356cc002e5824eb1a3f6a9a0c60c10dc24c 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/model/UserEventCodes.java @@ -38,6 +38,9 @@ public enum UserEventCodes { // TX TX_SENT, - TX_RECEIVED + TX_RECEIVED, + + // Message + MESSAGE_RECEIVED } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java index 690a76bce948fb867562e2ffa1af324b45a6273d..dec3f83554d0fbb80b3a5f2c71139a60801299be 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/BlockchainUserEventService.java @@ -204,8 +204,8 @@ public class BlockchainUserEventService extends AbstractService implements Chang Set<String> senders = ImmutableSet.copyOf(tx.getIssuers()); // Received - String senderNames = getNamesFromPubkeys(senders, true); - String sendersPubkeys = joinPubkeys(senders, false); + String senderNames = userService.joinNamesFromPubkeys(senders, DEFAULT_PUBKEYS_SEPARATOR, true); + String sendersPubkeys = ModelUtils.joinPubkeys(senders, DEFAULT_PUBKEYS_SEPARATOR, false); Set<String> receivers = new HashSet<>(); for (String output : tx.getOutputs()) { String[] parts = output.split(":"); @@ -220,8 +220,8 @@ public class BlockchainUserEventService extends AbstractService implements Chang // Sent if (CollectionUtils.isNotEmpty(receivers)) { - String receiverNames = getNamesFromPubkeys(receivers, true); - String receiverPubkeys = joinPubkeys(receivers, false); + String receiverNames = userService.joinNamesFromPubkeys(receivers, DEFAULT_PUBKEYS_SEPARATOR, true); + String receiverPubkeys = ModelUtils.joinPubkeys(receivers, DEFAULT_PUBKEYS_SEPARATOR, false); for (String sender : senders) { notifyUserEvent(block, sender, UserEventCodes.TX_SENT, I18n.n("duniter.user.event.tx.sent"), receiverPubkeys, receiverNames); } @@ -249,42 +249,5 @@ public class BlockchainUserEventService extends AbstractService implements Chang userEventService.deleteEventsByReference(new UserEvent.Reference(change.getIndex(), change.getType(), change.getId())); } - private String getNamesFromPubkeys(Set<String> pubkeys, boolean minify) { - Preconditions.checkNotNull(pubkeys); - Preconditions.checkArgument(pubkeys.size()>0); - if (pubkeys.size() == 1) { - String pubkey = pubkeys.iterator().next(); - String title = userService.getProfileTitle(pubkey); - return title != null ? title : - (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); - } - - Map<String, String> profileTitles = userService.getProfileTitles(pubkeys); - StringBuilder sb = new StringBuilder(); - pubkeys.stream().forEach((pubkey)-> { - String title = profileTitles != null ? profileTitles.get(pubkey) : null; - sb.append(DEFAULT_PUBKEYS_SEPARATOR); - sb.append(title != null ? title : - (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey)); - }); - - return sb.substring(DEFAULT_PUBKEYS_SEPARATOR.length()); - } - private String joinPubkeys(Set<String> pubkeys, boolean minify) { - Preconditions.checkNotNull(pubkeys); - Preconditions.checkArgument(pubkeys.size()>0); - if (pubkeys.size() == 1) { - String pubkey = pubkeys.iterator().next(); - return (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); - } - - StringBuilder sb = new StringBuilder(); - pubkeys.stream().forEach((pubkey)-> { - sb.append(DEFAULT_PUBKEYS_SEPARATOR); - sb.append(minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); - }); - - return sb.substring(DEFAULT_PUBKEYS_SEPARATOR.length()); - } } diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java index 78053edf2bbeeffc15ab8fc79899c00e657e9925..e6cd1aa07b5508576f2b4c88efbe155804292366 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/MessageService.java @@ -25,14 +25,17 @@ package org.duniter.elasticsearch.user.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import org.duniter.core.client.model.ModelUtils; import org.duniter.core.client.model.elasticsearch.Record; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; import org.duniter.elasticsearch.PluginSettings; import org.duniter.elasticsearch.exception.InvalidSignatureException; import org.duniter.elasticsearch.service.AbstractService; +import org.duniter.elasticsearch.service.BlockchainService; import org.duniter.elasticsearch.user.model.Message; import org.duniter.elasticsearch.user.model.UserEvent; +import org.duniter.elasticsearch.user.model.UserEventCodes; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.update.UpdateRequestBuilder; @@ -41,6 +44,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; +import org.nuiton.i18n.I18n; import java.io.IOException; import java.util.Map; @@ -54,10 +58,13 @@ public class MessageService extends AbstractService { public static final String RECORD_TYPE = "record"; public static final String OUTBOX_TYPE = "outbox"; + private final UserEventService userEventService; @Inject - public MessageService(Client client, PluginSettings settings, CryptoService cryptoService, UserService userService) { + public MessageService(Client client, PluginSettings settings, + CryptoService cryptoService, UserEventService userEventService) { super("duniter." + INDEX, client, settings, cryptoService); + this.userEventService = userEventService; } /** @@ -114,6 +121,8 @@ public class MessageService extends AbstractService { JsonNode actualObj = readAndVerifyIssuerSignature(recordJson); String issuer = getIssuer(actualObj); + String recipient = getMandatoryField(actualObj, Message.PROPERTY_RECIPIENT).asText(); + Long time = getMandatoryField(actualObj, Message.PROPERTY_TIME).asLong(); if (logger.isDebugEnabled()) { logger.debug(String.format("Indexing a message from issuer [%s]", issuer.substring(0, 8))); @@ -124,7 +133,17 @@ public class MessageService extends AbstractService { .setRefresh(false) .execute().actionGet(); - return response.getId(); + String messageId = response.getId(); + + // Notify recipient + userEventService.notifyUser(UserEvent.newBuilder(UserEvent.EventType.INFO, UserEventCodes.MESSAGE_RECEIVED.name()) + .setRecipient(recipient) + .setMessage(I18n.n("duniter.user.event.message.received"), issuer, ModelUtils.minifyPubkey(issuer)) + .setTime(time) + .setReference(INDEX, RECORD_TYPE, messageId) + .build()); + + return messageId; } public String indexOuboxFromJson(String recordJson) { diff --git a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java index 003d7089cf60b677850f2eb97cffeb94272aa248..96b162ccce719b308f733a0925b23e24140191f9 100644 --- a/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java +++ b/duniter4j-es-user/src/main/java/org/duniter/elasticsearch/user/service/UserService.java @@ -25,7 +25,9 @@ package org.duniter.elasticsearch.user.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Preconditions; import org.apache.commons.collections4.MapUtils; +import org.duniter.core.client.model.ModelUtils; import org.duniter.core.client.model.elasticsearch.UserProfile; import org.duniter.core.exception.TechnicalException; import org.duniter.core.service.CryptoService; @@ -214,6 +216,29 @@ public class UserService extends AbstractService { return result; } + public String joinNamesFromPubkeys(Set<String> pubkeys, String separator, boolean minify) { + Preconditions.checkNotNull(pubkeys); + Preconditions.checkNotNull(separator); + Preconditions.checkArgument(pubkeys.size()>0); + if (pubkeys.size() == 1) { + String pubkey = pubkeys.iterator().next(); + String title = getProfileTitle(pubkey); + return title != null ? title : + (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey); + } + + Map<String, String> profileTitles = getProfileTitles(pubkeys); + StringBuilder sb = new StringBuilder(); + pubkeys.stream().forEach((pubkey)-> { + String title = profileTitles != null ? profileTitles.get(pubkey) : null; + sb.append(separator); + sb.append(title != null ? title : + (minify ? ModelUtils.minifyPubkey(pubkey) : pubkey)); + }); + + return sb.substring(separator.length()); + } + /* -- Internal methods -- */ diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties index 4917ad91be8c20d95f6a655b90487d15f1721ef0..d5b454c0ffd555e237b519485c09d4f238ba9418 100644 --- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties +++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_en_GB.properties @@ -4,6 +4,7 @@ duniter.event.NODE_STARTED=Node started on cluster Duniter4j ES [%s] duniter.user.event.active= duniter.user.event.join= duniter.user.event.leave= +duniter.user.event.message.received= duniter.user.event.ms.active= duniter.user.event.ms.join= duniter.user.event.ms.leave= diff --git a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties index 03e86fc3ccb28cef89307ea245e4c383f9dbdd68..8e74a2b7152d1c12ceee321cf20dfd7ceea6501e 100644 --- a/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties +++ b/duniter4j-es-user/src/main/resources/i18n/duniter4j-es-user_fr_FR.properties @@ -1,6 +1,7 @@ duniter.event.NODE_BMA_DOWN=Noeud Duniter [%1$s\:%2$s] non joignable, depuis le noeud ES API [%3$s]. Dernière connexion à %4$d. Indexation de blockchain en attente. duniter.event.NODE_BMA_UP=Noeud Duniter [%1$s\:%2$s] à nouveau accessible. duniter.event.NODE_STARTED=Noeud ES API démarré sur le cluster Duniter [%1$s] +duniter.user.event.message.received=Vous avez reçu un message de %2$s duniter.user.event.ms.active=Votre adhésion comme membre a bien été renouvellée duniter.user.event.ms.join=Vous êtes maintenant membre de la monnaie duniter.user.event.ms.leave=Votre adhésion comme membre à expirée