User blocking (Following part) (#3035)
* block wip * UndoBlock * UnBlock * wip * follow * UI * fix
This commit is contained in:
		
							parent
							
								
									bcb0588409
								
							
						
					
					
						commit
						d64dc45899
					
				
					 17 changed files with 537 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -1203,6 +1203,9 @@ desktop/views/pages/user/user.profile.vue:
 | 
			
		|||
  mute: "ミュートする"
 | 
			
		||||
  muted: "ミュートしています"
 | 
			
		||||
  unmute: "ミュート解除"
 | 
			
		||||
  block: "ブロックする"
 | 
			
		||||
  unblock: "ブロック解除"
 | 
			
		||||
  block-confirm: "このユーザーをブロックしますか?"
 | 
			
		||||
  push-to-a-list: "リストに追加"
 | 
			
		||||
  list-pushed: "{user}を{list}に追加しました。"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,10 @@
 | 
			
		|||
			<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
 | 
			
		||||
			<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
 | 
			
		||||
		</ui-button>
 | 
			
		||||
		<ui-button @click="user.isBlocking ? unblock() : block()" v-if="$store.state.i.id != user.id">
 | 
			
		||||
			<span v-if="user.isBlocking">%fa:user% %i18n:@unblock%</span>
 | 
			
		||||
			<span v-if="!user.isBlocking">%fa:user-slash% %i18n:@block%</span>
 | 
			
		||||
		</ui-button>
 | 
			
		||||
		<ui-button @click="list">%fa:list% %i18n:@push-to-a-list%</ui-button>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +70,27 @@ export default Vue.extend({
 | 
			
		|||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		block() {
 | 
			
		||||
			if (!window.confirm('%i18n:@block-confirm%')) return;
 | 
			
		||||
			(this as any).api('blocking/create', {
 | 
			
		||||
				userId: this.user.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.user.isBlocking = true;
 | 
			
		||||
			}, () => {
 | 
			
		||||
				alert('error');
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		unblock() {
 | 
			
		||||
			(this as any).api('blocking/delete', {
 | 
			
		||||
				userId: this.user.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				this.user.isBlocking = false;
 | 
			
		||||
			}, () => {
 | 
			
		||||
				alert('error');
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		list() {
 | 
			
		||||
			const w = (this as any).os.new(MkUserListsWindow);
 | 
			
		||||
			w.$once('choosen', async list => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										41
									
								
								src/models/blocking.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/models/blocking.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
import * as mongo from 'mongodb';
 | 
			
		||||
import db from '../db/mongodb';
 | 
			
		||||
import isObjectId from '../misc/is-objectid';
 | 
			
		||||
 | 
			
		||||
const Blocking = db.get<IBlocking>('blocking');
 | 
			
		||||
Blocking.createIndex(['blockerId', 'blockeeId'], { unique: true });
 | 
			
		||||
export default Blocking;
 | 
			
		||||
 | 
			
		||||
export type IBlocking = {
 | 
			
		||||
	_id: mongo.ObjectID;
 | 
			
		||||
	createdAt: Date;
 | 
			
		||||
	blockeeId: mongo.ObjectID;
 | 
			
		||||
	blockerId: mongo.ObjectID;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Blockingを物理削除します
 | 
			
		||||
 */
 | 
			
		||||
export async function deleteBlocking(blocking: string | mongo.ObjectID | IBlocking) {
 | 
			
		||||
	let f: IBlocking;
 | 
			
		||||
 | 
			
		||||
	// Populate
 | 
			
		||||
	if (isObjectId(blocking)) {
 | 
			
		||||
		f = await Blocking.findOne({
 | 
			
		||||
			_id: blocking
 | 
			
		||||
		});
 | 
			
		||||
	} else if (typeof blocking === 'string') {
 | 
			
		||||
		f = await Blocking.findOne({
 | 
			
		||||
			_id: new mongo.ObjectID(blocking)
 | 
			
		||||
		});
 | 
			
		||||
	} else {
 | 
			
		||||
		f = blocking as IBlocking;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (f == null) return;
 | 
			
		||||
 | 
			
		||||
	// このBlockingを削除
 | 
			
		||||
	await Blocking.remove({
 | 
			
		||||
		_id: f._id
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import db from '../db/mongodb';
 | 
			
		|||
import isObjectId from '../misc/is-objectid';
 | 
			
		||||
import Note, { packMany as packNoteMany, deleteNote } from './note';
 | 
			
		||||
import Following, { deleteFollowing } from './following';
 | 
			
		||||
import Blocking, { deleteBlocking } from './blocking';
 | 
			
		||||
import Mute, { deleteMute } from './mute';
 | 
			
		||||
import { getFriendIds } from '../server/api/common/get-friends';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +276,16 @@ export async function deleteUser(user: string | mongo.ObjectID | IUser) {
 | 
			
		|||
		await FollowRequest.find({ followeeId: u._id })
 | 
			
		||||
	).map(x => deleteFollowRequest(x)));
 | 
			
		||||
 | 
			
		||||
	// このユーザーのBlockingをすべて削除
 | 
			
		||||
	await Promise.all((
 | 
			
		||||
		await Blocking.find({ blockerId: u._id })
 | 
			
		||||
	).map(x => deleteBlocking(x)));
 | 
			
		||||
 | 
			
		||||
	// このユーザーへのBlockingをすべて削除
 | 
			
		||||
	await Promise.all((
 | 
			
		||||
		await Blocking.find({ blockeeId: u._id })
 | 
			
		||||
	).map(x => deleteBlocking(x)));
 | 
			
		||||
 | 
			
		||||
	// このユーザーのSwSubscriptionをすべて削除
 | 
			
		||||
	await Promise.all((
 | 
			
		||||
		await SwSubscription.find({ userId: u._id })
 | 
			
		||||
| 
						 | 
				
			
			@ -427,7 +438,7 @@ export const pack = (
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (meId && !meId.equals(_user.id)) {
 | 
			
		||||
		const [following1, following2, followReq1, followReq2, mute] = await Promise.all([
 | 
			
		||||
		const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([
 | 
			
		||||
			Following.findOne({
 | 
			
		||||
				followerId: meId,
 | 
			
		||||
				followeeId: _user.id
 | 
			
		||||
| 
						 | 
				
			
			@ -444,6 +455,14 @@ export const pack = (
 | 
			
		|||
				followerId: _user.id,
 | 
			
		||||
				followeeId: meId
 | 
			
		||||
			}),
 | 
			
		||||
			Blocking.findOne({
 | 
			
		||||
				blockerId: meId,
 | 
			
		||||
				blockeeId: _user.id
 | 
			
		||||
			}),
 | 
			
		||||
			Blocking.findOne({
 | 
			
		||||
				blockerId: _user.id,
 | 
			
		||||
				blockeeId: meId
 | 
			
		||||
			}),
 | 
			
		||||
			Mute.findOne({
 | 
			
		||||
				muterId: meId,
 | 
			
		||||
				muteeId: _user.id
 | 
			
		||||
| 
						 | 
				
			
			@ -460,6 +479,12 @@ export const pack = (
 | 
			
		|||
		// Whether the user is followed
 | 
			
		||||
		_user.isFollowed = following2 !== null;
 | 
			
		||||
 | 
			
		||||
		// Whether the user is blocking
 | 
			
		||||
		_user.isBlocking = toBlocking !== null;
 | 
			
		||||
 | 
			
		||||
		// Whether the user is blocked
 | 
			
		||||
		_user.isBlocked = fromBlocked !== null;
 | 
			
		||||
 | 
			
		||||
		// Whether the user is muted
 | 
			
		||||
		_user.isMuted = mute !== null;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										34
									
								
								src/remote/activitypub/kernel/block/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/remote/activitypub/kernel/block/index.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
import * as mongo from 'mongodb';
 | 
			
		||||
import User, { IRemoteUser } from '../../../../models/user';
 | 
			
		||||
import config from '../../../../config';
 | 
			
		||||
import * as debug from 'debug';
 | 
			
		||||
import { IBlock } from '../../type';
 | 
			
		||||
import block from '../../../../services/blocking/create';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:activitypub');
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
 | 
			
		||||
	const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
 | 
			
		||||
 | 
			
		||||
	const uri = activity.id || activity;
 | 
			
		||||
 | 
			
		||||
	log(`Block: ${uri}`);
 | 
			
		||||
 | 
			
		||||
	if (!id.startsWith(config.url + '/')) {
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const blockee = await User.findOne({
 | 
			
		||||
		_id: new mongo.ObjectID(id.split('/').pop())
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (blockee === null) {
 | 
			
		||||
		throw new Error('blockee not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blockee.host != null) {
 | 
			
		||||
		throw new Error('ブロックしようとしているユーザーはローカルユーザーではありません');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block(actor, blockee);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ import accept from './accept';
 | 
			
		|||
import reject from './reject';
 | 
			
		||||
import add from './add';
 | 
			
		||||
import remove from './remove';
 | 
			
		||||
import block from './block';
 | 
			
		||||
 | 
			
		||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		||||
	switch (activity.type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +54,10 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		|||
		await undo(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Block':
 | 
			
		||||
		await block(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Collection':
 | 
			
		||||
	case 'OrderedCollection':
 | 
			
		||||
		// TODO
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										34
									
								
								src/remote/activitypub/kernel/undo/block.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/remote/activitypub/kernel/undo/block.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
import * as mongo from 'mongodb';
 | 
			
		||||
import User, { IRemoteUser } from '../../../../models/user';
 | 
			
		||||
import config from '../../../../config';
 | 
			
		||||
import * as debug from 'debug';
 | 
			
		||||
import { IBlock } from '../../type';
 | 
			
		||||
import unblock from '../../../../services/blocking/delete';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:activitypub');
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
 | 
			
		||||
	const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
 | 
			
		||||
 | 
			
		||||
	const uri = activity.id || activity;
 | 
			
		||||
 | 
			
		||||
	log(`UnBlock: ${uri}`);
 | 
			
		||||
 | 
			
		||||
	if (!id.startsWith(config.url + '/')) {
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const blockee = await User.findOne({
 | 
			
		||||
		_id: new mongo.ObjectID(id.split('/').pop())
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (blockee === null) {
 | 
			
		||||
		throw new Error('blockee not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (blockee.host != null) {
 | 
			
		||||
		throw new Error('ブロック解除しようとしているユーザーはローカルユーザーではありません');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unblock(actor, blockee);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
import * as debug from 'debug';
 | 
			
		||||
 | 
			
		||||
import { IRemoteUser } from '../../../../models/user';
 | 
			
		||||
import { IUndo, IFollow } from '../../type';
 | 
			
		||||
import { IUndo, IFollow, IBlock } from '../../type';
 | 
			
		||||
import unfollow from './follow';
 | 
			
		||||
import unblock from './block';
 | 
			
		||||
import Resolver from '../../resolver';
 | 
			
		||||
 | 
			
		||||
const log = debug('misskey:activitypub');
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,9 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => {
 | 
			
		|||
		case 'Follow':
 | 
			
		||||
			unfollow(actor, object as IFollow);
 | 
			
		||||
			break;
 | 
			
		||||
		case 'Block':
 | 
			
		||||
			unblock(actor, object as IBlock);
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return null;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								src/remote/activitypub/renderer/block.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/remote/activitypub/renderer/block.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import config from '../../../config';
 | 
			
		||||
import { ILocalUser, IRemoteUser } from "../../../models/user";
 | 
			
		||||
 | 
			
		||||
export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({
 | 
			
		||||
	type: 'Block',
 | 
			
		||||
	actor: `${config.url}/users/${blocker._id}`,
 | 
			
		||||
	object: blockee.uri
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -108,6 +108,10 @@ export interface IAnnounce extends IActivity {
 | 
			
		|||
	type: 'Announce';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IBlock extends IActivity {
 | 
			
		||||
	type: 'Block';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Object =
 | 
			
		||||
	ICollection |
 | 
			
		||||
	IOrderedCollection |
 | 
			
		||||
| 
						 | 
				
			
			@ -120,4 +124,5 @@ export type Object =
 | 
			
		|||
	IAdd |
 | 
			
		||||
	IRemove |
 | 
			
		||||
	ILike |
 | 
			
		||||
	IAnnounce;
 | 
			
		||||
	IAnnounce |
 | 
			
		||||
	IBlock;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										75
									
								
								src/server/api/endpoints/blocking/create.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/server/api/endpoints/blocking/create.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
			
		||||
const ms = require('ms');
 | 
			
		||||
import User, { pack, ILocalUser } from '../../../../models/user';
 | 
			
		||||
import Blocking from '../../../../models/blocking';
 | 
			
		||||
import create from '../../../../services/blocking/create';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	stability: 'stable',
 | 
			
		||||
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': '指定したユーザーをブロックします。',
 | 
			
		||||
		'en-US': 'Block a user.'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
		duration: ms('1hour'),
 | 
			
		||||
		max: 100
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'following-write',
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		userId: $.type(ID).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '対象のユーザーのID',
 | 
			
		||||
				'en-US': 'Target user ID'
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) return rej(psErr);
 | 
			
		||||
 | 
			
		||||
	const blocker = user;
 | 
			
		||||
 | 
			
		||||
	// 自分自身
 | 
			
		||||
	if (user._id.equals(ps.userId)) {
 | 
			
		||||
		return rej('blockee is yourself');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get blockee
 | 
			
		||||
	const blockee = await User.findOne({
 | 
			
		||||
		_id: ps.userId
 | 
			
		||||
	}, {
 | 
			
		||||
		fields: {
 | 
			
		||||
			data: false,
 | 
			
		||||
			profile: false
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (blockee === null) {
 | 
			
		||||
		return rej('user not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if already blocking
 | 
			
		||||
	const exist = await Blocking.findOne({
 | 
			
		||||
		blockerId: blocker._id,
 | 
			
		||||
		blockeeId: blockee._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (exist !== null) {
 | 
			
		||||
		return rej('already blocking');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create blocking
 | 
			
		||||
	await create(blocker, blockee);
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	res(await pack(blockee._id, user));
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										75
									
								
								src/server/api/endpoints/blocking/delete.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/server/api/endpoints/blocking/delete.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,75 @@
 | 
			
		|||
import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
 | 
			
		||||
const ms = require('ms');
 | 
			
		||||
import User, { pack, ILocalUser } from '../../../../models/user';
 | 
			
		||||
import Blocking from '../../../../models/blocking';
 | 
			
		||||
import deleteBlocking from '../../../../services/blocking/delete';
 | 
			
		||||
import getParams from '../../get-params';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	stability: 'stable',
 | 
			
		||||
 | 
			
		||||
	desc: {
 | 
			
		||||
		'ja-JP': '指定したユーザーのブロックを解除します。',
 | 
			
		||||
		'en-US': 'Unblock a user.'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	limit: {
 | 
			
		||||
		duration: ms('1hour'),
 | 
			
		||||
		max: 100
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	requireCredential: true,
 | 
			
		||||
 | 
			
		||||
	kind: 'following-write',
 | 
			
		||||
 | 
			
		||||
	params: {
 | 
			
		||||
		userId: $.type(ID).note({
 | 
			
		||||
			desc: {
 | 
			
		||||
				'ja-JP': '対象のユーザーのID',
 | 
			
		||||
				'en-US': 'Target user ID'
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
			
		||||
	const [ps, psErr] = getParams(meta, params);
 | 
			
		||||
	if (psErr) return rej(psErr);
 | 
			
		||||
 | 
			
		||||
	const blocker = user;
 | 
			
		||||
 | 
			
		||||
	// Check if the blockee is yourself
 | 
			
		||||
	if (user._id.equals(ps.userId)) {
 | 
			
		||||
		return rej('blockee is yourself');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get blockee
 | 
			
		||||
	const blockee = await User.findOne({
 | 
			
		||||
		_id: ps.userId
 | 
			
		||||
	}, {
 | 
			
		||||
		fields: {
 | 
			
		||||
			data: false,
 | 
			
		||||
			'profile': false
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (blockee === null) {
 | 
			
		||||
		return rej('user not found');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check not blocking
 | 
			
		||||
	const exist = await Blocking.findOne({
 | 
			
		||||
		blockerId: blocker._id,
 | 
			
		||||
		blockeeId: blockee._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (exist === null) {
 | 
			
		||||
		return rej('already not blocking');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Delete blocking
 | 
			
		||||
	await deleteBlocking(blocker, blockee);
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	res(await pack(blockee._id, user));
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +68,11 @@ export default (params: any, user: ILocalUser) => new Promise(async (res, rej) =
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Create following
 | 
			
		||||
	await create(follower, followee);
 | 
			
		||||
	try {
 | 
			
		||||
		await create(follower, followee);
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		return rej(e && e.message ? e.message : e);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Send response
 | 
			
		||||
	res(await pack(followee._id, user));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										121
									
								
								src/services/blocking/create.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/services/blocking/create.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
import User, { isLocalUser, isRemoteUser, pack as packUser, IUser } from '../../models/user';
 | 
			
		||||
import Following from '../../models/following';
 | 
			
		||||
import FollowRequest from '../../models/follow-request';
 | 
			
		||||
import { publishMainStream } from '../../stream';
 | 
			
		||||
import pack from '../../remote/activitypub/renderer';
 | 
			
		||||
import renderFollow from '../../remote/activitypub/renderer/follow';
 | 
			
		||||
import renderUndo from '../../remote/activitypub/renderer/undo';
 | 
			
		||||
import renderBlock from '../../remote/activitypub/renderer/block';
 | 
			
		||||
import { deliver } from '../../queue';
 | 
			
		||||
import renderReject from '../../remote/activitypub/renderer/reject';
 | 
			
		||||
import perUserFollowingChart from '../../chart/per-user-following';
 | 
			
		||||
import Blocking from '../../models/blocking';
 | 
			
		||||
 | 
			
		||||
export default async function(blocker: IUser, blockee: IUser) {
 | 
			
		||||
 | 
			
		||||
	await Promise.all([
 | 
			
		||||
		cancelRequest(blocker, blockee),
 | 
			
		||||
		cancelRequest(blockee, blocker),
 | 
			
		||||
		unFollow(blocker, blockee),
 | 
			
		||||
		unFollow(blockee, blocker)
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	await Blocking.insert({
 | 
			
		||||
		createdAt: new Date(),
 | 
			
		||||
		blockerId: blocker._id,
 | 
			
		||||
		blockeeId: blockee._id,
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (isLocalUser(blocker) && isRemoteUser(blockee)) {
 | 
			
		||||
		const content = pack(renderBlock(blocker, blockee));
 | 
			
		||||
		deliver(blocker, content, blockee.inbox);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function cancelRequest(follower: IUser, followee: IUser) {
 | 
			
		||||
	const request = await FollowRequest.findOne({
 | 
			
		||||
		followeeId: followee._id,
 | 
			
		||||
		followerId: follower._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (request == null) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	await FollowRequest.remove({
 | 
			
		||||
		followeeId: followee._id,
 | 
			
		||||
		followerId: follower._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	await User.update({ _id: followee._id }, {
 | 
			
		||||
		$inc: {
 | 
			
		||||
			pendingReceivedFollowRequestsCount: -1
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (isLocalUser(followee)) {
 | 
			
		||||
		packUser(followee, followee, {
 | 
			
		||||
			detail: true
 | 
			
		||||
		}).then(packed => publishMainStream(followee._id, 'meUpdated', packed));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (isLocalUser(follower)) {
 | 
			
		||||
		packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// リモートにフォローリクエストをしていたらUndoFollow送信
 | 
			
		||||
	if (isLocalUser(follower) && isRemoteUser(followee)) {
 | 
			
		||||
		const content = pack(renderUndo(renderFollow(follower, followee), follower));
 | 
			
		||||
		deliver(follower, content, followee.inbox);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// リモートからフォローリクエストを受けていたらReject送信
 | 
			
		||||
	if (isRemoteUser(follower) && isLocalUser(followee)) {
 | 
			
		||||
		const content = pack(renderReject(renderFollow(follower, followee, request.requestId), followee));
 | 
			
		||||
		deliver(followee, content, follower.inbox);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function unFollow(follower: IUser, followee: IUser) {
 | 
			
		||||
	const following = await Following.findOne({
 | 
			
		||||
		followerId: follower._id,
 | 
			
		||||
		followeeId: followee._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (following == null) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Following.remove({
 | 
			
		||||
		_id: following._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	//#region Decrement following count
 | 
			
		||||
	User.update({ _id: follower._id }, {
 | 
			
		||||
		$inc: {
 | 
			
		||||
			followingCount: -1
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	//#region Decrement followers count
 | 
			
		||||
	User.update({ _id: followee._id }, {
 | 
			
		||||
		$inc: {
 | 
			
		||||
			followersCount: -1
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	//#endregion
 | 
			
		||||
 | 
			
		||||
	perUserFollowingChart.update(follower, followee, false);
 | 
			
		||||
 | 
			
		||||
	// Publish unfollow event
 | 
			
		||||
	if (isLocalUser(follower)) {
 | 
			
		||||
		packUser(followee, follower).then(packed => publishMainStream(follower._id, 'unfollow', packed));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// リモートにフォローをしていたらUndoFollow送信
 | 
			
		||||
	if (isLocalUser(follower) && isRemoteUser(followee)) {
 | 
			
		||||
		const content = pack(renderUndo(renderFollow(follower, followee), follower));
 | 
			
		||||
		deliver(follower, content, followee.inbox);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								src/services/blocking/delete.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/services/blocking/delete.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import { isLocalUser, isRemoteUser, IUser } from '../../models/user';
 | 
			
		||||
import Blocking from '../../models/blocking';
 | 
			
		||||
import pack from '../../remote/activitypub/renderer';
 | 
			
		||||
import renderBlock from '../../remote/activitypub/renderer/block';
 | 
			
		||||
import renderUndo from '../../remote/activitypub/renderer/undo';
 | 
			
		||||
import { deliver } from '../../queue';
 | 
			
		||||
 | 
			
		||||
export default async function(blocker: IUser, blockee: IUser) {
 | 
			
		||||
	const blocking = await Blocking.findOne({
 | 
			
		||||
		blockerId: blocker._id,
 | 
			
		||||
		blockeeId: blockee._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	if (blocking == null) {
 | 
			
		||||
		console.warn('ブロック解除がリクエストされましたがブロックしていませんでした');
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Blocking.remove({
 | 
			
		||||
		_id: blocking._id
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// deliver if remote bloking
 | 
			
		||||
	if (isLocalUser(blocker) && isRemoteUser(blockee)) {
 | 
			
		||||
		const content = pack(renderUndo(renderBlock(blocker, blockee), blocker));
 | 
			
		||||
		deliver(blocker, content, blockee.inbox);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +1,45 @@
 | 
			
		|||
import User, { isLocalUser, isRemoteUser, pack as packUser, IUser } from '../../models/user';
 | 
			
		||||
import Following from '../../models/following';
 | 
			
		||||
import Blocking from '../../models/blocking';
 | 
			
		||||
import { publishMainStream } from '../../stream';
 | 
			
		||||
import notify from '../../notify';
 | 
			
		||||
import pack from '../../remote/activitypub/renderer';
 | 
			
		||||
import renderFollow from '../../remote/activitypub/renderer/follow';
 | 
			
		||||
import renderAccept from '../../remote/activitypub/renderer/accept';
 | 
			
		||||
import renderReject from '../../remote/activitypub/renderer/reject';
 | 
			
		||||
import { deliver } from '../../queue';
 | 
			
		||||
import createFollowRequest from './requests/create';
 | 
			
		||||
import perUserFollowingChart from '../../chart/per-user-following';
 | 
			
		||||
 | 
			
		||||
export default async function(follower: IUser, followee: IUser, requestId?: string) {
 | 
			
		||||
	// check blocking
 | 
			
		||||
	const [ blocking, blocked ] = await Promise.all([
 | 
			
		||||
		Blocking.findOne({
 | 
			
		||||
			blockerId: follower._id,
 | 
			
		||||
			blockeeId: followee._id,
 | 
			
		||||
		}),
 | 
			
		||||
		Blocking.findOne({
 | 
			
		||||
			blockerId: followee._id,
 | 
			
		||||
			blockeeId: follower._id,
 | 
			
		||||
		})
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	if (isRemoteUser(follower) && isLocalUser(followee) && blocked) {
 | 
			
		||||
		// リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。
 | 
			
		||||
		const content = pack(renderReject(renderFollow(follower, followee, requestId), followee));
 | 
			
		||||
		deliver(followee , content, follower.inbox);
 | 
			
		||||
		return;
 | 
			
		||||
	} else if (isRemoteUser(follower) && isLocalUser(followee) && blocking) {
 | 
			
		||||
		// リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。
 | 
			
		||||
		await Blocking.remove({
 | 
			
		||||
			_id: blocking._id
 | 
			
		||||
		});
 | 
			
		||||
	} else {
 | 
			
		||||
		// それ以外は単純に例外
 | 
			
		||||
		if (blocking != null) throw new Error('blocking');
 | 
			
		||||
		if (blocked != null) throw new Error('blocked');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// フォロー対象が鍵アカウントである or
 | 
			
		||||
	// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
 | 
			
		||||
	// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,8 +5,24 @@ import pack from '../../../remote/activitypub/renderer';
 | 
			
		|||
import renderFollow from '../../../remote/activitypub/renderer/follow';
 | 
			
		||||
import { deliver } from '../../../queue';
 | 
			
		||||
import FollowRequest from '../../../models/follow-request';
 | 
			
		||||
import Blocking from '../../../models/blocking';
 | 
			
		||||
 | 
			
		||||
export default async function(follower: IUser, followee: IUser, requestId?: string) {
 | 
			
		||||
	// check blocking
 | 
			
		||||
	const [ blocking, blocked ] = await Promise.all([
 | 
			
		||||
		Blocking.findOne({
 | 
			
		||||
			blockerId: follower._id,
 | 
			
		||||
			blockeeId: followee._id,
 | 
			
		||||
		}),
 | 
			
		||||
		Blocking.findOne({
 | 
			
		||||
			blockerId: followee._id,
 | 
			
		||||
			blockeeId: follower._id,
 | 
			
		||||
		})
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	if (blocking != null) throw new Error('blocking');
 | 
			
		||||
	if (blocked != null) throw new Error('blocked');
 | 
			
		||||
 | 
			
		||||
	await FollowRequest.insert({
 | 
			
		||||
		createdAt: new Date(),
 | 
			
		||||
		followerId: follower._id,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue