wip
This commit is contained in:
parent
08beb45935
commit
536277122d
13 changed files with 7 additions and 0 deletions
39
src/remote/activitypub/kernel/announce/index.ts
Normal file
39
src/remote/activitypub/kernel/announce/index.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import * as debug from 'debug';
|
||||
|
||||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import announceNote from './note';
|
||||
import { IAnnounce } from '../../type';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
||||
const uri = activity.id || activity;
|
||||
|
||||
log(`Announce: ${uri}`);
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
log(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
|
||||
switch (object.type) {
|
||||
case 'Note':
|
||||
announceNote(resolver, actor, activity, object);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Unknown announce type: ${object.type}`);
|
||||
break;
|
||||
}
|
||||
};
|
||||
45
src/remote/activitypub/kernel/announce/note.ts
Normal file
45
src/remote/activitypub/kernel/announce/note.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import * as debug from 'debug';
|
||||
|
||||
import Resolver from '../../resolver';
|
||||
import post from '../../../../services/note/create';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import { IAnnounce, INote } from '../../type';
|
||||
import { fetchNote, resolveNote } from '../../objects/note';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
/**
|
||||
* アナウンスアクティビティを捌きます
|
||||
*/
|
||||
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> {
|
||||
const uri = activity.id || activity;
|
||||
|
||||
if (typeof uri !== 'string') {
|
||||
throw new Error('invalid announce');
|
||||
}
|
||||
|
||||
// 既に同じURIを持つものが登録されていないかチェック
|
||||
const exist = await fetchNote(uri);
|
||||
if (exist) {
|
||||
return;
|
||||
}
|
||||
|
||||
const renote = await resolveNote(note);
|
||||
|
||||
log(`Creating the (Re)Note: ${uri}`);
|
||||
|
||||
//#region Visibility
|
||||
let visibility = 'public';
|
||||
if (!activity.to.includes('https://www.w3.org/ns/activitystreams#Public')) visibility = 'unlisted';
|
||||
if (activity.cc.length == 0) visibility = 'private';
|
||||
// TODO
|
||||
if (visibility != 'public') throw new Error('unspported visibility');
|
||||
//#endergion
|
||||
|
||||
await post(actor, {
|
||||
createdAt: new Date(activity.published),
|
||||
renote,
|
||||
visibility,
|
||||
uri
|
||||
});
|
||||
}
|
||||
6
src/remote/activitypub/kernel/create/image.ts
Normal file
6
src/remote/activitypub/kernel/create/image.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { IRemoteUser } from '../../../../models/user';
|
||||
import { createImage } from '../../objects/image';
|
||||
|
||||
export default async function(actor: IRemoteUser, image): Promise<void> {
|
||||
await createImage(image.url, actor);
|
||||
}
|
||||
44
src/remote/activitypub/kernel/create/index.ts
Normal file
44
src/remote/activitypub/kernel/create/index.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import * as debug from 'debug';
|
||||
|
||||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import createNote from './note';
|
||||
import createImage from './image';
|
||||
import { ICreate } from '../../type';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
||||
const uri = activity.id || activity;
|
||||
|
||||
log(`Create: ${uri}`);
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
log(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
|
||||
switch (object.type) {
|
||||
case 'Image':
|
||||
createImage(actor, object);
|
||||
break;
|
||||
|
||||
case 'Note':
|
||||
createNote(resolver, actor, object);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Unknown type: ${object.type}`);
|
||||
break;
|
||||
}
|
||||
};
|
||||
13
src/remote/activitypub/kernel/create/note.ts
Normal file
13
src/remote/activitypub/kernel/create/note.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import Resolver from '../../resolver';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import { createNote, fetchNote } from '../../objects/note';
|
||||
|
||||
/**
|
||||
* 投稿作成アクティビティを捌きます
|
||||
*/
|
||||
export default async function(resolver: Resolver, actor: IRemoteUser, note, silent = false): Promise<void> {
|
||||
const exist = await fetchNote(note);
|
||||
if (exist == null) {
|
||||
await createNote(note);
|
||||
}
|
||||
}
|
||||
36
src/remote/activitypub/kernel/delete/index.ts
Normal file
36
src/remote/activitypub/kernel/delete/index.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import Resolver from '../../resolver';
|
||||
import deleteNote from './note';
|
||||
import Note from '../../../../models/note';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
|
||||
/**
|
||||
* 削除アクティビティを捌きます
|
||||
*/
|
||||
export default async (actor: IRemoteUser, activity): Promise<void> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
const object = await resolver.resolve(activity.object);
|
||||
|
||||
const uri = (object as any).id;
|
||||
|
||||
switch (object.type) {
|
||||
case 'Note':
|
||||
deleteNote(actor, uri);
|
||||
break;
|
||||
|
||||
case 'Tombstone':
|
||||
const note = await Note.findOne({ uri });
|
||||
if (note != null) {
|
||||
deleteNote(actor, uri);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`Unknown type: ${object.type}`);
|
||||
break;
|
||||
}
|
||||
};
|
||||
30
src/remote/activitypub/kernel/delete/note.ts
Normal file
30
src/remote/activitypub/kernel/delete/note.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import * as debug from 'debug';
|
||||
|
||||
import Note from '../../../../models/note';
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async function(actor: IRemoteUser, uri: string): Promise<void> {
|
||||
log(`Deleting the Note: ${uri}`);
|
||||
|
||||
const note = await Note.findOne({ uri });
|
||||
|
||||
if (note == null) {
|
||||
throw new Error('note not found');
|
||||
}
|
||||
|
||||
if (!note.userId.equals(actor._id)) {
|
||||
throw new Error('投稿を削除しようとしているユーザーは投稿の作成者ではありません');
|
||||
}
|
||||
|
||||
Note.update({ _id: note._id }, {
|
||||
$set: {
|
||||
deletedAt: new Date(),
|
||||
text: null,
|
||||
textHtml: null,
|
||||
mediaIds: [],
|
||||
poll: null
|
||||
}
|
||||
});
|
||||
}
|
||||
24
src/remote/activitypub/kernel/follow.ts
Normal file
24
src/remote/activitypub/kernel/follow.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import User, { IRemoteUser } from '../../../models/user';
|
||||
import config from '../../../config';
|
||||
import follow from '../../../services/following/create';
|
||||
import { IFollow } from '../type';
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
if (!id.startsWith(config.url + '/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const followee = await User.findOne({ _id: id.split('/').pop() });
|
||||
|
||||
if (followee === null) {
|
||||
throw new Error('followee not found');
|
||||
}
|
||||
|
||||
if (followee.host != null) {
|
||||
throw new Error('フォローしようとしているユーザーはローカルユーザーではありません');
|
||||
}
|
||||
|
||||
await follow(actor, followee, activity);
|
||||
};
|
||||
51
src/remote/activitypub/kernel/index.ts
Normal file
51
src/remote/activitypub/kernel/index.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { Object } from '../type';
|
||||
import { IRemoteUser } from '../../../models/user';
|
||||
import create from './create';
|
||||
import performDeleteActivity from './delete';
|
||||
import follow from './follow';
|
||||
import undo from './undo';
|
||||
import like from './like';
|
||||
import announce from './announce';
|
||||
|
||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||
switch (activity.type) {
|
||||
case 'Create':
|
||||
await create(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Delete':
|
||||
await performDeleteActivity(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Follow':
|
||||
await follow(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Accept':
|
||||
// noop
|
||||
break;
|
||||
|
||||
case 'Announce':
|
||||
await announce(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Like':
|
||||
await like(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Undo':
|
||||
await undo(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Collection':
|
||||
case 'OrderedCollection':
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`unknown activity type: ${(activity as any).type}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default self;
|
||||
20
src/remote/activitypub/kernel/like.ts
Normal file
20
src/remote/activitypub/kernel/like.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import Note from '../../../models/note';
|
||||
import { IRemoteUser } from '../../../models/user';
|
||||
import { ILike } from '../type';
|
||||
import create from '../../../services/note/reaction/create';
|
||||
|
||||
export default async (actor: IRemoteUser, activity: ILike) => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
// Transform:
|
||||
// https://misskey.ex/notes/xxxx to
|
||||
// xxxx
|
||||
const noteId = id.split('/').pop();
|
||||
|
||||
const note = await Note.findOne({ _id: noteId });
|
||||
if (note === null) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
await create(actor, note, 'pudding');
|
||||
};
|
||||
24
src/remote/activitypub/kernel/undo/follow.ts
Normal file
24
src/remote/activitypub/kernel/undo/follow.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import User, { IRemoteUser } from '../../../../models/user';
|
||||
import config from '../../../../config';
|
||||
import unfollow from '../../../../services/following/delete';
|
||||
import { IFollow } from '../../type';
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
if (!id.startsWith(config.url + '/')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const followee = await User.findOne({ _id: id.split('/').pop() });
|
||||
|
||||
if (followee === null) {
|
||||
throw new Error('followee not found');
|
||||
}
|
||||
|
||||
if (followee.host != null) {
|
||||
throw new Error('フォロー解除しようとしているユーザーはローカルユーザーではありません');
|
||||
}
|
||||
|
||||
await unfollow(actor, followee, activity);
|
||||
};
|
||||
37
src/remote/activitypub/kernel/undo/index.ts
Normal file
37
src/remote/activitypub/kernel/undo/index.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import * as debug from 'debug';
|
||||
|
||||
import { IRemoteUser } from '../../../../models/user';
|
||||
import { IUndo } from '../../type';
|
||||
import unfollow from './follow';
|
||||
import Resolver from '../../resolver';
|
||||
|
||||
const log = debug('misskey:activitypub');
|
||||
|
||||
export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
|
||||
if ('actor' in activity && actor.uri !== activity.actor) {
|
||||
throw new Error('invalid actor');
|
||||
}
|
||||
|
||||
const uri = activity.id || activity;
|
||||
|
||||
log(`Undo: ${uri}`);
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
||||
let object;
|
||||
|
||||
try {
|
||||
object = await resolver.resolve(activity.object);
|
||||
} catch (e) {
|
||||
log(`Resolution failed: ${e}`);
|
||||
throw e;
|
||||
}
|
||||
|
||||
switch (object.type) {
|
||||
case 'Follow':
|
||||
unfollow(actor, object);
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue