diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts index 0bc454599..1771ae5d1 100644 --- a/packages/backend/src/server/api/mastodon/converters/note.ts +++ b/packages/backend/src/server/api/mastodon/converters/note.ts @@ -20,6 +20,7 @@ import { IsNull } from "typeorm"; import { MfmHelpers } from "@/server/api/mastodon/helpers/mfm.js"; import { getStubMastoContext, MastoContext } from "@/server/api/mastodon/index.js"; import { NoteHelpers } from "@/server/api/mastodon/helpers/note.js"; +import isQuote from "@/misc/is-quote.js"; export class NoteConverter { public static async encode(note: Note, ctx: MastoContext, recurse: boolean = true): Promise { @@ -88,7 +89,7 @@ export class NoteConverter { .then(p => p.filter(m => m)) as Promise; const quoteUri = Promise.resolve(renote).then(renote => { - if (!renote || note.text === null) return null; + if (!renote || !isQuote(note)) return null; return renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`; }); @@ -118,7 +119,7 @@ export class NoteConverter { account: Promise.resolve(noteUser).then(p => UserConverter.encode(p, ctx)), in_reply_to_id: note.replyId, in_reply_to_account_id: note.replyUserId, - reblog: reblog.then(reblog => note.text === null ? reblog : null), + reblog: reblog.then(reblog => !isQuote(note) ? reblog : null), content: content, content_type: 'text/x.misskeymarkdown', text: note.text, @@ -143,7 +144,7 @@ export class NoteConverter { pinned: isPinned, reactions: populated.then(populated => Promise.resolve(reaction).then(reaction => this.encodeReactions(note.reactions, reaction?.reaction, populated))), bookmarked: isBookmarked, - quote: reblog.then(reblog => note.text !== null ? reblog : null), + quote: reblog.then(reblog => isQuote(note) ? reblog : null), edited_at: note.updatedAt?.toISOString() }); } diff --git a/packages/backend/src/server/api/mastodon/converters/notification.ts b/packages/backend/src/server/api/mastodon/converters/notification.ts index dbfde26a2..80868ecb9 100644 --- a/packages/backend/src/server/api/mastodon/converters/notification.ts +++ b/packages/backend/src/server/api/mastodon/converters/notification.ts @@ -8,6 +8,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { getNote } from "@/server/api/common/getters.js"; import { getStubMastoContext, MastoContext } from "@/server/api/mastodon/index.js"; import { Notifications } from "@/models/index.js"; +import isQuote from "@/misc/is-quote.js"; type NotificationType = typeof notificationTypes[number]; @@ -30,7 +31,7 @@ export class NotificationConverter { const note = notification.note ?? (notification.noteId ? await getNote(notification.noteId, localUser) : null); if (note) { - const isPureRenote = note.renoteId !== null && note.text === null; + const isPureRenote = note.renoteId !== null && !isQuote(note); const encodedNote = isPureRenote ? getNote(note.renoteId!, localUser).then(note => NoteConverter.encode(note, ctx)) : NoteConverter.encode(note, ctx); diff --git a/packages/backend/src/server/api/mastodon/helpers/note.ts b/packages/backend/src/server/api/mastodon/helpers/note.ts index 2f7d52c2b..1bdacd76d 100644 --- a/packages/backend/src/server/api/mastodon/helpers/note.ts +++ b/packages/backend/src/server/api/mastodon/helpers/note.ts @@ -236,6 +236,8 @@ export class NoteHelpers { ) .andWhere("note.renoteId = :noteId", { noteId: note.id }) .andWhere("note.text IS NULL") // We don't want to count quotes as renotes + .andWhere('note.hasPoll = FALSE') + .andWhere("note.fileIds = '{}'") .innerJoinAndSelect("note.user", "user"); generateVisibilityQuery(query, user); diff --git a/packages/backend/src/server/api/mastodon/helpers/user.ts b/packages/backend/src/server/api/mastodon/helpers/user.ts index 5c46442e8..55d171dff 100644 --- a/packages/backend/src/server/api/mastodon/helpers/user.ts +++ b/packages/backend/src/server/api/mastodon/helpers/user.ts @@ -353,7 +353,9 @@ export class UserHelpers { query.andWhere( new Brackets(qb => { qb.where('note.renoteId IS NULL') - .orWhere('note.text IS NOT NULL'); + .orWhere('note.text IS NOT NULL') + .orWhere('note.hasPoll = TRUE') + .orWhere("note.fileIds != '{}'"); })); } diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/public.ts b/packages/backend/src/server/api/mastodon/streaming/channels/public.ts index ae608897e..7eb8c4d71 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/public.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/public.ts @@ -6,6 +6,7 @@ import { Note } from "@/models/entities/note.js"; import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { StreamMessages } from "@/server/api/stream/types.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; +import isQuote from "@/misc/is-quote.js"; export class MastodonStreamPublic extends MastodonStream { public static shouldShare = true; @@ -70,7 +71,7 @@ export class MastodonStreamPublic extends MastodonStream { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false; if (isUserRelated(note, this.muting)) return false; if (isUserRelated(note, this.blocking)) return false; - if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return false; + if (note.renote && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false; return true; diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts b/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts index d145cde85..f664f2dbb 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/tag.ts @@ -5,6 +5,7 @@ import { isInstanceMuted } from "@/misc/is-instance-muted.js"; import { Note } from "@/models/entities/note.js"; import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { StreamMessages } from "@/server/api/stream/types.js"; +import isQuote from "@/misc/is-quote.js"; export class MastodonStreamTag extends MastodonStream { public static shouldShare = false; @@ -62,7 +63,7 @@ export class MastodonStreamTag extends MastodonStream { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false; if (isUserRelated(note, this.muting)) return false; if (isUserRelated(note, this.blocking)) return false; - if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return false; + if (note.renote && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false; return true; diff --git a/packages/backend/src/server/api/mastodon/streaming/channels/user.ts b/packages/backend/src/server/api/mastodon/streaming/channels/user.ts index f9f4a46c3..588f21761 100644 --- a/packages/backend/src/server/api/mastodon/streaming/channels/user.ts +++ b/packages/backend/src/server/api/mastodon/streaming/channels/user.ts @@ -7,6 +7,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js"; import { StreamMessages } from "@/server/api/stream/types.js"; import { NotificationConverter } from "@/server/api/mastodon/converters/notification.js"; import { AnnouncementConverter } from "@/server/api/mastodon/converters/announcement.js"; +import isQuote from "@/misc/is-quote.js"; export class MastodonStreamUser extends MastodonStream { public static shouldShare = true; @@ -95,7 +96,7 @@ export class MastodonStreamUser extends MastodonStream { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return false; if (isUserRelated(note, this.muting)) return false; if (isUserRelated(note, this.blocking)) return false; - if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return false; + if (note.renote && !isQuote(note) && this.renoteMuting.has(note.userId)) return false; if (this.userProfile && (await getWordHardMute(note, this.user, this.userProfile.mutedWords))) return false; return true;