summaryrefslogtreecommitdiff
path: root/packages/backend/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'packages/backend/src/core')
-rw-r--r--packages/backend/src/core/activitypub/ApRendererService.ts34
-rw-r--r--packages/backend/src/core/activitypub/misc/contexts.ts4
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts28
-rw-r--r--packages/backend/src/core/activitypub/type.ts15
4 files changed, 70 insertions, 11 deletions
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 46a78687f3..23a52a248a 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -34,7 +34,7 @@ import { UtilityService } from '@/core/UtilityService.js';
import { JsonLdService } from './JsonLdService.js';
import { ApMfmService } from './ApMfmService.js';
import { CONTEXT } from './misc/contexts.js';
-import { getApId, IOrderedCollection, IOrderedCollectionPage } from './type.js';
+import { getApId, ILink, IOrderedCollection, IOrderedCollectionPage } from './type.js';
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
@Injectable()
@@ -419,7 +419,7 @@ export class ApRendererService {
inReplyTo = null;
}
- let quote;
+ let quote: string | undefined = undefined;
if (note.renoteId) {
const renote = await this.notesRepository.findOneBy({ id: note.renoteId });
@@ -500,12 +500,22 @@ export class ApRendererService {
const emojis = await this.getEmojis(note.emojis);
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
- const tag = [
+ const tag: IObject[] = [
...hashtagTags,
...mentionTags,
...apemojis,
];
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
+ if (quote) {
+ tag.push({
+ type: 'Link',
+ mediaType: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+ rel: 'https://misskey-hub.net/ns#_misskey_quote',
+ href: quote,
+ } satisfies ILink);
+ }
+
const asPoll = poll ? {
type: 'Question',
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
@@ -537,6 +547,8 @@ export class ApRendererService {
_misskey_quote: quote,
quoteUrl: quote,
quoteUri: quote,
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md
+ quote: quote,
published: this.idService.parse(note.id).date.toISOString(),
to,
cc,
@@ -774,7 +786,7 @@ export class ApRendererService {
inReplyTo = null;
}
- let quote;
+ let quote: string | undefined = undefined;
if (note.renoteId) {
const renote = await this.notesRepository.findOneBy({ id: note.renoteId });
@@ -852,12 +864,22 @@ export class ApRendererService {
const emojis = await this.getEmojis(note.emojis);
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
- const tag = [
+ const tag: IObject[] = [
...hashtagTags,
...mentionTags,
...apemojis,
];
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
+ if (quote) {
+ tag.push({
+ type: 'Link',
+ mediaType: 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
+ rel: 'https://misskey-hub.net/ns#_misskey_quote',
+ href: quote,
+ } satisfies ILink);
+ }
+
const asPoll = poll ? {
type: 'Question',
[poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
@@ -886,6 +908,8 @@ export class ApRendererService {
_misskey_quote: quote,
quoteUrl: quote,
quoteUri: quote,
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md
+ quote: quote,
published: this.idService.parse(note.id).date.toISOString(),
to,
cc,
diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts
index cedd1d8dd5..fa003b1791 100644
--- a/packages/backend/src/core/activitypub/misc/contexts.ts
+++ b/packages/backend/src/core/activitypub/misc/contexts.ts
@@ -540,6 +540,10 @@ const extension_context_definition = {
quoteUrl: 'as:quoteUrl',
fedibird: 'http://fedibird.com/ns#',
quoteUri: 'fedibird:quoteUri',
+ quote: {
+ '@id': 'https://w3id.org/fep/044f#quote',
+ '@type': '@id',
+ },
// Mastodon
toot: 'http://joinmastodon.org/ns#',
Emoji: 'toot:Emoji',
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 57d4303982..2a28405121 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -27,7 +27,7 @@ import { checkHttps } from '@/misc/check-https.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { isRetryableError } from '@/misc/is-retryable-error.js';
import { renderInlineError } from '@/misc/render-inline-error.js';
-import { getOneApId, getApId, validPost, isEmoji, getApType, isApObject, isDocument, IApDocument } from '../type.js';
+import { getOneApId, getApId, validPost, isEmoji, getApType, isApObject, isDocument, IApDocument, isLink } from '../type.js';
import { ApLoggerService } from '../ApLoggerService.js';
import { ApMfmService } from '../ApMfmService.js';
import { ApDbResolverService } from '../ApDbResolverService.js';
@@ -657,9 +657,29 @@ export class ApNoteService {
*/
private async getQuote(note: IPost, entryUri: string, resolver: Resolver): Promise<MiNote | null | undefined> {
const quoteUris = new Set<string>();
- if (note._misskey_quote) quoteUris.add(note._misskey_quote);
- if (note.quoteUrl) quoteUris.add(note.quoteUrl);
- if (note.quoteUri) quoteUris.add(note.quoteUri);
+ if (note._misskey_quote && typeof(note._misskey_quote as unknown) === 'string') quoteUris.add(note._misskey_quote);
+ if (note.quoteUrl && typeof(note.quoteUrl as unknown) === 'string') quoteUris.add(note.quoteUrl);
+ if (note.quoteUri && typeof(note.quoteUri as unknown) === 'string') quoteUris.add(note.quoteUri);
+
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md
+ if (note.quote && typeof(note.quote as unknown) === 'string') quoteUris.add(note.quote);
+
+ // https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
+ const tags = toArray(note.tag).filter(tag => typeof(tag) === 'object' && isLink(tag));
+ for (const tag of tags) {
+ if (!tag.href || typeof (tag.href as unknown) !== 'string') continue;
+
+ const mediaTypes = toArray(tag.mediaType);
+ if (
+ !mediaTypes.includes('application/ld+json; profile="https://www.w3.org/ns/activitystreams"') &&
+ !mediaTypes.includes('application/activity+json')
+ ) continue;
+
+ const rels = toArray(tag.rel);
+ if (!rels.includes('https://misskey-hub.net/ns#_misskey_quote')) continue;
+
+ quoteUris.add(tag.href);
+ }
// No quote, return undefined
if (quoteUris.size < 1) return undefined;
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index cc7599d394..554420d670 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -35,6 +35,7 @@ export interface IObject {
mediaType?: string;
url?: ApObject | string;
href?: string;
+ rel?: string | string[];
tag?: IObject | IObject[];
sensitive?: boolean;
}
@@ -55,6 +56,16 @@ export function isAnonymousObject(object: IObject): object is IAnonymousObject {
return object.id === undefined;
}
+export interface ILink extends IObject {
+ '@context'?: string | string[] | Obj | Obj[];
+ type: 'Link' | 'Mention';
+ href: string;
+}
+
+export const isLink = (object: IObject): object is ILink =>
+ (getApType(object) === 'Link' || getApType(object) === 'Link') &&
+ typeof object.href === 'string';
+
/**
* Get array of ActivityStreams Objects id
*/
@@ -204,6 +215,7 @@ export interface IPost extends IObject {
_misskey_content?: string;
quoteUrl?: string;
quoteUri?: string;
+ quote?: string;
updated?: string;
}
@@ -306,9 +318,8 @@ export const isPropertyValue = (object: IObject): object is IApPropertyValue =>
'value' in object &&
typeof object.value === 'string';
-export interface IApMention extends IObject {
+export interface IApMention extends ILink {
type: 'Mention';
- href: string;
name: string;
}