commit
						e06dd199a7
					
				
					 24 changed files with 641 additions and 21 deletions
				
			
		| 
						 | 
					@ -346,6 +346,9 @@ desktop:
 | 
				
			||||||
      failed: "Failed to setup. please ensure that the token is correct."
 | 
					      failed: "Failed to setup. please ensure that the token is correct."
 | 
				
			||||||
      info: "From the next sign in, enter the token that is displayed on the device in addition to the password."
 | 
					      info: "From the next sign in, enter the token that is displayed on the device in addition to the password."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mk-mute-setting:
 | 
				
			||||||
 | 
					      no-users: "No muted users"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mk-post-form:
 | 
					    mk-post-form:
 | 
				
			||||||
      post-placeholder: "What's happening?"
 | 
					      post-placeholder: "What's happening?"
 | 
				
			||||||
      reply-placeholder: "Reply to this post..."
 | 
					      reply-placeholder: "Reply to this post..."
 | 
				
			||||||
| 
						 | 
					@ -379,6 +382,7 @@ desktop:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mk-settings:
 | 
					    mk-settings:
 | 
				
			||||||
      profile: "Profile"
 | 
					      profile: "Profile"
 | 
				
			||||||
 | 
					      mute: "Mute"
 | 
				
			||||||
      drive: "Drive"
 | 
					      drive: "Drive"
 | 
				
			||||||
      security: "Security"
 | 
					      security: "Security"
 | 
				
			||||||
      password: "Password"
 | 
					      password: "Password"
 | 
				
			||||||
| 
						 | 
					@ -473,6 +477,11 @@ desktop:
 | 
				
			||||||
    mk-user:
 | 
					    mk-user:
 | 
				
			||||||
      last-used-at: "Last used at"
 | 
					      last-used-at: "Last used at"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      follows-you: "Follows you"
 | 
				
			||||||
 | 
					      mute: "Mute"
 | 
				
			||||||
 | 
					      muted: "Muting"
 | 
				
			||||||
 | 
					      unmute: "Unmute"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      photos:
 | 
					      photos:
 | 
				
			||||||
        title: "Photos"
 | 
					        title: "Photos"
 | 
				
			||||||
        loading: "Loading"
 | 
					        loading: "Loading"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,6 +346,9 @@ desktop:
 | 
				
			||||||
      failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
 | 
					      failed: "設定に失敗しました。トークンに誤りがないかご確認ください。"
 | 
				
			||||||
      info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
 | 
					      info: "次回サインインからは、同様にパスワードに加えてデバイスに表示されているトークンを入力します。"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mk-mute-setting:
 | 
				
			||||||
 | 
					      no-users: "ミュートしているユーザーはいません"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mk-post-form:
 | 
					    mk-post-form:
 | 
				
			||||||
      post-placeholder: "いまどうしてる?"
 | 
					      post-placeholder: "いまどうしてる?"
 | 
				
			||||||
      reply-placeholder: "この投稿への返信..."
 | 
					      reply-placeholder: "この投稿への返信..."
 | 
				
			||||||
| 
						 | 
					@ -379,6 +382,7 @@ desktop:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mk-settings:
 | 
					    mk-settings:
 | 
				
			||||||
      profile: "プロフィール"
 | 
					      profile: "プロフィール"
 | 
				
			||||||
 | 
					      mute: "ミュート"
 | 
				
			||||||
      drive: "ドライブ"
 | 
					      drive: "ドライブ"
 | 
				
			||||||
      security: "セキュリティ"
 | 
					      security: "セキュリティ"
 | 
				
			||||||
      password: "パスワード"
 | 
					      password: "パスワード"
 | 
				
			||||||
| 
						 | 
					@ -473,6 +477,11 @@ desktop:
 | 
				
			||||||
    mk-user:
 | 
					    mk-user:
 | 
				
			||||||
      last-used-at: "最終アクセス"
 | 
					      last-used-at: "最終アクセス"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      follows-you: "フォローされています"
 | 
				
			||||||
 | 
					      mute: "ミュートする"
 | 
				
			||||||
 | 
					      muted: "ミュートしています"
 | 
				
			||||||
 | 
					      unmute: "ミュート解除"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      photos:
 | 
					      photos:
 | 
				
			||||||
        title: "フォト"
 | 
					        title: "フォト"
 | 
				
			||||||
        loading: "読み込み中"
 | 
					        loading: "読み込み中"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import * as mongo from 'mongodb';
 | 
					import * as mongo from 'mongodb';
 | 
				
			||||||
import Notification from '../models/notification';
 | 
					import Notification from '../models/notification';
 | 
				
			||||||
 | 
					import Mute from '../models/mute';
 | 
				
			||||||
import event from '../event';
 | 
					import event from '../event';
 | 
				
			||||||
