diff --git a/packages/backend/src/mfm/to-html.ts b/packages/backend/src/mfm/to-html.ts
index 54e93964b..818a37d83 100644
--- a/packages/backend/src/mfm/to-html.ts
+++ b/packages/backend/src/mfm/to-html.ts
@@ -8,6 +8,7 @@ import { resolveMentionWithFallback } from "@/remote/resolve-user.js";
export async function toHtml(
nodes: mfm.MfmNode[] | null,
mentionedRemoteUsers: IMentionedRemoteUsers = [],
+ objectHost: string | null
) {
if (nodes == null) {
return null;
@@ -118,7 +119,7 @@ export async function toHtml(
el.setAttribute("translate", "no");
const a = doc.createElement("a");
const { username, host, acct } = node.props;
- a.href = await resolveMentionWithFallback(username, host, acct, mentionedRemoteUsers);
+ a.href = await resolveMentionWithFallback(username, host, objectHost, mentionedRemoteUsers);
a.className = "u-url mention";
const span = doc.createElement("span");
span.textContent = username;
diff --git a/packages/backend/src/remote/activitypub/misc/get-note-html.ts b/packages/backend/src/remote/activitypub/misc/get-note-html.ts
index 046d60d1a..162a2dbd6 100644
--- a/packages/backend/src/remote/activitypub/misc/get-note-html.ts
+++ b/packages/backend/src/remote/activitypub/misc/get-note-html.ts
@@ -4,5 +4,5 @@ import { toHtml } from "../../../mfm/to-html.js";
export default async function (note: Note) {
if (!note.text) return "";
- return toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
+ return toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers), note.userHost);
}
diff --git a/packages/backend/src/remote/activitypub/renderer/person.ts b/packages/backend/src/remote/activitypub/renderer/person.ts
index 5e010d733..f3beb789e 100644
--- a/packages/backend/src/remote/activitypub/renderer/person.ts
+++ b/packages/backend/src/remote/activitypub/renderer/person.ts
@@ -73,7 +73,7 @@ export async function renderPerson(user: ILocalUser) {
preferredUsername: user.username,
name: user.name,
summary: profile.description
- ? await toHtml(mfm.parse(profile.description))
+ ? await toHtml(mfm.parse(profile.description), [], profile.userHost)
: null,
icon: avatar ? renderImage(avatar) : null,
image: banner ? renderImage(banner) : null,
diff --git a/packages/backend/src/remote/resolve-user.ts b/packages/backend/src/remote/resolve-user.ts
index e97ea3ab6..576b89c91 100644
--- a/packages/backend/src/remote/resolve-user.ts
+++ b/packages/backend/src/remote/resolve-user.ts
@@ -177,11 +177,15 @@ export async function resolveUser(
return user;
}
-export async function resolveMentionWithFallback(username: string, host: string | null, acct: string, cache: IMentionedRemoteUsers): Promise {
- const fallback = `${config.url}/${acct}`;
+export async function resolveMentionWithFallback(username: string, host: string | null, objectHost: string | null, cache: IMentionedRemoteUsers): Promise {
+ let fallback = `${config.url}/@${username}`;
+ if (host !== null) fallback += `@${host}`;
+ else if (objectHost !== null) fallback += `@${objectHost}`;
+
const cached = cache.find(r => r.username.toLowerCase() === username.toLowerCase() && r.host === host);
if (cached) return cached.url ?? cached.uri;
if (host === null || host === config.domain) return fallback;
+
try {
const user = await resolveUser(username, host, false);
const profile = await UserProfiles.findOneBy({ userId: user.id });
diff --git a/packages/backend/src/server/api/mastodon/converters/announcement.ts b/packages/backend/src/server/api/mastodon/converters/announcement.ts
index a1adaa466..afadf071e 100644
--- a/packages/backend/src/server/api/mastodon/converters/announcement.ts
+++ b/packages/backend/src/server/api/mastodon/converters/announcement.ts
@@ -6,7 +6,7 @@ export class AnnouncementConverter {
public static async encode(announcement: Announcement, isRead: boolean): Promise {
return {
id: announcement.id,
- content: `${await MfmHelpers.toHtml(mfm.parse(announcement.title), []) ?? 'Announcement'}
${await MfmHelpers.toHtml(mfm.parse(announcement.text), []) ?? ''}`,
+ content: `${await MfmHelpers.toHtml(mfm.parse(announcement.title), [], null) ?? 'Announcement'}
${await MfmHelpers.toHtml(mfm.parse(announcement.text), [], null) ?? ''}`,
starts_at: null,
ends_at: null,
published: true,
diff --git a/packages/backend/src/server/api/mastodon/converters/note.ts b/packages/backend/src/server/api/mastodon/converters/note.ts
index 45b109c4f..fc1cfd4be 100644
--- a/packages/backend/src/server/api/mastodon/converters/note.ts
+++ b/packages/backend/src/server/api/mastodon/converters/note.ts
@@ -117,7 +117,7 @@ export class NoteConverter {
in_reply_to_id: note.replyId,
in_reply_to_account_id: note.replyUserId,
reblog: reblog.then(reblog => note.text === null ? reblog : null),
- content: text.then(async text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers)).then(p => p ?? escapeMFM(text)) : ""),
+ content: text.then(async text => text !== null ? MfmHelpers.toHtml(mfm.parse(text), JSON.parse(note.mentionedRemoteUsers), note.userHost).then(p => p ?? escapeMFM(text)) : ""),
text: text,
created_at: note.createdAt.toISOString(),
emojis: noteEmoji,
diff --git a/packages/backend/src/server/api/mastodon/converters/user.ts b/packages/backend/src/server/api/mastodon/converters/user.ts
index 4bc2150c3..01f6e4467 100644
--- a/packages/backend/src/server/api/mastodon/converters/user.ts
+++ b/packages/backend/src/server/api/mastodon/converters/user.ts
@@ -31,7 +31,7 @@ export class UserConverter {
acctUrl = `https://${u.host}/@${u.username}`;
}
const profile = UserProfiles.findOneBy({ userId: u.id });
- const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? ""), []).then(p => p ?? escapeMFM(profile?.description ?? "")));
+ const bio = profile.then(profile => MfmHelpers.toHtml(mfm.parse(profile?.description ?? ""), [], u.host).then(p => p ?? escapeMFM(profile?.description ?? "")));
const avatar = u.avatarId
? (DriveFiles.findOneBy({ id: u.avatarId }))
.then(p => p?.url ?? Users.getIdenticonUrl(u.id))
@@ -110,7 +110,7 @@ export class UserConverter {
private static async encodeField(f: Field, host: string | null): Promise {
return {
name: f.name,
- value: await MfmHelpers.toHtml(mfm.parse(f.value), [], true) ?? escapeMFM(f.value),
+ value: await MfmHelpers.toHtml(mfm.parse(f.value), [], host, true) ?? escapeMFM(f.value),
verified_at: f.verified ? (new Date()).toISOString() : null,
}
}
diff --git a/packages/backend/src/server/api/mastodon/helpers/mfm.ts b/packages/backend/src/server/api/mastodon/helpers/mfm.ts
index 390f5ff7a..a2f59ac42 100644
--- a/packages/backend/src/server/api/mastodon/helpers/mfm.ts
+++ b/packages/backend/src/server/api/mastodon/helpers/mfm.ts
@@ -9,7 +9,8 @@ export class MfmHelpers {
public static async toHtml(
nodes: mfm.MfmNode[] | null,
mentionedRemoteUsers: IMentionedRemoteUsers = [],
- inline: boolean = false
+ objectHost: string | null,
+ inline: boolean = false,
) {
if (nodes == null) {
return null;
@@ -138,7 +139,7 @@ export class MfmHelpers {
el.setAttribute("translate", "no");
const a = doc.createElement("a");
const { username, host, acct } = node.props;
- a.href = await resolveMentionWithFallback(username, host, acct, mentionedRemoteUsers);
+ a.href = await resolveMentionWithFallback(username, host, objectHost, mentionedRemoteUsers);
a.className = "u-url mention";
const span = doc.createElement("span");
span.textContent = username;
diff --git a/packages/backend/src/server/api/mastodon/helpers/note.ts b/packages/backend/src/server/api/mastodon/helpers/note.ts
index 5580f92d1..75df26f92 100644
--- a/packages/backend/src/server/api/mastodon/helpers/note.ts
+++ b/packages/backend/src/server/api/mastodon/helpers/note.ts
@@ -201,7 +201,7 @@ export class NoteHelpers {
const files = DriveFiles.packMany(edit.fileIds);
const item = {
account: account,
- content: MfmHelpers.toHtml(mfm.parse(edit.text ?? ''), JSON.parse(note.mentionedRemoteUsers)).then(p => p ?? ''),
+ content: MfmHelpers.toHtml(mfm.parse(edit.text ?? ''), JSON.parse(note.mentionedRemoteUsers), note.userHost).then(p => p ?? ''),
created_at: lastDate.toISOString(),
emojis: [],
sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false),
diff --git a/packages/backend/test/mfm.ts b/packages/backend/test/mfm.ts
index a4f54c550..642403875 100644
--- a/packages/backend/test/mfm.ts
+++ b/packages/backend/test/mfm.ts
@@ -8,13 +8,13 @@ describe("toHtml", () => {
it("br", async () => {
const input = "foo\nbar\nbaz";
const output = "foo
bar
baz
";
- assert.equal(await toHtml(mfm.parse(input)), output);
+ assert.equal(await toHtml(mfm.parse(input), [], null), output);
});
it("br alt", async () => {
const input = "foo\r\nbar\rbaz";
const output = "foo
bar
baz
";
- assert.equal(await toHtml(mfm.parse(input)), output);
+ assert.equal(await toHtml(mfm.parse(input), [], null), output);
});
});