aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2019-02-23 18:26:05 +0300
committerGravatar Vitaly Takmazov2019-02-23 18:26:05 +0300
commit116d87c9473b8135d0466be149f070e067e8b810 (patch)
tree0ab85186dd1554c7fd3d2d39c6b606416bf1bb09
parent42af5984ef2decf3763fab51f274fd9940ec23c7 (diff)
* Add text descriptions to ActivityPub error codes
* Append ActivityPub attachments as urls * Added failing test for Hubzilla activity with Link as object
-rw-r--r--src/main/java/com/juick/formatters/PlainTextFormatter.java8
-rw-r--r--src/main/java/com/juick/server/api/activity/Profile.java67
-rw-r--r--src/test/java/com/juick/server/tests/ServerTests.java21
-rw-r--r--src/test/resources/hubzilla_activity.json185
4 files changed, 254 insertions, 27 deletions
diff --git a/src/main/java/com/juick/formatters/PlainTextFormatter.java b/src/main/java/com/juick/formatters/PlainTextFormatter.java
index 378a523f..4cb07950 100644
--- a/src/main/java/com/juick/formatters/PlainTextFormatter.java
+++ b/src/main/java/com/juick/formatters/PlainTextFormatter.java
@@ -84,6 +84,14 @@ public class PlainTextFormatter {
return "https://juick.com/m/" + jmsg.getMid();
}
+ public static String markdownUrl(String url, String description) {
+ if (StringUtils.isNotBlank(description)) {
+ return String.format("[%s](%s)", description, url);
+ } else {
+ return url;
+ }
+ }
+
public static String formatPostNumber(com.juick.Message jmsg) {
if (jmsg.getRid() > 0) {
return String.format("%d/%d", jmsg.getMid(), jmsg.getRid());
diff --git a/src/main/java/com/juick/server/api/activity/Profile.java b/src/main/java/com/juick/server/api/activity/Profile.java
index bd8b9ea8..c26941ef 100644
--- a/src/main/java/com/juick/server/api/activity/Profile.java
+++ b/src/main/java/com/juick/server/api/activity/Profile.java
@@ -3,6 +3,7 @@ package com.juick.server.api.activity;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.Message;
import com.juick.User;
+import com.juick.formatters.PlainTextFormatter;
import com.juick.model.CommandResult;
import com.juick.server.ActivityPubManager;
import com.juick.server.CommandsManager;
@@ -259,7 +260,7 @@ public class Profile {
}
@PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
- public ResponseEntity<Void> processInbox(InputStream inboxData) throws Exception {
+ public ResponseEntity<CommandResult> processInbox(InputStream inboxData) throws Exception {
String inbox = IOUtils.toString(inboxData, StandardCharsets.UTF_8);
logger.info("Inbox: {}", inbox);
Activity activity = jsonMapper.readValue(inbox, Activity.class);
@@ -269,7 +270,7 @@ public class Profile {
Follow followRequest = (Follow) activity;
applicationEventPublisher.publishEvent(
new FollowEvent(this, followRequest));
- return new ResponseEntity<>(HttpStatus.ACCEPTED);
+ return new ResponseEntity<>(CommandResult.fromString("Follow request accepted"), HttpStatus.ACCEPTED);
}
if (activity instanceof Undo) {
@@ -278,10 +279,10 @@ public class Profile {
String objectObject = (String) object.get("object");
if (objectType.equals("Follow")) {
applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), objectObject));
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Undo follow request accepted"), HttpStatus.OK);
} else if (objectType.equals("Like") || objectType.equals("Announce")) {
applicationEventPublisher.publishEvent(new UndoAnnounceEvent(this, activity.getActor(), objectObject));
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Undo like/announce request accepted"), HttpStatus.OK);
}
}
if (activity instanceof Create) {
@@ -290,7 +291,7 @@ public class Profile {
if (note.get("type").equals("Note")) {
URI noteId = URI.create((String) note.get("id"));
if (messagesService.replyExists(noteId)) {
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), HttpStatus.OK);
} else {
String inReplyTo = (String) note.get("inReplyTo");
if (StringUtils.isNotBlank(inReplyTo)) {
@@ -298,38 +299,50 @@ public class Profile {
String postId = activityPubManager.postId(inReplyTo);
User user = new User();
user.setUri(URI.create(activity.getActor()));
- String attachment = StringUtils.EMPTY;
- if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) {
- Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0);
- attachment = (String) attachmentObj.get("url");
- }
- String markdown = remarkConverter.convertFragment((String)note.get("content"));
- CommandResult result = commandsManager.processCommand(user, String.format("#%s %s", postId, markdown), URI.create(attachment));
+ String markdown = remarkConverter.convertFragment((String) note.get("content"));
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce((source, url) -> String.format("%s\n%s", source, url))
+ .orElse(markdown);
+
+ CommandResult result = commandsManager.processCommand(
+ user, String.format("#%s %s", postId, commandBody),
+ URI.create(StringUtils.EMPTY));
logger.info(jsonMapper.writeValueAsString(result));
if (result.getNewMessage().isPresent()) {
messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(result, HttpStatus.OK);
} else {
- return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
} else {
Message reply = messagesService.getReplyByUri(inReplyTo);
if (reply != null) {
User user = new User();
user.setUri(URI.create(activity.getActor()));
- String attachment = StringUtils.EMPTY;
- if (note.get("attachment") != null && ((List) note.get("attachment")).size() > 0) {
- Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0);
- attachment = (String) attachmentObj.get("url");
- }
String markdown = remarkConverter.convertFragment((String)note.get("content"));
- CommandResult result = commandsManager.processCommand(user, String.format("#%d/%d %s", reply.getMid(), reply.getRid(), markdown), URI.create(attachment));
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce((source, url) -> String.format("%s\n%s", source, url))
+ .orElse(markdown);
+ CommandResult result = commandsManager.processCommand(
+ user,
+ String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody),
+ URI.create(StringUtils.EMPTY));
logger.info(jsonMapper.writeValueAsString(result));
if (result.getNewMessage().isPresent()) {
messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(result, HttpStatus.OK);
} else {
- return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
}
}
@@ -344,25 +357,25 @@ public class Profile {
URI actor = URI.create(activity.getActor());
URI reply = URI.create((String)tombstone.get("id"));
messagesService.deleteReply(actor, reply);
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.OK);
}
}
if (activity instanceof Like || activity instanceof Announce) {
applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), (String)activity.getObject()));
- return new ResponseEntity<>(HttpStatus.OK);
+ return new ResponseEntity<>(CommandResult.fromString("Like/announce request accepted"), HttpStatus.OK);
}
logger.warn("Unknown activity: {}", jsonMapper.writeValueAsString(activity));
- return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+ return new ResponseEntity<>(CommandResult.fromString("Unknown activity"), HttpStatus.NOT_IMPLEMENTED);
}
if (activity instanceof Delete) {
if (activity.getObject() instanceof String) {
// Delete gone user
if (activity.getActor().equals(activity.getObject())) {
- return new ResponseEntity<>(HttpStatus.ACCEPTED);
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED);
}
}
}
- return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+ return new ResponseEntity<>(CommandResult.fromString("Can not authenticate"), HttpStatus.UNAUTHORIZED);
}
@PostMapping(value = "/u/", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User fetchUser(@RequestParam URI uri) {
diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java
index 9e1e68ea..b1a71c65 100644
--- a/src/test/java/com/juick/server/tests/ServerTests.java
+++ b/src/test/java/com/juick/server/tests/ServerTests.java
@@ -44,6 +44,7 @@ import com.juick.server.util.ImageUtils;
import com.juick.server.www.WebApp;
import com.juick.service.*;
import com.juick.service.component.MessageEvent;
+import com.juick.test.util.MockUtils;
import com.juick.util.DateFormattersHolder;
import com.juick.util.MessageUtils;
import com.mitchellbosecke.pebble.PebbleEngine;
@@ -209,6 +210,8 @@ public class ServerTests {
private Resource cmykJpeg;
@Value("classpath:nojfif.jpg")
private Resource nojfif;
+ @Value("classpath:hubzilla_activity.json")
+ private Resource hubzillaActivity;
@Inject
private KeystoreManager testKeystoreManager;
@@ -1932,4 +1935,22 @@ public class ServerTests {
.andExpect(status().isAccepted());
apClient.setRequestFactory(originalRequestFactory);
}
+
+ @Test
+ public void legacyAvatarEndpoint() throws Exception {
+ mockMvc.perform(get("/api/avatar")
+ .param("uname", "unknown"))
+ .andExpect(status().isOk())
+ .andExpect(content().bytes(IOUtils.toByteArray(defaultAvatar.getInputStream())));
+ }
+ @Test
+ public void federatedAttachmentsAsLinks() throws Exception {
+ int mid = messagesService.createMessage(ugnich.getUid(), "test", StringUtils.EMPTY, Collections.emptyList());
+ Message testMessage = MockUtils.mockMessage(mid, freefd, "reply");
+ }
+ @Test
+ public void hubzillaActor() throws Exception {
+ String activity = IOUtils.toString(hubzillaActivity.getInputStream(), StandardCharsets.UTF_8);
+ Create create = jsonMapper.readValue(activity, Create.class);
+ }
}
diff --git a/src/test/resources/hubzilla_activity.json b/src/test/resources/hubzilla_activity.json
new file mode 100644
index 00000000..b25aca87
--- /dev/null
+++ b/src/test/resources/hubzilla_activity.json
@@ -0,0 +1,185 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ "https://ussr.win/apschema/v1.2"
+ ],
+ "type": "Create",
+ "id": "https://ussr.win/activity/e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2%40ussr.win",
+ "published": "2019-02-14T11:23:29Z",
+ "updated": "2019-02-14T11:38:48Z",
+ "actor": {
+ "type": "Person",
+ "id": "https://ussr.win/channel/zlax",
+ "preferredUsername": "zlax",
+ "name": "ivan zlax",
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/jpeg",
+ "url": "https://ussr.win/photo/profile/l/2",
+ "height": 300,
+ "width": 300
+ },
+ "url": {
+ "type": "Link",
+ "mediaType": "text/html",
+ "href": "https://ussr.win/channel/zlax"
+ },
+ "inbox": "https://ussr.win/inbox/zlax",
+ "outbox": "https://ussr.win/outbox/zlax",
+ "followers": "https://ussr.win/followers/zlax",
+ "following": "https://ussr.win/following/zlax",
+ "endpoints": {
+ "sharedInbox": "https://ussr.win/inbox"
+ },
+ "publicKey": {
+ "id": "https://ussr.win/channel/zlax/public_key_pem",
+ "owner": "https://ussr.win/channel/zlax",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtFSIKFVzo+9NzzY0xho9\nA8CqO0j12f3HEOZUDBDj1NBbQ6Cj1p1f5mB1AlRr+a05fqpraCWIJyCXVTtdyjem\niQ/ObT2PuZIhAY04/ptv78S9EM9ImZi2aDNWp2nWQu23dJajb2TMpIxGNS1M3DVR\ngXwRVEVIEEDzftWx7vF4335/2uJvPfwTzKI3pR972xp1iZCd/G3BtYzUW4swIFSu\n+5ZLKhgwO6A4Ge5+sn2k6x+K56YCgeEEQheOg+PbggIQ68xfvYCQyfuShqAXEadv\nOvQ1dN8WO+tgKqYDRQgILIONB8/2/XXMoQbSVRZs2GmLKXHhIeVKiSZ1vKwHsPx1\nCYUayQo/BB1JpbjkmyDQcHAX+KZ7PQUhpQn976f6Ycp5rznvBH5Zm96VKySaGMy7\nGWefafOfcCNfVZpq17gPPfSbC0XOHdvm6T1e1OPwFl/ho9HxatbA60DIDEd7xf4V\nq9aTSZ2MeQW5JXaxJXSgwucPZ9mhVfucFtCyLdlHZ7gA5yJ+pyYmMe8pjCSNWztD\nUOoJC46vv2l1RbMGTJy2AY9ZuPvyrmJHlsRsTYcmDYRFSk6sgGi1eswSDHlruE3u\nKG4E0nsA8qxzgzpCViQV4DIgXhMInBwo7ZhSyWD+tF2a4tpXorHqmg35x/aqJlaq\nE2xF+yrfh/Kx8O+7P6C1aGcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
+ },
+ "nomadicLocations": [
+ {
+ "id": "https://ussr.win/locs/zlax",
+ "type": "nomadicLocation",
+ "locationAddress": "acct:zlax@ussr.win",
+ "locationPrimary": true,
+ "locationDeleted": false
+ }
+ ]
+ },
+ "object": {
+ "type": "Note",
+ "id": "https://ussr.win/item/e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2%40ussr.win",
+ "published": "2019-02-14T11:23:29Z",
+ "updated": "2019-02-14T11:38:48Z",
+ "url": {
+ "type": "text/html",
+ "rel": "alternate",
+ "href": "https://ussr.win/channel/zlax/?f=&mid=e46a6100e4ba83d602eab5fe0e035405a32d7d36094c95dc85c5c9fcd3d93ab2@ussr.win"
+ },
+ "attributedTo": "https://ussr.win/channel/zlax",
+ "content": "<span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://twitter.com/Dalatrm/status/1095677403198906369\" target=\"_blank\" rel=\"nofollow noopener\" >https://twitter.com/Dalatrm/status/1095677403198906369</a><br /><span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://twitter.com/Dalatrm/status/1095677401433022466\" target=\"_blank\" rel=\"nofollow noopener\" >https://twitter.com/Dalatrm/status/1095677401433022466</a><br /><img class=\"zrl\" src=\"https://ussr.win/photo/6776e315a9692860af53c19518e524f50ab047a4317435403532c922a7c484d4-2.jpg\" style=\"width: 100%; max-width: 590px;\" alt=\"Image/photo\" /><br /><br /><img class=\"zrl\" src=\"https://ussr.win/photo/afb21af40be39ad19589dec5dfa0161b2364b59331bba95aae823e68b3acfbf3-2.jpg\" style=\"width: 100%; max-width: 623px;\" alt=\"Image/photo\" /><br />Продюсер BBC признал постановочными сцены после химатаки в Сирии<br /><span class=\"bookmark-identifier\">#^</span><a class=\"bookmark\" href=\"https://www.vedomosti.ru/politics/news/2019/02/14/794104-bbc-himataki\" target=\"_blank\" rel=\"nofollow noopener\" >https://www.vedomosti.ru/politics/news/2019/02/14/794104-bbc-himataki</a><br /><blockquote>Сцены в госпитале сирийского города Дума после предполагаемой химической атаки были постановочными для достижения «максимального эффекта». Об этом в своем твиттере написал продюсер BBC по Сирии Риам Далати. «Спустя почти шесть месяцев расследования я могу подтвердить без тени сомнения, что сцены в госпитале Думы были постановочные. Погибших в больнице не было», — признал он.<br /><br />Сам факт химической атаки, якобы имевшей место 7 апреля 2018 г., пока не подтвержден Организацией по запрещению химического оружия (ОЗХО). «Атака была, зарин не использовался, но мы должны дождаться, когда ОЗХО подтвердит, использовался ли хлор или что-то еще. Однако все остальное вокруг атаки было создано для максимального эффекта», — написал Далати.<br /><br />Информацию о применении в сирийской Думе химического оружия с использованием хлора и нервно-паралитического агента распространили общественные организации, в том числе «Белые каски». По данным организации, жертвами атаки стали около 70 человек.</blockquote><br />#<a class=\"zrl\" href=\"https://ussr.win/search?tag=bbc\" target=\"_blank\" rel=\"nofollow noopener\" >bbc</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=capitalism\" target=\"_blank\" rel=\"nofollow noopener\" >capitalism</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=conspiracy\" target=\"_blank\" rel=\"nofollow noopener\" >conspiracy</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=history\" target=\"_blank\" rel=\"nofollow noopener\" >history</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=hoax\" target=\"_blank\" rel=\"nofollow noopener\" >hoax</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=metaprogramming\" target=\"_blank\" rel=\"nofollow noopener\" >metaprogramming</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=revision\" target=\"_blank\" rel=\"nofollow noopener\" >revision</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=syria\" target=\"_blank\" rel=\"nofollow noopener\" >syria</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=terrorism\" target=\"_blank\" rel=\"nofollow noopener\" >terrorism</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=uk\" target=\"_blank\" rel=\"nofollow noopener\" >uk</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=war\" target=\"_blank\" rel=\"nofollow noopener\" >war</a> #<a class=\"zrl\" href=\"https://ussr.win/search?tag=whitehelments\" target=\"_blank\" rel=\"nofollow noopener\" >whitehelments</a>",
+ "actor": {
+ "type": "Person",
+ "id": "https://ussr.win/channel/zlax",
+ "preferredUsername": "zlax",
+ "name": "ivan zlax",
+ "icon": {
+ "type": "Image",
+ "mediaType": "image/jpeg",
+ "url": "https://ussr.win/photo/profile/l/2",
+ "height": 300,
+ "width": 300
+ },
+ "url": {
+ "type": "Link",
+ "mediaType": "text/html",
+ "href": "https://ussr.win/channel/zlax"
+ },
+ "inbox": "https://ussr.win/inbox/zlax",
+ "outbox": "https://ussr.win/outbox/zlax",
+ "followers": "https://ussr.win/followers/zlax",
+ "following": "https://ussr.win/following/zlax",
+ "endpoints": {
+ "sharedInbox": "https://ussr.win/inbox"
+ },
+ "publicKey": {
+ "id": "https://ussr.win/channel/zlax/public_key_pem",
+ "owner": "https://ussr.win/channel/zlax",
+ "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtFSIKFVzo+9NzzY0xho9\nA8CqO0j12f3HEOZUDBDj1NBbQ6Cj1p1f5mB1AlRr+a05fqpraCWIJyCXVTtdyjem\niQ/ObT2PuZIhAY04/ptv78S9EM9ImZi2aDNWp2nWQu23dJajb2TMpIxGNS1M3DVR\ngXwRVEVIEEDzftWx7vF4335/2uJvPfwTzKI3pR972xp1iZCd/G3BtYzUW4swIFSu\n+5ZLKhgwO6A4Ge5+sn2k6x+K56YCgeEEQheOg+PbggIQ68xfvYCQyfuShqAXEadv\nOvQ1dN8WO+tgKqYDRQgILIONB8/2/XXMoQbSVRZs2GmLKXHhIeVKiSZ1vKwHsPx1\nCYUayQo/BB1JpbjkmyDQcHAX+KZ7PQUhpQn976f6Ycp5rznvBH5Zm96VKySaGMy7\nGWefafOfcCNfVZpq17gPPfSbC0XOHdvm6T1e1OPwFl/ho9HxatbA60DIDEd7xf4V\nq9aTSZ2MeQW5JXaxJXSgwucPZ9mhVfucFtCyLdlHZ7gA5yJ+pyYmMe8pjCSNWztD\nUOoJC46vv2l1RbMGTJy2AY9ZuPvyrmJHlsRsTYcmDYRFSk6sgGi1eswSDHlruE3u\nKG4E0nsA8qxzgzpCViQV4DIgXhMInBwo7ZhSyWD+tF2a4tpXorHqmg35x/aqJlaq\nE2xF+yrfh/Kx8O+7P6C1aGcCAwEAAQ==\n-----END PUBLIC KEY-----\n"
+ },
+ "nomadicLocations": [
+ {
+ "id": "https://ussr.win/locs/zlax",
+ "type": "nomadicLocation",
+ "locationAddress": "acct:zlax@ussr.win",
+ "locationPrimary": true,
+ "locationDeleted": false
+ }
+ ]
+ },
+ "tag": [
+ {
+ "id": "https://ussr.win/search?tag=metaprogramming",
+ "name": "#metaprogramming"
+ },
+ {
+ "id": "https://ussr.win/search?tag=whitehelments",
+ "name": "#whitehelments"
+ },
+ {
+ "id": "https://ussr.win/search?tag=capitalism",
+ "name": "#capitalism"
+ },
+ {
+ "id": "https://ussr.win/search?tag=conspiracy",
+ "name": "#conspiracy"
+ },
+ {
+ "id": "https://ussr.win/search?tag=terrorism",
+ "name": "#terrorism"
+ },
+ {
+ "id": "https://ussr.win/search?tag=revision",
+ "name": "#revision"
+ },
+ {
+ "id": "https://ussr.win/search?tag=history",
+ "name": "#history"
+ },
+ {
+ "id": "https://ussr.win/search?tag=syria",
+ "name": "#syria"
+ },
+ {
+ "id": "https://ussr.win/search?tag=hoax",
+ "name": "#hoax"
+ },
+ {
+ "id": "https://ussr.win/search?tag=bbc",
+ "name": "#bbc"
+ },
+ {
+ "id": "https://ussr.win/search?tag=war",
+ "name": "#war"
+ },
+ {
+ "id": "https://ussr.win/search?tag=uk",
+ "name": "#uk"
+ }
+ ],
+ "attachment": [
+ {
+ "type": "Image",
+ "url": "https://ussr.win/photo/6776e315a9692860af53c19518e524f50ab047a4317435403532c922a7c484d4-2.jpg"
+ },
+ {
+ "type": "Image",
+ "url": "https://ussr.win/photo/afb21af40be39ad19589dec5dfa0161b2364b59331bba95aae823e68b3acfbf3-2.jpg"
+ }
+ ],
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "cc": [
+ "https://ussr.win/followers/zlax"
+ ]
+ },
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "cc": [
+ "https://ussr.win/followers/zlax"
+ ],
+ "signature": {
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1"
+ ],
+ "type": "RsaSignature2017",
+ "nonce": "c4f8b31f4277d4322a528697d550aefd78a4a7709f0da25285032f7529d3f550",
+ "creator": "https://ussr.win/channel/zlax/public_key_pem",
+ "created": "2019-02-14T11:39:01Z",
+ "signatureValue": "WnXFcSm3nR1Su4QwSVJq6vQ7xdtPQTfs5iTMcAcxasoGHphJaAZRISV1QRPuMhw5jap4Lmb4ZaFRfqGm4M4HND+mI06dC2HlgCh0vnNvGsbcDa2RybSnIUo/xswEa/Mcpf6uy2dcLwaZY6tXOkkJmtZOseN54CUO3bQHbd3KmSiqkvREMXv2+qrVJhohZ4R9tLXamUTkoJmq1wSS2s8XSUlmO2v+Th3OAEoHeiS1SehglvIzua83IKlU2/iAs8akGVyYng5uLO7YeUsmiKHggef1ss8XKcNuhaAW8b3DUTnUwBZCPiiCoeXEQMfYdciCtTAKARHLr5RlHPE7etf1Fqk9Pozo/b2EDpfTxbZ26ZKY02pl4g82u95L43hTmCLbZ+wbKt5M0uHRld+/WkJrkIVS2Vj0MJ84Pp94Ij5A0MaXnBDiIj8YpvbRWWVEdXWKs/N8Dy0c11NiHCPhurZTMQDwv96bBphLQGlTp4USYjbKlS95JyyxbfjRbbubM4Q3MGsZFWbRi7C0exvZiw8zHBGn3hnbnZsDdlDax2weuXsSzHPlw8DxG6XEXZy3GWuK4LlQCVXaa9AcSd9LODVIrxaelr0pdW38a2rot1rlkGHdYPR8oD5zV3fj4kWs3n68mU9VJRTvubbX1zKV8EIX9UvVJ6wuRHqyVOFKJPkKloc="
+ }
+} \ No newline at end of file