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:
parent
e5d9eb3082
commit
54578f6965
6
.github/workflows/docker.yml
vendored
6
.github/workflows/docker.yml
vendored
@ -1,6 +1,12 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- stable
|
||||
paths:
|
||||
- packages/**
|
||||
- locales/**
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
BIN
packages/backend/assets/transparent.png
Normal file
BIN
packages/backend/assets/transparent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 B |
@ -339,6 +339,13 @@ export class MfmService {
|
||||
mention: (node) => {
|
||||
const a = doc.createElement('a');
|
||||
const { username, host, acct } = node.props;
|
||||
/* if (mastodon) {
|
||||
const splitacct = acct.split("@");
|
||||
a.setAttribute('href', splitacct[2] !== this.config.host && splitacct[2] !== undefined ? `https://${splitacct[2]}/@${splitacct[1]}` : `https://${this.config.host}/${acct}`);
|
||||
a.className = 'u-url mention';
|
||||
a.textContent = acct;
|
||||
return a;
|
||||
} */
|
||||
const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
|
||||
a.setAttribute('href', remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`);
|
||||
a.className = 'u-url mention';
|
||||
|
@ -27,7 +27,7 @@ export const meta = {
|
||||
requireCredential: true,
|
||||
|
||||
limit: {
|
||||
duration: ms('1hour'),
|
||||
duration: ms('1minute'),
|
||||
max: 30,
|
||||
},
|
||||
|
||||
|
@ -3,7 +3,7 @@ import megalodon, { Entity, MegalodonInterface } from 'megalodon';
|
||||
import querystring from 'querystring';
|
||||
import { IsNull } from 'typeorm';
|
||||
import multer from 'fastify-multer';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import type { NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Config } from '@/config.js';
|
||||
@ -12,6 +12,7 @@ import { convertId, IdConvertType as IdType, convertAccount, convertAnnouncement
|
||||
import { getInstance } from './endpoints/meta.js';
|
||||
import { ApiAuthMastodon, ApiAccountMastodon, ApiFilterMastodon, ApiNotifyMastodon, ApiSearchMastodon, ApiTimelineMastodon, ApiStatusMastodon } from './endpoints.js';
|
||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface {
|
||||
const accessTokenArr = authorization?.split(' ') ?? [null];
|
||||
@ -26,9 +27,14 @@ export class MastodonApiServerService {
|
||||
constructor(
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
private metaService: MetaService,
|
||||
private userEntityService: UserEntityService,
|
||||
) { }
|
||||
|
||||
@bindThis
|
||||
@ -256,8 +262,10 @@ export class MastodonApiServerService {
|
||||
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
|
||||
// displayed without being logged in
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
reply.send(await account.lookup());
|
||||
const data = await client.search((_request.query as any).acct, { type: 'accounts' });
|
||||
const profile = await this.userProfilesRepository.findOneBy({userId: data.data.accounts[0].id});
|
||||
data.data.accounts[0].fields = profile?.fields.map(f => ({...f, verified_at: null})) || [];
|
||||
reply.send(convertAccount(data.data.accounts[0]));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -294,6 +302,8 @@ export class MastodonApiServerService {
|
||||
try {
|
||||
const sharkId = convertId(_request.params.id, IdType.SharkeyId);
|
||||
const data = await client.getAccount(sharkId);
|
||||
const profile = await this.userProfilesRepository.findOneBy({userId: sharkId});
|
||||
data.data.fields = profile?.fields.map(f => ({...f, verified_at: null})) || [];
|
||||
reply.send(convertAccount(data.data));
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
@ -744,7 +754,7 @@ export class MastodonApiServerService {
|
||||
//#endregion
|
||||
|
||||
//#region Timelines
|
||||
const TLEndpoint = new ApiTimelineMastodon(fastify);
|
||||
const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.userEntityService);
|
||||
|
||||
// GET Endpoints
|
||||
TLEndpoint.getTL();
|
||||
@ -769,7 +779,7 @@ export class MastodonApiServerService {
|
||||
//#endregion
|
||||
|
||||
//#region Status
|
||||
const NoteEndpoint = new ApiStatusMastodon(fastify);
|
||||
const NoteEndpoint = new ApiStatusMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.userEntityService);
|
||||
|
||||
// GET Endpoints
|
||||
NoteEndpoint.getStatus();
|
||||
|
@ -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, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
.replace(/`/g, "`")
|
||||
.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(/&/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: {
|
||||
|
@ -63,7 +63,7 @@ export class ApiAccountMastodon {
|
||||
const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' });
|
||||
return convertAccount(data.data.accounts[0]);
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
/* console.error(e)
|
||||
console.error(e.response.data); */
|
||||
return e.response;
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
import querystring from 'querystring';
|
||||
import { emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertAttachment, convertPoll, convertStatus, convertStatusSource } from '../converters.js';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js';
|
||||
import { getClient } from '../MastodonApiServerService.js';
|
||||
import { convertTimelinesArgsId, limitToInt } from './timeline.js';
|
||||
import type { Entity } from 'megalodon';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { Config } from '@/config.js';
|
||||
import { NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
function normalizeQuery(data: any) {
|
||||
const str = querystring.stringify(data);
|
||||
@ -13,9 +16,11 @@ function normalizeQuery(data: any) {
|
||||
|
||||
export class ApiStatusMastodon {
|
||||
private fastify: FastifyInstance;
|
||||
private mastoconverter: MastoConverters;
|
||||
|
||||
constructor(fastify: FastifyInstance) {
|
||||
constructor(fastify: FastifyInstance, config: Config, usersrepo: UsersRepository, notesrepo: NotesRepository, userentity: UserEntityService) {
|
||||
this.fastify = fastify;
|
||||
this.mastoconverter = new MastoConverters(config, usersrepo, notesrepo, userentity);
|
||||
}
|
||||
|
||||
public async getStatus() {
|
||||
@ -25,7 +30,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(_request.is404 ? 404 : 401).send(e.response.data);
|
||||
@ -59,8 +64,8 @@ export class ApiStatusMastodon {
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(limitToInt(query)),
|
||||
);
|
||||
data.data.ancestors = data.data.ancestors.map((status: Entity.Status) => convertStatus(status));
|
||||
data.data.descendants = data.data.descendants.map((status: Entity.Status) => convertStatus(status));
|
||||
data.data.ancestors = await Promise.all(data.data.ancestors.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)));
|
||||
data.data.descendants = await Promise.all(data.data.descendants.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status)));
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
@ -219,7 +224,7 @@ export class ApiStatusMastodon {
|
||||
}
|
||||
|
||||
const data = await client.postStatus(text, body);
|
||||
reply.send(convertStatus(data.data as Entity.Status));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data as Entity.Status));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -240,7 +245,7 @@ export class ApiStatusMastodon {
|
||||
body.media_ids = (body.media_ids as string[]).map((p) => convertId(p, IdType.SharkeyId));
|
||||
}
|
||||
const data = await client.editStatus(convertId(_request.params.id, IdType.SharkeyId), body);
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(_request.is404 ? 404 : 401).send(e.response.data);
|
||||
@ -258,7 +263,7 @@ export class ApiStatusMastodon {
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
'❤',
|
||||
)) as any;
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -276,7 +281,7 @@ export class ApiStatusMastodon {
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
'❤',
|
||||
);
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -291,7 +296,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.reblogStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -306,7 +311,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unreblogStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -321,7 +326,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.bookmarkStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -336,7 +341,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unbookmarkStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -351,7 +356,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.pinStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -366,7 +371,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unpinStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -381,7 +386,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.createEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
@ -396,7 +401,7 @@ export class ApiStatusMastodon {
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.deleteEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
|
||||
reply.send(convertStatus(data.data));
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { ParsedUrlQuery } from 'querystring';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, convertStatus } from '../converters.js';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, MastoConverters } from '../converters.js';
|
||||
import { getClient } from '../MastodonApiServerService.js';
|
||||
import type { Entity } from 'megalodon';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { Config } from '@/config.js';
|
||||
import { NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
export function limitToInt(q: ParsedUrlQuery) {
|
||||
const object: any = q;
|
||||
@ -38,9 +41,11 @@ export function convertTimelinesArgsId(q: ParsedUrlQuery) {
|
||||
|
||||
export class ApiTimelineMastodon {
|
||||
private fastify: FastifyInstance;
|
||||
private mastoconverter: MastoConverters;
|
||||
|
||||
constructor(fastify: FastifyInstance) {
|
||||
constructor(fastify: FastifyInstance, config: Config, usersRepository: UsersRepository, notesRepository: NotesRepository, userEntityService: UserEntityService) {
|
||||
this.fastify = fastify;
|
||||
this.mastoconverter = new MastoConverters(config, usersRepository, notesRepository, userEntityService);
|
||||
}
|
||||
|
||||
public async getTL() {
|
||||
@ -53,7 +58,7 @@ export class ApiTimelineMastodon {
|
||||
const data = query.local === 'true'
|
||||
? await client.getLocalTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query))))
|
||||
: await client.getPublicTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query))));
|
||||
reply.send(data.data.map((status: Entity.Status) => convertStatus(status)));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
@ -70,7 +75,7 @@ export class ApiTimelineMastodon {
|
||||
try {
|
||||
const query: any = _request.query;
|
||||
const data = await client.getHomeTimeline(convertTimelinesArgsId(limitToInt(query)));
|
||||
reply.send(data.data.map((status: Entity.Status) => convertStatus(status)));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
@ -88,7 +93,7 @@ export class ApiTimelineMastodon {
|
||||
const query: any = _request.query;
|
||||
const params: any = _request.params;
|
||||
const data = await client.getTagTimeline(params.hashtag, convertTimelinesArgsId(limitToInt(query)));
|
||||
reply.send(data.data.map((status: Entity.Status) => convertStatus(status)));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
@ -106,7 +111,7 @@ export class ApiTimelineMastodon {
|
||||
const query: any = _request.query;
|
||||
const params: any = _request.params;
|
||||
const data = await client.getListTimeline(convertId(params.id, IdType.SharkeyId), convertTimelinesArgsId(limitToInt(query)));
|
||||
reply.send(data.data.map((status: Entity.Status) => convertStatus(status)));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -2122,6 +2122,40 @@ export default class Misskey implements MegalodonInterface {
|
||||
): Promise<Response<Entity.Results>> {
|
||||
switch (options.type) {
|
||||
case 'accounts': {
|
||||
if (q.startsWith("http://") || q.startsWith("https://")) {
|
||||
return this.client
|
||||
.post("/api/ap/show", { uri: q })
|
||||
.then(async (res) => {
|
||||
if (res.status != 200 || res.data.type != "User") {
|
||||
res.status = 200;
|
||||
res.statusText = "OK";
|
||||
res.data = {
|
||||
accounts: [],
|
||||
statuses: [],
|
||||
hashtags: [],
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const account = await MisskeyAPI.Converter.userDetail(
|
||||
res.data.object as MisskeyAPI.Entity.UserDetail,
|
||||
this.baseUrl,
|
||||
);
|
||||
|
||||
return {
|
||||
...res,
|
||||
data: {
|
||||
accounts:
|
||||
options?.max_id && options?.max_id >= account.id
|
||||
? []
|
||||
: [account],
|
||||
statuses: [],
|
||||
hashtags: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
let params = {
|
||||
query: q
|
||||
}
|
||||
@ -2151,7 +2185,7 @@ export default class Misskey implements MegalodonInterface {
|
||||
});
|
||||
}
|
||||
try {
|
||||
const match = q.match(/^@?(?<user>[a-zA-Z0-9_]+)(?:@(?<host>[a-zA-Z0-9-.]+\.[a-zA-Z0-9-]+)|)$/);
|
||||
const match = params.query.match(/^@?(?<user>[a-zA-Z0-9_]+)(?:@(?<host>[a-zA-Z0-9-.]+\.[a-zA-Z0-9-]+)|)$/);
|
||||
if (match) {
|
||||
const lookupQuery = {
|
||||
username: match.groups?.user,
|
||||
@ -2195,6 +2229,38 @@ export default class Misskey implements MegalodonInterface {
|
||||
}))
|
||||
}
|
||||
case 'statuses': {
|
||||
if (q.startsWith("http://") || q.startsWith("https://")) {
|
||||
return this.client
|
||||
.post("/api/ap/show", { uri: q })
|
||||
.then(async (res) => {
|
||||
if (res.status != 200 || res.data.type != "Note") {
|
||||
res.status = 200;
|
||||
res.statusText = "OK";
|
||||
res.data = {
|
||||
accounts: [],
|
||||
statuses: [],
|
||||
hashtags: [],
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const post = await MisskeyAPI.Converter.note(
|
||||
res.data.object as MisskeyAPI.Entity.Note,
|
||||
this.baseUrl
|
||||
);
|
||||
|
||||
return {
|
||||
...res,
|
||||
data: {
|
||||
accounts: [],
|
||||
statuses:
|
||||
options?.max_id && options.max_id >= post.id ? [] : [post],
|
||||
hashtags: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
let params = {
|
||||
query: q
|
||||
}
|
||||
|
@ -93,11 +93,11 @@ namespace MisskeyAPI {
|
||||
following_count: u.followingCount ? u.followingCount : 0,
|
||||
statuses_count: u.notesCount ? u.notesCount : 0,
|
||||
note: u.description ? u.description : '',
|
||||
url: acctUrl,
|
||||
avatar: u.avatarUrl,
|
||||
avatar_static: u.avatarUrl,
|
||||
header: u.bannerUrl ? u.bannerUrl : '',
|
||||
header_static: u.bannerUrl ? u.bannerUrl : '',
|
||||
url: u.uri ?? acctUrl,
|
||||
avatar: u.avatarUrl ? u.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
avatar_static: u.avatarUrl ? u.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
header: u.bannerUrl ? u.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
header_static: u.bannerUrl ? u.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
emojis: mapEmojis(u.emojis),
|
||||
moved: null,
|
||||
fields: [],
|
||||
@ -128,11 +128,11 @@ namespace MisskeyAPI {
|
||||
following_count: u.followingCount,
|
||||
statuses_count: u.notesCount,
|
||||
note: u.description ? u.description.replace(/\n|\\n/g, "<br>") : '',
|
||||
url: acctUrl,
|
||||
avatar: u.avatarUrl,
|
||||
avatar_static: u.avatarUrl,
|
||||
header: u.bannerUrl,
|
||||
header_static: u.bannerUrl,
|
||||
url: u.uri ?? acctUrl,
|
||||
avatar: u.avatarUrl ? u.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
avatar_static: u.avatarUrl ? u.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
header: u.bannerUrl ? u.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
header_static: u.bannerUrl ? u.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
emojis: mapEmojis(u.emojis),
|
||||
moved: null,
|
||||
fields: [],
|
||||
|
@ -12,6 +12,7 @@ namespace MisskeyEntity {
|
||||
notesCount?: number
|
||||
host: string | null
|
||||
avatarUrl: string
|
||||
uri?: string
|
||||
bannerUrl?: string | null
|
||||
avatarColor: string
|
||||
emojis: Array<Emoji> | { [key: string]: string }
|
||||
|
@ -16,6 +16,7 @@ namespace MisskeyEntity {
|
||||
emojis: Array<Emoji> | { [key: string]: string }
|
||||
createdAt: string
|
||||
bannerUrl: string
|
||||
uri: string
|
||||
bannerColor: string
|
||||
isLocked: boolean
|
||||
isSilenced: boolean
|
||||
|
Loading…
Reference in New Issue
Block a user