From 109738ccb9ef8c203685e6f4bc31986ac2a17046 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 17 Sep 2018 09:00:20 +0900 Subject: ハッシュタグタイムラインを実装 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/notes/search_by_tag.ts | 45 +++++++++++------------ src/server/api/stream/hashtag.ts | 48 +++++++++++++++++++++++++ src/server/api/streaming.ts | 6 ++++ 3 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 src/server/api/stream/hashtag.ts (limited to 'src/server/api') diff --git a/src/server/api/endpoints/notes/search_by_tag.ts b/src/server/api/endpoints/notes/search_by_tag.ts index 11bfe34724..7beb97aec6 100644 --- a/src/server/api/endpoints/notes/search_by_tag.ts +++ b/src/server/api/endpoints/notes/search_by_tag.ts @@ -13,12 +13,18 @@ export const meta = { }, params: { - tag: $.str.note({ + tag: $.str.optional.note({ desc: { 'ja-JP': 'タグ' } }), + query: $.arr($.arr($.str)).optional.note({ + desc: { + 'ja-JP': 'クエリ' + } + }), + includeUserIds: $.arr($.type(ID)).optional.note({ default: [] }), @@ -59,11 +65,9 @@ export const meta = { } }), - withFiles: $.bool.optional.nullable.note({ - default: null, - + withFiles: $.bool.optional.note({ desc: { - 'ja-JP': 'ファイルが添付された投稿に限定するか否か' + 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します' } }), @@ -126,8 +130,14 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => } const q: any = { - $and: [{ + $and: [ps.tag ? { tagsLower: ps.tag.toLowerCase() + } : { + $or: ps.query.map(tags => ({ + $and: tags.map(t => ({ + tagsLower: t.toLowerCase() + })) + })) }], deletedAt: { $exists: false } }; @@ -281,25 +291,10 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => const withFiles = ps.withFiles != null ? ps.withFiles : ps.media; - if (withFiles != null) { - if (withFiles) { - push({ - fileIds: { - $exists: true, - $ne: null - } - }); - } else { - push({ - $or: [{ - fileIds: { - $exists: false - } - }, { - fileIds: null - }] - }); - } + if (withFiles) { + push({ + fileIds: { $exists: true, $ne: [] } + }); } if (ps.poll != null) { diff --git a/src/server/api/stream/hashtag.ts b/src/server/api/stream/hashtag.ts new file mode 100644 index 0000000000..db2806e796 --- /dev/null +++ b/src/server/api/stream/hashtag.ts @@ -0,0 +1,48 @@ +import * as websocket from 'websocket'; +import Xev from 'xev'; + +import { IUser } from '../../../models/user'; +import Mute from '../../../models/mute'; +import { pack } from '../../../models/note'; + +export default async function( + request: websocket.request, + connection: websocket.connection, + subscriber: Xev, + user?: IUser +) { + const mute = user ? await Mute.find({ muterId: user._id }) : null; + const mutedUserIds = mute ? mute.map(m => m.muteeId.toString()) : []; + + const q: Array = JSON.parse((request.resourceURL.query as any).q); + + // Subscribe stream + subscriber.on('hashtag', async note => { + const matched = q.some(tags => tags.every(tag => note.tags.map((t: string) => t.toLowerCase()).includes(tag.toLowerCase()))); + if (!matched) return; + + // Renoteなら再pack + if (note.renoteId != null) { + note.renote = await pack(note.renoteId, user, { + detail: true + }); + } + + //#region 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する + if (mutedUserIds.indexOf(note.userId) != -1) { + return; + } + if (note.reply != null && mutedUserIds.indexOf(note.reply.userId) != -1) { + return; + } + if (note.renote != null && mutedUserIds.indexOf(note.renote.userId) != -1) { + return; + } + //#endregion + + connection.send(JSON.stringify({ + type: 'note', + body: note + })); + }); +} diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index e6094a40b2..873719d031 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -14,6 +14,7 @@ import reversiGameStream from './stream/games/reversi-game'; import reversiStream from './stream/games/reversi'; import serverStatsStream from './stream/server-stats'; import notesStatsStream from './stream/notes-stats'; +import hashtagStream from './stream/hashtag'; import { ParsedUrlQuery } from 'querystring'; import authenticate from './authenticate'; @@ -57,6 +58,11 @@ module.exports = (server: http.Server) => { return; } + if (request.resourceURL.pathname === '/hashtag') { + hashtagStream(request, connection, ev, user); + return; + } + if (user == null) { connection.send('authentication-failed'); connection.close(); -- cgit v1.2.3-freya