upd: change deps, fix a few bugs, update converter
Fixes User and Notes count bug (transfem-org/Sharkey#113) Fixes build issues due to types (transfem-org/Sharkey#111) Return accounts and notes like Iceshrimp Use MFM class from Iceshrimp to fix HTML output for mastodon
This commit is contained in:
parent
b0a7fd6ddb
commit
82c10de265
14 changed files with 421 additions and 242 deletions
|
|
@ -3,14 +3,13 @@ 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 mfm from 'mfm-js';
|
||||
import { GetterService } from '../GetterService.js';
|
||||
import type { IMentionedRemoteUsers } from '@/models/Note.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
|
||||
export enum IdConvertType {
|
||||
MastodonId,
|
||||
|
|
@ -18,13 +17,13 @@ export enum IdConvertType {
|
|||
}
|
||||
|
||||
export const escapeMFM = (text: string): string => text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/`/g, "`")
|
||||
.replace(/\r?\n/g, "<br>");
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/`/g, "`")
|
||||
.replace(/\r?\n/g, "<br>");
|
||||
|
||||
export class MastoConverters {
|
||||
private MfmService: MfmService;
|
||||
|
|
@ -49,7 +48,7 @@ export class MastoConverters {
|
|||
this.GetterService = new GetterService(this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
|
||||
}
|
||||
|
||||
private encode(u: MiUser, m: IMentionedRemoteUsers): MastodonEntity.Mention {
|
||||
private encode(u: MiUser, m: IMentionedRemoteUsers): Entity.Mention {
|
||||
let acct = u.username;
|
||||
let acctUrl = `https://${u.host || this.config.host}/@${u.username}`;
|
||||
let url: string | null = null;
|
||||
|
|
@ -73,89 +72,97 @@ export class MastoConverters {
|
|||
});
|
||||
}
|
||||
|
||||
public async convertAccount(account: Entity.Account) {
|
||||
return awaitAll({
|
||||
id: account.id,
|
||||
username: account.username,
|
||||
acct: account.acct,
|
||||
fqn: account.fqn,
|
||||
display_name: account.display_name || account.username,
|
||||
locked: account.locked,
|
||||
created_at: account.created_at,
|
||||
followers_count: account.followers_count,
|
||||
following_count: account.following_count,
|
||||
statuses_count: account.statuses_count,
|
||||
note: account.note,
|
||||
url: account.url,
|
||||
avatar: account.avatar,
|
||||
avatar_static: account.avatar,
|
||||
header: account.header,
|
||||
header_static: account.header,
|
||||
emojis: account.emojis,
|
||||
moved: null, //FIXME
|
||||
fields: [],
|
||||
bot: false,
|
||||
discoverable: true,
|
||||
});
|
||||
}
|
||||
|
||||
public async convertStatus(status: Entity.Status) {
|
||||
status.account = convertAccount(status.account);
|
||||
const convertedAccount = this.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(/&/g, "&").replaceAll(`<span>&</span><a href="${this.config.url}/tags/39;" rel="tag">#39;</a>`, "<span>\'</span>") as string : status.content;
|
||||
if (status.poll) status.poll = convertPoll(status.poll);
|
||||
if (status.reblog) status.reblog = convertStatus(status.reblog);
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
.then(p => p.filter(m => m)) as Promise<Entity.Mention[]>;
|
||||
|
||||
export function convertId(in_id: string, id_convert_type: IdConvertType): string {
|
||||
switch (id_convert_type) {
|
||||
case IdConvertType.MastodonId: {
|
||||
let out = BigInt(0);
|
||||
const lowerCaseId = in_id.toLowerCase();
|
||||
for (let i = 0; i < lowerCaseId.length; i++) {
|
||||
const charValue = numFromChar(lowerCaseId.charAt(i));
|
||||
out += BigInt(charValue) * BigInt(36) ** BigInt(i);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
case IdConvertType.SharkeyId: {
|
||||
let input = BigInt(in_id);
|
||||
let outStr = '';
|
||||
while (input > BigInt(0)) {
|
||||
const remainder = Number(input % BigInt(36));
|
||||
outStr = charFromNum(remainder) + outStr;
|
||||
input /= BigInt(36);
|
||||
}
|
||||
const ReversedoutStr = outStr.split('').reduce((acc, char) => char + acc, '');
|
||||
return ReversedoutStr;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error('Invalid ID conversion type');
|
||||
}
|
||||
}
|
||||
const content = note.text !== null
|
||||
? this.MfmService.toMastoHtml(mfm.parse(note.text!), JSON.parse(note.mentionedRemoteUsers), false, null)
|
||||
.then(p => p ?? escapeMFM(note.text!))
|
||||
: '';
|
||||
|
||||
function numFromChar(character: string): number {
|
||||
for (let i = 0; i < CHAR_COLLECTION.length; i++) {
|
||||
if (CHAR_COLLECTION.charAt(i) === character) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
const tags = note.tags.map(tag => {
|
||||
return {
|
||||
name: tag,
|
||||
url: `${this.config.url}/tags/${tag}`,
|
||||
} as Entity.Tag;
|
||||
});
|
||||
|
||||
throw new Error('Invalid character in parsed base36 id');
|
||||
}
|
||||
|
||||
function charFromNum(number: number): string {
|
||||
if (number >= 0 && number < CHAR_COLLECTION.length) {
|
||||
return CHAR_COLLECTION.charAt(number);
|
||||
} else {
|
||||
throw new Error('Invalid number for base-36 encoding');
|
||||
// noinspection ES6MissingAwait
|
||||
return await awaitAll({
|
||||
id: note.id,
|
||||
uri: note.uri ?? `https://${this.config.host}/notes/${note.id}`,
|
||||
url: note.url ?? note.uri ?? `https://${this.config.host}/notes/${note.id}`,
|
||||
account: convertedAccount,
|
||||
in_reply_to_id: note.replyId,
|
||||
in_reply_to_account_id: note.replyUserId,
|
||||
reblog: status.reblog,
|
||||
content: content,
|
||||
content_type: 'text/x.misskeymarkdown',
|
||||
text: note.text,
|
||||
created_at: status.created_at,
|
||||
emojis: status.emojis,
|
||||
replies_count: note.repliesCount,
|
||||
reblogs_count: note.renoteCount,
|
||||
favourites_count: status.favourites_count,
|
||||
reblogged: false,
|
||||
favourited: status.favourited,
|
||||
muted: status.muted,
|
||||
sensitive: status.sensitive,
|
||||
spoiler_text: note.cw ? note.cw : '',
|
||||
visibility: status.visibility,
|
||||
media_attachments: status.media_attachments,
|
||||
mentions: mentions,
|
||||
tags: tags,
|
||||
card: null, //FIXME
|
||||
poll: status.poll ?? null,
|
||||
application: null, //FIXME
|
||||
language: null, //FIXME
|
||||
pinned: null,
|
||||
reactions: status.emoji_reactions,
|
||||
emoji_reactions: status.emoji_reactions,
|
||||
bookmarked: false,
|
||||
quote: false,
|
||||
edited_at: note.updatedAt?.toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function simpleConvert(data: any) {
|
||||
// copy the object to bypass weird pass by reference bugs
|
||||
const result = Object.assign({}, data);
|
||||
result.id = convertId(data.id, IdConvertType.MastodonId);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +187,6 @@ export function convertFeaturedTag(tag: Entity.FeaturedTag) {
|
|||
|
||||
export function convertNotification(notification: Entity.Notification) {
|
||||
notification.account = convertAccount(notification.account);
|
||||
notification.id = convertId(notification.id, IdConvertType.MastodonId);
|
||||
if (notification.status) notification.status = convertStatus(notification.status);
|
||||
return notification;
|
||||
}
|
||||
|
|
@ -200,19 +206,9 @@ export function convertRelationship(relationship: Entity.Relationship) {
|
|||
|
||||
export function convertStatus(status: Entity.Status) {
|
||||
status.account = convertAccount(status.account);
|
||||
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),
|
||||
);
|
||||
status.mentions = status.mentions.map((mention) => ({
|
||||
...mention,
|
||||
id: convertId(mention.id, IdConvertType.MastodonId),
|
||||
}));
|
||||
if (status.poll) status.poll = convertPoll(status.poll);
|
||||
if (status.reblog) status.reblog = convertStatus(status.reblog);
|
||||
|
||||
|
|
@ -224,7 +220,6 @@ export function convertStatusSource(status: Entity.StatusSource) {
|
|||
}
|
||||
|
||||
export function convertConversation(conversation: Entity.Conversation) {
|
||||
conversation.id = convertId(conversation.id, IdConvertType.MastodonId);
|
||||
conversation.accounts = conversation.accounts.map(convertAccount);
|
||||
if (conversation.last_status) {
|
||||
conversation.last_status = convertStatus(conversation.last_status);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue