AP featured collectionの修正 / Collection Activityの対応 / typeの修正など (#5460)
* resolver type / fix updateFeatured * type ApObject * fix strange type * AP Activity * Collection Activityが失敗したらとりあえず無視
This commit is contained in:
		
							parent
							
								
									3a093f8bd7
								
							
						
					
					
						commit
						e14509574d
					
				
					 10 changed files with 85 additions and 106 deletions
				
			
		|  | @ -120,3 +120,11 @@ export function cumulativeSum(xs: number[]): number[] { | |||
| export function fromEntries(xs: [string, any][]): { [x: string]: any; } { | ||||
| 	return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; }); | ||||
| } | ||||
| 
 | ||||
| export function toArray<T>(x: T | T[] | undefined): T[] { | ||||
| 	return Array.isArray(x) ? x : x != null ? [x] : []; | ||||
| } | ||||
| 
 | ||||
| export function toSingle<T>(x: T | T[] | undefined): T | undefined { | ||||
| 	return Array.isArray(x) ? x[0] : x; | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import Resolver from '../../resolver'; | ||||
| import { IRemoteUser } from '../../../../models/entities/user'; | ||||
| import announceNote from './note'; | ||||
| import { IAnnounce, INote, validPost, getApId } from '../../type'; | ||||
| import { IAnnounce, validPost, getApId } from '../../type'; | ||||
| import { apLogger } from '../../logger'; | ||||
| 
 | ||||
| const logger = apLogger; | ||||
|  | @ -23,7 +23,7 @@ export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => | |||
| 	} | ||||
| 
 | ||||
