Implement announce
And bug fixes
This commit is contained in:
39
src/remote/activitypub/act/announce/index.ts
Normal file
39
src/remote/activitypub/act/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;
|
||||
}
|
||||
};
|
52
src/remote/activitypub/act/announce/note.ts
Normal file
52
src/remote/activitypub/act/announce/note.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import * as debug from 'debug';
|
||||
|
||||
import Resolver from '../../resolver';
|
||||
import Note from '../../../../models/note';
|
||||
import post from '../../../../services/note/create';
|
||||
import { IRemoteUser, isRemoteUser } from '../../../../models/user';
|
||||
import { IAnnounce, INote } from '../../type';
|
||||
import createNote from '../create/note';
|
||||
import resolvePerson from '../../resolve-person';
|
||||
|
||||
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 Note.findOne({ uri });
|
||||
if (exist) {
|
||||
return;
|
||||
}
|
||||
|
||||
// アナウンス元の投稿の投稿者をフェッチ
|
||||
const announcee = await resolvePerson(note.attributedTo);
|
||||
|
||||
const renote = isRemoteUser(announcee)
|
||||
? await createNote(resolver, announcee, note, true)
|
||||
: await Note.findOne({ _id: note.id.split('/').pop() });
|
||||
|
||||
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
|
||||
});
|
||||
}
|
@ -5,6 +5,7 @@ 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) {
|
||||
@ -24,6 +25,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
|
||||
// noop
|
||||
break;
|
||||
|
||||
case 'Announce':
|
||||
await announce(actor, activity);
|
||||
break;
|
||||
|
||||
case 'Like':
|
||||
await like(actor, activity);
|
||||
break;
|
||||
|
@ -7,7 +7,7 @@ export default async (actor: IRemoteUser, activity: ILike) => {
|
||||
const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
|
||||
|
||||
// Transform:
|
||||
// https://misskey.ex/@syuilo/xxxx to
|
||||
// https://misskey.ex/notes/xxxx to
|
||||
// xxxx
|
||||
const noteId = id.split('/').pop();
|
||||
|
||||
|
4
src/remote/activitypub/renderer/announce.ts
Normal file
4
src/remote/activitypub/renderer/announce.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export default object => ({
|
||||
type: 'Announce',
|
||||
object
|
||||
});
|
@ -1,6 +1,7 @@
|
||||
import config from '../../../config';
|
||||
import { ILocalUser } from '../../../models/user';
|
||||
|
||||
export default (user, note) => {
|
||||
export default (user: ILocalUser, note) => {
|
||||
return {
|
||||
type: 'Like',
|
||||
actor: `${config.url}/@${user.username}`,
|
||||
|
@ -3,9 +3,9 @@ import renderHashtag from './hashtag';
|
||||
import config from '../../../config';
|
||||
import DriveFile from '../../../models/drive-file';
|
||||
import Note, { INote } from '../../../models/note';
|
||||
import User, { IUser } from '../../../models/user';
|
||||
import User from '../../../models/user';
|
||||
|
||||
export default async (user: IUser, note: INote) => {
|
||||
export default async (note: INote) => {
|
||||
const promisedFiles = note.mediaIds
|
||||
? DriveFile.find({ _id: { $in: note.mediaIds } })
|
||||
: Promise.resolve([]);
|
||||
@ -30,6 +30,10 @@ export default async (user: IUser, note: INote) => {
|
||||
inReplyTo = null;
|
||||
}
|
||||
|
||||
const user = await User.findOne({
|
||||
_id: note.userId
|
||||
});
|
||||
|
||||
const attributedTo = `${config.url}/@${user.username}`;
|
||||
|
||||
return {
|
||||
|
@ -2,18 +2,18 @@ import { JSDOM } from 'jsdom';
|
||||
import { toUnicode } from 'punycode';
|
||||
import parseAcct from '../../acct/parse';
|
||||
import config from '../../config';
|
||||
import User, { validateUsername, isValidName, isValidDescription } from '../../models/user';
|
||||
import User, { validateUsername, isValidName, isValidDescription, IUser } from '../../models/user';
|
||||
import webFinger from '../webfinger';
|
||||
import Resolver from './resolver';
|
||||
import uploadFromUrl from '../../services/drive/upload-from-url';
|
||||
import { isCollectionOrOrderedCollection } from './type';
|
||||
import { isCollectionOrOrderedCollection, IObject } from './type';
|
||||
|
||||
export default async (value, verifier?: string) => {
|
||||
const id = value.id || value;
|
||||
export default async (value: string | IObject, verifier?: string): Promise<IUser> => {
|
||||
const id = typeof value == 'string' ? value : value.id;
|
||||
const localPrefix = config.url + '/@';
|
||||
|
||||
if (id.startsWith(localPrefix)) {
|
||||
return User.findOne(parseAcct(id.slice(localPrefix)));
|
||||
return await User.findOne(parseAcct(id.substr(localPrefix.length)));
|
||||
}
|
||||
|
||||
const resolver = new Resolver();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as request from 'request-promise-native';
|
||||
import * as debug from 'debug';
|
||||
import { IObject } from './type';
|
||||
//import config from '../../config';
|
||||
|
||||
const log = debug('misskey:activitypub:resolver');
|
||||
|
||||
@ -47,6 +48,11 @@ export default class Resolver {
|
||||
|
||||
this.history.add(value);
|
||||
|
||||
//#region resolve local objects
|
||||
// TODO
|
||||
//if (value.startsWith(`${config.url}/@`)) {
|
||||
//#endregion
|
||||
|
||||
const object = await request({
|
||||
url: value,
|
||||
headers: {
|
||||
@ -60,6 +66,7 @@ export default class Resolver {
|
||||
!object['@context'].includes('https://www.w3.org/ns/activitystreams') :
|
||||
object['@context'] !== 'https://www.w3.org/ns/activitystreams'
|
||||
)) {
|
||||
log(`invalid response: ${JSON.stringify(object, null, 2)}`);
|
||||
throw new Error('invalid response');
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,10 @@ export interface IObject {
|
||||
type: string;
|
||||
id?: string;
|
||||
summary?: string;
|
||||
published?: string;
|
||||
cc?: string[];
|
||||
to?: string[];
|
||||
attributedTo: string;
|
||||
}
|
||||
|
||||
export interface IActivity extends IObject {
|
||||
@ -26,6 +30,10 @@ export interface IOrderedCollection extends IObject {
|
||||
orderedItems: IObject | string | IObject[] | string[];
|
||||
}
|
||||
|
||||
export interface INote extends IObject {
|
||||
type: 'Note';
|
||||
}
|
||||
|
||||
export const isCollection = (object: IObject): object is ICollection =>
|
||||
object.type === 'Collection';
|
||||
|
||||
@ -59,6 +67,10 @@ export interface ILike extends IActivity {
|
||||
type: 'Like';
|
||||
}
|
||||
|
||||
export interface IAnnounce extends IActivity {
|
||||
type: 'Announce';
|
||||
}
|
||||
|
||||
export type Object =
|
||||
ICollection |
|
||||
IOrderedCollection |
|
||||
@ -67,4 +79,5 @@ export type Object =
|
||||
IUndo |
|
||||
IFollow |
|
||||
IAccept |
|
||||
ILike;
|
||||
ILike |
|
||||
IAnnounce;
|
||||
|
Reference in New Issue
Block a user