diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index ea74d3a84e..a168126a2f 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -57,6 +57,8 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import { LatestNoteService } from '@/core/LatestNoteService.js'; import { CollapsedQueue } from '@/misc/collapsed-queue.js'; import { CacheService } from '@/core/CacheService.js'; +import { NoteVisibilityService } from '@/core/NoteVisibilityService.js'; +import { isPureRenote } from '@/misc/is-renote.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -223,6 +225,7 @@ export class NoteCreateService implements OnApplicationShutdown { private userBlockingService: UserBlockingService, private cacheService: CacheService, private latestNoteService: LatestNoteService, + private readonly noteVisibilityService: NoteVisibilityService, ) { this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount); } @@ -284,6 +287,10 @@ export class NoteCreateService implements OnApplicationShutdown { } if (data.renote) { + if (isPureRenote(data.renote)) { + throw new IdentifiableError('fd4cc33e-2a37-48dd-99cc-9b806eb2031a', 'Cannot renote a pure renote (boost)'); + } + switch (data.renote.visibility) { case 'public': // public noteは無条件にrenote可能 @@ -297,7 +304,7 @@ export class NoteCreateService implements OnApplicationShutdown { case 'followers': // 他人のfollowers noteはreject if (data.renote.userId !== user.id) { - throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is not public or home'); + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Renote target is not public or home'); } // Renote対象がfollowersならfollowersにする @@ -305,25 +312,55 @@ export class NoteCreateService implements OnApplicationShutdown { break; case 'specified': // specified / direct noteはreject - throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is not public or home'); + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Renote target is not public or home'); + } + + if (data.renote.userId !== user.id) { + // Check local-only + if (data.renote.localOnly && user.host != null) { + throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Remote user cannot renote a local-only note'); + } + + // Check visibility + const visibilityCheck = await this.noteVisibilityService.checkNoteVisibilityAsync(data.renote, user.id); + if (!visibilityCheck.accessible) { + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Cannot renote an invisible note'); + } + + // Check blocking + if (await this.userBlockingService.checkBlocked(data.renote.userId, user.id)) { + throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is blocked'); + } + } + } + + if (data.reply) { + if (isPureRenote(data.reply)) { + throw new IdentifiableError('3ac74a84-8fd5-4bb0-870f-01804f82ce15', 'Cannot reply to a pure renote (boost)'); + } + + if (data.reply.userId !== user.id) { + // Check local-only + if (data.reply.localOnly && user.host != null) { + throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Remote user cannot reply to a local-only note'); + } + + // Check visibility + const visibilityCheck = await this.noteVisibilityService.checkNoteVisibilityAsync(data.reply, user.id); + if (!visibilityCheck.accessible) { + throw new IdentifiableError('b98980fa-3780-406c-a935-b6d0eeee10d1', 'Cannot reply to an invisible note'); + } + + // Check blocking + if (await this.userBlockingService.checkBlocked(data.reply.userId, user.id)) { + throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Reply target is blocked'); + } } } // Check quote permissions await this.checkQuotePermissions(data, user); - // Check blocking - if (this.isRenote(data) && !this.isQuote(data)) { - if (data.renote.userHost === null) { - if (data.renote.userId !== user.id) { - const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id); - if (blocked) { - throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is blocked'); - } - } - } - } - // 返信対象がpublicではないならhomeにする if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') { data.visibility = 'home'; @@ -481,14 +518,6 @@ export class NoteCreateService implements OnApplicationShutdown { mandatoryCW: data.mandatoryCW, }); - // should really not happen, but better safe than sorry - if (data.reply?.id === insert.id) { - throw new IdentifiableError('ea93b7c2-3d6c-4e10-946b-00d50b1a75cb', 'A note can\'t reply to itself'); - } - if (data.renote?.id === insert.id) { - throw new IdentifiableError('ea93b7c2-3d6c-4e10-946b-00d50b1a75cb', 'A note can\'t renote itself'); - } - if (data.uri != null) insert.uri = data.uri; if (data.url != null) insert.url = data.url; diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts index 9e8bb8b4fd..af9538dc50 100644 --- a/packages/backend/src/core/NoteEditService.ts +++ b/packages/backend/src/core/NoteEditService.ts @@ -52,6 +52,8 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import { LatestNoteService } from '@/core/LatestNoteService.js'; import { CollapsedQueue } from '@/misc/collapsed-queue.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { NoteVisibilityService } from '@/core/NoteVisibilityService.js'; +import { isPureRenote } from '@/misc/is-renote.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention' | 'edited'; @@ -220,6 +222,7 @@ export class NoteEditService implements OnApplicationShutdown { private cacheService: CacheService, private latestNoteService: LatestNoteService, private noteCreateService: NoteCreateService, + private readonly noteVisibilityService: NoteVisibilityService, ) { this.updateNotesCountQueue = new CollapsedQueue(process.env.NODE_ENV !== 'test' ? 60 * 1000 * 5 : 0, this.collapseNotesCount, this.performUpdateNotesCount); } @@ -303,8 +306,8 @@ export class NoteEditService implements OnApplicationShutdown { } if (this.isRenote(data)) { - if (data.renote.id === oldnote.id) { - throw new IdentifiableError('ea93b7c2-3d6c-4e10-946b-00d50b1a75cb', `edit failed for ${oldnote.id}: cannot renote itself`); + if (isPureRenote(data.renote)) { + throw new IdentifiableError('fd4cc33e-2a37-48dd-99cc-9b806eb2031a', 'Cannot renote a pure renote (boost)'); } switch (data.renote.visibility) { @@ -320,7 +323,7 @@ export class NoteEditService implements OnApplicationShutdown { case 'followers': // 他人のfollowers noteはreject if (data.renote.userId !== user.id) { - throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is not public or home'); + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Renote target is not public or home'); } // Renote対象がfollowersならfollowersにする @@ -328,25 +331,45 @@ export class NoteEditService implements OnApplicationShutdown { break; case 'specified': // specified / direct noteはreject - throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is not public or home'); + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Renote target is not public or home'); + } + + if (data.renote.userId !== user.id) { + // Check local-only + if (data.renote.localOnly && user.host != null) { + throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Remote user cannot renote a local-only note'); + } + + // Check visibility + const visibilityCheck = await this.noteVisibilityService.checkNoteVisibilityAsync(data.renote, user.id); + if (!visibilityCheck.accessible) { + throw new IdentifiableError('be9529e9-fe72-4de0-ae43-0b363c4938af', 'Cannot renote an invisible note'); + } + + // Check blocking + if (await this.userBlockingService.checkBlocked(data.renote.userId, user.id)) { + throw new IdentifiableError('b6352a84-e5cd-4b05-a26c-63437a6b98ba', 'Renote target is blocked'); + } + } + + // Check for recursion + if (data.renote.id === oldnote.id) { + throw new IdentifiableError('33510210-8452-094c-6227-4a6c05d99f02', `edit failed for ${oldnote.id}: note cannot quote itself`); + } + for (let nextRenoteId = data.renote.renoteId; nextRenoteId != null;) { + if (nextRenoteId === oldnote.id) { + throw new IdentifiableError('ea93b7c2-3d6c-4e10-946b-00d50b1a75cb', `edit failed for ${oldnote.id}: note cannot quote a quote of itself`); + } + + // TODO create something like threadId but for quotes, that way we don't need full recursion + const next = await this.notesRepository.findOne({ where: { id: nextRenoteId }, select: { renoteId: true } }); + nextRenoteId = next?.renoteId ?? null; } } // Check quote permissions await this.noteCreateService.checkQuotePermissions(data, user); - // Check blocking - if (this.isRenote(data) && !this.isQuote(data)) { - if (data.renote.userHost === null) { - if (data.renote.userId !== user.id) { - const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id); - if (blocked) { - throw new Error('blocked'); - } - } - } - } - // 返信対象がpublicではないならhomeにする if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') { data.visibility = 'home'; diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 5a5a76f7d6..15c4546063 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -42,6 +42,19 @@ export class ApAudienceService { others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), )).filter(x => x != null); + // If no audience is specified, then assume public + if ( + toGroups.public.length === 0 && toGroups.followers.length === 0 && + ccGroups.public.length === 0 && ccGroups.followers.length === 0 && + others.length === 0 + ) { + return { + visibility: 'public', + mentionedUsers: [], + visibleUsers: [], + }; + } + if (toGroups.public.length > 0) { return { visibility: 'public', diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 32661fb6d9..652efd46b2 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -239,15 +239,12 @@ export class ApNoteService { } const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); - let visibility = noteAudience.visibility; + const visibility = noteAudience.visibility; const visibleUsers = noteAudience.visibleUsers; // Audience (to, cc) が指定されてなかった場合 if (visibility === 'specified' && visibleUsers.length === 0) { - if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している - // こちらから匿名GET出来たものならばpublic - visibility = 'public'; - } + throw new IdentifiableError('dc2ad0d1-36bf-41f5-8e4c-a4d265a28387', `failed to create note ${entryUri}: could not resolve any recipients`); } const processErrors: string[] = []; @@ -283,13 +280,6 @@ export class ApNoteService { processErrors.push('quoteUnavailable'); } - if (reply && reply.userHost == null && reply.localOnly) { - throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Cannot reply to local-only note'); - } - if (quote && quote.userHost == null && quote.localOnly) { - throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Cannot quote a local-only note'); - } - // vote if (reply && reply.hasPoll) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); @@ -430,15 +420,12 @@ export class ApNoteService { //#endregion const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); - let visibility = noteAudience.visibility; + const visibility = noteAudience.visibility; const visibleUsers = noteAudience.visibleUsers; // Audience (to, cc) が指定されてなかった場合 if (visibility === 'specified' && visibleUsers.length === 0) { - if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している - // こちらから匿名GET出来たものならばpublic - visibility = 'public'; - } + throw new IdentifiableError('dc2ad0d1-36bf-41f5-8e4c-a4d265a28387', `failed to create note ${entryUri}: could not resolve any recipients`); } const processErrors: string[] = []; @@ -472,10 +459,6 @@ export class ApNoteService { processErrors.push('quoteUnavailable'); } - if (quote && quote.userHost == null && quote.localOnly) { - throw new IdentifiableError('12e23cec-edd9-442b-aa48-9c21f0c3b215', 'Cannot quote a local-only note'); - } - // vote if (reply && reply.hasPoll) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 4e7ac59f41..2b3defb189 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -19,7 +19,6 @@ import { ReactionsBufferingService } from '@/core/ReactionsBufferingService.js'; import { QueryService } from '@/core/QueryService.js'; import type { Config } from '@/config.js'; import { NoteVisibilityService } from '@/core/NoteVisibilityService.js'; -import type { PopulatedNote } from '@/core/NoteVisibilityService.js'; import type { NoteVisibilityData } from '@/core/NoteVisibilityService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { CacheService } from '../CacheService.js'; @@ -508,6 +507,8 @@ export class NoteEntityService implements OnModuleInit { me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: boolean; + recurseReply?: boolean; // Defaults to the value of detail, which defaults to true. + recurseRenote?: boolean; // Defaults to the value of detail, which defaults to true. skipHide?: boolean; withReactionAndUserPairCache?: boolean; bypassSilence?: boolean; @@ -535,6 +536,8 @@ export class NoteEntityService implements OnModuleInit { skipHide: false, withReactionAndUserPairCache: false, }, options); + opts.recurseRenote ??= opts.detail; + opts.recurseReply ??= opts.detail; const meId = me ? me.id : null; const note = typeof src === 'object' ? src : await this.noteLoader.load(src); @@ -647,27 +650,30 @@ export class NoteEntityService implements OnModuleInit { ...(opts.detail ? { clippedCount: note.clippedCount, processErrors: note.processErrors, - - reply: note.replyId ? this.pack(note.reply ?? opts._hint_?.notes.get(note.replyId) ?? note.replyId, me, { - detail: false, - skipHide: opts.skipHide, - withReactionAndUserPairCache: opts.withReactionAndUserPairCache, - _hint_: options?._hint_, - - // Don't silence target of self-reply, since the outer note will already be silenced. - bypassSilence: bypassSilence || note.userId === note.replyUserId, - }) : undefined, - - renote: note.renoteId ? this.pack(note.renote ?? opts._hint_?.notes.get(note.renoteId) ?? note.renoteId, me, { - detail: true, - skipHide: opts.skipHide, - withReactionAndUserPairCache: opts.withReactionAndUserPairCache, - _hint_: options?._hint_, - - // Don't silence target of self-renote, since the outer note will already be silenced. - bypassSilence: bypassSilence || note.userId === note.renoteUserId, - }) : undefined, } : {}), + + reply: opts.recurseReply && note.replyId ? this.pack(note.reply ?? opts._hint_?.notes.get(note.replyId) ?? note.replyId, me, { + detail: false, + skipHide: opts.skipHide, + withReactionAndUserPairCache: opts.withReactionAndUserPairCache, + _hint_: options?._hint_, + + // Don't silence target of self-reply, since the outer note will already be silenced. + bypassSilence: bypassSilence || note.userId === note.replyUserId, + }) : undefined, + + // The renote target needs to be packed with the reply, but we *must not* recurse any further. + // Pass detail=false and recurseReply=true to make sure we only include the right data. + renote: opts.recurseRenote && note.renoteId ? this.pack(note.renote ?? opts._hint_?.notes.get(note.renoteId) ?? note.renoteId, me, { + detail: false, + recurseReply: true, + skipHide: opts.skipHide, + withReactionAndUserPairCache: opts.withReactionAndUserPairCache, + _hint_: options?._hint_, + + // Don't silence target of self-renote, since the outer note will already be silenced. + bypassSilence: bypassSilence || note.userId === note.renoteUserId, + }) : undefined, }); this.noteVisibilityService.syncVisibility(packed); diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index d6fccd1b84..4aece5353e 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -18,7 +18,6 @@ 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 = { @@ -262,7 +261,6 @@ export default class extends Endpoint { // 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,31 +301,6 @@ export default class extends Endpoint { // eslint- if (renote == null) { throw new ApiError(meta.errors.noSuchRenoteTarget); - } else if (isRenote(renote) && !isQuote(renote)) { - throw new ApiError(meta.errors.cannotReRenote); - } else if (!(await this.noteVisibilityService.checkNoteVisibilityAsync(renote, me)).accessible) { - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); - } - - // Check blocking - if (renote.userId !== me.id) { - const blockExist = await this.blockingsRepository.exists({ - where: { - blockerId: renote.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } - - if (renote.visibility === 'followers' && renote.userId !== me.id) { - // 他人のfollowers noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); - } else if (renote.visibility === 'specified') { - // specified / direct noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); } if (renote.channelId && renote.channelId !== ps.channelId) { @@ -351,26 +324,9 @@ export default class extends Endpoint { // eslint- if (reply == null) { throw new ApiError(meta.errors.noSuchReplyTarget); - } else if (isRenote(reply) && !isQuote(reply)) { - throw new ApiError(meta.errors.cannotReplyToPureRenote); - } 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); } - - // Check blocking - if (reply.userId !== me.id) { - const blockExist = await this.blockingsRepository.exists({ - where: { - blockerId: reply.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } } if (ps.poll) { @@ -428,6 +384,16 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.containsTooManyMentions); } else if (e.id === '1c0ea108-d1e3-4e8e-aa3f-4d2487626153') { throw new ApiError(meta.errors.quoteDisabledForUser); + } else if (e.id === 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a') { + throw new ApiError(meta.errors.cannotReRenote); + } else if (e.id === 'b6352a84-e5cd-4b05-a26c-63437a6b98ba') { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } else if (e.id === 'be9529e9-fe72-4de0-ae43-0b363c4938af') { + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } else if (e.id === '3ac74a84-8fd5-4bb0-870f-01804f82ce15') { + throw new ApiError(meta.errors.cannotReplyToPureRenote); + } else if (e.id === 'b98980fa-3780-406c-a935-b6d0eeee10d1') { + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); } } throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index 717dab59e1..276f2672d0 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -17,7 +17,6 @@ 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 = { @@ -312,7 +311,6 @@ export default class extends Endpoint { // 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) { @@ -347,44 +345,12 @@ export default class extends Endpoint { // eslint- } let renote: MiNote | null = null; - - if (ps.renoteId === ps.editId) { - throw new ApiError(meta.errors.cannotQuoteCurrentPost); - } - if (ps.renoteId != null) { // Fetch renote to note renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); if (renote == null) { throw new ApiError(meta.errors.noSuchRenoteTarget); - } else if (isRenote(renote) && !isQuote(renote)) { - throw new ApiError(meta.errors.cannotReRenote); - } - - if (renote.renoteId === ps.editId) { - throw new ApiError(meta.errors.cannotQuoteaQuoteOfCurrentPost); - } - - // Check blocking - if (renote.userId !== me.id) { - const blockExist = await this.blockingsRepository.exists({ - where: { - blockerId: renote.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } - - if (renote.visibility === 'followers' && renote.userId !== me.id) { - // 他人のfollowers noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); - } else if (renote.visibility === 'specified') { - // specified / direct noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); } if (renote.channelId && renote.channelId !== ps.channelId) { @@ -408,26 +374,9 @@ export default class extends Endpoint { // eslint- if (reply == null) { throw new ApiError(meta.errors.noSuchReplyTarget); - } else if (isRenote(reply) && !isQuote(reply)) { - throw new ApiError(meta.errors.cannotReplyToPureRenote); - } 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); } - - // Check blocking - if (reply.userId !== me.id) { - const blockExist = await this.blockingsRepository.exists({ - where: { - blockerId: reply.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } } if (ps.poll) { @@ -483,6 +432,20 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.containsTooManyMentions); } else if (e.id === '1c0ea108-d1e3-4e8e-aa3f-4d2487626153') { throw new ApiError(meta.errors.quoteDisabledForUser); + } else if (e.id === '33510210-8452-094c-6227-4a6c05d99f02') { + throw new ApiError(meta.errors.cannotQuoteCurrentPost); + } else if (e.id === 'ea93b7c2-3d6c-4e10-946b-00d50b1a75cb') { + throw new ApiError(meta.errors.cannotQuoteaQuoteOfCurrentPost); + } else if (e.id === 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a') { + throw new ApiError(meta.errors.cannotReRenote); + } else if (e.id === 'b6352a84-e5cd-4b05-a26c-63437a6b98ba') { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } else if (e.id === 'be9529e9-fe72-4de0-ae43-0b363c4938af') { + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } else if (e.id === '3ac74a84-8fd5-4bb0-870f-01804f82ce15') { + throw new ApiError(meta.errors.cannotReplyToPureRenote); + } else if (e.id === 'b98980fa-3780-406c-a935-b6d0eeee10d1') { + throw new ApiError(meta.errors.cannotReplyToInvisibleNote); } } throw e;