| 	if (validPost.includes(object.type)) { | ||||
| 		announceNote(resolver, actor, activity, object as INote); | ||||
| 		announceNote(resolver, actor, activity, object); | ||||
| 	} else { | ||||
| 		logger.warn(`Unknown announce type: ${object.type}`); | ||||
| 	} | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import Resolver from '../../resolver'; | ||||
| import post from '../../../../services/note/create'; | ||||
| import { IRemoteUser, User } from '../../../../models/entities/user'; | ||||
| import { IAnnounce, INote, getApId, getApIds } from '../../type'; | ||||
| import { IAnnounce, IObject, getApId, getApIds } from '../../type'; | ||||
| import { fetchNote, resolveNote } from '../../models/note'; | ||||
| import { resolvePerson } from '../../models/person'; | ||||
| import { apLogger } from '../../logger'; | ||||
|  | @ -14,7 +14,7 @@ const logger = apLogger; | |||
| /** | ||||
|  * アナウンスアクティビティを捌きます | ||||
|  */ | ||||
| export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> { | ||||
| export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: IObject): Promise<void> { | ||||
| 	const uri = getApId(activity); | ||||
| 
 | ||||
| 	// アナウンサーが凍結されていたらスキップ
 | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import config from '../../../../config'; | ||||
| import { IBlock } from '../../type'; | ||||
| import { IBlock, getApId } from '../../type'; | ||||
| import block from '../../../../services/blocking/create'; | ||||
| import { apLogger } from '../../logger'; | ||||
| import { Users } from '../../../../models'; | ||||
|  | @ -8,10 +8,9 @@ import { IRemoteUser } from '../../../../models/entities/user'; | |||
| const logger = apLogger; | ||||
| 
 | ||||
| export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => { | ||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||
| 	if (id == null) throw new Error('missing id'); | ||||
| 	const id = getApId(activity.object); | ||||
| 
 | ||||
| 	const uri = activity.id || activity; | ||||
| 	const uri = getApId(activity); | ||||
| 
 | ||||
| 	logger.info(`Block: ${uri}`); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,13 @@ | |||
| import Resolver from '../../resolver'; | ||||
| import { IRemoteUser } from '../../../../models/entities/user'; | ||||
| import { createNote, fetchNote } from '../../models/note'; | ||||
| import { getApId } from '../../type'; | ||||
| import { getApId, IObject } from '../../type'; | ||||
| import { getApLock } from '../../../../misc/app-lock'; | ||||
| 
 | ||||
| /** | ||||
|  * 投稿作成アクティビティを捌きます | ||||
|  */ | ||||
| export default async function(resolver: Resolver, actor: IRemoteUser, note: any, silent = false): Promise<void> { | ||||
| export default async function(resolver: Resolver, actor: IRemoteUser, note: IObject, silent = false): Promise<void> { | ||||
| 	const uri = getApId(note); | ||||
| 
 | ||||
| 	const unlock = await getApLock(uri); | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { Object } from '../type'; | ||||
| import { IObject, isCreate, isDelete, isUpdate, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection  } from '../type'; | ||||
| import { IRemoteUser } from '../../../models/entities/user'; | ||||
| import create from './create'; | ||||
| import performDeleteActivity from './delete'; | ||||
|  | @ -13,68 +13,53 @@ import add from './add'; | |||
| import remove from './remove'; | ||||
| import block from './block'; | ||||
| import { apLogger } from '../logger'; | ||||
| import Resolver from '../resolver'; | ||||
| import { toArray } from '../../../prelude/array'; | ||||
| 
 | ||||
| const self = async (actor: IRemoteUser, activity: Object): Promise<void> => { | ||||
| export async function performActivity(actor: IRemoteUser, activity: IObject) { | ||||
| 	if (isCollectionOrOrderedCollection(activity)) { | ||||
| 		const resolver = new Resolver(); | ||||
| 		for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { | ||||
| 			const act = await resolver.resolve(item); | ||||
| 			try { | ||||
| 				await performOneActivity(actor, act); | ||||
| 			} catch (e) { | ||||
| 				apLogger.error(e); | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		await performOneActivity(actor, activity); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| async function performOneActivity(actor: IRemoteUser, activity: IObject): Promise<void> { | ||||
| 	if (actor.isSuspended) return; | ||||
| 
 | ||||
| 	switch (activity.type) { | ||||
| 	case 'Create': | ||||
| 	if (isCreate(activity)) { | ||||
| 		await create(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Delete': | ||||
| 	} else if (isDelete(activity)) { | ||||
| 		await performDeleteActivity(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Update': | ||||
| 	} else if (isUpdate(activity)) { | ||||
| 		await performUpdateActivity(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Follow': | ||||
| 	} else if (isFollow(activity)) { | ||||
| 		await follow(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Accept': | ||||
| 	} else if (isAccept(activity)) { | ||||
| 		await accept(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Reject': | ||||
| 	} else if (isReject(activity)) { | ||||
| 		await reject(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Add': | ||||
| 	} else if (isAdd(activity)) { | ||||
| 		await add(actor, activity).catch(err => apLogger.error(err)); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Remove': | ||||
| 	} else if (isRemove(activity)) { | ||||
| 		await remove(actor, activity).catch(err => apLogger.error(err)); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Announce': | ||||
| 	} else if (isAnnounce(activity)) { | ||||
| 		await announce(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Like': | ||||
| 	} else if (isLike(activity)) { | ||||
| 		await like(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Undo': | ||||
| 	} else if (isUndo(activity)) { | ||||
| 		await undo(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Block': | ||||
| 	} else if (isBlock(activity)) { | ||||
| 		await block(actor, activity); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'Collection': | ||||
| 	case 'OrderedCollection': | ||||
| 		// TODO
 | ||||
| 		break; | ||||
| 
 | ||||
| 	default: | ||||
| 	} else { | ||||
| 		apLogger.warn(`unknown activity type: ${(activity as any).type}`); | ||||
| 		return; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| export default self; | ||||
| } | ||||
|  |  | |||
|  | @ -26,6 +26,8 @@ import { UserProfile } from '../../../models/entities/user-profile'; | |||
| import { validActor } from '../../../remote/activitypub/type'; | ||||
| import { getConnection } from 'typeorm'; | ||||
| import { ensure } from '../../../prelude/ensure'; | ||||
| import { toArray } from '../../../prelude/array'; | ||||
| 
 | ||||
| const logger = apLogger; | ||||
| 
 | ||||
| /** | ||||
|  | @ -463,8 +465,7 @@ export async function updateFeatured(userId: User['id']) { | |||
| 
 | ||||
| 	// 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`); | ||||
| 	const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x))); | ||||
| 
 | ||||
| 	// Resolve and regist Notes
 | ||||
| 	const limit = promiseLimit<Note | null>(2); | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { Object } from './type'; | ||||
| import { IObject } from './type'; | ||||
| import { IRemoteUser } from '../../models/entities/user'; | ||||
| import kernel from './kernel'; | ||||
| import { performActivity } from './kernel'; | ||||
| 
 | ||||
| export default async (actor: IRemoteUser, activity: Object): Promise<void> => { | ||||
| 	await kernel(actor, activity); | ||||
| export default async (actor: IRemoteUser, activity: IObject): Promise<void> => { | ||||
| 	await performActivity(actor, activity); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import * as request from 'request-promise-native'; | ||||
| import { IObject } from './type'; | ||||
| import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type'; | ||||
| import config from '../../config'; | ||||
| 
 | ||||
| export default class Resolver { | ||||
|  | @ -14,31 +14,19 @@ export default class Resolver { | |||
| 		return Array.from(this.history); | ||||
| 	} | ||||
| 
 | ||||
| 	public async resolveCollection(value: any) { | ||||
| 	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> { | ||||
| 		const collection = typeof value === 'string' | ||||
| 			? await this.resolve(value) | ||||
| 			: value; | ||||
| 
 | ||||
| 		switch (collection.type) { | ||||
| 			case 'Collection': { | ||||
| 				collection.objects = collection.items; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			case 'OrderedCollection': { | ||||
| 				collection.objects = collection.orderedItems; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			default: { | ||||
| 				throw new Error(`unknown collection type: ${collection.type}`); | ||||
| 			} | ||||
| 		if (isCollectionOrOrderedCollection(collection)) { | ||||
| 			return collection; | ||||
| 		} else { | ||||
| 			throw new Error(`unknown collection type: ${collection.type}`); | ||||
| 		} | ||||
| 
 | ||||
| 		return collection; | ||||
| 	} | ||||
| 
 | ||||
| 	public async resolve(value: any): Promise<IObject> { | ||||
| 	public async resolve(value: string | IObject): Promise<IObject> { | ||||
| 		if (value == null) { | ||||
| 			throw new Error('resolvee is null (or undefined)'); | ||||
| 		} | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| export type obj = { [x: string]: any }; | ||||
| export type ApObject = IObject | string | (IObject | string)[]; | ||||
| 
 | ||||
| export interface IObject { | ||||
| 	'@context': string | obj | obj[]; | ||||
|  | @ -6,9 +7,9 @@ export interface IObject { | |||
| 	id?: string; | ||||
| 	summary?: string; | ||||
| 	published?: string; | ||||
| 	cc?: IObject | string | (IObject | string)[]; | ||||
| 	to?: IObject | string | (IObject | string)[]; | ||||
| 	attributedTo: IObject | string | (IObject | string)[]; | ||||
| 	cc?: ApObject; | ||||
| 	to?: ApObject; | ||||
| 	attributedTo: ApObject; | ||||
| 	attachment?: any[]; | ||||
| 	inReplyTo?: any; | ||||
| 	replies?: ICollection; | ||||
|  | @ -26,7 +27,7 @@ export interface IObject { | |||
| /** | ||||
|  * Get array of ActivityStreams Objects id | ||||
|  */ | ||||
| export function getApIds(value: IObject | string | (IObject | string)[] | undefined): string[] { | ||||
| export function getApIds(value: ApObject | undefined): string[] { | ||||
| 	if (value == null) return []; | ||||
| 	const array = Array.isArray(value) ? value : [value]; | ||||
| 	return array.map(x => getApId(x)); | ||||
|  | @ -35,7 +36,7 @@ export function getApIds(value: IObject | string | (IObject | string)[] | undefi | |||
| /** | ||||
|  * Get first ActivityStreams Object id | ||||
|  */ | ||||
| export function getOneApId(value: IObject | string | (IObject | string)[]): string { | ||||
| export function getOneApId(value: ApObject): string { | ||||
| 	const firstOne = Array.isArray(value) ? value[0] : value; | ||||
| 	return getApId(firstOne); | ||||
| } | ||||
|  | @ -59,13 +60,13 @@ export interface IActivity extends IObject { | |||
| export interface ICollection extends IObject { | ||||
| 	type: 'Collection'; | ||||
| 	totalItems: number; | ||||
| 	items: IObject | string | IObject[] | string[]; | ||||
| 	items: ApObject; | ||||
| } | ||||
| 
 | ||||
| export interface IOrderedCollection extends IObject { | ||||
| 	type: 'OrderedCollection'; | ||||
| 	totalItems: number; | ||||
| 	orderedItems: IObject | string | IObject[] | string[]; | ||||
| 	orderedItems: ApObject; | ||||
| } | ||||
| 
 | ||||
| export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video']; | ||||
|  | @ -170,18 +171,15 @@ export interface IBlock extends IActivity { | |||
| 	type: 'Block'; | ||||
| } | ||||
| 
 | ||||
| export type Object = | ||||
| 	ICollection | | ||||
| 	IOrderedCollection | | ||||
| 	ICreate | | ||||
| 	IDelete | | ||||
| 	IUpdate | | ||||
| 	IUndo | | ||||
| 	IFollow | | ||||
| 	IAccept | | ||||
| 	IReject | | ||||
| 	IAdd | | ||||
| 	IRemove | | ||||
| 	ILike | | ||||
| 	IAnnounce | | ||||
| 	IBlock; | ||||
| export const isCreate = (object: IObject): object is ICreate => object.type === 'Create'; | ||||
| export const isDelete = (object: IObject): object is IDelete => object.type === 'Delete'; | ||||
| export const isUpdate = (object: IObject): object is IUpdate => object.type === 'Update'; | ||||
| export const isUndo = (object: IObject): object is IUndo => object.type === 'Undo'; | ||||
| export const isFollow = (object: IObject): object is IFollow => object.type === 'Follow'; | ||||
| export const isAccept = (object: IObject): object is IAccept => object.type === 'Accept'; | ||||
| export const isReject = (object: IObject): object is IReject => object.type === 'Reject'; | ||||
| export const isAdd = (object: IObject): object is IAdd => object.type === 'Add'; | ||||
| export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove'; | ||||
| export const isLike = (object: IObject): object is ILike => object.type === 'Like'; | ||||
| export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce'; | ||||
| export const isBlock = (object: IObject): object is IBlock => object.type === 'Block'; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue