Skip to content

Commit

Permalink
細かいミュートの処理の修正 (#13695)
Browse files Browse the repository at this point in the history
* fix: some replies are removed from global timeline

* refactor: 各チャンネルのミュートとブロックの処理をまとめる

* fix: リノートをミュートでその人のノートのリノートをミュートしていたを修正

* refactor: isPureRenotePackedを他のところでも使う

* docs(changelog): CHANGELOGを更新

* test: withReplies = falseでフォローしてる人によるリプライが流れてくる

* test: ノートミュートしているユーザーの通常ノートのリノートが流れてくる/含まれる
  • Loading branch information
anatawa12 authored Apr 17, 2024
1 parent 977e2d2 commit e423b8c
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 104 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
- Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
- Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
- Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正

## 2024.3.1

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/core/FanoutTimelineEndpointService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class FanoutTimelineEndpointService {
filter = (note) => {
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
if (isRenote(note) && !isQuote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
if (!ps.ignoreAuthorFromMute && isRenote(note) && !isQuote(note) && userIdsWhoMeMutingRenotes.has(note.userId)) return false;
if (isInstanceMuted(note, userMutedInstances)) return false;

return parentFilter(note);
Expand Down
31 changes: 31 additions & 0 deletions packages/backend/src/misc/is-renote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import type { MiNote } from '@/models/Note.js';
import type { Packed } from '@/misc/json-schema.js';

type Renote =
MiNote & {
Expand Down Expand Up @@ -34,3 +35,33 @@ export function isQuote(note: Renote): note is Quote {
note.hasPoll ||
note.fileIds.length > 0;
}

type PackedRenote =
Packed<'Note'> & {
renoteId: NonNullable<Packed<'Note'>['renoteId']>
};

type PackedQuote =
PackedRenote & ({
text: NonNullable<Packed<'Note'>['text']>
} | {
cw: NonNullable<Packed<'Note'>['cw']>
} | {
replyId: NonNullable<Packed<'Note'>['replyId']>
} | {
poll: NonNullable<Packed<'Note'>['poll']>
} | {
fileIds: NonNullable<Packed<'Note'>['fileIds']>
});

export function isRenotePacked(note: Packed<'Note'>): note is PackedRenote {
return note.renoteId != null;
}

export function isQuotePacked(note: PackedRenote): note is PackedQuote {
return note.text != null ||
note.cw != null ||
note.replyId != null ||
note.poll != null ||
(note.fileIds != null && note.fileIds.length > 0);
}
22 changes: 22 additions & 0 deletions packages/backend/src/server/api/stream/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
*/

import { bindThis } from '@/decorators.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import type { Packed } from '@/misc/json-schema.js';
import type Connection from './Connection.js';

/**
Expand Down Expand Up @@ -54,6 +58,24 @@ export default abstract class Channel {
return this.connection.subscriber;
}

/*
* ミュートとブロックされてるを処理する
*/
protected isNoteMutedOrBlocked(note: Packed<'Note'>): boolean {
// 流れてきたNoteがインスタンスミュートしたインスタンスが関わる
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return true;

// 流れてきたNoteがミュートしているユーザーが関わる
if (isUserRelated(note, this.userIdsWhoMeMuting)) return true;
// 流れてきたNoteがブロックされているユーザーが関わる
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return true;

// 流れてきたNoteがリノートをミュートしてるユーザが行ったもの
if (isRenotePacked(note) && !isQuotePacked(note) && this.userIdsWhoMeMutingRenotes.has(note.user.id)) return true;

return false;
}

constructor(id: string, connection: Connection) {
this.id = id;
this.connection = connection;
Expand Down
8 changes: 1 addition & 7 deletions packages/backend/src/server/api/stream/channels/antenna.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/

import { Injectable } from '@nestjs/common';
import { isUserRelated } from '@/misc/is-user-related.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import type { GlobalEvents } from '@/core/GlobalEventService.js';
Expand Down Expand Up @@ -40,12 +39,7 @@ class AntennaChannel extends Channel {
if (data.type === 'note') {
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.isNoteMutedOrBlocked(note)) return;

this.connection.cacheNote(note);

Expand Down
11 changes: 3 additions & 8 deletions packages/backend/src/server/api/stream/channels/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*/

import { Injectable } from '@nestjs/common';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class ChannelChannel extends Channel {
Expand Down Expand Up @@ -38,14 +38,9 @@ class ChannelChannel extends Channel {
private async onNote(note: Packed<'Note'>) {
if (note.channelId !== this.channelId) return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;

if (this.user && note.renoteId && !note.text) {
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
note.renote.myReaction = myRenoteReaction;
Expand Down
25 changes: 4 additions & 21 deletions packages/backend/src/server/api/stream/channels/global-timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
*/

import { Injectable } from '@nestjs/common';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class GlobalTimelineChannel extends Channel {
Expand Down Expand Up @@ -52,26 +50,11 @@ class GlobalTimelineChannel extends Channel {
if (note.visibility !== 'public') return;
if (note.channelId != null) return;

// 関係ない返信は除外
if (note.reply && !this.following[note.userId]?.withReplies) {
const reply = note.reply;
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
}

if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;

// Ignore notes from instances the user has muted
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (this.user && note.renoteId && !note.text) {
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
note.renote.myReaction = myRenoteReaction;
Expand Down
11 changes: 3 additions & 8 deletions packages/backend/src/server/api/stream/channels/hashtag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

import { Injectable } from '@nestjs/common';
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class HashtagChannel extends Channel {
Expand Down Expand Up @@ -43,14 +43,9 @@ class HashtagChannel extends Channel {
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
if (!matched) return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;

if (this.user && note.renoteId && !note.text) {
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
note.renote.myReaction = myRenoteReaction;
Expand Down
18 changes: 4 additions & 14 deletions packages/backend/src/server/api/stream/channels/home-timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
*/

import { Injectable } from '@nestjs/common';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class HomeTimelineChannel extends Channel {
Expand Down Expand Up @@ -51,9 +49,6 @@ class HomeTimelineChannel extends Channel {
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
}

// Ignore notes from instances the user has muted
if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;

if (note.visibility === 'followers') {
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
} else if (note.visibility === 'specified') {
Expand All @@ -72,7 +67,7 @@ class HomeTimelineChannel extends Channel {
}

// 純粋なリノート(引用リノートでないリノート)の場合
if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && note.poll == null) {
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
if (!this.withRenotes) return;
if (note.renote.reply) {
const reply = note.renote.reply;
Expand All @@ -81,14 +76,9 @@ class HomeTimelineChannel extends Channel {
}
}

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (this.user && note.renoteId && !note.text) {
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
note.renote.myReaction = myRenoteReaction;
Expand Down
16 changes: 3 additions & 13 deletions packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@
*/

import { Injectable } from '@nestjs/common';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class HybridTimelineChannel extends Channel {
Expand Down Expand Up @@ -71,8 +69,7 @@ class HybridTimelineChannel extends Channel {
if (!isMe && !note.visibleUserIds!.includes(this.user!.id)) return;
}

// Ignore notes from instances the user has muted
if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (note.reply) {
const reply = note.reply;
Expand All @@ -85,14 +82,7 @@ class HybridTimelineChannel extends Channel {
}
}

if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;

if (this.user && note.renoteId && !note.text) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
Expand Down
14 changes: 4 additions & 10 deletions packages/backend/src/server/api/stream/channels/local-timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
*/

import { Injectable } from '@nestjs/common';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { MetaService } from '@/core/MetaService.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
import Channel, { type MiChannelService } from '../channel.js';

class LocalTimelineChannel extends Channel {
Expand Down Expand Up @@ -61,16 +60,11 @@ class LocalTimelineChannel extends Channel {
if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return;
}

if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
if (this.isNoteMutedOrBlocked(note)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;

if (this.user && note.renoteId && !note.text) {
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
note.renote.myReaction = myRenoteReaction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/

import { Injectable } from '@nestjs/common';
import { isUserRelated } from '@/misc/is-user-related.js';
import type { Packed } from '@/misc/json-schema.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
Expand Down Expand Up @@ -46,12 +44,7 @@ class RoleTimelineChannel extends Channel {
}
if (note.visibility !== 'public') return;

// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;

if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
if (this.isNoteMutedOrBlocked(note)) return;

this.send('note', note);
} else {
Expand Down
Loading

0 comments on commit e423b8c

Please sign in to comment.