summaryrefslogtreecommitdiff
path: root/src/api/endpoints
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/endpoints')
-rw-r--r--src/api/endpoints/i/notifications.ts23
-rw-r--r--src/api/endpoints/messaging/history.ts11
-rw-r--r--src/api/endpoints/messaging/messages/create.ts12
-rw-r--r--src/api/endpoints/messaging/unread.ts10
-rw-r--r--src/api/endpoints/mute/create.ts61
-rw-r--r--src/api/endpoints/mute/delete.ts63
-rw-r--r--src/api/endpoints/mute/list.ts73
-rw-r--r--src/api/endpoints/notifications/get_unread_count.ts10
-rw-r--r--src/api/endpoints/posts/create.ts20
-rw-r--r--src/api/endpoints/posts/search.ts89
-rw-r--r--src/api/endpoints/posts/timeline.ts25
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) {