diff options
| author | Akihiko Odaki <nekomanma@pixiv.co.jp> | 2018-04-02 17:11:14 +0900 |
|---|---|---|
| committer | Akihiko Odaki <nekomanma@pixiv.co.jp> | 2018-04-02 17:11:14 +0900 |
| commit | ce7efc4dbb9dca05b6b99b5ada22205890ca823f (patch) | |
| tree | 7889620f2172fef7277b66a7abb00557e966fbf3 /src/post | |
| parent | Introduce acct directory (diff) | |
| download | misskey-ce7efc4dbb9dca05b6b99b5ada22205890ca823f.tar.gz misskey-ce7efc4dbb9dca05b6b99b5ada22205890ca823f.tar.bz2 misskey-ce7efc4dbb9dca05b6b99b5ada22205890ca823f.zip | |
Distribute posts from remote
Diffstat (limited to 'src/post')
| -rw-r--r-- | src/post/create.ts | 50 | ||||
| -rw-r--r-- | src/post/distribute.ts | 216 | ||||
| -rw-r--r-- | src/post/watch.ts | 26 |
3 files changed, 292 insertions, 0 deletions
diff --git a/src/post/create.ts b/src/post/create.ts new file mode 100644 index 0000000000..ecea37382d --- /dev/null +++ b/src/post/create.ts @@ -0,0 +1,50 @@ +import parseAcct from '../acct/parse'; +import Post from '../models/post'; +import User from '../models/user'; + +export default async (post, reply, repost, atMentions) => { + post.mentions = []; + + function addMention(mentionee) { + // Reject if already added + if (post.mentions.some(x => x.equals(mentionee))) return; + + // Add mention + post.mentions.push(mentionee); + } + + if (reply) { + // Add mention + addMention(reply.userId); + post.replyId = reply._id; + post._reply = { userId: reply.userId }; + } else { + post.replyId = null; + post._reply = null; + } + + if (repost) { + if (post.text) { + // Add mention + addMention(repost.userId); + } + + post.repostId = repost._id; + post._repost = { userId: repost.userId }; + } else { + post.repostId = null; + post._repost = null; + } + + await Promise.all(atMentions.map(async mention => { + // Fetch mentioned user + // SELECT _id + const { _id } = await User + .findOne(parseAcct(mention), { _id: true }); + + // Add mention + addMention(_id); + })); + + return Post.insert(post); +}; diff --git a/src/post/distribute.ts b/src/post/distribute.ts new file mode 100644 index 0000000000..3925d4128e --- /dev/null +++ b/src/post/distribute.ts @@ -0,0 +1,216 @@ +import Channel from '../models/channel'; +import Mute from '../models/mute'; +import Following from '../models/following'; +import Post from '../models/post'; +import Watching from '../models/post-watching'; +import ChannelWatching from '../models/channel-watching'; +import User from '../models/user'; +import stream, { publishChannelStream } from '../publishers/stream'; +import notify from '../publishers/notify'; +import pushSw from '../publishers/push-sw'; +import watch from './watch'; + +export default async (user, mentions, post) => { + const promises = [ + User.update({ _id: user._id }, { + // Increment my posts count + $inc: { + postsCount: 1 + }, + + $set: { + latestPost: post._id + } + }), + ] as Array<Promise<any>>; + + function addMention(mentionee, reason) { + // Publish event + if (!user._id.equals(mentionee)) { + promises.push(Mute.find({ + muterId: mentionee, + deletedAt: { $exists: false } + }).then(mentioneeMutes => { + const mentioneesMutedUserIds = mentioneeMutes.map(m => m.muteeId.toString()); + if (mentioneesMutedUserIds.indexOf(user._id.toString()) == -1) { + stream(mentionee, reason, post); + pushSw(mentionee, reason, post); + } + })); + } + } + + // タイムラインへの投稿 + if (!post.channelId) { + // Publish event to myself's stream + stream(user._id, 'post', post); + + // Fetch all followers + const followers = await Following + .find({ + followeeId: user._id, + // 削除されたドキュメントは除く + deletedAt: { $exists: false } + }, { + followerId: true, + _id: false + }); + + // Publish event to followers stream + followers.forEach(following => + stream(following.followerId, 'post', post)); + } + + // チャンネルへの投稿 + if (post.channelId) { + // Increment channel index(posts count) + promises.push(Channel.update({ _id: post.channelId }, { + $inc: { + index: 1 + } + })); + + // Publish event to channel + publishChannelStream(post.channelId, 'post', post); + + // Get channel watchers + const watches = await ChannelWatching.find({ + channelId: post.channelId, + // 削除されたドキュメントは除く + deletedAt: { $exists: false } + }); + + // チャンネルの視聴者(のタイムライン)に配信 + watches.forEach(w => { + stream(w.userId, 'post', post); + }); + } + + // If has in reply to post + if (post.replyId) { + promises.push( + // Increment replies count + Post.update({ _id: post.replyId }, { + $inc: { + repliesCount: 1 + } + }), + + // 自分自身へのリプライでない限りは通知を作成 + notify(post.reply.userId, user._id, 'reply', { + postId: post._id + }), + + // Fetch watchers + Watching + .find({ + postId: post.replyId, + userId: { $ne: user._id }, + // 削除されたドキュメントは除く + deletedAt: { $exists: false } + }, { + fields: { + userId: true + } + }) + .then(watchers => { + watchers.forEach(watcher => { + notify(watcher.userId, user._id, 'reply', { + postId: post._id + }); + }); + }) + ); + + // Add mention + addMention(post.reply.userId, 'reply'); + + // この投稿をWatchする + if (user.account.settings.autoWatch !== false) { + promises.push(watch(user._id, post.reply)); + } + } + + // If it is repost + if (post.repostId) { + const type = post.text ? 'quote' : 'repost'; + + promises.push( + // Notify + notify(post.repost.userId, user._id, type, { + postId: post._id + }), + + // Fetch watchers + Watching + .find({ + postId: post.repostId, + userId: { $ne: user._id }, + // 削除されたドキュメントは除く + deletedAt: { $exists: false } + }, { + fields: { + userId: true + } + }) + .then(watchers => { + watchers.forEach(watcher => { + notify(watcher.userId, user._id, type, { + postId: post._id + }); + }); + }), + + // この投稿をWatchする + // TODO: ユーザーが「Repostしたときに自動でWatchする」設定を + // オフにしていた場合はしない + watch(user._id, post.repost) + ); + + // If it is quote repost + if (post.text) { + // Add mention + addMention(post.repost.userId, 'quote'); + } else { + // Publish event + if (!user._id.equals(post.repost.userId)) { + stream(post.repost.userId, 'repost', post); + } + } + + // 今までで同じ投稿をRepostしているか + const existRepost = await Post.findOne({ + userId: user._id, + repostId: post.repostId, + _id: { + $ne: post._id + } + }); + + if (!existRepost) { + // Update repostee status + promises.push(Post.update({ _id: post.repostId }, { + $inc: { + repostCount: 1 + } + })); + } + } + + // Resolve all mentions + await Promise.all(mentions.map(async mention => { + // 既に言及されたユーザーに対する返信や引用repostの場合も無視 + if (post.reply && post.reply.userId.equals(mention)) return; + if (post.repost && post.repost.userId.equals(mention)) return; + + // Add mention + addMention(mention, 'mention'); + + // Create notification + await notify(mention, user._id, 'mention', { + postId: post._id + }); + })); + + return Promise.all(promises); +}; diff --git a/src/post/watch.ts b/src/post/watch.ts new file mode 100644 index 0000000000..61ea444430 --- /dev/null +++ b/src/post/watch.ts @@ -0,0 +1,26 @@ +import * as mongodb from 'mongodb'; +import Watching from '../models/post-watching'; + +export default async (me: mongodb.ObjectID, post: object) => { + // 自分の投稿はwatchできない + if (me.equals((post as any).userId)) { + return; + } + + // if watching now + const exist = await Watching.findOne({ + postId: (post as any)._id, + userId: me, + deletedAt: { $exists: false } + }); + + if (exist !== null) { + return; + } + + await Watching.insert({ + createdAt: new Date(), + postId: (post as any)._id, + userId: me + }); +}; |