completely re-implement note visibility as NoteVisibilityService
This commit is contained in:
parent
d1912362e0
commit
85ca2269e4
27 changed files with 925 additions and 420 deletions
|
|
@ -93,7 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoin('(select "host" from "instance" where "isBubbled" = true)', 'bubbleInstance', '"bubbleInstance"."host" = "note"."userHost"')
|
||||
.andWhere('"bubbleInstance" IS NOT NULL');
|
||||
this.queryService
|
||||
.leftJoin(query, 'note.userInstance', 'userInstance', '"userInstance"."host" = "bubbleInstance"."host"');
|
||||
.leftJoin(query, 'note.userInstance', 'userInstance');
|
||||
|
||||
this.queryService.generateBlockedHostQueryForNote(query);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { NoteCreateService } from '@/core/NoteCreateService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -261,6 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private noteCreateService: NoteCreateService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
if (ps.text && ps.text.length > this.config.maxNoteLength) {
|
||||
|
|
@ -303,7 +305,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
||||
} else if (isRenote(renote) && !isQuote(renote)) {
|
||||
throw new ApiError(meta.errors.cannotReRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(renote, me.id)) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(renote, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotRenoteDueToVisibility);
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +353,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(reply, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { NoteEditService } from '@/core/NoteEditService.js';
|
|||
import { DI } from '@/di-symbols.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -311,6 +312,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private noteEditService: NoteEditService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
if (ps.text && ps.text.length > this.config.maxNoteLength) {
|
||||
|
|
@ -408,7 +410,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
} else if (isRenote(reply) && !isQuote(reply)) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
} else if (!await this.noteEntityService.isVisibleForMe(reply, me.id, { me })) {
|
||||
} else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(reply, me)).accessible) {
|
||||
throw new ApiError(meta.errors.cannotReplyToInvisibleNote);
|
||||
} else if (reply.visibility === 'specified' && ps.visibility !== 'specified') {
|
||||
throw new ApiError(meta.errors.cannotReplyToSpecifiedVisibilityNoteWithExtendedVisibility);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { DI } from '@/di-symbols.js';
|
|||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { hasText } from '@/models/Note.js';
|
||||
import { ApiLoggerService } from '@/server/api/ApiLoggerService.js';
|
||||
import { NoteVisibilityService } from '@/core/NoteVisibilityService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
|
|
@ -84,6 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private roleService: RoleService,
|
||||
private readonly cacheService: CacheService,
|
||||
private readonly loggerService: ApiLoggerService,
|
||||
private readonly noteVisibilityService: NoteVisibilityService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const note = await this.getterService.getNote(ps.noteId).catch(err => {
|
||||
|
|
@ -91,7 +93,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw err;
|
||||
});
|
||||
|
||||
if (!(await this.noteEntityService.isVisibleForMe(note, me?.id ?? null, { me }))) {
|
||||
const { accessible } = await this.noteVisibilityService.checkNoteVisibilityAsync(note, me);
|
||||
if (!accessible) {
|
||||
throw new ApiError(meta.errors.cannotTranslateInvisibleNote);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateSuspendedUserQueryForNote(query, true);
|
||||
this.queryService.generateSilencedUserQueryForNotes(query, me, true);
|
||||
if (me) {
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, { id: ps.userId });
|
||||
this.queryService.generateMutedUserQueryForNotes(query, me, true);
|
||||
this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import type { JsonObject, JsonValue } from '@/misc/json-value.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { deepClone } from '@/misc/clone.js';
|
||||
import type Connection from '@/server/api/stream/Connection.js';
|
||||
import { NoteVisibilityFilters } from '@/core/NoteVisibilityService.js';
|
||||
|
||||
/**
|
||||
* Stream channel
|
||||
|
|
@ -26,6 +27,10 @@ export default abstract class Channel {
|
|||
public static readonly requireCredential: boolean;
|
||||
public static readonly kind?: string | null;
|
||||
|
||||
protected get noteVisibilityService() {
|
||||
return this.noteEntityService.noteVisibilityService;
|
||||
}
|
||||
|
||||
protected get user() {
|
||||
return this.connection.user;
|
||||
}
|
||||
|
|
@ -105,8 +110,14 @@ export default abstract class Channel {
|
|||
return this.connection.myRecentFavorites;
|
||||
}
|
||||
|
||||
protected async checkNoteVisibility(note: Packed<'Note'>, filters?: NoteVisibilityFilters) {
|
||||
// Don't use any of the local cached data, because this does everything through CacheService which is just as fast with updated data.
|
||||
return await this.noteVisibilityService.checkNoteVisibilityAsync(note, this.user, { filters });
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a note is visible to the current user *excluding* blocks and mutes.
|
||||
* @deprecated use isNoteHidden instead
|
||||
*/
|
||||
protected isNoteVisibleToMe(note: Packed<'Note'>): boolean {
|
||||
if (note.visibility === 'public') return true;
|
||||
|
|
@ -120,8 +131,9 @@ export default abstract class Channel {
|
|||
return note.visibleUserIds.includes(this.user.id);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* ミュートとブロックされてるを処理する
|
||||
* @deprecated use isNoteHidden instead
|
||||
*/
|
||||
protected isNoteMutedOrBlocked(note: Packed<'Note'>): boolean {
|
||||
// Ignore notes that require sign-in
|
||||
|
|
@ -196,12 +208,11 @@ export default abstract class Channel {
|
|||
// If we didn't clone the notes here, different connections would asynchronously write
|
||||
// different values to the same object, resulting in a random value being sent to each frontend. -- Dakkar
|
||||
const clonedNote = deepClone(note);
|
||||
const notes = crawl(clonedNote);
|
||||
|
||||
// Hide notes before everything else, since this modifies fields that the other functions will check.
|
||||
await this.noteEntityService.hideNotes(notes, this.user.id);
|
||||
const notes = crawl(clonedNote);
|
||||
|
||||
const [myReactions, myRenotes, myFavorites, myThreadMutings, myNoteMutings] = await Promise.all([
|
||||
const [myReactions, myRenotes, myFavorites, myThreadMutings, myNoteMutings, myFollowings] = await Promise.all([
|
||||
this.noteEntityService.populateMyReactions(notes, this.user.id, {
|
||||
myReactions: this.myRecentReactions,
|
||||
}),
|
||||
|
|
@ -213,13 +224,25 @@ export default abstract class Channel {
|
|||
}),
|
||||
this.noteEntityService.populateMyTheadMutings(notes, this.user.id),
|
||||
this.noteEntityService.populateMyNoteMutings(notes, this.user.id),
|
||||
this.cacheService.userFollowingsCache.fetch(this.user.id),
|
||||
]);
|
||||
|
||||
note.myReaction = myReactions.get(note.id) ?? null;
|
||||
note.isRenoted = myRenotes.has(note.id);
|
||||
note.isFavorited = myFavorites.has(note.id);
|
||||
note.isMutingThread = myThreadMutings.has(note.id);
|
||||
note.isMutingNote = myNoteMutings.has(note.id);
|
||||
for (const n of notes) {
|
||||
// Sync visibility in case there's something like "makeNotesFollowersOnlyBefore" enabled
|
||||
this.noteVisibilityService.syncVisibility(n);
|
||||
|
||||
n.myReaction = myReactions.get(n.id) ?? null;
|
||||
n.isRenoted = myRenotes.has(n.id);
|
||||
n.isFavorited = myFavorites.has(n.id);
|
||||
n.isMutingThread = myThreadMutings.has(n.id);
|
||||
n.isMutingNote = myNoteMutings.has(n.id);
|
||||
n.user.bypassSilence = n.userId === this.user.id || myFollowings.has(n.userId);
|
||||
}
|
||||
|
||||
// Hide notes *after* we sync visibility
|
||||
await this.noteEntityService.hideNotes(notes, this.user.id, {
|
||||
userFollowings: myFollowings,
|
||||
});
|
||||
|
||||
return clonedNote;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ class AntennaChannel extends Channel {
|
|||
if (data.type === 'note') {
|
||||
const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true });
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
this.send('note', note);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ 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';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import Channel, { MiChannelService } from '../channel.js';
|
||||
|
||||
class BubbleTimelineChannel extends Channel {
|
||||
|
|
@ -47,8 +47,6 @@ class BubbleTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
|
|
@ -56,27 +54,9 @@ class BubbleTimelineChannel extends Channel {
|
|||
if (note.channelId != null) return;
|
||||
if (!this.utilityService.isBubbledHost(note.user.host)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
class ChannelChannel extends Channel {
|
||||
|
|
@ -45,9 +46,9 @@ class ChannelChannel extends Channel {
|
|||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
|
||||
if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ 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 { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -48,36 +48,15 @@ class GlobalTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
if (note.visibility !== 'public') return;
|
||||
if (note.channelId != null) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ class HashtagChannel extends Channel {
|
|||
const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag))));
|
||||
if (!matched) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Injectable } from '@nestjs/common';
|
|||
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 { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -50,28 +50,9 @@ class HomeTimelineChannel extends Channel {
|
|||
if (!isMe && !this.following.has(note.userId)) return;
|
||||
}
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ 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 { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -67,28 +67,10 @@ class HybridTimelineChannel extends Channel {
|
|||
(note.channelId != null && this.followingChannels.has(note.channelId))
|
||||
)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies && !this.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
if (!this.withReplies && note.replyId != null) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ 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 { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -50,8 +50,6 @@ class LocalTimelineChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
|
||||
if (!this.withBots && note.user.isBot) return;
|
||||
|
||||
|
|
@ -59,28 +57,10 @@ class LocalTimelineChannel extends Channel {
|
|||
if (note.visibility !== 'public') return;
|
||||
if (note.channelId != null) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
// 関係ない返信は除外
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
if (!this.withReplies && note.replyId != null) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -35,24 +35,19 @@ class MainChannel extends Channel {
|
|||
if (isUserFromMutedInstance(data.body, this.userMutedInstances)) return;
|
||||
if (data.body.userId && this.userIdsWhoMeMuting.has(data.body.userId)) return;
|
||||
|
||||
if (data.body.note && data.body.note.isHidden) {
|
||||
if (this.isNoteMutedOrBlocked(data.body.note)) return;
|
||||
if (!this.isNoteVisibleToMe(data.body.id)) return;
|
||||
const note = await this.noteEntityService.pack(data.body.note.id, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
data.body.note = note;
|
||||
if (data.body.note) {
|
||||
const { accessible, silence } = await this.checkNoteVisibility(data.body.note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
data.body.note = await this.rePackNote(data.body.note);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'mention': {
|
||||
if (this.isNoteMutedOrBlocked(data.body)) return;
|
||||
if (data.body.isHidden) {
|
||||
const note = await this.noteEntityService.pack(data.body.id, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
data.body = note;
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(data.body, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
|
||||
data.body = await this.rePackNote(data.body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { RoleService } from '@/core/RoleService.js';
|
||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import { isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote, isQuotePacked, isRenotePacked } from '@/misc/is-renote.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
class RoleTimelineChannel extends Channel {
|
||||
|
|
@ -49,26 +49,8 @@ class RoleTimelineChannel extends Channel {
|
|||
}
|
||||
if (note.visibility !== 'public') return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note);
|
||||
if (!accessible || silence) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type { Packed } from '@/misc/json-schema.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { isRenotePacked, isQuotePacked } from '@/misc/is-renote.js';
|
||||
import { isPackedPureRenote } from '@/misc/is-renote.js';
|
||||
import type { JsonObject } from '@/misc/json-value.js';
|
||||
import Channel, { type MiChannelService } from '../channel.js';
|
||||
|
||||
|
|
@ -82,8 +82,6 @@ class UserListChannel extends Channel {
|
|||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
const isMe = this.user?.id === note.userId;
|
||||
|
||||
// チャンネル投稿は無視する
|
||||
if (note.channelId) return;
|
||||
|
||||
|
|
@ -91,28 +89,9 @@ class UserListChannel extends Channel {
|
|||
|
||||
if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
|
||||
|
||||
if (this.isNoteMutedOrBlocked(note)) return;
|
||||
if (!this.isNoteVisibleToMe(note)) return;
|
||||
|
||||
if (note.reply) {
|
||||
const reply = note.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
if (!this.following.get(note.userId)?.withReplies) {
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||
}
|
||||
}
|
||||
|
||||
// 純粋なリノート(引用リノートでないリノート)の場合
|
||||
if (isRenotePacked(note) && !isQuotePacked(note) && note.renote) {
|
||||
if (!this.withRenotes) return;
|
||||
if (note.renote.reply) {
|
||||
const reply = note.renote.reply;
|
||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信のリノートは弾く
|
||||
if (!this.isNoteVisibleToMe(reply)) return;
|
||||
}
|
||||
}
|
||||
const { accessible, silence } = await this.checkNoteVisibility(note, { includeReplies: true });
|
||||
if (!accessible || silence) return;
|
||||
if (!this.withRenotes && isPackedPureRenote(note)) return;
|
||||
|
||||
const clonedNote = await this.rePackNote(note);
|
||||
this.send('note', clonedNote);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue