summaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2020-07-27 13:34:20 +0900
committerGitHub <noreply@github.com>2020-07-27 13:34:20 +0900
commitcf43dd6ec530ba4a3f589ae917e89533b352f6a3 (patch)
tree76f35d06299b40370ec061ee5ed58182847d2e6e /src/server
parentrefactor(client): Do not mutate prop directly (diff)
downloadsharkey-cf43dd6ec530ba4a3f589ae917e89533b352f6a3.tar.gz
sharkey-cf43dd6ec530ba4a3f589ae917e89533b352f6a3.tar.bz2
sharkey-cf43dd6ec530ba4a3f589ae917e89533b352f6a3.zip
ワードミュート (#6594)
* wip * wip * wip * wip * wip * wip * wip * wip * wip
Diffstat (limited to 'src/server')
-rw-r--r--src/server/api/common/generate-muted-note-query.ts13
-rw-r--r--src/server/api/endpoints/i/update.ts10
-rw-r--r--src/server/api/endpoints/notes/global-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/hybrid-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/local-timeline.ts2
-rw-r--r--src/server/api/endpoints/notes/timeline.ts2
-rw-r--r--src/server/api/stream/channel.ts4
-rw-r--r--src/server/api/stream/channels/global-timeline.ts8
-rw-r--r--src/server/api/stream/channels/home-timeline.ts8
-rw-r--r--src/server/api/stream/channels/hybrid-timeline.ts8
-rw-r--r--src/server/api/stream/channels/local-timeline.ts8
-rw-r--r--src/server/api/stream/index.ts16
12 files changed, 81 insertions, 2 deletions
diff --git a/src/server/api/common/generate-muted-note-query.ts b/src/server/api/common/generate-muted-note-query.ts
new file mode 100644
index 0000000000..498930476c
--- /dev/null
+++ b/src/server/api/common/generate-muted-note-query.ts
@@ -0,0 +1,13 @@
+import { User } from '../../../models/entities/user';
+import { MutedNotes } from '../../../models';
+import { SelectQueryBuilder } from 'typeorm';
+
+export function generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: User) {
+ const mutedQuery = MutedNotes.createQueryBuilder('muted')
+ .select('muted.noteId')
+ .where('muted.userId = :userId', { userId: me.id });
+
+ q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`);
+
+ q.setParameters(mutedQuery.getParameters());
+}
diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts
index 48b5e48fc2..e1889df22d 100644
--- a/src/server/api/endpoints/i/update.ts
+++ b/src/server/api/endpoints/i/update.ts
@@ -142,7 +142,11 @@ export const meta = {
desc: {
'ja-JP': 'ピン留めするページID'
}
- }
+ },
+
+ mutedWords: {
+ validator: $.optional.arr($.arr($.str))
+ },
},
errors: {
@@ -193,6 +197,10 @@ export default define(meta, async (ps, user, token) => {
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
+ if (ps.mutedWords !== undefined) {
+ profileUpdates.mutedWords = ps.mutedWords;
+ profileUpdates.enableWordMute = ps.mutedWords.length > 0;
+ }
if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked;
if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot;
if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot;
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index 26b0cb0f5a..4361b8a299 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -10,6 +10,7 @@ import { activeUsersChart } from '../../../../services/chart';
import { generateRepliesQuery } from '../../common/generate-replies-query';
import { injectPromo } from '../../common/inject-promo';
import { injectFeatured } from '../../common/inject-featured';
+import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
export const meta = {
desc: {
@@ -83,6 +84,7 @@ export default define(meta, async (ps, user) => {
generateRepliesQuery(query, user);
if (user) generateMuteQuery(query, user);
+ if (user) generateMutedNoteQuery(query, user);
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
diff --git a/src/server/api/endpoints/notes/hybrid-timeline.ts b/src/server/api/endpoints/notes/hybrid-timeline.ts
index b0a73d1d7d..82199e607e 100644
--- a/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -12,6 +12,7 @@ import { activeUsersChart } from '../../../../services/chart';
import { generateRepliesQuery } from '../../common/generate-replies-query';
import { injectPromo } from '../../common/inject-promo';
import { injectFeatured } from '../../common/inject-featured';
+import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
export const meta = {
desc: {
@@ -133,6 +134,7 @@ export default define(meta, async (ps, user) => {
generateRepliesQuery(query, user);
generateVisibilityQuery(query, user);
generateMuteQuery(query, user);
+ generateMutedNoteQuery(query, user);
if (ps.includeMyRenotes === false) {
query.andWhere(new Brackets(qb => {
diff --git a/src/server/api/endpoints/notes/local-timeline.ts b/src/server/api/endpoints/notes/local-timeline.ts
index a74dc3b15c..9d51b3b48b 100644
--- a/src/server/api/endpoints/notes/local-timeline.ts
+++ b/src/server/api/endpoints/notes/local-timeline.ts
@@ -12,6 +12,7 @@ import { Brackets } from 'typeorm';
import { generateRepliesQuery } from '../../common/generate-replies-query';
import { injectPromo } from '../../common/inject-promo';
import { injectFeatured } from '../../common/inject-featured';
+import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
export const meta = {
desc: {
@@ -101,6 +102,7 @@ export default define(meta, async (ps, user) => {
generateRepliesQuery(query, user);
generateVisibilityQuery(query, user);
if (user) generateMuteQuery(query, user);
+ if (user) generateMutedNoteQuery(query, user);
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index d60136a9ca..c6929f4a51 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -10,6 +10,7 @@ import { Brackets } from 'typeorm';
import { generateRepliesQuery } from '../../common/generate-replies-query';
import { injectPromo } from '../../common/inject-promo';
import { injectFeatured } from '../../common/inject-featured';
+import { generateMutedNoteQuery } from '../../common/generate-muted-note-query';
export const meta = {
desc: {
@@ -126,6 +127,7 @@ export default define(meta, async (ps, user) => {
generateRepliesQuery(query, user);
generateVisibilityQuery(query, user);
generateMuteQuery(query, user);
+ generateMutedNoteQuery(query, user);
if (ps.includeMyRenotes === false) {
query.andWhere(new Brackets(qb => {
diff --git a/src/server/api/stream/channel.ts b/src/server/api/stream/channel.ts
index 18fa651820..82a95ad3d7 100644
--- a/src/server/api/stream/channel.ts
+++ b/src/server/api/stream/channel.ts
@@ -15,6 +15,10 @@ export default abstract class Channel {
return this.connection.user;
}
+ protected get userProfile() {
+ return this.connection.userProfile;
+ }
+
protected get following() {
return this.connection.following;
}
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index a3ecf8e706..39800fa775 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -4,6 +4,7 @@ import Channel from '../channel';
import { fetchMeta } from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models';
import { PackedNote } from '../../../../models/repositories/note';
+import { checkWordMute } from '../../../../misc/check-word-mute';
export default class extends Channel {
public readonly chName = 'globalTimeline';
@@ -47,6 +48,13 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (shouldMuteThisNote(note, this.muting)) return;
+ // 流れてきたNoteがミュートすべきNoteだったら無視する
+ // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
+ // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
+ // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
+ // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
+ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/home-timeline.ts b/src/server/api/stream/channels/home-timeline.ts
index 3cf57c294c..8504d4547b 100644
--- a/src/server/api/stream/channels/home-timeline.ts
+++ b/src/server/api/stream/channels/home-timeline.ts
@@ -3,6 +3,7 @@ import shouldMuteThisNote from '../../../../misc/should-mute-this-note';
import Channel from '../channel';
import { Notes } from '../../../../models';
import { PackedNote } from '../../../../models/repositories/note';
+import { checkWordMute } from '../../../../misc/check-word-mute';
export default class extends Channel {
public readonly chName = 'homeTimeline';
@@ -52,6 +53,13 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (shouldMuteThisNote(note, this.muting)) return;
+ // 流れてきたNoteがミュートすべきNoteだったら無視する
+ // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
+ // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
+ // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
+ // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
+ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/hybrid-timeline.ts b/src/server/api/stream/channels/hybrid-timeline.ts
index 40686f4b28..bc491934ea 100644
--- a/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/src/server/api/stream/channels/hybrid-timeline.ts
@@ -5,6 +5,7 @@ import { fetchMeta } from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models';
import { PackedNote } from '../../../../models/repositories/note';
import { PackedUser } from '../../../../models/repositories/user';
+import { checkWordMute } from '../../../../misc/check-word-mute';
export default class extends Channel {
public readonly chName = 'hybridTimeline';
@@ -61,6 +62,13 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (shouldMuteThisNote(note, this.muting)) return;
+ // 流れてきたNoteがミュートすべきNoteだったら無視する
+ // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
+ // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
+ // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
+ // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
+ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+
this.send('note', note);
}
diff --git a/src/server/api/stream/channels/local-timeline.ts b/src/server/api/stream/channels/local-timeline.ts
index 4b7f74e4f7..3279912f87 100644
--- a/src/server/api/stream/channels/local-timeline.ts
+++ b/src/server/api/stream/channels/local-timeline.ts
@@ -5,6 +5,7 @@ import { fetchMeta } from '../../../../misc/fetch-meta';
import { Notes } from '../../../../models';
import { PackedNote } from '../../../../models/repositories/note';
import { PackedUser } from '../../../../models/repositories/user';
+import { checkWordMute } from '../../../../misc/check-word-mute';
export default class extends Channel {
public readonly chName = 'localTimeline';
@@ -49,6 +50,13 @@ export default class extends Channel {
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (shouldMuteThisNote(note, this.muting)) return;
+ // 流れてきたNoteがミュートすべきNoteだったら無視する
+ // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
+ // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
+ // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
+ // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
+ if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
+
this.send('note', note);
}
diff --git a/src/server/api/stream/index.ts b/src/server/api/stream/index.ts
index b7cefcf5ab..bebf88a7cd 100644
--- a/src/server/api/stream/index.ts
+++ b/src/server/api/stream/index.ts
@@ -7,15 +7,17 @@ import Channel from './channel';
import channels from './channels';
import { EventEmitter } from 'events';
import { User } from '../../../models/entities/user';
-import { Users, Followings, Mutings } from '../../../models';
+import { Users, Followings, Mutings, UserProfiles } from '../../../models';
import { ApiError } from '../error';
import { AccessToken } from '../../../models/entities/access-token';
+import { UserProfile } from '../../../models/entities/user-profile';
/**
* Main stream connection
*/
export default class Connection {
public user?: User;
+ public userProfile?: UserProfile;
public following: User['id'][] = [];
public muting: User['id'][] = [];
public token?: AccessToken;
@@ -25,6 +27,7 @@ export default class Connection {
private subscribingNotes: any = {};
private followingClock: NodeJS.Timer;
private mutingClock: NodeJS.Timer;
+ private userProfileClock: NodeJS.Timer;
constructor(
wsConnection: websocket.connection,
@@ -49,6 +52,9 @@ export default class Connection {
this.updateMuting();
this.mutingClock = setInterval(this.updateMuting, 5000);
+
+ this.updateUserProfile();
+ this.userProfileClock = setInterval(this.updateUserProfile, 5000);
}
}
@@ -262,6 +268,13 @@ export default class Connection {
this.muting = mutings.map(x => x.muteeId);
}
+ @autobind
+ private async updateUserProfile() {
+ this.userProfile = await UserProfiles.findOne({
+ userId: this.user!.id
+ });
+ }
+
/**
* ストリームが切れたとき
*/
@@ -273,5 +286,6 @@ export default class Connection {
if (this.followingClock) clearInterval(this.followingClock);
if (this.mutingClock) clearInterval(this.mutingClock);
+ if (this.userProfileClock) clearInterval(this.userProfileClock);
}
}