ストーキング実装

Closes #1511
This commit is contained in:
syuilo
2018-04-19 12:43:25 +09:00
parent d403869945
commit 0d5bc3be66
22 changed files with 310 additions and 80 deletions

View File

@ -1,10 +1,10 @@
import * as mongodb from 'mongodb';
import Following from '../../../models/following';
export default async (me: mongodb.ObjectID, includeMe: boolean = true) => {
export const getFriendIds = async (me: mongodb.ObjectID, includeMe = true) => {
// Fetch relation to other users who the I follows
// SELECT followee
const myfollowing = await Following
const followings = await Following
.find({
followerId: me
}, {
@ -14,7 +14,7 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => {
});
// ID list of other users who the I follows
const myfollowingIds = myfollowing.map(follow => follow.followeeId);
const myfollowingIds = followings.map(following => following.followeeId);
if (includeMe) {
myfollowingIds.push(me);
@ -22,3 +22,26 @@ export default async (me: mongodb.ObjectID, includeMe: boolean = true) => {
return myfollowingIds;
};
export const getFriends = async (me: mongodb.ObjectID, includeMe = true) => {
// Fetch relation to other users who the I follows
const followings = await Following
.find({
followerId: me
});
// ID list of other users who the I follows
const myfollowings = followings.map(following => ({
id: following.followeeId,
stalk: following.stalk
}));
if (includeMe) {
myfollowings.push({
id: me,
stalk: true
});
}
return myfollowings;
};

View File

@ -426,6 +426,24 @@ const endpoints: Endpoint[] = [
},
kind: 'following-write'
},
{
name: 'following/stalk',
withCredential: true,
limit: {
duration: ms('1hour'),
max: 100
},
kind: 'following-write'
},
{
name: 'following/unstalk',
withCredential: true,
limit: {
duration: ms('1hour'),
max: 100
},
kind: 'following-write'
},
{
name: 'notes'

View File

@ -0,0 +1,36 @@
import $ from 'cafy';
import Following from '../../../../models/following';
import { isLocalUser } from '../../../../models/user';
/**
* Stalk a user
*/
module.exports = (params, user) => new Promise(async (res, rej) => {
const follower = user;
// Get 'userId' parameter
const [userId, userIdErr] = $(params.userId).id().$;
if (userIdErr) return rej('invalid userId param');
// Fetch following
const following = await Following.findOne({
followerId: follower._id,
followeeId: userId
});
if (following === null) {
return rej('following not found');
}
// Stalk
await Following.update({ _id: following._id }, {
$set: {
stalk: true
}
});
// Send response
res();
// TODO: イベント
});

View File

@ -0,0 +1,35 @@
import $ from 'cafy';
import Following from '../../../../models/following';
/**
* Unstalk a user
*/
module.exports = (params, user) => new Promise(async (res, rej) => {
const follower = user;
// Get 'userId' parameter
const [userId, userIdErr] = $(params.userId).id().$;
if (userIdErr) return rej('invalid userId param');
// Fetch following
const following = await Following.findOne({
followerId: follower._id,
followeeId: userId
});
if (following === null) {
return rej('following not found');
}
// Stalk
await Following.update({ _id: following._id }, {
$set: {
stalk: false
}
});
// Send response
res();
// TODO: イベント
});

View File

@ -5,7 +5,7 @@ import $ from 'cafy';
import Notification from '../../../../models/notification';
import Mute from '../../../../models/mute';
import { pack } from '../../../../models/notification';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
import read from '../../common/read-notification';
/**
@ -62,7 +62,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
if (following) {
// ID list of the user itself and other users who the user follows
const followingIds = await getFriends(user._id);
const followingIds = await getFriendIds(user._id);
query.$and.push({
notifierId: {

View File

@ -4,7 +4,7 @@
import $ from 'cafy';
import Mute from '../../../../models/mute';
import { pack } from '../../../../models/user';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
/**
* Get muted users of a user
@ -34,7 +34,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
if (iknow) {
// Get my friends
const myFriends = await getFriends(me._id);
const myFriends = await getFriendIds(me._id);
query.muteeId = {
$in: myFriends

View File

@ -3,7 +3,7 @@
*/
import $ from 'cafy';
import Note from '../../../../models/note';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
import { pack } from '../../../../models/note';
/**
@ -46,7 +46,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => {
};
if (following) {
const followingIds = await getFriends(user._id);
const followingIds = await getFriendIds(user._id);
query.userId = {
$in: followingIds

View File

@ -6,7 +6,7 @@ const escapeRegexp = require('escape-regexp');
import Note from '../../../../models/note';
import User from '../../../../models/user';
import Mute from '../../../../models/mute';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
import { pack } from '../../../../models/note';
/**
@ -156,7 +156,7 @@ async function search(
}
if (following != null && me != null) {
const ids = await getFriends(me._id, false);
const ids = await getFriendIds(me._id, false);
push({
userId: following ? {
$in: ids

View File

@ -2,11 +2,10 @@
* Module dependencies
*/
import $ from 'cafy';
import rap from '@prezzemolo/rap';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
import ChannelWatching from '../../../../models/channel-watching';
import getFriends from '../../common/get-friends';
import { getFriends } from '../../common/get-friends';
import { pack } from '../../../../models/note';
/**
@ -38,41 +37,66 @@ module.exports = async (params, user, app) => {
throw 'only one of sinceId, untilId, sinceDate, untilDate 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),
const [followings, watchingChannelIds, mutedUserIds] = await Promise.all([
// フォローを取得
// Fetch following
getFriends(user._id),
// Watchしているチャンネルを取得
watchingChannelIds: ChannelWatching.find({
ChannelWatching.find({
userId: user._id,
// 削除されたドキュメントは除く
deletedAt: { $exists: false }
}).then(watches => watches.map(w => w.channelId)),
// ミュートしているユーザーを取得
mutedUserIds: Mute.find({
Mute.find({
muterId: user._id
}).then(ms => ms.map(m => m.muteeId))
});
]);
//#region Construct query
const sort = {
_id: -1
};
const followQuery = followings.map(f => f.stalk ? {
userId: f.id
} : {
userId: f.id,
// ストーキングしてないならリプライは含めない(ただし投稿者自身の投稿へのリプライ、自分の投稿へのリプライ、自分のリプライは含める)
$or: [{
// リプライでない
replyId: null
}, { // または
// リプライだが返信先が投稿者自身の投稿
$expr: {
'$_reply.userId': '$userId'
}
}, { // または
// リプライだが返信先が自分(フォロワー)の投稿
'_reply.userId': user._id
}, { // または
// 自分(フォロワー)が送信したリプライ
userId: user._id
}]
});
const query = {
$or: [{
// フォローしている人のタイムラインへの投稿
userId: {
$in: followingIds
},
// 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
$or: [{
channelId: {
$exists: false
}
$and: [{
// フォローしている人のタイムラインへの投稿
$or: followQuery
}, {
channelId: null
// 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
$or: [{
channelId: {
$exists: false
}
}, {
channelId: null
}]
}]
}, {
// Watchしているチャンネルへの投稿

View File

@ -5,7 +5,7 @@ import $ from 'cafy';
import User from '../../../../models/user';
import Following from '../../../../models/following';
import { pack } from '../../../../models/user';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
/**
* Get followers of a user
@ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
// ログインしていてかつ iknow フラグがあるとき
if (me && iknow) {
// Get my friends
const myFriends = await getFriends(me._id);
const myFriends = await getFriendIds(me._id);
query.followerId = {
$in: myFriends

View File

@ -5,7 +5,7 @@ import $ from 'cafy';
import User from '../../../../models/user';
import Following from '../../../../models/following';
import { pack } from '../../../../models/user';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
/**
* Get following users of a user
@ -52,7 +52,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
// ログインしていてかつ iknow フラグがあるとき
if (me && iknow) {
// Get my friends
const myFriends = await getFriends(me._id);
const myFriends = await getFriendIds(me._id);
query.followeeId = {
$in: myFriends

View File

@ -4,7 +4,7 @@
const ms = require('ms');
import $ from 'cafy';
import User, { pack } from '../../../../models/user';
import getFriends from '../../common/get-friends';
import { getFriendIds } from '../../common/get-friends';
import Mute from '../../../../models/mute';
/**
@ -24,7 +24,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
if (offsetErr) return rej('invalid offset param');
// ID list of the user itself and other users who the user follows
const followingIds = await getFriends(me._id);
const followingIds = await getFriendIds(me._id);
// ミュートしているユーザーを取得
const mutedUserIds = (await Mute.find({