From 194fff3603e0220fd02c46fd7d4e76f74fe19047 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 2 Dec 2022 19:31:57 +0100 Subject: [PATCH] activitypub: hashtags no longer displaying as links Some hashtags sent from Mastodon were erroneously displayed as links. This is because Mastodon seems to mangle hashtags containing non-ASCII codepoints (such as e.g. umlauts). This lead to the previous code which depended on the list of hashtags to not recognize a hashtag. Instead, the `rel="tag"` microformat is recognized instead. This makes the `htmlToMfm` wrapper function unnecessary so it was removed. Changelog: Fixed --- packages/backend/src/mfm/from-html.ts | 4 ++-- .../backend/src/remote/activitypub/misc/html-to-mfm.ts | 9 --------- packages/backend/src/remote/activitypub/models/note.ts | 4 ++-- packages/backend/src/remote/activitypub/models/person.ts | 5 ++--- 4 files changed, 6 insertions(+), 16 deletions(-) delete mode 100644 packages/backend/src/remote/activitypub/misc/html-to-mfm.ts diff --git a/packages/backend/src/mfm/from-html.ts b/packages/backend/src/mfm/from-html.ts index 1d8e117c2..087d59187 100644 --- a/packages/backend/src/mfm/from-html.ts +++ b/packages/backend/src/mfm/from-html.ts @@ -7,7 +7,7 @@ const treeAdapter = parse5.defaultTreeAdapter; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; -export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: string | null): string { +export function fromHtml(html: string, quoteUri?: string | null): string { const dom = parse5.parseFragment( // some AP servers like Pixelfed use br tags as well as newlines html.replace(/\r?\n/gi, '\n'), @@ -63,7 +63,7 @@ export function fromHtml(html: string, hashtagNames?: string[], quoteUri?: strin const href = node.attrs.find(x => x.name === 'href'); // hashtags - if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) { + if (txt.startsWith('#') && href && /\btag\b/.test(rel?.value)) { text += txt; // mentions } else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) { diff --git a/packages/backend/src/remote/activitypub/misc/html-to-mfm.ts b/packages/backend/src/remote/activitypub/misc/html-to-mfm.ts deleted file mode 100644 index 41d2e3b62..000000000 --- a/packages/backend/src/remote/activitypub/misc/html-to-mfm.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { IObject } from '../type.js'; -import { extractApHashtagObjects } from '../models/tag.js'; -import { fromHtml } from '@/mfm/from-html.js'; - -export function htmlToMfm(html: string, tag?: IObject | IObject[], quoteUri?: string | null) { - const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); - - return fromHtml(html, hashtagNames, quoteUri); -} diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 528d1b2db..a53783785 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -16,11 +16,11 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { getApLock } from '@/misc/app-lock.js'; import { createMessage } from '@/services/messages/create.js'; import { StatusError } from '@/misc/fetch.js'; +import { fromHtml } from '@/mfm/from-html.js'; import { parseAudience } from '../audience.js'; import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js'; import DbResolver from '../db-resolver.js'; import Resolver from '../resolver.js'; -import { htmlToMfm } from '../misc/html-to-mfm.js'; import { apLogger } from '../logger.js'; import { resolvePerson } from './person.js'; import { resolveImage } from './image.js'; @@ -199,7 +199,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver = } else if (typeof note._misskey_content !== 'undefined') { text = note._misskey_content; } else if (typeof note.content === 'string') { - text = htmlToMfm(note.content, note.tag, quote?.uri); + text = fromHtml(note.content, quote?.uri); } // vote diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index cf13c1cec..a34612a74 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -24,7 +24,6 @@ import { uriPersonCache } from '@/services/user-cache.js'; import { publishInternalEvent } from '@/services/stream.js'; import { db } from '@/db/postgre.js'; import { apLogger } from '../logger.js'; -import { htmlToMfm } from '../misc/html-to-mfm.js'; import { fromHtml } from '@/mfm/from-html.js'; import { isCollectionOrOrderedCollection, isCollection, IActor, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType, isActor } from '../type.js'; import Resolver from '../resolver.js'; @@ -185,7 +184,7 @@ export async function createPerson(uri: string, resolver?: Resolver = new Resolv await transactionalEntityManager.save(new UserProfile({ userId: user.id, - description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, + description: person.summary ? fromHtml(truncate(person.summary, summaryLength)) : null, url: getOneApHrefNullable(person.url), fields, birthday: bday ? bday[0] : null, @@ -361,7 +360,7 @@ export async function updatePerson(uri: string, resolver?: Resolver = new Resolv await UserProfiles.update({ userId: exist.id }, { url: getOneApHrefNullable(person.url), fields, - description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, + description: person.summary ? fromHtml(truncate(person.summary, summaryLength)) : null, birthday: bday ? bday[0] : null, location: person['vcard:Address'] || null, });