summaryrefslogtreecommitdiff
path: root/src/api/endpoints/posts/timeline.ts
blob: c41cfdb8bdbeb64ec60f85471e6e98511ee51128 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/**
 * Module dependencies
 */
import $ from 'cafy';
import rap from '@prezzemolo/rap';
import Post from '../../models/post';
import Mute from '../../models/mute';
import ChannelWatching from '../../models/channel-watching';
import getFriends from '../../common/get-friends';
import { pack } from '../../models/post';

/**
 * Get timeline of myself
 *
 * @param {any} params
 * @param {any} user
 * @param {any} app
 * @return {Promise<any>}
 */
module.exports = async (params, user, app) => {
	// Get 'limit' parameter
	const [limit = 10, limitErr] = $(params.limit).optional.number().range(1, 100).$;
	if (limitErr) throw 'invalid limit param';

	// Get 'since_id' parameter
	const [sinceId, sinceIdErr] = $(params.since_id).optional.id().$;
	if (sinceIdErr) throw 'invalid since_id param';

	// Get 'until_id' parameter
	const [untilId, untilIdErr] = $(params.until_id).optional.id().$;
	if (untilIdErr) throw 'invalid until_id param';

	// Get 'since_date' parameter
	const [sinceDate, sinceDateErr] = $(params.since_date).optional.number().$;
	if (sinceDateErr) throw 'invalid since_date param';

	// Get 'until_date' parameter
	const [untilDate, untilDateErr] = $(params.until_date).optional.number().$;
	if (untilDateErr) throw 'invalid until_date param';

	// Check if only one of since_id, until_id, since_date, until_date specified
	if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) {
		throw 'only one of since_id, until_id, since_date, until_date can be specified';
	}

	const { followingIds, watchingChannelIds, mutedUserIds } = await rap({
		// ID list of the user itself and other users who the user follows
		followingIds: getFriends(user._id),

		// Watchしているチャンネルを取得
		watchingChannelIds: ChannelWatching.find({
			user_id: user._id,
			// 削除されたドキュメントは除く
			deleted_at: { $exists: false }
		}).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
	const sort = {
		_id: -1
	};

	const query = {
		$or: [{
			// フォローしている人のタイムラインへの投稿
			user_id: {
				$in: followingIds
			},
			// 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
			$or: [{
				channel_id: {
					$exists: false
				}
			}, {
				channel_id: null
			}]
		}, {
			// Watchしているチャンネルへの投稿
			channel_id: {
				$in: watchingChannelIds
			}
		}],
		// mute
		user_id: {
			$nin: mutedUserIds
		},
		'_reply.user_id': {
			$nin: mutedUserIds
		},
		'_repost.user_id': {
			$nin: mutedUserIds
		},
	} as any;

	if (sinceId) {
		sort._id = 1;
		query._id = {
			$gt: sinceId
		};
	} else if (untilId) {
		query._id = {
			$lt: untilId
		};
	} else if (sinceDate) {
		sort._id = 1;
		query.created_at = {
			$gt: new Date(sinceDate)
		};
	} else if (untilDate) {
		query.created_at = {
			$lt: new Date(untilDate)
		};
	}
	//#endregion

	// Issue query
	const timeline = await Post
		.find(query, {
			limit: limit,
			sort: sort
		});

	// Serialize
	return await Promise.all(timeline.map(post => pack(post, user)));
};