upd: add MFM to HTML support and Mentions parsing to mastodon api (#33)

* upd: attempt to turn MFM to html on mastodon

* revert: recent change until better implementation later

* chore: remove unused packages

* Update docker.yml

* upd: add MFM to HTML for timelines and status view

* chore: lint

* upd: megalodon resolve urls

* upd: add spliting

* test: local user mention

* test: change local user url in mention

* upd: change check

* test: megalodon changes

* upd: edit resolving of local users

This is starting to drive me nuts

* upd: remove the @ symbol in query

* fix: make renderPerson return host instead of null for local

* upd: change url for local user

* upd: change limit

* upd: add url to output

* upd: add mastodon boolean

* test: test different format

* fix: test of different format

* test: change up resolving

* fix: forgot to provide url

* upd: change lookup function a bit

* test: substring

* test: regex

* upd: remove substr

* test: new regexs

* dirty test

* test: one last attempt for today

* upd: fix build error

* upd: take input from iceshrimp dev

* upd: parse remote statuses

* upd: fix pleroma users misformatted urls

* upd: add uri to normal user

* fix: forgot to push updated types

* fix: resolving broke

* fix: html not converting correctly

* fix: return default img if no banner

* upd: swap out img used for no header, set fallback avatar

* fix: html escaped & and ' symbols

* upd: fix ' converting into 39; and get profile fields

* upd: resolve fields on lookup

---------

Co-authored-by: Amelia Yukii <123300075+Insert5StarName@users.noreply.github.com>
This commit is contained in:
Marie 2023-10-01 01:58:06 +02:00 committed by GitHub
parent e5d9eb3082
commit 54578f6965
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 237 additions and 41 deletions

View file

@ -1,4 +1,14 @@
import type { Config } from '@/config.js';
import { MfmService } from '@/core/MfmService.js';
import { DI } from '@/di-symbols.js';
import { Inject } from '@nestjs/common';
import { Entity } from 'megalodon';
import { parse } from 'mfm-js';
import { GetterService } from '../GetterService.js';
import type { IMentionedRemoteUsers } from '@/models/Note.js';
import type { MiUser } from '@/models/User.js';
import type { NotesRepository, UsersRepository } from '@/models/_.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz';
@ -7,6 +17,91 @@ export enum IdConvertType {
SharkeyId,
}
export const escapeMFM = (text: string): string => text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;")
.replace(/`/g, "&#x60;")
.replace(/\r?\n/g, "<br>");
export class MastoConverters {
private MfmService: MfmService;
private GetterService: GetterService;
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
private userEntityService: UserEntityService
) {
this.MfmService = new MfmService(this.config);
this.GetterService = new GetterService(this.usersRepository, this.notesRepository, this.userEntityService);
}
private encode(u: MiUser, m: IMentionedRemoteUsers): MastodonEntity.Mention {
let acct = u.username;
let acctUrl = `https://${u.host || this.config.host}/@${u.username}`;
let url: string | null = null;
if (u.host) {
const info = m.find(r => r.username === u.username && r.host === u.host);
acct = `${u.username}@${u.host}`;
acctUrl = `https://${u.host}/@${u.username}`;
if (info) url = info.url ?? info.uri;
}
return {
id: u.id,
username: u.username,
acct: acct,
url: url ?? acctUrl,
};
}
public async getUser(id: string): Promise<MiUser> {
return this.GetterService.getUser(id).then(p => {
return p;
})
}
public async convertStatus(status: Entity.Status) {
status.account = convertAccount(status.account);
const note = await this.GetterService.getNote(status.id);
status.id = convertId(status.id, IdConvertType.MastodonId);
if (status.in_reply_to_account_id) status.in_reply_to_account_id = convertId(
status.in_reply_to_account_id,
IdConvertType.MastodonId,
);
if (status.in_reply_to_id) status.in_reply_to_id = convertId(status.in_reply_to_id, IdConvertType.MastodonId);
status.media_attachments = status.media_attachments.map((attachment) =>
convertAttachment(attachment),
);
// This will eventually be improved with a rewrite of this file
const mentions = Promise.all(note.mentions.map(p =>
this.getUser(p)
.then(u => this.encode(u, JSON.parse(note.mentionedRemoteUsers)))
.catch(() => null)))
.then(p => p.filter(m => m)) as Promise<MastodonEntity.Mention[]>;
status.mentions = await mentions;
status.mentions = status.mentions.map((mention) => ({
...mention,
id: convertId(mention.id, IdConvertType.MastodonId),
}));
const convertedMFM = this.MfmService.toHtml(parse(status.content), JSON.parse(note.mentionedRemoteUsers));
status.content = status.content ? convertedMFM?.replace(/&amp;/g , "&").replaceAll(`<span>&</span><a href="${this.config.url}/tags/39;" rel="tag">#39;</a>` , "<span>\'</span>")! : status.content;
if (status.poll) status.poll = convertPoll(status.poll);
if (status.reblog) status.reblog = convertStatus(status.reblog);
return status;
}
}
export function convertId(in_id: string, id_convert_type: IdConvertType): string {
switch (id_convert_type) {
case IdConvertType.MastodonId: {