summaryrefslogtreecommitdiff
path: root/src/remote/activitypub
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-10-08 15:37:24 +0900
committerGitHub <noreply@github.com>2018-10-08 15:37:24 +0900
commit9c170c426be01773afb15a9868ff3c278e09409c (patch)
tree0229bb52dd9197308d193f4e41bbc11d3dcb95a1 /src/remote/activitypub
parentNew translations ja-JP.yml (Norwegian) (diff)
parentfix(package): update @types/mongodb to version 3.1.10 (#2849) (diff)
downloadmisskey-9c170c426be01773afb15a9868ff3c278e09409c.tar.gz
misskey-9c170c426be01773afb15a9868ff3c278e09409c.tar.bz2
misskey-9c170c426be01773afb15a9868ff3c278e09409c.zip
Merge branch 'develop' into l10n_develop
Diffstat (limited to 'src/remote/activitypub')
-rw-r--r--src/remote/activitypub/kernel/add/index.ts22
-rw-r--r--src/remote/activitypub/kernel/index.ts10
-rw-r--r--src/remote/activitypub/kernel/remove/index.ts22
-rw-r--r--src/remote/activitypub/misc/get-note-html.ts15
-rw-r--r--src/remote/activitypub/models/note.ts12
-rw-r--r--src/remote/activitypub/models/person.ts64
-rw-r--r--src/remote/activitypub/renderer/add.ts9
-rw-r--r--src/remote/activitypub/renderer/announce.ts2
-rw-r--r--src/remote/activitypub/renderer/hashtag.ts2
-rw-r--r--src/remote/activitypub/renderer/note.ts34
-rw-r--r--src/remote/activitypub/renderer/ordered-collection.ts4
-rw-r--r--src/remote/activitypub/renderer/person.ts1
-rw-r--r--src/remote/activitypub/renderer/remove.ts9
-rw-r--r--src/remote/activitypub/renderer/tombstone.ts4
-rw-r--r--src/remote/activitypub/renderer/update.ts14
-rw-r--r--src/remote/activitypub/request.ts28
-rw-r--r--src/remote/activitypub/resolver.ts11
-rw-r--r--src/remote/activitypub/type.ts12
18 files changed, 234 insertions, 41 deletions
diff --git a/src/remote/activitypub/kernel/add/index.ts b/src/remote/activitypub/kernel/add/index.ts
new file mode 100644
index 0000000000..eb2dba5b21
--- /dev/null
+++ b/src/remote/activitypub/kernel/add/index.ts
@@ -0,0 +1,22 @@
+import { IRemoteUser } from '../../../../models/user';
+import { IAdd } from '../../type';
+import { resolveNote } from '../../models/note';
+import { addPinned } from '../../../../services/i/pin';
+
+export default async (actor: IRemoteUser, activity: IAdd): Promise<void> => {
+ if ('actor' in activity && actor.uri !== activity.actor) {
+ throw new Error('invalid actor');
+ }
+
+ if (activity.target == null) {
+ throw new Error('target is null');
+ }
+
+ if (activity.target === actor.featured) {
+ const note = await resolveNote(activity.object);
+ await addPinned(actor, note._id);
+ return;
+ }
+
+ throw new Error(`unknown target: ${activity.target}`);
+};
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
index 752a9bd2e2..52b0efc730 100644
--- a/src/remote/activitypub/kernel/index.ts
+++ b/src/remote/activitypub/kernel/index.ts
@@ -8,6 +8,8 @@ import like from './like';
import announce from './announce';
import accept from './accept';
import reject from './reject';
+import add from './add';
+import remove from './remove';
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
switch (activity.type) {
@@ -31,6 +33,14 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
await reject(actor, activity);
break;
+ case 'Add':
+ await add(actor, activity).catch(err => console.log(err));
+ break;
+
+ case 'Remove':
+ await remove(actor, activity).catch(err => console.log(err));
+ break;
+
case 'Announce':
await announce(actor, activity);
break;
diff --git a/src/remote/activitypub/kernel/remove/index.ts b/src/remote/activitypub/kernel/remove/index.ts
new file mode 100644
index 0000000000..91b207c80d
--- /dev/null
+++ b/src/remote/activitypub/kernel/remove/index.ts
@@ -0,0 +1,22 @@
+import { IRemoteUser } from '../../../../models/user';
+import { IRemove } from '../../type';
+import { resolveNote } from '../../models/note';
+import { removePinned } from '../../../../services/i/pin';
+
+export default async (actor: IRemoteUser, activity: IRemove): Promise<void> => {
+ if ('actor' in activity && actor.uri !== activity.actor) {
+ throw new Error('invalid actor');
+ }
+
+ if (activity.target == null) {
+ throw new Error('target is null');
+ }
+
+ if (activity.target === actor.featured) {
+ const note = await resolveNote(activity.object);
+ await removePinned(actor, note._id);
+ return;
+ }
+
+ throw new Error(`unknown target: ${activity.target}`);
+};
diff --git a/src/remote/activitypub/misc/get-note-html.ts b/src/remote/activitypub/misc/get-note-html.ts
index 8df440930b..0a607bd48c 100644
--- a/src/remote/activitypub/misc/get-note-html.ts
+++ b/src/remote/activitypub/misc/get-note-html.ts
@@ -1,23 +1,10 @@
import { INote } from '../../../models/note';
import toHtml from '../../../mfm/html';
import parse from '../../../mfm/parse';
-import config from '../../../config';
export default function(note: INote) {
- if (note.text == null) return null;
-
let html = toHtml(parse(note.text), note.mentionedRemoteUsers);
-
- if (note.poll != null) {
- const url = `${config.url}/notes/${note._id}`;
- // TODO: i18n
- html += `<p><a href="${url}">【Misskeyで投票を見る】</a></p>`;
- }
-
- if (note.renoteId != null) {
- const url = `${config.url}/notes/${note.renoteId}`;
- html += `<p>RE: <a href="${url}">${url}</a></p>`;
- }
+ if (html == null) html = '';
return html;
}
diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
index 1dfeebfdf7..d49cf53079 100644
--- a/src/remote/activitypub/models/note.ts
+++ b/src/remote/activitypub/models/note.ts
@@ -56,7 +56,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
log(`Creating the Note: ${note.id}`);
// 投稿者をフェッチ
- const actor = await resolvePerson(note.attributedTo) as IRemoteUser;
+ const actor = await resolvePerson(note.attributedTo, null, resolver) as IRemoteUser;
// 投稿者が凍結されていたらスキップ
if (actor.isSuspended) {
@@ -73,16 +73,16 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
visibility = 'followers';
} else {
visibility = 'specified';
- visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri)));
+ visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, null, resolver)));
}
}
//#endergion
- // 添付メディア
+ // 添付ファイル
// TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない
// Noteがsensitiveなら添付もsensitiveにする
- const media = note.attachment
+ const files = note.attachment
.map(attach => attach.sensitive = note.sensitive)
? await Promise.all(note.attachment.map(x => resolveImage(actor, x)))
: [];
@@ -91,7 +91,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
const reply = note.inReplyTo ? await resolveNote(note.inReplyTo, resolver) : null;
// テキストのパース
- const text = htmlToMFM(note.content);
+ const text = note._misskey_content ? note._misskey_content : htmlToMFM(note.content);
// ユーザーの情報が古かったらついでに更新しておく
if (actor.updatedAt == null || Date.now() - actor.updatedAt.getTime() > 1000 * 60 * 60 * 24) {
@@ -100,7 +100,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
return await post(actor, {
createdAt: new Date(note.published),
- media,
+ files: files,
reply,
renote: undefined,
cw: note.summary,
diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts
index 3bd4e16763..ee95e43ad3 100644
--- a/src/remote/activitypub/models/person.ts
+++ b/src/remote/activitypub/models/person.ts
@@ -3,15 +3,16 @@ import { toUnicode } from 'punycode';
import * as debug from 'debug';
import config from '../../../config';
-import User, { validateUsername, isValidName, IUser, IRemoteUser } from '../../../models/user';
+import User, { validateUsername, isValidName, IUser, IRemoteUser, isRemoteUser } from '../../../models/user';
import Resolver from '../resolver';
import { resolveImage } from './image';
-import { isCollectionOrOrderedCollection, IPerson } from '../type';
+import { isCollectionOrOrderedCollection, isCollection, IPerson } from '../type';
import { IDriveFile } from '../../../models/drive-file';
import Meta from '../../../models/meta';
import htmlToMFM from '../../../mfm/html-to-mfm';
import { updateUserStats } from '../../../services/update-chart';
import { URL } from 'url';
+import { resolveNote } from './note';
const log = debug('misskey:activitypub');
@@ -139,6 +140,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
avatarId: null,
bannerId: null,
createdAt: Date.parse(person.published) || null,
+ updatedAt: new Date(),
description: htmlToMFM(person.summary),
followersCount,
followingCount,
@@ -154,6 +156,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
},
inbox: person.inbox,
sharedInbox: person.sharedInbox,
+ featured: person.featured,
endpoints: person.endpoints,
uri: person.id,
url: person.url,
@@ -210,15 +213,18 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<IU
user.bannerUrl = bannerUrl;
//#endregion
+ await updateFeatured(user._id).catch(err => console.log(err));
return user;
}
/**
* Personの情報を更新します。
- *
* Misskeyに対象のPersonが登録されていなければ無視します。
+ * @param uri URI of Person
+ * @param resolver Resolver
+ * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します)
*/
-export async function updatePerson(uri: string, resolver?: Resolver): Promise<void> {
+export async function updatePerson(uri: string, resolver?: Resolver, hint?: object): Promise<void> {
if (typeof uri !== 'string') throw 'uri is not string';
// URIがこのサーバーを指しているならスキップ
@@ -236,7 +242,7 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
if (resolver == null) resolver = new Resolver();
- const object = await resolver.resolve(uri) as any;
+ const object = hint || await resolver.resolve(uri) as any;
const err = validatePerson(object, uri);
@@ -279,6 +285,7 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
updatedAt: new Date(),
inbox: person.inbox,
sharedInbox: person.sharedInbox,
+ featured: person.featured,
avatarId: avatar ? avatar._id : null,
bannerId: banner ? banner._id : null,
avatarUrl: (avatar && avatar.metadata.thumbnailUrl) ? avatar.metadata.thumbnailUrl : (avatar && avatar.metadata.url) ? avatar.metadata.url : null,
@@ -290,9 +297,18 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
name: person.name,
url: person.url,
endpoints: person.endpoints,
- isCat: (person as any).isCat === true ? true : false
+ isBot: object.type == 'Service',
+ isCat: (person as any).isCat === true ? true : false,
+ isLocked: person.manuallyApprovesFollowers,
+ createdAt: Date.parse(person.published) || null,
+ publicKey: {
+ id: person.publicKey.id,
+ publicKeyPem: person.publicKey.publicKeyPem
+ },
}
});
+
+ await updateFeatured(exist._id).catch(err => console.log(err));
}
/**
@@ -301,7 +317,7 @@ export async function updatePerson(uri: string, resolver?: Resolver): Promise<vo
* Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
*/
-export async function resolvePerson(uri: string, verifier?: string): Promise<IUser> {
+export async function resolvePerson(uri: string, verifier?: string, resolver?: Resolver): Promise<IUser> {
if (typeof uri !== 'string') throw 'uri is not string';
//#region このサーバーに既に登録されていたらそれを返す
@@ -313,5 +329,37 @@ export async function resolvePerson(uri: string, verifier?: string): Promise<IUs
//#endregion
// リモートサーバーからフェッチしてきて登録
- return await createPerson(uri);
+ if (resolver == null) resolver = new Resolver();
+ return await createPerson(uri, resolver);
+}
+
+export async function updateFeatured(userId: mongo.ObjectID) {
+ const user = await User.findOne({ _id: userId });
+ if (!isRemoteUser(user)) return;
+ if (!user.featured) return;
+
+ log(`Updating the featured: ${user.uri}`);
+
+ const resolver = new Resolver();
+
+ // Resolve to (Ordered)Collection Object
+ const collection = await resolver.resolveCollection(user.featured);
+ if (!isCollectionOrOrderedCollection(collection)) throw new Error(`Object is not Collection or OrderedCollection`);
+
+ // Resolve to Object(may be Note) arrays
+ const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
+ const items = await resolver.resolve(unresolvedItems);
+ if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
+
+ // Resolve and regist Notes
+ const featuredNotes = await Promise.all(items
+ .filter(item => item.type === 'Note')
+ .slice(0, 5)
+ .map(item => resolveNote(item, resolver)));
+
+ await User.update({ _id: user._id }, {
+ $set: {
+ pinnedNoteIds: featuredNotes.map(note => note._id)
+ }
+ });
}
diff --git a/src/remote/activitypub/renderer/add.ts b/src/remote/activitypub/renderer/add.ts
new file mode 100644
index 0000000000..4d6fe392aa
--- /dev/null
+++ b/src/remote/activitypub/renderer/add.ts
@@ -0,0 +1,9 @@
+import config from '../../../config';
+import { ILocalUser } from '../../../models/user';
+
+export default (user: ILocalUser, target: any, object: any) => ({
+ type: 'Add',
+ actor: `${config.url}/users/${user._id}`,
+ target,
+ object
+});
diff --git a/src/remote/activitypub/renderer/announce.ts b/src/remote/activitypub/renderer/announce.ts
index f6276ade04..18e23cc336 100644
--- a/src/remote/activitypub/renderer/announce.ts
+++ b/src/remote/activitypub/renderer/announce.ts
@@ -5,7 +5,7 @@ export default (object: any, note: INote) => {
const attributedTo = `${config.url}/users/${note.userId}`;
return {
- id: `${config.url}/notes/${note._id}`,
+ id: `${config.url}/notes/${note._id}/activity`,
actor: `${config.url}/users/${note.userId}`,
type: 'Announce',
published: note.createdAt.toISOString(),
diff --git a/src/remote/activitypub/renderer/hashtag.ts b/src/remote/activitypub/renderer/hashtag.ts
index a37ba63532..36563c2df5 100644
--- a/src/remote/activitypub/renderer/hashtag.ts
+++ b/src/remote/activitypub/renderer/hashtag.ts
@@ -3,5 +3,5 @@ import config from '../../../config';
export default (tag: string) => ({
type: 'Hashtag',
href: `${config.url}/tags/${encodeURIComponent(tag)}`,
- name: '#' + tag
+ name: `#${tag}`
});
diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
index 1d169d3088..b3ce1c03e4 100644
--- a/src/remote/activitypub/renderer/note.ts
+++ b/src/remote/activitypub/renderer/note.ts
@@ -6,10 +6,11 @@ import DriveFile, { IDriveFile } from '../../../models/drive-file';
import Note, { INote } from '../../../models/note';
import User from '../../../models/user';
import toHtml from '../misc/get-note-html';
+import parseMfm from '../../../mfm/parse';
export default async function renderNote(note: INote, dive = true): Promise<any> {
- const promisedFiles: Promise<IDriveFile[]> = note.mediaIds
- ? DriveFile.find({ _id: { $in: note.mediaIds } })
+ const promisedFiles: Promise<IDriveFile[]> = note.fileIds
+ ? DriveFile.find({ _id: { $in: note.fileIds } })
: Promise.resolve([]);
let inReplyTo;
@@ -81,12 +82,39 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
const files = await promisedFiles;
+ let text = note.text;
+
+ if (note.poll != null) {
+ if (text == null) text = '';
+ const url = `${config.url}/notes/${note._id}`;
+ // TODO: i18n
+ text += `\n\n[投票を見る](${url})`;
+ }
+
+ if (note.renoteId != null) {
+ if (text == null) text = '';
+ const url = `${config.url}/notes/${note.renoteId}`;
+ text += `\n\nRE: ${url}`;
+ }
+
+ // 省略されたメンションのホストを復元する
+ if (text != null) {
+ text = parseMfm(text).map(x => {
+ if (x.type == 'mention' && x.host == null) {
+ return `${x.content}@${config.host}`;
+ } else {
+ return x.content;
+ }
+ }).join('');
+ }
+
return {
id: `${config.url}/notes/${note._id}`,
type: 'Note',
attributedTo,
summary: note.cw,
- content: toHtml(note),
+ content: toHtml(Object.assign({}, note, { text })),
+ _misskey_content: text,
published: note.createdAt.toISOString(),
to,
cc,
diff --git a/src/remote/activitypub/renderer/ordered-collection.ts b/src/remote/activitypub/renderer/ordered-collection.ts
index 3c448cf873..5461005983 100644
--- a/src/remote/activitypub/renderer/ordered-collection.ts
+++ b/src/remote/activitypub/renderer/ordered-collection.ts
@@ -4,8 +4,9 @@
* @param totalItems Total number of items
* @param first URL of first page (optional)
* @param last URL of last page (optional)
+ * @param orderedItems attached objects (optional)
*/
-export default function(id: string, totalItems: any, first: string, last: string) {
+export default function(id: string, totalItems: any, first?: string, last?: string, orderedItems?: object) {
const page: any = {
id,
type: 'OrderedCollection',
@@ -14,6 +15,7 @@ export default function(id: string, totalItems: any, first: string, last: string
if (first) page.first = first;
if (last) page.last = last;
+ if (orderedItems) page.orderedItems = orderedItems;
return page;
}
diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts
index 78918af368..52485e6959 100644
--- a/src/remote/activitypub/renderer/person.ts
+++ b/src/remote/activitypub/renderer/person.ts
@@ -21,6 +21,7 @@ export default async (user: ILocalUser) => {
outbox: `${id}/outbox`,
followers: `${id}/followers`,
following: `${id}/following`,
+ featured: `${id}/collections/featured`,
sharedInbox: `${config.url}/inbox`,
url: `${config.url}/@${user.username}`,
preferredUsername: user.username,
diff --git a/src/remote/activitypub/renderer/remove.ts b/src/remote/activitypub/renderer/remove.ts
new file mode 100644
index 0000000000..ed840be751
--- /dev/null
+++ b/src/remote/activitypub/renderer/remove.ts
@@ -0,0 +1,9 @@
+import config from '../../../config';
+import { ILocalUser } from '../../../models/user';
+
+export default (user: ILocalUser, target: any, object: any) => ({
+ type: 'Remove',
+ actor: `${config.url}/users/${user._id}`,
+ target,
+ object
+});
diff --git a/src/remote/activitypub/renderer/tombstone.ts b/src/remote/activitypub/renderer/tombstone.ts
new file mode 100644
index 0000000000..553406b93b
--- /dev/null
+++ b/src/remote/activitypub/renderer/tombstone.ts
@@ -0,0 +1,4 @@
+export default (id: string) => ({
+ id,
+ type: 'Tombstone'
+});
diff --git a/src/remote/activitypub/renderer/update.ts b/src/remote/activitypub/renderer/update.ts
new file mode 100644
index 0000000000..cf9acc9acb
--- /dev/null
+++ b/src/remote/activitypub/renderer/update.ts
@@ -0,0 +1,14 @@
+import config from '../../../config';
+import { ILocalUser } from '../../../models/user';
+
+export default (object: any, user: ILocalUser) => {
+ const activity = {
+ id: `${config.url}/users/${user._id}#updates/${new Date().getTime()}`,
+ actor: `${config.url}/users/${user._id}`,
+ type: 'Update',
+ to: [ 'https://www.w3.org/ns/activitystreams#Public' ],
+ object
+ } as any;
+
+ return activity;
+};
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts
index 6238d3acb1..177b6f458e 100644
--- a/src/remote/activitypub/request.ts
+++ b/src/remote/activitypub/request.ts
@@ -2,6 +2,7 @@ import { request } from 'https';
const { sign } = require('http-signature');
import { URL } from 'url';
import * as debug from 'debug';
+const crypto = require('crypto');
import config from '../../config';
import { ILocalUser } from '../../models/user';
@@ -11,22 +12,33 @@ const log = debug('misskey:activitypub:deliver');
export default (user: ILocalUser, url: string, object: any) => new Promise((resolve, reject) => {
log(`--> ${url}`);
+ const timeout = 10 * 1000;
+
const { protocol, hostname, port, pathname, search } = new URL(url);
+ const data = JSON.stringify(object);
+
+ const sha256 = crypto.createHash('sha256');
+ sha256.update(data);
+ const hash = sha256.digest('base64');
+
const req = request({
protocol,
hostname,
port,
method: 'POST',
path: pathname + search,
+ timeout,
headers: {
- 'Content-Type': 'application/activity+json'
+ 'User-Agent': config.user_agent,
+ 'Content-Type': 'application/activity+json',
+ 'Digest': `SHA-256=${hash}`
}
}, res => {
log(`${url} --> ${res.statusCode}`);
if (res.statusCode >= 400) {
- reject();
+ reject(res);
} else {
resolve();
}
@@ -35,7 +47,8 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
sign(req, {
authorizationHeaderName: 'Signature',
key: user.keypair,
- keyId: `${config.url}/users/${user._id}/publickey`
+ keyId: `${config.url}/users/${user._id}/publickey`,
+ headers: ['date', 'host', 'digest']
});
// Signature: Signature ... => Signature: ...
@@ -43,5 +56,12 @@ export default (user: ILocalUser, url: string, object: any) => new Promise((reso
sig = sig.replace(/^Signature /, '');
req.setHeader('Signature', sig);
- req.end(JSON.stringify(object));
+ req.on('timeout', () => req.abort());
+
+ req.on('error', e => {
+ if (req.aborted) reject('timeout');
+ reject(e);
+ });
+
+ req.end(data);
});
diff --git a/src/remote/activitypub/resolver.ts b/src/remote/activitypub/resolver.ts
index 0b053ca774..ff26971758 100644
--- a/src/remote/activitypub/resolver.ts
+++ b/src/remote/activitypub/resolver.ts
@@ -1,12 +1,13 @@
import * as request from 'request-promise-native';
import * as debug from 'debug';
import { IObject } from './type';
-//import config from '../../config';
+import config from '../../config';
const log = debug('misskey:activitypub:resolver');
export default class Resolver {
private history: Set<string>;
+ private timeout = 10 * 1000;
constructor() {
this.history = new Set();
@@ -19,11 +20,11 @@ export default class Resolver {
switch (collection.type) {
case 'Collection':
- collection.objects = collection.object.items;
+ collection.objects = collection.items;
break;
case 'OrderedCollection':
- collection.objects = collection.object.orderedItems;
+ collection.objects = collection.orderedItems;
break;
default:
@@ -50,10 +51,14 @@ export default class Resolver {
const object = await request({
url: value,
+ timeout: this.timeout,
headers: {
+ 'User-Agent': config.user_agent,
Accept: 'application/activity+json, application/ld+json'
},
json: true
+ }).catch(e => {
+ throw new Error(`request error: ${e.message}`);
});
if (object === null || (
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
index 3d40ad48cb..5c06ee4ffe 100644
--- a/src/remote/activitypub/type.ts
+++ b/src/remote/activitypub/type.ts
@@ -40,6 +40,7 @@ export interface IOrderedCollection extends IObject {
export interface INote extends IObject {
type: 'Note';
+ _misskey_content: string;
}
export interface IPerson extends IObject {
@@ -52,6 +53,7 @@ export interface IPerson extends IObject {
publicKey: any;
followers: any;
following: any;
+ featured?: any;
outbox: any;
endpoints: string[];
}
@@ -89,6 +91,14 @@ export interface IReject extends IActivity {
type: 'Reject';
}
+export interface IAdd extends IActivity {
+ type: 'Add';
+}
+
+export interface IRemove extends IActivity {
+ type: 'Remove';
+}
+
export interface ILike extends IActivity {
type: 'Like';
_misskey_reaction: string;
@@ -107,5 +117,7 @@ export type Object =
IFollow |
IAccept |
IReject |
+ IAdd |
+ IRemove |
ILike |
IAnnounce;