diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2017-12-22 17:42:03 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-22 17:42:03 +0900 |
| commit | e06dd199a7f3f0b1fd3bd67e06e463c2aa58633c (patch) | |
| tree | 5e9dc021c78288097cc02671f0c9a4ef04a83e2d /src/api/endpoints | |
| parent | Fix bug (diff) | |
| parent | Update create.ts (diff) | |
| download | sharkey-e06dd199a7f3f0b1fd3bd67e06e463c2aa58633c.tar.gz sharkey-e06dd199a7f3f0b1fd3bd67e06e463c2aa58633c.tar.bz2 sharkey-e06dd199a7f3f0b1fd3bd67e06e463c2aa58633c.zip | |
Merge pull request #1028 from syuilo/mute
Mute
Diffstat (limited to 'src/api/endpoints')
| -rw-r--r-- | src/api/endpoints/i/notifications.ts | 23 | ||||
| -rw-r--r-- | src/api/endpoints/messaging/history.ts | 11 | ||||
| -rw-r--r-- | src/api/endpoints/messaging/messages/create.ts | 12 | ||||
| -rw-r--r-- | src/api/endpoints/messaging/unread.ts | 10 | ||||
| -rw-r--r-- | src/api/endpoints/mute/create.ts | 61 | ||||
| -rw-r--r-- | src/api/endpoints/mute/delete.ts | 63 | ||||
| -rw-r--r-- | src/api/endpoints/mute/list.ts | 73 | ||||
| -rw-r--r-- | src/api/endpoints/notifications/get_unread_count.ts | 10 | ||||
| -rw-r--r-- | src/api/endpoints/posts/create.ts | 20 | ||||
| -rw-r--r-- | src/api/endpoints/posts/search.ts | 89 | ||||
| -rw-r--r-- | src/api/endpoints/posts/timeline.ts | 25 |
11 files changed, 381 insertions, 16 deletions
diff --git a/src/api/endpoints/i/notifications.ts b/src/api/endpoints/i/notifications.ts index 48254e5e67..fb9be7f61b 100644 --- a/src/api/endpoints/i/notifications.ts +++ b/src/api/endpoints/i/notifications.ts @@ -3,6 +3,7 @@ */ import $ from 'cafy'; import Notification from '../../models/notification'; +import Mute from '../../models/mute'; import serialize from '../../serializers/notification'; import getFriends from '../../common/get-friends'; 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'); } + const mute = await Mute.find({ + muter_id: user._id, + deleted_at: { $exists: false } + }); + const query = { - notifiee_id: user._id + notifiee_id: user._id, + $and: [{ + notifier_id: { + $nin: mute.map(m => m.mutee_id) + } + }] } as any; const sort = { @@ -54,12 +65,14 @@ module.exports = (params, user) => new Promise(async (res, rej) => { }; 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); - query.notifier_id = { - $in: followingIds - }; + query.$and.push({ + notifier_id: { + $in: followingIds + } + }); } if (type) { diff --git a/src/api/endpoints/messaging/history.ts b/src/api/endpoints/messaging/history.ts index 5f7c9276dd..f14740dff5 100644 --- a/src/api/endpoints/messaging/history.ts +++ b/src/api/endpoints/messaging/history.ts @@ -3,6 +3,7 @@ */ import $ from 'cafy'; import History from '../../models/messaging-history'; +import Mute from '../../models/mute'; 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).$; if (limitErr) return rej('invalid limit param'); + const mute = await Mute.find({ + muter_id: user._id, + deleted_at: { $exists: false } + }); + // Get history const history = await History .find({ - user_id: user._id + user_id: user._id, + partner: { + $nin: mute.map(m => m.mutee_id) + } }, { limit: limit, sort: { diff --git a/src/api/endpoints/messaging/messages/create.ts b/src/api/endpoints/messaging/messages/create.ts index 3c7689f967..4e9d10197c 100644 --- a/src/api/endpoints/messaging/messages/create.ts +++ b/src/api/endpoints/messaging/messages/create.ts @@ -6,6 +6,7 @@ import Message from '../../../models/messaging-message'; import { isValidText } from '../../../models/messaging-message'; import History from '../../../models/messaging-history'; import User from '../../../models/user'; +import Mute from '../../../models/mute'; import DriveFile from '../../../models/drive-file'; import serialize from '../../../serializers/messaging-message'; import publishUserStream from '../../../event'; @@ -97,6 +98,17 @@ module.exports = (params, user) => new Promise(async (res, rej) => { setTimeout(async () => { const freshMessage = await Message.findOne({ _id: message._id }, { is_read: true }); 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); pushSw(message.recipient_id, 'unread_messaging_message', messageObj); } diff --git a/src/api/endpoints/messaging/unread.ts b/src/api/endpoints/messaging/unread.ts index 40bc83fe1c..c4326e1d22 100644 --- a/src/api/endpoints/messaging/unread.ts +++ b/src/api/endpoints/messaging/unread.ts @@ -2,6 +2,7 @@ * Module dependencies */ import Message from '../../models/messaging-message'; +import Mute from '../../models/mute'; /** * Get count of unread messages @@ -11,8 +12,17 @@ import Message from '../../models/messaging-message'; * @return {Promise<any>} */ 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 .count({ + user_id: { + $nin: mutedUserIds + }, recipient_id: user._id, is_read: false }); diff --git a/src/api/endpoints/mute/create.ts b/src/api/endpoints/mute/create.ts new file mode 100644 index 0000000000..f44854ab52 --- /dev/null +++ b/src/api/endpoints/mute/create.ts @@ -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(); +}); diff --git a/src/api/endpoints/mute/delete.ts b/src/api/endpoints/mute/delete.ts new file mode 100644 index 0000000000..d6bff3353a --- /dev/null +++ b/src/api/endpoints/mute/delete.ts @@ -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(); +}); diff --git a/src/api/endpoints/mute/list.ts b/src/api/endpoints/mute/list.ts new file mode 100644 index 0000000000..740e19f0bb --- /dev/null +++ b/src/api/endpoints/mute/list.ts @@ -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, + }); +}); diff --git a/src/api/endpoints/notifications/get_unread_count.ts b/src/api/endpoints/notifications/get_unread_count.ts index 9514e78713..845d6b29ce 100644 --- a/src/api/endpoints/notifications/get_unread_count.ts +++ b/src/api/endpoints/notifications/get_unread_count.ts @@ -2,6 +2,7 @@ * Module dependencies */ import Notification from '../../models/notification'; +import Mute from '../../models/mute'; /** * Get count of unread notifications @@ -11,9 +12,18 @@ import Notification from '../../models/notification'; * @return {Promise<any>} */ 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 .count({ notifiee_id: user._id, + notifier_id: { + $nin: mutedUserIds + }, is_read: false }); diff --git a/src/api/endpoints/posts/create.ts b/src/api/endpoints/posts/create.ts index 7270efaf71..a1d05c67c6 100644 --- a/src/api/endpoints/posts/create.ts +++ b/src/api/endpoints/posts/create.ts @@ -8,6 +8,7 @@ import { default as Post, IPost, isValidText } from '../../models/post'; import { default as User, IUser } from '../../models/user'; import { default as Channel, IChannel } from '../../models/channel'; import Following from '../../models/following'; +import Mute from '../../models/mute'; import DriveFile from '../../models/drive-file'; import Watching from '../../models/post-watching'; import ChannelWatching from '../../models/channel-watching'; @@ -215,7 +216,11 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { poll: poll, text: text, 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 @@ -236,7 +241,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const mentions = []; - function addMention(mentionee, reason) { + async function addMention(mentionee, reason) { // Reject if already added 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 if (!user._id.equals(mentionee)) { - event(mentionee, reason, postObj); - pushSw(mentionee, reason, postObj); + const mentioneeMutes = await Mute.find({ + 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); + } } } diff --git a/src/api/endpoints/posts/search.ts b/src/api/endpoints/posts/search.ts index ac25652a0b..f722231d4c 100644 --- a/src/api/endpoints/posts/search.ts +++ b/src/api/endpoints/posts/search.ts @@ -6,6 +6,7 @@ import $ from 'cafy'; const escapeRegexp = require('escape-regexp'); import Post from '../../models/post'; import User from '../../models/user'; +import Mute from '../../models/mute'; import getFriends from '../../common/get-friends'; import serialize from '../../serializers/post'; 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().$; 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 const [reply = null, replyErr] = $(params.reply).optional.nullable.boolean().$; 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 not, search by MongoDB (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 -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 = { $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) { push({ @@ -236,7 +319,7 @@ async function byNative(res, rej, me, text, userId, following, reply, repost, me } // 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'); es.search({ diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts index 91cba0a047..da7ffd0c14 100644 --- a/src/api/endpoints/posts/timeline.ts +++ b/src/api/endpoints/posts/timeline.ts @@ -4,6 +4,7 @@ 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 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'; } - const { followingIds, watchingChannelIds } = await rap({ + 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)) + }).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 @@ -77,7 +86,17 @@ module.exports = async (params, user, app) => { channel_id: { $in: watchingChannelIds } - }] + }], + // mute + user_id: { + $nin: mutedUserIds + }, + '_reply.user_id': { + $nin: mutedUserIds + }, + '_repost.user_id': { + $nin: mutedUserIds + }, } as any; if (sinceId) { |