細かいミュートの処理の修正 (#13695)
* fix: some replies are removed from global timeline * refactor: 各チャンネルのミュートとブロックの処理をまとめる * fix: リノートをミュートでその人のノートのリノートをミュートしていたを修正 * refactor: isPureRenotePackedを他のところでも使う * docs(changelog): CHANGELOGを更新 * test: withReplies = falseでフォローしてる人によるリプライが流れてくる * test: ノートミュートしているユーザーの通常ノートのリノートが流れてくる/含まれる
This commit is contained in:
parent
977e2d2c09
commit
e423b8ce4b
15 changed files with 124 additions and 104 deletions
|
@ -55,6 +55,9 @@
|
||||||
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
|
- Fix: 登録にメール認証が必須になっている場合、登録されているメールアドレスを削除できないように
|
||||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/606)
|
||||||
- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
|
- Fix: nginx経由で/files/にRangeリクエストされた場合に正しく応答できないのを修正
|
||||||
|
- Fix: 一部のタイムラインのストリーミングでインスタンスミュートが効かない問題を修正
|
||||||
|
- Fix: グローバルタイムラインで返信が表示されないことがある問題を修正
|
||||||
|
- Fix: リノートをミュートしたユーザの投稿のリノートがミュートされる問題を修正
|
||||||
|
|
||||||
## 2024.3.1
|
## 2024.3.1
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,7 @@ export class FanoutTimelineEndpointService {
|
||||||
filter = (note) => {
|
filter = (note) => {
|
||||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||||
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) 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;
|
if (isInstanceMuted(note, userMutedInstances)) return false;
|
||||||
|
|
||||||
return parentFilter(note);
|
return parentFilter(note);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { MiNote } from '@/models/Note.js';
|
import type { MiNote } from '@/models/Note.js';
|
||||||
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
|
|
||||||
type Renote =
|
type Renote =
|
||||||
MiNote & {
|
MiNote & {
|
||||||
|
@ -34,3 +35,33 @@ export function isQuote(note: Renote): note is Quote {
|
||||||
note.hasPoll ||
|
note.hasPoll ||
|
||||||
note.fileIds.length > 0;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { bindThis } from '@/decorators.js';
|
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';
|
import type Connection from './Connection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +58,24 @@ export default abstract class Channel {
|
||||||
return this.connection.subscriber;
|
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) {
|
constructor(id: string, connection: Connection) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
|
@ -40,12 +39,7 @@ class AntennaChannel extends Channel {
|
||||||
if (data.type === 'note') {
|
if (data.type === 'note') {
|
||||||
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
|
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
|
||||||
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
|
|
||||||
|
|
||||||
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
|
|
||||||
|
|
||||||
this.connection.cacheNote(note);
|
this.connection.cacheNote(note);
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class ChannelChannel extends Channel {
|
class ChannelChannel extends Channel {
|
||||||
|
@ -38,14 +38,9 @@ class ChannelChannel extends Channel {
|
||||||
private async onNote(note: Packed<'Note'>) {
|
private async onNote(note: Packed<'Note'>) {
|
||||||
if (note.channelId !== this.channelId) return;
|
if (note.channelId !== this.channelId) return;
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
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.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
|
|
|
@ -4,14 +4,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
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 type { Packed } from '@/misc/json-schema.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class GlobalTimelineChannel extends Channel {
|
class GlobalTimelineChannel extends Channel {
|
||||||
|
@ -52,26 +50,11 @@ class GlobalTimelineChannel extends Channel {
|
||||||
if (note.visibility !== 'public') return;
|
if (note.visibility !== 'public') return;
|
||||||
if (note.channelId != null) return;
|
if (note.channelId != null) return;
|
||||||
|
|
||||||
// 関係ない返信は除外
|
if (isRenotePacked(note) && !isQuotePacked(note) && !this.withRenotes) 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;
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
|
|
||||||
// Ignore notes from instances the user has muted
|
if (this.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
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 (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
|
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { normalizeForSearch } from '@/misc/normalize-for-search.js';
|
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 type { Packed } from '@/misc/json-schema.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class HashtagChannel extends Channel {
|
class HashtagChannel extends Channel {
|
||||||
|
@ -43,14 +43,9 @@ class HashtagChannel extends Channel {
|
||||||
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
||||||
if (!matched) return;
|
if (!matched) return;
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
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.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
|
|
|
@ -4,12 +4,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
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 type { Packed } from '@/misc/json-schema.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class HomeTimelineChannel extends Channel {
|
class HomeTimelineChannel extends Channel {
|
||||||
|
@ -51,9 +49,6 @@ class HomeTimelineChannel extends Channel {
|
||||||
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
|
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 (note.visibility === 'followers') {
|
||||||
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
|
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
|
||||||
} else if (note.visibility === 'specified') {
|
} else if (note.visibility === 'specified') {
|
||||||
|
@ -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 (!this.withRenotes) return;
|
||||||
if (note.renote.reply) {
|
if (note.renote.reply) {
|
||||||
const reply = note.renote.reply;
|
const reply = note.renote.reply;
|
||||||
|
@ -81,14 +76,9 @@ class HomeTimelineChannel extends Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
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.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
|
|
|
@ -4,14 +4,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
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 type { Packed } from '@/misc/json-schema.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class HybridTimelineChannel extends Channel {
|
class HybridTimelineChannel extends Channel {
|
||||||
|
@ -71,8 +69,7 @@ class HybridTimelineChannel extends Channel {
|
||||||
if (!isMe && !note.visibleUserIds!.includes(this.user!.id)) return;
|
if (!isMe && !note.visibleUserIds!.includes(this.user!.id)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore notes from instances the user has muted
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
|
|
||||||
|
|
||||||
if (note.reply) {
|
if (note.reply) {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
|
@ -85,14 +82,7 @@ class HybridTimelineChannel extends Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
|
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
if (this.user && note.renoteId && !note.text) {
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
|
|
|
@ -4,13 +4,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
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 type { Packed } from '@/misc/json-schema.js';
|
||||||
import { MetaService } from '@/core/MetaService.js';
|
import { MetaService } from '@/core/MetaService.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class LocalTimelineChannel extends Channel {
|
class LocalTimelineChannel extends Channel {
|
||||||
|
@ -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 (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 (this.isNoteMutedOrBlocked(note)) return;
|
||||||
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.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
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 { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
@ -46,12 +44,7 @@ class RoleTimelineChannel extends Channel {
|
||||||
}
|
}
|
||||||
if (note.visibility !== 'public') return;
|
if (note.visibility !== 'public') return;
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
|
||||||
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
|
|
||||||
|
|
||||||
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
|
|
||||||
|
|
||||||
this.send('note', note);
|
this.send('note', note);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -5,12 +5,11 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
|
||||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
|
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||||
import Channel, { type MiChannelService } from '../channel.js';
|
import Channel, { type MiChannelService } from '../channel.js';
|
||||||
|
|
||||||
class UserListChannel extends Channel {
|
class UserListChannel extends Channel {
|
||||||
|
@ -106,25 +105,17 @@ class UserListChannel extends Channel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (this.isNoteMutedOrBlocked(note)) return;
|
||||||
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.user && isRenotePacked(note) && !isQuotePacked(note)) {
|
||||||
|
|
||||||
if (this.user && note.renoteId && !note.text) {
|
|
||||||
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
if (note.renote && Object.keys(note.renote.reactions).length > 0) {
|
||||||
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
|
||||||
note.renote.myReaction = myRenoteReaction;
|
note.renote.myReaction = myRenoteReaction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているインスタンスに関わるものだったら無視する
|
|
||||||
if (isInstanceMuted(note, this.userMutedInstances)) return;
|
|
||||||
|
|
||||||
this.connection.cacheNote(note);
|
this.connection.cacheNote(note);
|
||||||
|
|
||||||
this.send('note', note);
|
this.send('note', note);
|
||||||
|
|
|
@ -63,6 +63,22 @@ describe('Renote Mute', () => {
|
||||||
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
|
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #12956
|
||||||
|
test('タイムラインにリノートミュートしているユーザーの通常ノートのリノートが含まれる', async () => {
|
||||||
|
const carolNote = await post(carol, { text: 'hi' });
|
||||||
|
const bobRenote = await post(bob, { renoteId: carolNote.id });
|
||||||
|
|
||||||
|
// redisに追加されるのを待つ
|
||||||
|
await sleep(100);
|
||||||
|
|
||||||
|
const res = await api('notes/local-timeline', {}, alice);
|
||||||
|
|
||||||
|
assert.strictEqual(res.status, 200);
|
||||||
|
assert.strictEqual(Array.isArray(res.body), true);
|
||||||
|
assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), true);
|
||||||
|
assert.strictEqual(res.body.some((note: any) => note.id === bobRenote.id), true);
|
||||||
|
});
|
||||||
|
|
||||||
test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {
|
test('ストリームにリノートミュートしているユーザーのリノートが流れない', async () => {
|
||||||
const bobNote = await post(bob, { text: 'hi' });
|
const bobNote = await post(bob, { text: 'hi' });
|
||||||
|
|
||||||
|
@ -86,4 +102,17 @@ describe('Renote Mute', () => {
|
||||||
|
|
||||||
assert.strictEqual(fired, true);
|
assert.strictEqual(fired, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// #12956
|
||||||
|
test('ストリームにリノートミュートしているユーザーの通常ノートのリノートが流れてくる', async () => {
|
||||||
|
const carolbNote = await post(carol, { text: 'hi' });
|
||||||
|
|
||||||
|
const fired = await waitFire(
|
||||||
|
alice, 'localTimeline',
|
||||||
|
() => api('notes/create', { renoteId: carolbNote.id }, bob),
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === bob.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,7 +63,7 @@ describe('Streaming', () => {
|
||||||
takumiNote = await post(takumi, { text: 'piyo' });
|
takumiNote = await post(takumi, { text: 'piyo' });
|
||||||
|
|
||||||
// Follow: ayano => kyoko
|
// Follow: ayano => kyoko
|
||||||
await api('following/create', { userId: kyoko.id }, ayano);
|
await api('following/create', { userId: kyoko.id, withReplies: false }, ayano);
|
||||||
|
|
||||||
// Follow: ayano => akari
|
// Follow: ayano => akari
|
||||||
await follow(ayano, akari);
|
await follow(ayano, akari);
|
||||||
|
@ -509,6 +509,16 @@ describe('Streaming', () => {
|
||||||
|
|
||||||
assert.strictEqual(fired, false);
|
assert.strictEqual(fired, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('withReplies = falseでフォローしてる人によるリプライが流れてくる', async () => {
|
||||||
|
const fired = await waitFire(
|
||||||
|
ayano, 'globalTimeline', // ayano:Global
|
||||||
|
() => api('notes/create', { text: 'foo', replyId: kanakoNote.id }, kyoko), // kyoko posts
|
||||||
|
msg => msg.type === 'note' && msg.body.userId === kyoko.id, // wait kyoko
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.strictEqual(fired, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('UserList Timeline', () => {
|
describe('UserList Timeline', () => {
|
||||||
|
|
Loading…
Reference in a new issue