import serialize from '../serializers/notification';
 | 
					import serialize from '../serializers/notification';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +33,17 @@ export default (
 | 
				
			||||||
	setTimeout(async () => {
 | 
						setTimeout(async () => {
 | 
				
			||||||
		const fresh = await Notification.findOne({ _id: notification._id }, { is_read: true });
 | 
							const fresh = await Notification.findOne({ _id: notification._id }, { is_read: true });
 | 
				
			||||||
		if (!fresh.is_read) {
 | 
							if (!fresh.is_read) {
 | 
				
			||||||
 | 
								//#region ただしミュートしているユーザーからの通知なら無視
 | 
				
			||||||
 | 
								const mute = await Mute.find({
 | 
				
			||||||
 | 
									muter_id: notifiee,
 | 
				
			||||||
 | 
									deleted_at: { $exists: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								const mutedUserIds = mute.map(m => m.mutee_id.toString());
 | 
				
			||||||
 | 
								if (mutedUserIds.indexOf(notifier.toString()) != -1) {
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			event(notifiee, 'unread_notification', await serialize(notification));
 | 
								event(notifiee, 'unread_notification', await serialize(notification));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}, 3000);
 | 
						}, 3000);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,6 +222,23 @@ const endpoints: Endpoint[] = [
 | 
				
			||||||
		withCredential: true,
 | 
							withCredential: true,
 | 
				
			||||||
		kind: 'notification-read'
 | 
							kind: 'notification-read'
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							name: 'mute/create',
 | 
				
			||||||
 | 
							withCredential: true,
 | 
				
			||||||
 | 
							kind: 'account/write'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							name: 'mute/delete',
 | 
				
			||||||
 | 
							withCredential: true,
 | 
				
			||||||
 | 
							kind: 'account/write'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							name: 'mute/list',
 | 
				
			||||||
 | 
							withCredential: true,
 | 
				
			||||||
 | 
							kind: 'account/read'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		name: 'notifications/get_unread_count',
 | 
							name: 'notifications/get_unread_count',
 | 
				
			||||||
		withCredential: true,
 | 
							withCredential: true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import Notification from '../../models/notification';
 | 
					import Notification from '../../models/notification';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
import serialize from '../../serializers/notification';
 | 
					import serialize from '../../serializers/notification';
 | 
				
			||||||
import getFriends from '../../common/get-friends';
 | 
					import getFriends from '../../common/get-friends';
 | 
				
			||||||
import read from '../../common/read-notification';
 | 
					import read from '../../common/read-notification';
 | 
				
			||||||
| 
						 | 
					@ -45,8 +46,18 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
		return rej('cannot set since_id and until_id');
 | 
							return rej('cannot set since_id and until_id');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const mute = await Mute.find({
 | 
				
			||||||
 | 
							muter_id: user._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const query = {
 | 
						const query = {
 | 
				
			||||||
		notifiee_id: user._id
 | 
							notifiee_id: user._id,
 | 
				
			||||||
 | 
							$and: [{
 | 
				
			||||||
 | 
								notifier_id: {
 | 
				
			||||||
 | 
									$nin: mute.map(m => m.mutee_id)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}]
 | 
				
			||||||
	} as any;
 | 
						} as any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const sort = {
 | 
						const sort = {
 | 
				
			||||||
| 
						 | 
					@ -54,12 +65,14 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (following) {
 | 
						if (following) {
 | 
				
			||||||
		// ID list of the user $self and other users who the user follows
 | 
							// ID list of the user itself and other users who the user follows
 | 
				
			||||||
		const followingIds = await getFriends(user._id);
 | 
							const followingIds = await getFriends(user._id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		query.notifier_id = {
 | 
							query.$and.push({
 | 
				
			||||||
			$in: followingIds
 | 
								notifier_id: {
 | 
				
			||||||
		};
 | 
									$in: followingIds
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (type) {
 | 
						if (type) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import History from '../../models/messaging-history';
 | 
					import History from '../../models/messaging-history';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
import serialize from '../../serializers/messaging-message';
 | 
					import serialize from '../../serializers/messaging-message';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -17,10 +18,18 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
	const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
 | 
						const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
 | 
				
			||||||
	if (limitErr) return rej('invalid limit param');
 | 
						if (limitErr) return rej('invalid limit param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const mute = await Mute.find({
 | 
				
			||||||
 | 
							muter_id: user._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get history
 | 
						// Get history
 | 
				
			||||||
	const history = await History
 | 
						const history = await History
 | 
				
			||||||
		.find({
 | 
							.find({
 | 
				
			||||||
			user_id: user._id
 | 
								user_id: user._id,
 | 
				
			||||||
 | 
								partner: {
 | 
				
			||||||
 | 
									$nin: mute.map(m => m.mutee_id)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}, {
 | 
							}, {
 | 
				
			||||||
			limit: limit,
 | 
								limit: limit,
 | 
				
			||||||
			sort: {
 | 
								sort: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import Message from '../../../models/messaging-message';
 | 
				
			||||||
import { isValidText } from '../../../models/messaging-message';
 | 
					import { isValidText } from '../../../models/messaging-message';
 | 
				
			||||||
import History from '../../../models/messaging-history';
 | 
					import History from '../../../models/messaging-history';
 | 
				
			||||||
import User from '../../../models/user';
 | 
					import User from '../../../models/user';
 | 
				
			||||||
 | 
					import Mute from '../../../models/mute';
 | 
				
			||||||
import DriveFile from '../../../models/drive-file';
 | 
					import DriveFile from '../../../models/drive-file';
 | 
				
			||||||
import serialize from '../../../serializers/messaging-message';
 | 
					import serialize from '../../../serializers/messaging-message';
 | 
				
			||||||
import publishUserStream from '../../../event';
 | 
					import publishUserStream from '../../../event';
 | 
				
			||||||
| 
						 | 
					@ -97,6 +98,17 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
	setTimeout(async () => {
 | 
						setTimeout(async () => {
 | 
				
			||||||
		const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true });
 | 
							const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true });
 | 
				
			||||||
		if (!freshMessage.is_read) {
 | 
							if (!freshMessage.is_read) {
 | 
				
			||||||
 | 
								//#region ただしミュートされているなら発行しない
 | 
				
			||||||
 | 
								const mute = await Mute.find({
 | 
				
			||||||
 | 
									muter_id: recipient._id,
 | 
				
			||||||
 | 
									deleted_at: { $exists: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								const mutedUserIds = mute.map(m => m.mutee_id.toString());
 | 
				
			||||||
 | 
								if (mutedUserIds.indexOf(user._id.toString()) != -1) {
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			publishUserStream(message.recipient_id, 'unread_messaging_message', messageObj);
 | 
								publishUserStream(message.recipient_id, 'unread_messaging_message', messageObj);
 | 
				
			||||||
			pushSw(message.recipient_id, 'unread_messaging_message', messageObj);
 | 
								pushSw(message.recipient_id, 'unread_messaging_message', messageObj);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
 * Module dependencies
 | 
					 * Module dependencies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import Message from '../../models/messaging-message';
 | 
					import Message from '../../models/messaging-message';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Get count of unread messages
 | 
					 * Get count of unread messages
 | 
				
			||||||
| 
						 | 
					@ -11,8 +12,17 @@ import Message from '../../models/messaging-message';
 | 
				
			||||||
 * @return {Promise<any>}
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
					module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const mute = await Mute.find({
 | 
				
			||||||
 | 
							muter_id: user._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const mutedUserIds = mute.map(m => m.mutee_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const count = await Message
 | 
						const count = await Message
 | 
				
			||||||
		.count({
 | 
							.count({
 | 
				
			||||||
 | 
								user_id: {
 | 
				
			||||||
 | 
									$nin: mutedUserIds
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			recipient_id: user._id,
 | 
								recipient_id: user._id,
 | 
				
			||||||
			is_read: false
 | 
								is_read: false
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										61
									
								
								src/api/endpoints/mute/create.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/api/endpoints/mute/create.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,61 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Module dependencies
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import $ from 'cafy';
 | 
				
			||||||
 | 
					import User from '../../models/user';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Mute a user
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {any} params
 | 
				
			||||||
 | 
					 * @param {any} user
 | 
				
			||||||
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const muter = user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get 'user_id' parameter
 | 
				
			||||||
 | 
						const [userId, userIdErr] = $(params.user_id).id().$;
 | 
				
			||||||
 | 
						if (userIdErr) return rej('invalid user_id param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 自分自身
 | 
				
			||||||
 | 
						if (user._id.equals(userId)) {
 | 
				
			||||||
 | 
							return rej('mutee is yourself');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get mutee
 | 
				
			||||||
 | 
						const mutee = await User.findOne({
 | 
				
			||||||
 | 
							_id: userId
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							fields: {
 | 
				
			||||||
 | 
								data: false,
 | 
				
			||||||
 | 
								profile: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mutee === null) {
 | 
				
			||||||
 | 
							return rej('user not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if already muting
 | 
				
			||||||
 | 
						const exist = await Mute.findOne({
 | 
				
			||||||
 | 
							muter_id: muter._id,
 | 
				
			||||||
 | 
							mutee_id: mutee._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (exist !== null) {
 | 
				
			||||||
 | 
							return rej('already muting');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create mute
 | 
				
			||||||
 | 
						await Mute.insert({
 | 
				
			||||||
 | 
							created_at: new Date(),
 | 
				
			||||||
 | 
							muter_id: muter._id,
 | 
				
			||||||
 | 
							mutee_id: mutee._id,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send response
 | 
				
			||||||
 | 
						res();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/api/endpoints/mute/delete.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/api/endpoints/mute/delete.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Module dependencies
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import $ from 'cafy';
 | 
				
			||||||
 | 
					import User from '../../models/user';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Unmute a user
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {any} params
 | 
				
			||||||
 | 
					 * @param {any} user
 | 
				
			||||||
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const muter = user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get 'user_id' parameter
 | 
				
			||||||
 | 
						const [userId, userIdErr] = $(params.user_id).id().$;
 | 
				
			||||||
 | 
						if (userIdErr) return rej('invalid user_id param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if the mutee is yourself
 | 
				
			||||||
 | 
						if (user._id.equals(userId)) {
 | 
				
			||||||
 | 
							return rej('mutee is yourself');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get mutee
 | 
				
			||||||
 | 
						const mutee = await User.findOne({
 | 
				
			||||||
 | 
							_id: userId
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							fields: {
 | 
				
			||||||
 | 
								data: false,
 | 
				
			||||||
 | 
								profile: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mutee === null) {
 | 
				
			||||||
 | 
							return rej('user not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check not muting
 | 
				
			||||||
 | 
						const exist = await Mute.findOne({
 | 
				
			||||||
 | 
							muter_id: muter._id,
 | 
				
			||||||
 | 
							mutee_id: mutee._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (exist === null) {
 | 
				
			||||||
 | 
							return rej('already not muting');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Delete mute
 | 
				
			||||||
 | 
						await Mute.update({
 | 
				
			||||||
 | 
							_id: exist._id
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							$set: {
 | 
				
			||||||
 | 
								deleted_at: new Date()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send response
 | 
				
			||||||
 | 
						res();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/api/endpoints/mute/list.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/api/endpoints/mute/list.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,73 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Module dependencies
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					import $ from 'cafy';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
 | 
					import serialize from '../../serializers/user';
 | 
				
			||||||
 | 
					import getFriends from '../../common/get-friends';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get muted users of a user
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param {any} params
 | 
				
			||||||
 | 
					 * @param {any} me
 | 
				
			||||||
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					module.exports = (params, me) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						// Get 'iknow' parameter
 | 
				
			||||||
 | 
						const [iknow = false, iknowErr] = $(params.iknow).optional.boolean().$;
 | 
				
			||||||
 | 
						if (iknowErr) return rej('invalid iknow param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get 'limit' parameter
 | 
				
			||||||
 | 
						const [limit = 30, limitErr] = $(params.limit).optional.number().range(1, 100).$;
 | 
				
			||||||
 | 
						if (limitErr) return rej('invalid limit param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get 'cursor' parameter
 | 
				
			||||||
 | 
						const [cursor = null, cursorErr] = $(params.cursor).optional.id().$;
 | 
				
			||||||
 | 
						if (cursorErr) return rej('invalid cursor param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Construct query
 | 
				
			||||||
 | 
						const query = {
 | 
				
			||||||
 | 
							muter_id: me._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						} as any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (iknow) {
 | 
				
			||||||
 | 
							// Get my friends
 | 
				
			||||||
 | 
							const myFriends = await getFriends(me._id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							query.mutee_id = {
 | 
				
			||||||
 | 
								$in: myFriends
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// カーソルが指定されている場合
 | 
				
			||||||
 | 
						if (cursor) {
 | 
				
			||||||
 | 
							query._id = {
 | 
				
			||||||
 | 
								$lt: cursor
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get mutes
 | 
				
			||||||
 | 
						const mutes = await Mute
 | 
				
			||||||
 | 
							.find(query, {
 | 
				
			||||||
 | 
								limit: limit + 1,
 | 
				
			||||||
 | 
								sort: { _id: -1 }
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 「次のページ」があるかどうか
 | 
				
			||||||
 | 
						const inStock = mutes.length === limit + 1;
 | 
				
			||||||
 | 
						if (inStock) {
 | 
				
			||||||
 | 
							mutes.pop();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Serialize
 | 
				
			||||||
 | 
						const users = await Promise.all(mutes.map(async m =>
 | 
				
			||||||
 | 
							await serialize(m.mutee_id, me, { detail: true })));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Response
 | 
				
			||||||
 | 
						res({
 | 
				
			||||||
 | 
							users: users,
 | 
				
			||||||
 | 
							next: inStock ? mutes[mutes.length - 1]._id : null,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
 * Module dependencies
 | 
					 * Module dependencies
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import Notification from '../../models/notification';
 | 
					import Notification from '../../models/notification';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Get count of unread notifications
 | 
					 * Get count of unread notifications
 | 
				
			||||||
| 
						 | 
					@ -11,9 +12,18 @@ import Notification from '../../models/notification';
 | 
				
			||||||
 * @return {Promise<any>}
 | 
					 * @return {Promise<any>}
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
					module.exports = (params, user) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const mute = await Mute.find({
 | 
				
			||||||
 | 
							muter_id: user._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const mutedUserIds = mute.map(m => m.mutee_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const count = await Notification
 | 
						const count = await Notification
 | 
				
			||||||
		.count({
 | 
							.count({
 | 
				
			||||||
			notifiee_id: user._id,
 | 
								notifiee_id: user._id,
 | 
				
			||||||
 | 
								notifier_id: {
 | 
				
			||||||
 | 
									$nin: mutedUserIds
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
			is_read: false
 | 
								is_read: false
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import { default as Post, IPost, isValidText } from '../../models/post';
 | 
				
			||||||
import { default as User, IUser } from '../../models/user';
 | 
					import { default as User, IUser } from '../../models/user';
 | 
				
			||||||
import { default as Channel, IChannel } from '../../models/channel';
 | 
					import { default as Channel, IChannel } from '../../models/channel';
 | 
				
			||||||
import Following from '../../models/following';
 | 
					import Following from '../../models/following';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
import DriveFile from '../../models/drive-file';
 | 
					import DriveFile from '../../models/drive-file';
 | 
				
			||||||
import Watching from '../../models/post-watching';
 | 
					import Watching from '../../models/post-watching';
 | 
				
			||||||
import ChannelWatching from '../../models/channel-watching';
 | 
					import ChannelWatching from '../../models/channel-watching';
 | 
				
			||||||
| 
						 | 
					@ -215,7 +216,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
 | 
				
			||||||
		poll: poll,
 | 
							poll: poll,
 | 
				
			||||||
		text: text,
 | 
							text: text,
 | 
				
			||||||
		user_id: user._id,
 | 
							user_id: user._id,
 | 
				
			||||||
		app_id: app ? app._id : null
 | 
							app_id: app ? app._id : null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 以下非正規化データ
 | 
				
			||||||
 | 
							_reply: reply ? { user_id: reply.user_id } : undefined,
 | 
				
			||||||
 | 
							_repost: repost ? { user_id: repost.user_id } : undefined,
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Serialize
 | 
						// Serialize
 | 
				
			||||||
| 
						 | 
					@ -236,7 +241,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const mentions = [];
 | 
						const mentions = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function addMention(mentionee, reason) {
 | 
						async function addMention(mentionee, reason) {
 | 
				
			||||||
		// Reject if already added
 | 
							// Reject if already added
 | 
				
			||||||
		if (mentions.some(x => x.equals(mentionee))) return;
 | 
							if (mentions.some(x => x.equals(mentionee))) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,8 +250,15 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Publish event
 | 
							// Publish event
 | 
				
			||||||
		if (!user._id.equals(mentionee)) {
 | 
							if (!user._id.equals(mentionee)) {
 | 
				
			||||||
			event(mentionee, reason, postObj);
 | 
								const mentioneeMutes = await Mute.find({
 | 
				
			||||||
			pushSw(mentionee, reason, postObj);
 | 
									muter_id: mentionee,
 | 
				
			||||||
 | 
									deleted_at: { $exists: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								const mentioneesMutedUserIds = mentioneeMutes.map(m => m.mutee_id.toString());
 | 
				
			||||||
 | 
								if (mentioneesMutedUserIds.indexOf(user._id.toString()) == -1) {
 | 
				
			||||||
 | 
									event(mentionee, reason, postObj);
 | 
				
			||||||
 | 
									pushSw(mentionee, reason, postObj);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import $ from 'cafy';
 | 
				
			||||||
const escapeRegexp = require('escape-regexp');
 | 
					const escapeRegexp = require('escape-regexp');
 | 
				
			||||||
import Post from '../../models/post';
 | 
					import Post from '../../models/post';
 | 
				
			||||||
import User from '../../models/user';
 | 
					import User from '../../models/user';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
import getFriends from '../../common/get-friends';
 | 
					import getFriends from '../../common/get-friends';
 | 
				
			||||||
import serialize from '../../serializers/post';
 | 
					import serialize from '../../serializers/post';
 | 
				
			||||||
import config from '../../../conf';
 | 
					import config from '../../../conf';
 | 
				
			||||||
| 
						 | 
					@ -34,6 +35,10 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 | 
				
			||||||
	const [following = null, followingErr] = $(params.following).optional.nullable.boolean().$;
 | 
						const [following = null, followingErr] = $(params.following).optional.nullable.boolean().$;
 | 
				
			||||||
	if (followingErr) return rej('invalid following param');
 | 
						if (followingErr) return rej('invalid following param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get 'mute' parameter
 | 
				
			||||||
 | 
						const [mute = 'mute_all', muteErr] = $(params.mute).optional.string().$;
 | 
				
			||||||
 | 
						if (muteErr) return rej('invalid mute param');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Get 'reply' parameter
 | 
						// Get 'reply' parameter
 | 
				
			||||||
	const [reply = null, replyErr] = $(params.reply).optional.nullable.boolean().$;
 | 
						const [reply = null, replyErr] = $(params.reply).optional.nullable.boolean().$;
 | 
				
			||||||
	if (replyErr) return rej('invalid reply param');
 | 
						if (replyErr) return rej('invalid reply param');
 | 
				
			||||||
| 
						 | 
					@ -80,11 +85,11 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
 | 
				
			||||||
	// If Elasticsearch is available, search by it
 | 
						// If Elasticsearch is available, search by it
 | 
				
			||||||
	// If not, search by MongoDB
 | 
						// If not, search by MongoDB
 | 
				
			||||||
	(config.elasticsearch.enable ? byElasticsearch : byNative)
 | 
						(config.elasticsearch.enable ? byElasticsearch : byNative)
 | 
				
			||||||
		(res, rej, me, text, user, following, reply, repost, media, poll, sinceDate, untilDate, offset, limit);
 | 
							(res, rej, me, text, user, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, limit);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Search by MongoDB
 | 
					// Search by MongoDB
 | 
				
			||||||
async function byNative(res, rej, me, text, userId, following, reply, repost, media, poll, sinceDate, untilDate, offset, max) {
 | 
					async function byNative(res, rej, me, text, userId, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) {
 | 
				
			||||||
	let q: any = {
 | 
						let q: any = {
 | 
				
			||||||
		$and: []
 | 
							$and: []
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
| 
						 | 
					@ -116,6 +121,84 @@ async function byNative(res, rej, me, text, userId, following, reply, repost, me
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (me != null) {
 | 
				
			||||||
 | 
							const mutes = await Mute.find({
 | 
				
			||||||
 | 
								muter_id: me._id,
 | 
				
			||||||
 | 
								deleted_at: { $exists: false }
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							const mutedUserIds = mutes.map(m => m.mutee_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (mute) {
 | 
				
			||||||
 | 
								case 'mute_all':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										user_id: {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										'_reply.user_id': {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										'_repost.user_id': {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 'mute_related':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										'_reply.user_id': {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										'_repost.user_id': {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 'mute_direct':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										user_id: {
 | 
				
			||||||
 | 
											$nin: mutedUserIds
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 'direct_only':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										user_id: {
 | 
				
			||||||
 | 
											$in: mutedUserIds
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 'related_only':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										$or: [{
 | 
				
			||||||
 | 
											'_reply.user_id': {
 | 
				
			||||||
 | 
												$in: mutedUserIds
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}, {
 | 
				
			||||||
 | 
											'_repost.user_id': {
 | 
				
			||||||
 | 
												$in: mutedUserIds
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}]
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								case 'all_only':
 | 
				
			||||||
 | 
									push({
 | 
				
			||||||
 | 
										$or: [{
 | 
				
			||||||
 | 
											user_id: {
 | 
				
			||||||
 | 
												$in: mutedUserIds
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}, {
 | 
				
			||||||
 | 
											'_reply.user_id': {
 | 
				
			||||||
 | 
												$in: mutedUserIds
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}, {
 | 
				
			||||||
 | 
											'_repost.user_id': {
 | 
				
			||||||
 | 
												$in: mutedUserIds
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}]
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (reply != null) {
 | 
						if (reply != null) {
 | 
				
			||||||
		if (reply) {
 | 
							if (reply) {
 | 
				
			||||||
			push({
 | 
								push({
 | 
				
			||||||
| 
						 | 
					@ -236,7 +319,7 @@ async function byNative(res, rej, me, text, userId, following, reply, repost, me
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Search by Elasticsearch
 | 
					// Search by Elasticsearch
 | 
				
			||||||
async function byElasticsearch(res, rej, me, text, userId, following, reply, repost, media, poll, sinceDate, untilDate, offset, max) {
 | 
					async function byElasticsearch(res, rej, me, text, userId, following, mute, reply, repost, media, poll, sinceDate, untilDate, offset, max) {
 | 
				
			||||||
	const es = require('../../db/elasticsearch');
 | 
						const es = require('../../db/elasticsearch');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	es.search({
 | 
						es.search({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import rap from '@prezzemolo/rap';
 | 
					import rap from '@prezzemolo/rap';
 | 
				
			||||||
import Post from '../../models/post';
 | 
					import Post from '../../models/post';
 | 
				
			||||||
 | 
					import Mute from '../../models/mute';
 | 
				
			||||||
import ChannelWatching from '../../models/channel-watching';
 | 
					import ChannelWatching from '../../models/channel-watching';
 | 
				
			||||||
import getFriends from '../../common/get-friends';
 | 
					import getFriends from '../../common/get-friends';
 | 
				
			||||||
import serialize from '../../serializers/post';
 | 
					import serialize from '../../serializers/post';
 | 
				
			||||||
| 
						 | 
					@ -42,15 +43,23 @@ module.exports = async (params, user, app) => {
 | 
				
			||||||
		throw 'only one of since_id, until_id, since_date, until_date can be specified';
 | 
							throw 'only one of since_id, until_id, since_date, until_date can be specified';
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const { followingIds, watchingChannelIds } = await rap({
 | 
						const { followingIds, watchingChannelIds, mutedUserIds } = await rap({
 | 
				
			||||||
		// ID list of the user itself and other users who the user follows
 | 
							// ID list of the user itself and other users who the user follows
 | 
				
			||||||
		followingIds: getFriends(user._id),
 | 
							followingIds: getFriends(user._id),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Watchしているチャンネルを取得
 | 
							// Watchしているチャンネルを取得
 | 
				
			||||||
		watchingChannelIds: ChannelWatching.find({
 | 
							watchingChannelIds: ChannelWatching.find({
 | 
				
			||||||
			user_id: user._id,
 | 
								user_id: user._id,
 | 
				
			||||||
			// 削除されたドキュメントは除く
 | 
								// 削除されたドキュメントは除く
 | 
				
			||||||
			deleted_at: { $exists: false }
 | 
								deleted_at: { $exists: false }
 | 
				
			||||||
		}).then(watches => watches.map(w => w.channel_id))
 | 
							}).then(watches => watches.map(w => w.channel_id)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// ミュートしているユーザーを取得
 | 
				
			||||||
 | 
							mutedUserIds: Mute.find({
 | 
				
			||||||
 | 
								muter_id: user._id,
 | 
				
			||||||
 | 
								// 削除されたドキュメントは除く
 | 
				
			||||||
 | 
								deleted_at: { $exists: false }
 | 
				
			||||||
 | 
							}).then(ms => ms.map(m => m.mutee_id))
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region Construct query
 | 
						//#region Construct query
 | 
				
			||||||
| 
						 | 
					@ -77,7 +86,17 @@ module.exports = async (params, user, app) => {
 | 
				
			||||||
			channel_id: {
 | 
								channel_id: {
 | 
				
			||||||
				$in: watchingChannelIds
 | 
									$in: watchingChannelIds
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}]
 | 
							}],
 | 
				
			||||||
 | 
							// mute
 | 
				
			||||||
 | 
							user_id: {
 | 
				
			||||||
 | 
								$nin: mutedUserIds
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							'_reply.user_id': {
 | 
				
			||||||
 | 
								$nin: mutedUserIds
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							'_repost.user_id': {
 | 
				
			||||||
 | 
								$nin: mutedUserIds
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	} as any;
 | 
						} as any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sinceId) {
 | 
						if (sinceId) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								src/api/models/mute.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/api/models/mute.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					import db from '../../db/mongodb';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default db.get('mute') as any; // fuck type definition
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import deepcopy = require('deepcopy');
 | 
				
			||||||
import { default as User, IUser } from '../models/user';
 | 
					import { default as User, IUser } from '../models/user';
 | 
				
			||||||
import serializePost from './post';
 | 
					import serializePost from './post';
 | 
				
			||||||
import Following from '../models/following';
 | 
					import Following from '../models/following';
 | 
				
			||||||
 | 
					import Mute from '../models/mute';
 | 
				
			||||||
import getFriends from '../common/get-friends';
 | 
					import getFriends from '../common/get-friends';
 | 
				
			||||||
import config from '../../conf';
 | 
					import config from '../../conf';
 | 
				
			||||||
import rap from '@prezzemolo/rap';
 | 
					import rap from '@prezzemolo/rap';
 | 
				
			||||||
| 
						 | 
					@ -113,7 +114,7 @@ export default (
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (meId && !meId.equals(_user.id)) {
 | 
						if (meId && !meId.equals(_user.id)) {
 | 
				
			||||||
		// If the user is following
 | 
							// Whether the user is following
 | 
				
			||||||
		_user.is_following = (async () => {
 | 
							_user.is_following = (async () => {
 | 
				
			||||||
			const follow = await Following.findOne({
 | 
								const follow = await Following.findOne({
 | 
				
			||||||
				follower_id: meId,
 | 
									follower_id: meId,
 | 
				
			||||||
| 
						 | 
					@ -123,7 +124,7 @@ export default (
 | 
				
			||||||
			return follow !== null;
 | 
								return follow !== null;
 | 
				
			||||||
		})();
 | 
							})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// If the user is followed
 | 
							// Whether the user is followed
 | 
				
			||||||
		_user.is_followed = (async () => {
 | 
							_user.is_followed = (async () => {
 | 
				
			||||||
			const follow2 = await Following.findOne({
 | 
								const follow2 = await Following.findOne({
 | 
				
			||||||
				follower_id: _user.id,
 | 
									follower_id: _user.id,
 | 
				
			||||||
| 
						 | 
					@ -132,6 +133,16 @@ export default (
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
			return follow2 !== null;
 | 
								return follow2 !== null;
 | 
				
			||||||
		})();
 | 
							})();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Whether the user is muted
 | 
				
			||||||
 | 
							_user.is_muted = (async () => {
 | 
				
			||||||
 | 
								const mute = await Mute.findOne({
 | 
				
			||||||
 | 
									muter_id: meId,
 | 
				
			||||||
 | 
									mutee_id: _user.id,
 | 
				
			||||||
 | 
									deleted_at: { $exists: false }
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								return mute !== null;
 | 
				
			||||||
 | 
							})();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (opts.detail) {
 | 
						if (opts.detail) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,19 +3,48 @@ import * as redis from 'redis';
 | 
				
			||||||
import * as debug from 'debug';
 | 
					import * as debug from 'debug';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import User from '../models/user';
 | 
					import User from '../models/user';
 | 
				
			||||||
 | 
					import Mute from '../models/mute';
 | 
				
			||||||
import serializePost from '../serializers/post';
 | 
					import serializePost from '../serializers/post';
 | 
				
			||||||
import readNotification from '../common/read-notification';
 | 
					import readNotification from '../common/read-notification';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const log = debug('misskey');
 | 
					const log = debug('misskey');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function homeStream(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any): void {
 | 
					export default async function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient, user: any) {
 | 
				
			||||||
	// Subscribe Home stream channel
 | 
						// Subscribe Home stream channel
 | 
				
			||||||
	subscriber.subscribe(`misskey:user-stream:${user._id}`);
 | 
						subscriber.subscribe(`misskey:user-stream:${user._id}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const mute = await Mute.find({
 | 
				
			||||||
 | 
							muter_id: user._id,
 | 
				
			||||||
 | 
							deleted_at: { $exists: false }
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						const mutedUserIds = mute.map(m => m.mutee_id.toString());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	subscriber.on('message', async (channel, data) => {
 | 
						subscriber.on('message', async (channel, data) => {
 | 
				
			||||||
		switch (channel.split(':')[1]) {
 | 
							switch (channel.split(':')[1]) {
 | 
				
			||||||
			case 'user-stream':
 | 
								case 'user-stream':
 | 
				
			||||||
				connection.send(data);
 | 
									try {
 | 
				
			||||||
 | 
										const x = JSON.parse(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (x.type == 'post') {
 | 
				
			||||||
 | 
											if (mutedUserIds.indexOf(x.body.user_id) != -1) {
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											if (x.body.reply != null && mutedUserIds.indexOf(x.body.reply.user_id) != -1) {
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											if (x.body.repost != null && mutedUserIds.indexOf(x.body.repost.user_id) != -1) {
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else if (x.type == 'notification') {
 | 
				
			||||||
 | 
											if (mutedUserIds.indexOf(x.body.user_id) != -1) {
 | 
				
			||||||
 | 
												return;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										connection.send(data);
 | 
				
			||||||
 | 
									} catch (e) {
 | 
				
			||||||
 | 
										connection.send(data);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			case 'post-stream':
 | 
								case 'post-stream':
 | 
				
			||||||
				const postId = channel.split(':')[2];
 | 
									const postId = channel.split(':')[2];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
		<p class={ active: page == 'web' } onmousedown={ setPage.bind(null, 'web') }>%fa:desktop .fw%Web</p>
 | 
							<p class={ active: page == 'web' } onmousedown={ setPage.bind(null, 'web') }>%fa:desktop .fw%Web</p>
 | 
				
			||||||
		<p class={ active: page == 'notification' } onmousedown={ setPage.bind(null, 'notification') }>%fa:R bell .fw%通知</p>
 | 
							<p class={ active: page == 'notification' } onmousedown={ setPage.bind(null, 'notification') }>%fa:R bell .fw%通知</p>
 | 
				
			||||||
		<p class={ active: page == 'drive' } onmousedown={ setPage.bind(null, 'drive') }>%fa:cloud .fw%%i18n:desktop.tags.mk-settings.drive%</p>
 | 
							<p class={ active: page == 'drive' } onmousedown={ setPage.bind(null, 'drive') }>%fa:cloud .fw%%i18n:desktop.tags.mk-settings.drive%</p>
 | 
				
			||||||
 | 
							<p class={ active: page == 'mute' } onmousedown={ setPage.bind(null, 'mute') }>%fa:ban .fw%%i18n:desktop.tags.mk-settings.mute%</p>
 | 
				
			||||||
		<p class={ active: page == 'apps' } onmousedown={ setPage.bind(null, 'apps') }>%fa:puzzle-piece .fw%アプリ</p>
 | 
							<p class={ active: page == 'apps' } onmousedown={ setPage.bind(null, 'apps') }>%fa:puzzle-piece .fw%アプリ</p>
 | 
				
			||||||
		<p class={ active: page == 'twitter' } onmousedown={ setPage.bind(null, 'twitter') }>%fa:B twitter .fw%Twitter</p>
 | 
							<p class={ active: page == 'twitter' } onmousedown={ setPage.bind(null, 'twitter') }>%fa:B twitter .fw%Twitter</p>
 | 
				
			||||||
		<p class={ active: page == 'security' } onmousedown={ setPage.bind(null, 'security') }>%fa:unlock-alt .fw%%i18n:desktop.tags.mk-settings.security%</p>
 | 
							<p class={ active: page == 'security' } onmousedown={ setPage.bind(null, 'security') }>%fa:unlock-alt .fw%%i18n:desktop.tags.mk-settings.security%</p>
 | 
				
			||||||
| 
						 | 
					@ -26,6 +27,11 @@
 | 
				
			||||||
			<mk-drive-setting/>
 | 
								<mk-drive-setting/>
 | 
				
			||||||
		</section>
 | 
							</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							<section class="mute" show={ page == 'mute' }>
 | 
				
			||||||
 | 
								<h1>%i18n:desktop.tags.mk-settings.mute%</h1>
 | 
				
			||||||
 | 
								<mk-mute-setting/>
 | 
				
			||||||
 | 
							</section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<section class="apps" show={ page == 'apps' }>
 | 
							<section class="apps" show={ page == 'apps' }>
 | 
				
			||||||
			<h1>アプリケーション</h1>
 | 
								<h1>アプリケーション</h1>
 | 
				
			||||||
			<mk-authorized-apps/>
 | 
								<mk-authorized-apps/>
 | 
				
			||||||
| 
						 | 
					@ -386,3 +392,35 @@
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	</script>
 | 
						</script>
 | 
				
			||||||
</mk-drive-setting>
 | 
					</mk-drive-setting>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<mk-mute-setting>
 | 
				
			||||||
 | 
						<div class="none ui info" if={ !fetching && users.length == 0 }>
 | 
				
			||||||
 | 
							<p>%fa:info-circle%%i18n:desktop.tags.mk-mute-setting.no-users%</p>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
						<div class="users" if={ users.length != 0 }>
 | 
				
			||||||
 | 
							<div each={ user in users }>
 | 
				
			||||||
 | 
								<p><b>{ user.name }</b> @{ user.username }</p>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<style>
 | 
				
			||||||
 | 
							:scope
 | 
				
			||||||
 | 
								display block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						</style>
 | 
				
			||||||
 | 
						<script>
 | 
				
			||||||
 | 
							this.mixin('api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.apps = [];
 | 
				
			||||||
 | 
							this.fetching = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.on('mount', () => {
 | 
				
			||||||
 | 
								this.api('mute/list').then(x => {
 | 
				
			||||||
 | 
									this.update({
 | 
				
			||||||
 | 
										fetching: false,
 | 
				
			||||||
 | 
										users: x.users
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						</script>
 | 
				
			||||||
 | 
					</mk-mute-setting>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -226,7 +226,9 @@
 | 
				
			||||||
<mk-user-profile>
 | 
					<mk-user-profile>
 | 
				
			||||||
	<div class="friend-form" if={ SIGNIN && I.id != user.id }>
 | 
						<div class="friend-form" if={ SIGNIN && I.id != user.id }>
 | 
				
			||||||
		<mk-big-follow-button user={ user }/>
 | 
							<mk-big-follow-button user={ user }/>
 | 
				
			||||||
		<p class="followed" if={ user.is_followed }>フォローされています</p>
 | 
							<p class="followed" if={ user.is_followed }>%i18n:desktop.tags.mk-user.follows-you%</p>
 | 
				
			||||||
 | 
							<p if={ user.is_muted }>%i18n:desktop.tags.mk-user.muted% <a onclick={ unmute }>%i18n:desktop.tags.mk-user.unmute%</a></p>
 | 
				
			||||||
 | 
							<p if={ !user.is_muted }><a onclick={ mute }>%i18n:desktop.tags.mk-user.mute%</a></p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div class="description" if={ user.description }>{ user.description }</div>
 | 
						<div class="description" if={ user.description }>{ user.description }</div>
 | 
				
			||||||
	<div class="birthday" if={ user.profile.birthday }>
 | 
						<div class="birthday" if={ user.profile.birthday }>
 | 
				
			||||||
| 
						 | 
					@ -311,6 +313,7 @@
 | 
				
			||||||
		this.age = require('s-age');
 | 
							this.age = require('s-age');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.mixin('i');
 | 
							this.mixin('i');
 | 
				
			||||||
 | 
							this.mixin('api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.user = this.opts.user;
 | 
							this.user = this.opts.user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,6 +328,28 @@
 | 
				
			||||||
				user: this.user
 | 
									user: this.user
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.mute = () => {
 | 
				
			||||||
 | 
								this.api('mute/create', {
 | 
				
			||||||
 | 
									user_id: this.user.id
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									this.user.is_muted = true;
 | 
				
			||||||
 | 
									this.update();
 | 
				
			||||||
 | 
								}, e => {
 | 
				
			||||||
 | 
									alert('error');
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.unmute = () => {
 | 
				
			||||||
 | 
								this.api('mute/delete', {
 | 
				
			||||||
 | 
									user_id: this.user.id
 | 
				
			||||||
 | 
								}).then(() => {
 | 
				
			||||||
 | 
									this.user.is_muted = false;
 | 
				
			||||||
 | 
									this.update();
 | 
				
			||||||
 | 
								}, e => {
 | 
				
			||||||
 | 
									alert('error');
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
	</script>
 | 
						</script>
 | 
				
			||||||
</mk-user-profile>
 | 
					</mk-user-profile>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,6 +75,12 @@ props:
 | 
				
			||||||
    optional: true
 | 
					    optional: true
 | 
				
			||||||
    desc:
 | 
					    desc:
 | 
				
			||||||
      ja: "自分がこのユーザーにフォローされているか"
 | 
					      ja: "自分がこのユーザーにフォローされているか"
 | 
				
			||||||
 | 
					  - name: "is_muted"
 | 
				
			||||||
 | 
					    type: "boolean"
 | 
				
			||||||
 | 
					    optional: true
 | 
				
			||||||
 | 
					    desc:
 | 
				
			||||||
 | 
					      ja: "自分がこのユーザーをミュートしているか"
 | 
				
			||||||
 | 
					      en: "Whether you muted this user"
 | 
				
			||||||
  - name: "last_used_at"
 | 
					  - name: "last_used_at"
 | 
				
			||||||
    type: "date"
 | 
					    type: "date"
 | 
				
			||||||
    optional: false
 | 
					    optional: false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								src/web/docs/mute.ja.pug
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/web/docs/mute.ja.pug
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					h1 ミュート
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					p ユーザーページから、そのユーザーをミュートすることができます。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					p ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
 | 
				
			||||||
 | 
					ul
 | 
				
			||||||
 | 
						li タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRepost)
 | 
				
			||||||
 | 
						li そのユーザーからの通知
 | 
				
			||||||
 | 
						li メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					p ミュートを行ったことは相手に通知されず、ミュートされていることを知ることもできません。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					p 設定>ミュート から、自分がミュートしているユーザー一覧を確認することができます。
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,22 @@ section
 | 
				
			||||||
					| false ... フォローしていないユーザーに限定。
 | 
										| false ... フォローしていないユーザーに限定。
 | 
				
			||||||
					br
 | 
										br
 | 
				
			||||||
					| null ... 特に限定しない(デフォルト)
 | 
										| null ... 特に限定しない(デフォルト)
 | 
				
			||||||
 | 
								tr
 | 
				
			||||||
 | 
									td mute
 | 
				
			||||||
 | 
									td
 | 
				
			||||||
 | 
										| mute_all ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostを除外する(デフォルト)
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| mute_related ... ミュートしているユーザーの投稿に対する返信やRepostだけ除外する
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| mute_direct ... ミュートしているユーザーの投稿だけ除外する
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| disabled ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostも含める
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| direct_only ... ミュートしているユーザーの投稿だけに限定
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| related_only ... ミュートしているユーザーの投稿に対する返信やRepostだけに限定
 | 
				
			||||||
 | 
										br
 | 
				
			||||||
 | 
										| all_only ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostに限定
 | 
				
			||||||
			tr
 | 
								tr
 | 
				
			||||||
				td reply
 | 
									td reply
 | 
				
			||||||
				td
 | 
									td
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										67
									
								
								tools/migration/node.2017-12-22.hiseikika.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tools/migration/node.2017-12-22.hiseikika.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					// for Node.js interpret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { default: Post } = require('../../built/api/models/post')
 | 
				
			||||||
 | 
					const { default: zip } = require('@prezzemolo/zip')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const migrate = async (post) => {
 | 
				
			||||||
 | 
						const x = {};
 | 
				
			||||||
 | 
						if (post.reply_id != null) {
 | 
				
			||||||
 | 
							const reply = await Post.findOne({
 | 
				
			||||||
 | 
								_id: post.reply_id
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							x['_reply.user_id'] = reply.user_id;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (post.repost_id != null) {
 | 
				
			||||||
 | 
							const repost = await Post.findOne({
 | 
				
			||||||
 | 
								_id: post.repost_id
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							x['_repost.user_id'] = repost.user_id;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (post.reply_id != null || post.repost_id != null) {
 | 
				
			||||||
 | 
							const result = await Post.update(post._id, {
 | 
				
			||||||
 | 
								$set: x,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
							return result.ok === 1;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function main() {
 | 
				
			||||||
 | 
						const query = {
 | 
				
			||||||
 | 
							$or: [{
 | 
				
			||||||
 | 
								reply_id: {
 | 
				
			||||||
 | 
									$exists: true,
 | 
				
			||||||
 | 
									$ne: null
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}, {
 | 
				
			||||||
 | 
								repost_id: {
 | 
				
			||||||
 | 
									$exists: true,
 | 
				
			||||||
 | 
									$ne: null
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const count = await Post.count(query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const dop = Number.parseInt(process.argv[2]) || 5
 | 
				
			||||||
 | 
						const idop = ((count - (count % dop)) / dop) + 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return zip(
 | 
				
			||||||
 | 
							1,
 | 
				
			||||||
 | 
							async (time) => {
 | 
				
			||||||
 | 
								console.log(`${time} / ${idop}`)
 | 
				
			||||||
 | 
								const doc = await Post.find(query, {
 | 
				
			||||||
 | 
									limit: dop, skip: time * dop
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
								return Promise.all(doc.map(migrate))
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							idop
 | 
				
			||||||
 | 
						).then(a => {
 | 
				
			||||||
 | 
							const rv = []
 | 
				
			||||||
 | 
							a.forEach(e => rv.push(...e))
 | 
				
			||||||
 | 
							return rv
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main().then(console.dir).catch(console.error)
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue