From d6a8889d84f17f4e4530d4b5dcb555d1f3978b01 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 9 May 2020 08:25:23 +0900
Subject: [PATCH] Receive Flag (#6331)

---
 src/models/repositories/user.ts             |  2 +-
 src/remote/activitypub/kernel/flag/index.ts | 28 +++++++++++++++++++++
 src/remote/activitypub/kernel/index.ts      |  5 +++-
 src/remote/activitypub/type.ts              |  9 +++++--
 4 files changed, 40 insertions(+), 4 deletions(-)
 create mode 100644 src/remote/activitypub/kernel/flag/index.ts

diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts
index c6bc35030..c4c9503e0 100644
--- a/src/models/repositories/user.ts
+++ b/src/models/repositories/user.ts
@@ -289,7 +289,7 @@ export class UserRepository extends Repository<User> {
 
 	//#region Validators
 	public validateLocalUsername = $.str.match(/^\w{1,20}$/);
-	public validateRemoteUsername = $.str.match(/^\w([\w-]*\w)?$/);
+	public validateRemoteUsername = $.str.match(/^\w([\w-.]*\w)?$/);
 	public validatePassword = $.str.min(1);
 	public validateName = $.str.min(1).max(50);
 	public validateDescription = $.str.min(1).max(500);
diff --git a/src/remote/activitypub/kernel/flag/index.ts b/src/remote/activitypub/kernel/flag/index.ts
new file mode 100644
index 000000000..9b3065b11
--- /dev/null
+++ b/src/remote/activitypub/kernel/flag/index.ts
@@ -0,0 +1,28 @@
+import { IRemoteUser } from '../../../../models/entities/user';
+import config from '../../../../config';
+import { IFlag, getApIds } from '../../type';
+import { AbuseUserReports, Users } from '../../../../models';
+import { In } from 'typeorm';
+import { genId } from '../../../../misc/gen-id';
+
+export default async (actor: IRemoteUser, activity: IFlag): Promise<string> => {
+	// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
+	// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
+	const uris = getApIds(activity.object);
+
+	const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop());
+	const users = await Users.find({
+		id: In(userIds)
+	});
+	if (users.length < 1) return `skip`;
+
+	await AbuseUserReports.insert({
+		id: genId(),
+		createdAt: new Date(),
+		userId: users[0].id,
+		reporterId: actor.id,
+		comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`
+	});
+
+	return `ok`;
+};
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
index 615edff88..abf5a436c 100644
--- a/src/remote/activitypub/kernel/index.ts
+++ b/src/remote/activitypub/kernel/index.ts
@@ -1,4 +1,4 @@
-import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection } from '../type';
+import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type';
 import { IRemoteUser } from '../../../models/entities/user';
 import create from './create';
 import performDeleteActivity from './delete';
@@ -13,6 +13,7 @@ import reject from './reject';
 import add from './add';
 import remove from './remove';
 import block from './block';
+import flag from './flag';
 import { apLogger } from '../logger';
 import Resolver from '../resolver';
 import { toArray } from '../../../prelude/array';
@@ -62,6 +63,8 @@ async function performOneActivity(actor: IRemoteUser, activity: IObject): Promis
 		await undo(actor, activity);
 	} else if (isBlock(activity)) {
 		await block(actor, activity);
+	} else if (isFlag(activity)) {
+		await flag(actor, activity);
 	} else {
 		apLogger.warn(`unknown activity type: ${(activity as any).type}`);
 	}
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
index 1a1a9d647..d47a0967f 100644
--- a/src/remote/activitypub/type.ts
+++ b/src/remote/activitypub/type.ts
@@ -120,10 +120,10 @@ interface IQuestionChoice {
 	_misskey_votes?: number;
 }
 
-export const validActor = ['Person', 'Service', 'Group', 'Organization'];
+export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application'];
 
 export interface IPerson extends IObject {
-	type: 'Person';
+	type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application';
 	name?: string;
 	preferredUsername?: string;
 	manuallyApprovesFollowers?: boolean;
@@ -243,6 +243,10 @@ export interface IBlock extends IActivity {
 	type: 'Block';
 }
 
+export interface IFlag extends IActivity {
+	type: 'Flag';
+}
+
 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';
@@ -256,3 +260,4 @@ export const isRemove = (object: IObject): object is IRemove => object.type ===
 export const isLike = (object: IObject): object is ILike => object.type === 'Like' || object.type === 'EmojiReaction' || object.type === 'EmojiReact';
 export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce';
 export const isBlock = (object: IObject): object is IBlock => object.type === 'Block';
+export const isFlag = (object: IObject): object is IFlag => object.type === 'Flag';