diff --git a/packages/backend/src/mfm/to-html.ts b/packages/backend/src/mfm/to-html.ts
index 8d06b2a95..5ae4cda2a 100644
--- a/packages/backend/src/mfm/to-html.ts
+++ b/packages/backend/src/mfm/to-html.ts
@@ -113,21 +113,21 @@ export async function toHtml(
return a;
},
- mention(node) {
+ async mention(node) {
const { username, host, acct } = node.props;
- const href = resolveMentionFromCache(username, host, objectHost, mentionedRemoteUsers);
+ const resolved = await resolveMentionFromCache(username, host, objectHost, mentionedRemoteUsers);
const el = doc.createElement("span");
- if (href === null) {
+ if (resolved === null) {
el.textContent = acct;
} else {
el.setAttribute("class", "h-card");
el.setAttribute("translate", "no");
const a = doc.createElement("a");
- a.href = href;
+ a.href = resolved.href;
a.className = "u-url mention";
const span = doc.createElement("span");
- span.textContent = username;
+ span.textContent = resolved.username;
a.textContent = '@';
a.appendChild(span);
el.appendChild(a);
diff --git a/packages/backend/src/remote/resolve-user.ts b/packages/backend/src/remote/resolve-user.ts
index 13cddc2d8..b2fa71432 100644
--- a/packages/backend/src/remote/resolve-user.ts
+++ b/packages/backend/src/remote/resolve-user.ts
@@ -13,6 +13,7 @@ import { IMentionedRemoteUsers } from "@/models/entities/note.js";
const logger = remoteLogger.createSubLogger("resolve-user");
const uriHostCache = new Cache("resolveUserUriHost", 60 * 60 * 24);
+const localUsernameCache = new Cache("localUserNameCapitalization", 60 * 60 * 24);
export async function resolveUser(
username: string,
@@ -184,7 +185,6 @@ export async function resolveUser(
export async function resolveMentionToUserAndProfile(username: string, host: string | null, objectHost: string | null) {
try {
- //const fallback = getMentionFallbackUri(username, host, objectHost);
const user = await resolveUser(username, host ?? objectHost, false);
const profile = await UserProfiles.findOneBy({ userId: user.id });
const data = { username, host: host ?? objectHost };
@@ -206,12 +206,30 @@ export function getMentionFallbackUri(username: string, host: string | null, obj
return fallback;
}
-export function resolveMentionFromCache(username: string, host: string | null, objectHost: string | null, cache: IMentionedRemoteUsers): string | null {
- const fallback = getMentionFallbackUri(username, host, objectHost);
+async function getLocalUsernameCached(username: string): Promise {
+ return localUsernameCache.fetch(username.toLowerCase(), () =>
+ Users.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })
+ .then(p => p ? p.username : null));
+}
+export async function resolveMentionFromCache(username: string, host: string | null, objectHost: string | null, cache: IMentionedRemoteUsers): Promise<{ username: string, href: string } | null> {
+ const isLocal = (host === null && objectHost === null) || host === config.domain;
+ if (isLocal) {
+ const finalUsername = await getLocalUsernameCached(username);
+ if (finalUsername === null) return null;
+ username = finalUsername;
+ }
+ try {
+ if (isLocal) username = await resolveUser(username, null).then(p => p?.username ?? username);
+ } catch {
+ return null;
+ }
+
+ const fallback = getMentionFallbackUri(username, host, objectHost);
const cached = cache.find(r => r.username.toLowerCase() === username.toLowerCase() && r.host === host);
- if (cached) return cached.url ?? cached.uri ?? fallback;
- if ((host === null && objectHost === null) || host === config.domain) return fallback;
+ const href = cached?.url ?? cached?.uri;
+ if (cached && href != null) return { username: cached.username, href: href };
+ if (isLocal) return { username: username, href: fallback };
return null;
}
diff --git a/packages/backend/src/server/api/mastodon/helpers/mfm.ts b/packages/backend/src/server/api/mastodon/helpers/mfm.ts
index 055ac9110..c1f69e3af 100644
--- a/packages/backend/src/server/api/mastodon/helpers/mfm.ts
+++ b/packages/backend/src/server/api/mastodon/helpers/mfm.ts
@@ -135,21 +135,21 @@ export class MfmHelpers {
return a;
},
- mention(node) {
+ async mention(node) {
const { username, host, acct } = node.props;
- const href = resolveMentionFromCache(username, host, objectHost, mentionedRemoteUsers);
+ const resolved = await resolveMentionFromCache(username, host, objectHost, mentionedRemoteUsers);
const el = doc.createElement("span");
- if (href === null) {
+ if (resolved === null) {
el.textContent = acct;
} else {
el.setAttribute("class", "h-card");
el.setAttribute("translate", "no");
const a = doc.createElement("a");
- a.href = href;
+ a.href = resolved.href;
a.className = "u-url mention";
const span = doc.createElement("span");
- span.textContent = username;
+ span.textContent = resolved.username;
a.textContent = '@';
a.appendChild(span);
el.appendChild(a);