merge: mastodon api changes (#120)
This commit is contained in:
commit
20ec6300f5
22 changed files with 764 additions and 513 deletions
|
@ -388,4 +388,212 @@ export class MfmService {
|
|||
|
||||
return `<p>${doc.body.innerHTML}</p>`;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async toMastoHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = [], inline = false, quoteUri: string | null = null) {
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { window } = new Window();
|
||||
|
||||
const doc = window.document;
|
||||
|
||||
async function appendChildren(children: mfm.MfmNode[], targetElement: any): Promise<void> {
|
||||
if (children) {
|
||||
for (const child of await Promise.all(children.map(async (x) => await (handlers as any)[x.type](x)))) targetElement.appendChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
const handlers: {
|
||||
[K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => any;
|
||||
} = {
|
||||
async bold(node) {
|
||||
const el = doc.createElement('span');
|
||||
el.textContent = '**';
|
||||
await appendChildren(node.children, el);
|
||||
el.textContent += '**';
|
||||
return el;
|
||||
},
|
||||
|
||||
async small(node) {
|
||||
const el = doc.createElement('small');
|
||||
await appendChildren(node.children, el);
|
||||
return el;
|
||||
},
|
||||
|
||||
async strike(node) {
|
||||
const el = doc.createElement('span');
|
||||
el.textContent = '~~';
|
||||
await appendChildren(node.children, el);
|
||||
el.textContent += '~~';
|
||||
return el;
|
||||
},
|
||||
|
||||
async italic(node) {
|
||||
const el = doc.createElement('span');
|
||||
el.textContent = '*';
|
||||
await appendChildren(node.children, el);
|
||||
el.textContent += '*';
|
||||
return el;
|
||||
},
|
||||
|
||||
async fn(node) {
|
||||
const el = doc.createElement('span');
|
||||
el.textContent = '*';
|
||||
await appendChildren(node.children, el);
|
||||
el.textContent += '*';
|
||||
return el;
|
||||
},
|
||||
|
||||
blockCode(node) {
|
||||
const pre = doc.createElement('pre');
|
||||
const inner = doc.createElement('code');
|
||||
|
||||
const nodes = node.props.code
|
||||
.split(/\r\n|\r|\n/)
|
||||
.map((x) => doc.createTextNode(x));
|
||||
|
||||
for (const x of intersperse<FIXME | 'br'>('br', nodes)) {
|
||||
inner.appendChild(x === 'br' ? doc.createElement('br') : x);
|
||||
}
|
||||
|
||||
pre.appendChild(inner);
|
||||
return pre;
|
||||
},
|
||||
|
||||
async center(node) {
|
||||
const el = doc.createElement('div');
|
||||
await appendChildren(node.children, el);
|
||||
return el;
|
||||
},
|
||||
|
||||
emojiCode(node) {
|
||||
return doc.createTextNode(`\u200B:${node.props.name}:\u200B`);
|
||||
},
|
||||
|
||||
unicodeEmoji(node) {
|
||||
return doc.createTextNode(node.props.emoji);
|
||||
},
|
||||
|
||||
hashtag: (node) => {
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('href', `${this.config.url}/tags/${node.props.hashtag}`);
|
||||
a.textContent = `#${node.props.hashtag}`;
|
||||
a.setAttribute('rel', 'tag');
|
||||
a.setAttribute('class', 'hashtag');
|
||||
return a;
|
||||
},
|
||||
|
||||
inlineCode(node) {
|
||||
const el = doc.createElement('code');
|
||||
el.textContent = node.props.code;
|
||||
return el;
|
||||
},
|
||||
|
||||
mathInline(node) {
|
||||
const el = doc.createElement('code');
|
||||
el.textContent = node.props.formula;
|
||||
return el;
|
||||
},
|
||||
|
||||
mathBlock(node) {
|
||||
const el = doc.createElement('code');
|
||||
el.textContent = node.props.formula;
|
||||
return el;
|
||||
},
|
||||
|
||||
async link(node) {
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('rel', 'nofollow noopener noreferrer');
|
||||
a.setAttribute('target', '_blank');
|
||||
a.setAttribute('href', node.props.url);
|
||||
await appendChildren(node.children, a);
|
||||
return a;
|
||||
},
|
||||
|
||||
async mention(node) {
|
||||
const { username, host, acct } = node.props;
|
||||
const resolved = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
|
||||
|
||||
const el = doc.createElement('span');
|
||||
if (!resolved) {
|
||||
el.textContent = acct;
|
||||
} else {
|
||||
el.setAttribute('class', 'h-card');
|
||||
el.setAttribute('translate', 'no');
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('href', resolved.url ? resolved.url : resolved.uri);
|
||||
a.className = 'u-url mention';
|
||||
const span = doc.createElement('span');
|
||||
span.textContent = resolved.username || username;
|
||||
a.textContent = '@';
|
||||
a.appendChild(span);
|
||||
el.appendChild(a);
|
||||
}
|
||||
|
||||
return el;
|
||||
},
|
||||
|
||||
async quote(node) {
|
||||
const el = doc.createElement('blockquote');
|
||||
await appendChildren(node.children, el);
|
||||
return el;
|
||||
},
|
||||
|
||||
text(node) {
|
||||
const el = doc.createElement('span');
|
||||
const nodes = node.props.text
|
||||
.split(/\r\n|\r|\n/)
|
||||
.map((x) => doc.createTextNode(x));
|
||||
|
||||
for (const x of intersperse<FIXME | 'br'>('br', nodes)) {
|
||||
el.appendChild(x === 'br' ? doc.createElement('br') : x);
|
||||
}
|
||||
|
||||
return el;
|
||||
},
|
||||
|
||||
url(node) {
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('rel', 'nofollow noopener noreferrer');
|
||||
a.setAttribute('target', '_blank');
|
||||
a.setAttribute('href', node.props.url);
|
||||
a.textContent = node.props.url.replace(/^https?:\/\//, '');
|
||||
return a;
|
||||
},
|
||||
|
||||
search: (node) => {
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('href', `https"google.com/${node.props.query}`);
|
||||
a.textContent = node.props.content;
|
||||
return a;
|
||||
},
|
||||
|
||||
async plain(node) {
|
||||
const el = doc.createElement('span');
|
||||
await appendChildren(node.children, el);
|
||||
return el;
|
||||
},
|
||||
};
|
||||
|
||||
await appendChildren(nodes, doc.body);
|
||||
|
||||
if (quoteUri !== null) {
|
||||
const a = doc.createElement('a');
|
||||
a.setAttribute('href', quoteUri);
|
||||
a.textContent = quoteUri.replace(/^https?:\/\//, '');
|
||||
|
||||
const quote = doc.createElement('span');
|
||||
quote.setAttribute('class', 'quote-inline');
|
||||
quote.appendChild(doc.createElement('br'));
|
||||
quote.appendChild(doc.createElement('br'));
|
||||
quote.innerHTML += 'RE: ';
|
||||
quote.appendChild(a);
|
||||
|
||||
doc.body.appendChild(quote);
|
||||
}
|
||||
|
||||
return inline ? doc.body.innerHTML : `<p>${doc.body.innerHTML}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ export class SearchService {
|
|||
}
|
||||
}
|
||||
const res = await this.meilisearchNoteIndex!.search(q, {
|
||||
sort: [`createdAt:${opts.order}`],
|
||||
sort: [`createdAt:${opts.order ? opts.order : 'desc'}`],
|
||||
matchingStrategy: 'all',
|
||||
attributesToRetrieve: ['id', 'createdAt'],
|
||||
filter: compileQuery(filter),
|
||||
|
|
|
@ -142,3 +142,7 @@ export function toArray<T>(x: T | T[] | undefined): T[] {
|
|||
export function toSingle<T>(x: T | T[] | undefined): T | undefined {
|
||||
return Array.isArray(x) ? x[0] : x;
|
||||
}
|
||||
|
||||
export function toSingleLast<T>(x: T | T[] | undefined): T | undefined {
|
||||
return Array.isArray(x) ? x.at(-1) : x;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import { SigninService } from './api/SigninService.js';
|
|||
import { SignupApiService } from './api/SignupApiService.js';
|
||||
import { StreamingApiServerService } from './api/StreamingApiServerService.js';
|
||||
import { ClientServerService } from './web/ClientServerService.js';
|
||||
import { MastoConverters } from './api/mastodon/converters.js';
|
||||
import { FeedService } from './web/FeedService.js';
|
||||
import { UrlPreviewService } from './web/UrlPreviewService.js';
|
||||
import { MainChannelService } from './api/stream/channels/main.js';
|
||||
|
@ -87,6 +88,7 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
|
|||
OpenApiServerService,
|
||||
MastodonApiServerService,
|
||||
OAuth2ProviderService,
|
||||
MastoConverters,
|
||||
],
|
||||
exports: [
|
||||
ServerService,
|
||||
|
|
|
@ -3,16 +3,17 @@ import megalodon, { Entity, MegalodonInterface } from 'megalodon';
|
|||
import querystring from 'querystring';
|
||||
import { IsNull } from 'typeorm';
|
||||
import multer from 'fastify-multer';
|
||||
import type { NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import type { AccessTokensRepository, NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList } from './converters.js';
|
||||
import { convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList, MastoConverters } from './converters.js';
|
||||
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';
|
||||
import { DriveService } from '@/core/DriveService.js';
|
||||
|
||||
export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface {
|
||||
const accessTokenArr = authorization?.split(' ') ?? [null];
|
||||
|
@ -33,10 +34,14 @@ export class MastodonApiServerService {
|
|||
private userProfilesRepository: UserProfilesRepository,
|
||||
@Inject(DI.noteEditRepository)
|
||||
private noteEditRepository: NoteEditRepository,
|
||||
@Inject(DI.accessTokensRepository)
|
||||
private accessTokensRepository: AccessTokensRepository,
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
private metaService: MetaService,
|
||||
private userEntityService: UserEntityService,
|
||||
private driveService: DriveService,
|
||||
private mastoConverter: MastoConverters,
|
||||
) { }
|
||||
|
||||
@bindThis
|
||||
|
@ -101,8 +106,8 @@ export class MastodonApiServerService {
|
|||
},
|
||||
order: { id: 'ASC' },
|
||||
});
|
||||
const contact = admin == null ? null : convertAccount((await client.getAccount(admin.id)).data);
|
||||
reply.send(await getInstance(data.data, contact, this.config, await this.metaService.fetch()));
|
||||
const contact = admin == null ? null : await this.mastoConverter.convertAccount((await client.getAccount(admin.id)).data);
|
||||
reply.send(await getInstance(data.data, contact as Entity.Account, this.config, await this.metaService.fetch()));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -128,7 +133,7 @@ export class MastodonApiServerService {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.dismissInstanceAnnouncement(
|
||||
convertId(_request.body['id'], IdType.SharkeyId),
|
||||
_request.body['id'],
|
||||
);
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
|
@ -202,6 +207,25 @@ export class MastodonApiServerService {
|
|||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/trends/tags', async (_request, reply) => {
|
||||
const BASE_URL = `${_request.protocol}://${_request.hostname}`;
|
||||
const accessTokens = _request.headers.authorization;
|
||||
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 data = await client.getInstanceTrends();
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
reply.code(401).send(e.response.data);
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/trends/links', async (_request, reply) => {
|
||||
// As we do not have any system for news/links this will just return empty
|
||||
reply.send([]);
|
||||
});
|
||||
|
||||
fastify.post('/v1/apps', async (_request, reply) => {
|
||||
const BASE_URL = `${_request.protocol}://${_request.hostname}`;
|
||||
const client = getClient(BASE_URL, ''); // we are using this here, because in private mode some info isnt
|
||||
|
@ -236,7 +260,7 @@ 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);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.verifyCredentials());
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
|
@ -244,16 +268,69 @@ export class MastodonApiServerService {
|
|||
}
|
||||
});
|
||||
|
||||
fastify.patch('/v1/accounts/update_credentials', { preHandler: upload.none() }, async (_request, reply) => {
|
||||
fastify.patch('/v1/accounts/update_credentials', { preHandler: upload.any() }, async (_request, reply) => {
|
||||
const BASE_URL = `${_request.protocol}://${_request.hostname}`;
|
||||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt
|
||||
// displayed without being logged in
|
||||
try {
|
||||
// Check if there is an Header or Avatar being uploaded, if there is proceed to upload it to the drive of the user and then set it.
|
||||
if (_request.files.length > 0 && accessTokens) {
|
||||
const tokeninfo = await this.accessTokensRepository.findOneBy({ token: accessTokens.replace('Bearer ', '') });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const avatar = (_request.files as any).find((obj: any) => {
|
||||
return obj.fieldname === 'avatar';
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const header = (_request.files as any).find((obj: any) => {
|
||||
return obj.fieldname === 'header';
|
||||
});
|
||||
|
||||
if (tokeninfo && avatar) {
|
||||
const upload = await this.driveService.addFile({
|
||||
user: { id: tokeninfo.userId, host: null },
|
||||
path: avatar.path,
|
||||
name: avatar.originalname !== null && avatar.originalname !== 'file' ? avatar.originalname : undefined,
|
||||
sensitive: false,
|
||||
});
|
||||
if (upload.type.startsWith('image/')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(_request.body as any).avatar = upload.id;
|
||||
}
|
||||
} else if (tokeninfo && header) {
|
||||
const upload = await this.driveService.addFile({
|
||||
user: { id: tokeninfo.userId, host: null },
|
||||
path: header.path,
|
||||
name: header.originalname !== null && header.originalname !== 'file' ? header.originalname : undefined,
|
||||
sensitive: false,
|
||||
});
|
||||
if (upload.type.startsWith('image/')) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(_request.body as any).header = upload.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if ((_request.body as any).fields_attributes) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const fields = (_request.body as any).fields_attributes.map((field: any) => {
|
||||
if (!(field.name.trim() === '' && field.value.trim() === '')) {
|
||||
if (field.name.trim() === '') return reply.code(400).send('Field name can not be empty');
|
||||
if (field.value.trim() === '') return reply.code(400).send('Field value can not be empty');
|
||||
}
|
||||
return {
|
||||
...field,
|
||||
};
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(_request.body as any).fields_attributes = fields.filter((field: any) => field.name.trim().length > 0 && field.value.length > 0);
|
||||
}
|
||||
|
||||
const data = await client.updateCredentials(_request.body!);
|
||||
reply.send(convertAccount(data.data));
|
||||
reply.send(await this.mastoConverter.convertAccount(data.data));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
//console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
}
|
||||
});
|
||||
|
@ -267,7 +344,7 @@ export class MastodonApiServerService {
|
|||
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]));
|
||||
reply.send(await this.mastoConverter.convertAccount(data.data.accounts[0]));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -281,12 +358,12 @@ export class MastodonApiServerService {
|
|||
// displayed without being logged in
|
||||
let users;
|
||||
try {
|
||||
let ids = _request.query ? (_request.query as any)['id[]'] : null;
|
||||
let ids = _request.query ? (_request.query as any)['id[]'] ?? (_request.query as any)['id'] : null;
|
||||
if (typeof ids === 'string') {
|
||||
ids = [ids];
|
||||
}
|
||||
users = ids;
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getRelationships(users));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
|
@ -302,11 +379,10 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const sharkId = convertId(_request.params.id, IdType.SharkeyId);
|
||||
const sharkId = _request.params.id;
|
||||
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));
|
||||
const account = await this.mastoConverter.convertAccount(data.data);
|
||||
reply.send(account);
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
console.error(e.response.data); */
|
||||
|
@ -319,7 +395,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getStatuses());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -347,7 +423,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getFollowers());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -361,7 +437,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getFollowing());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -375,7 +451,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getAccountLists(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.getAccountLists(_request.params.id);
|
||||
reply.send(data.data.map((list) => convertList(list)));
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -389,7 +465,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.addFollow());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -403,7 +479,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.rmFollow());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -417,7 +493,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.addBlock());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -431,7 +507,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.rmBlock());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -445,7 +521,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.addMute());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -459,7 +535,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.rmMute());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -487,7 +563,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getBookmarks());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -501,7 +577,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getFavourites());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -515,7 +591,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getMutes());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -529,7 +605,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.getBlocks());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -544,7 +620,7 @@ export class MastodonApiServerService {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getFollowRequests( ((_request.query as any) || { limit: 20 }).limit );
|
||||
reply.send(data.data.map((account) => convertAccount(account as Entity.Account)));
|
||||
reply.send(await Promise.all(data.data.map(async (account) => await this.mastoConverter.convertAccount(account as Entity.Account))));
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
console.error(e.response.data); */
|
||||
|
@ -557,7 +633,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.acceptFollow());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -571,7 +647,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL);
|
||||
const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await account.rejectFollow());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -587,7 +663,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL);
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await search.SearchV1());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -601,7 +677,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL);
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await search.SearchV2());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -615,7 +691,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL);
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await search.getStatusTrends());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -629,7 +705,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL);
|
||||
const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
|
||||
reply.send(await search.getSuggestions());
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
|
@ -756,7 +832,7 @@ export class MastodonApiServerService {
|
|||
//#endregion
|
||||
|
||||
//#region Timelines
|
||||
const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
|
||||
const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.mastoConverter);
|
||||
|
||||
// GET Endpoints
|
||||
TLEndpoint.getTL();
|
||||
|
@ -781,7 +857,7 @@ export class MastodonApiServerService {
|
|||
//#endregion
|
||||
|
||||
//#region Status
|
||||
const NoteEndpoint = new ApiStatusMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
|
||||
const NoteEndpoint = new ApiStatusMastodon(fastify, this.mastoConverter);
|
||||
|
||||
// GET Endpoints
|
||||
NoteEndpoint.getStatus();
|
||||
|
@ -813,7 +889,7 @@ export class MastodonApiServerService {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.updateMedia(convertId(_request.params.id, IdType.SharkeyId), _request.body!);
|
||||
const data = await client.updateMedia(_request.params.id, _request.body!);
|
||||
reply.send(convertAttachment(data.data));
|
||||
} catch (e: any) {
|
||||
/* console.error(e); */
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import type { Config } from '@/config.js';
|
||||
import { MfmService } from '@/core/MfmService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Entity } from 'megalodon';
|
||||
import { parse } from 'mfm-js';
|
||||
import { GetterService } from '../GetterService.js';
|
||||
import mfm from 'mfm-js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { MfmService } from '@/core/MfmService.js';
|
||||
import type { Config } from '@/config.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 type { NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { GetterService } from '../GetterService.js';
|
||||
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
|
||||
export enum IdConvertType {
|
||||
MastodonId,
|
||||
|
@ -18,18 +19,16 @@ 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>');
|
||||
|
||||
@Injectable()
|
||||
export class MastoConverters {
|
||||
private MfmService: MfmService;
|
||||
private GetterService: GetterService;
|
||||
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
@ -37,19 +36,21 @@ export class MastoConverters {
|
|||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
@Inject(DI.noteEditRepository)
|
||||
private noteEditRepository: NoteEditRepository,
|
||||
|
||||
private userEntityService: UserEntityService
|
||||
|
||||
private mfmService: MfmService,
|
||||
private getterService: GetterService,
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private idService: IdService,
|
||||
private driveFileEntityService: DriveFileEntityService,
|
||||
) {
|
||||
this.MfmService = new MfmService(this.config);
|
||||
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;
|
||||
|
@ -67,95 +68,223 @@ export class MastoConverters {
|
|||
};
|
||||
}
|
||||
|
||||
public fileType(s: string): 'unknown' | 'image' | 'gifv' | 'video' | 'audio' {
|
||||
if (s === 'image/gif') {
|
||||
return 'gifv';
|
||||
}
|
||||
if (s.includes('image')) {
|
||||
return 'image';
|
||||
}
|
||||
if (s.includes('video')) {
|
||||
return 'video';
|
||||
}
|
||||
if (s.includes('audio')) {
|
||||
return 'audio';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
public encodeFile(f: any): Entity.Attachment {
|
||||
return {
|
||||
id: f.id,
|
||||
type: this.fileType(f.type),
|
||||
url: f.url,
|
||||
remote_url: f.url,
|
||||
preview_url: f.thumbnailUrl,
|
||||
text_url: f.url,
|
||||
meta: {
|
||||
width: f.properties.width,
|
||||
height: f.properties.height
|
||||
},
|
||||
description: f.comment ? f.comment : null,
|
||||
blurhash: f.blurhash ? f.blurhash : null
|
||||
};
|
||||
}
|
||||
|
||||
public async getUser(id: string): Promise<MiUser> {
|
||||
return this.GetterService.getUser(id).then(p => {
|
||||
return this.getterService.getUser(id).then(p => {
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
private async encodeField(f: Entity.Field): Promise<Entity.Field> {
|
||||
return {
|
||||
name: f.name,
|
||||
value: await this.mfmService.toMastoHtml(mfm.parse(f.value), [], true) ?? escapeMFM(f.value),
|
||||
verified_at: null,
|
||||
};
|
||||
}
|
||||
|
||||
public async convertAccount(account: Entity.Account | MiUser) {
|
||||
const user = await this.getUser(account.id);
|
||||
const profile = await this.userProfilesRepository.findOneBy({ userId: user.id });
|
||||
const emojis = await this.customEmojiService.populateEmojis(user.emojis, user.host ? user.host : this.config.host);
|
||||
const emoji: Entity.Emoji[] = [];
|
||||
Object.entries(emojis).forEach(entry => {
|
||||
const [key, value] = entry;
|
||||
emoji.push({
|
||||
shortcode: key,
|
||||
static_url: value,
|
||||
url: value,
|
||||
visible_in_picker: true,
|
||||
category: undefined,
|
||||
});
|
||||
});
|
||||
const fqn = `${user.username}@${user.host ?? this.config.hostname}`;
|
||||
let acct = user.username;
|
||||
let acctUrl = `https://${user.host || this.config.host}/@${user.username}`;
|
||||
if (user.host) {
|
||||
acct = `${user.username}@${user.host}`;
|
||||
acctUrl = `https://${user.host}/@${user.username}`;
|
||||
}
|
||||
return awaitAll({
|
||||
id: account.id,
|
||||
username: user.username,
|
||||
acct: acct,
|
||||
fqn: fqn,
|
||||
display_name: user.name ?? user.username,
|
||||
locked: user.isLocked,
|
||||
created_at: this.idService.parse(user.id).date.toISOString(),
|
||||
followers_count: user.followersCount,
|
||||
following_count: user.followingCount,
|
||||
statuses_count: user.notesCount,
|
||||
note: profile?.description ?? '',
|
||||
url: user.uri ?? acctUrl,
|
||||
avatar: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
avatar_static: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
header: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
header_static: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
emojis: emoji,
|
||||
moved: null, //FIXME
|
||||
fields: Promise.all(profile?.fields.map(async p => this.encodeField(p)) ?? []),
|
||||
bot: user.isBot,
|
||||
discoverable: user.isExplorable,
|
||||
});
|
||||
}
|
||||
|
||||
public async getEdits(id: string) {
|
||||
const note = await this.getterService.getNote(id);
|
||||
if (!note) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const noteUser = await this.getUser(note.userId).then(async (p) => await this.convertAccount(p));
|
||||
const edits = await this.noteEditRepository.find({ where: { noteId: note.id }, order: { id: 'ASC' } });
|
||||
const history: Promise<any>[] = [];
|
||||
|
||||
let lastDate = this.idService.parse(note.id).date;
|
||||
for (const edit of edits) {
|
||||
const files = this.driveFileEntityService.packManyByIds(edit.fileIds);
|
||||
const item = {
|
||||
account: noteUser,
|
||||
content: this.mfmService.toMastoHtml(mfm.parse(edit.newText ?? ''), JSON.parse(note.mentionedRemoteUsers)).then(p => p ?? ''),
|
||||
created_at: lastDate.toISOString(),
|
||||
emojis: [],
|
||||
sensitive: files.then(files => files.length > 0 ? files.some((f) => f.isSensitive) : false),
|
||||
spoiler_text: edit.cw ?? '',
|
||||
poll: null,
|
||||
media_attachments: files.then(files => files.length > 0 ? files.map((f) => this.encodeFile(f)) : [])
|
||||
};
|
||||
lastDate = edit.updatedAt;
|
||||
history.push(awaitAll(item));
|
||||
}
|
||||
|
||||
return await Promise.all(history);
|
||||
}
|
||||
|
||||
private async convertReblog(status: Entity.Status | null): Promise<any> {
|
||||
if (!status) return null;
|
||||
return await this.convertStatus(status);
|
||||
}
|
||||
|
||||
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 convertedAccount = this.convertAccount(status.account);
|
||||
const note = await this.getterService.getNote(status.id);
|
||||
const noteUser = await this.getUser(status.account.id);
|
||||
|
||||
const emojis = await this.customEmojiService.populateEmojis(note.emojis, noteUser.host ? noteUser.host : this.config.host);
|
||||
const emoji: Entity.Emoji[] = [];
|
||||
Object.entries(emojis).forEach(entry => {
|
||||
const [key, value] = entry;
|
||||
emoji.push({
|
||||
shortcode: key,
|
||||
static_url: value,
|
||||
url: value,
|
||||
visible_in_picker: true,
|
||||
category: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
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 tags = note.tags.map(tag => {
|
||||
return {
|
||||
name: tag,
|
||||
url: `${this.config.url}/tags/${tag}`,
|
||||
} as Entity.Tag;
|
||||
});
|
||||
|
||||
function numFromChar(character: string): number {
|
||||
for (let i = 0; i < CHAR_COLLECTION.length; i++) {
|
||||
if (CHAR_COLLECTION.charAt(i) === character) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
const isQuote = note.renoteId && note.text ? true : false;
|
||||
|
||||
throw new Error('Invalid character in parsed base36 id');
|
||||
}
|
||||
const renote = note.renoteId ? this.getterService.getNote(note.renoteId) : null;
|
||||
|
||||
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');
|
||||
const quoteUri = Promise.resolve(renote).then(renote => {
|
||||
if (!renote || !isQuote) return null;
|
||||
return renote.url ?? renote.uri ?? `${this.config.url}/notes/${renote.id}`;
|
||||
});
|
||||
|
||||
const content = note.text !== null
|
||||
? quoteUri.then(quoteUri => this.mfmService.toMastoHtml(mfm.parse(note.text!), JSON.parse(note.mentionedRemoteUsers), false, quoteUri))
|
||||
.then(p => p ?? escapeMFM(note.text!))
|
||||
: '';
|
||||
|
||||
// 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: !isQuote ? await this.convertReblog(status.reblog) : null,
|
||||
content: content,
|
||||
content_type: 'text/x.misskeymarkdown',
|
||||
text: note.text,
|
||||
created_at: status.created_at,
|
||||
emojis: emoji,
|
||||
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: isQuote ? await this.convertReblog(status.reblog) : null,
|
||||
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 +309,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 +328,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 +342,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);
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { convertId, IdConvertType as IdType, convertAccount, convertRelationship, convertStatus } from '../converters.js';
|
||||
import { argsToBools, convertTimelinesArgsId, limitToInt } from './timeline.js';
|
||||
import { MastoConverters, convertRelationship } from '../converters.js';
|
||||
import { argsToBools, limitToInt } from './timeline.js';
|
||||
import type { MegalodonInterface } from 'megalodon';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
const relationshipModel = {
|
||||
id: '',
|
||||
|
@ -20,12 +24,13 @@ const relationshipModel = {
|
|||
note: '',
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ApiAccountMastodon {
|
||||
private request: FastifyRequest;
|
||||
private client: MegalodonInterface;
|
||||
private BASE_URL: string;
|
||||
|
||||
constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string) {
|
||||
constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string, private mastoconverter: MastoConverters) {
|
||||
this.request = request;
|
||||
this.client = client;
|
||||
this.BASE_URL = BASE_URL;
|
||||
|
@ -34,23 +39,17 @@ export class ApiAccountMastodon {
|
|||
public async verifyCredentials() {
|
||||
try {
|
||||
const data = await this.client.verifyAccountCredentials();
|
||||
const acct = data.data;
|
||||
acct.id = convertId(acct.id, IdType.MastodonId);
|
||||
acct.display_name = acct.display_name || acct.username;
|
||||
acct.url = `${this.BASE_URL}/@${acct.url}`;
|
||||
acct.note = acct.note || '';
|
||||
acct.avatar_static = acct.avatar;
|
||||
acct.header = acct.header || '/static-assets/transparent.png';
|
||||
acct.header_static = acct.header || '/static-assets/transparent.png';
|
||||
acct.source = {
|
||||
note: acct.note,
|
||||
fields: acct.fields,
|
||||
privacy: '',
|
||||
sensitive: false,
|
||||
language: '',
|
||||
};
|
||||
console.log(acct);
|
||||
return acct;
|
||||
const acct = await this.mastoconverter.convertAccount(data.data);
|
||||
const newAcct = Object.assign({}, acct, {
|
||||
source: {
|
||||
note: acct.note,
|
||||
fields: acct.fields,
|
||||
privacy: '',
|
||||
sensitive: false,
|
||||
language: '',
|
||||
},
|
||||
});
|
||||
return newAcct;
|
||||
} catch (e: any) {
|
||||
/* console.error(e);
|
||||
console.error(e.response.data); */
|
||||
|
@ -61,7 +60,7 @@ export class ApiAccountMastodon {
|
|||
public async lookup() {
|
||||
try {
|
||||
const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' });
|
||||
return convertAccount(data.data.accounts[0]);
|
||||
return this.mastoconverter.convertAccount(data.data.accounts[0]);
|
||||
} catch (e: any) {
|
||||
/* console.error(e)
|
||||
console.error(e.response.data); */
|
||||
|
@ -79,7 +78,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
const reqIds = [];
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
reqIds.push(convertId(users[i], IdType.SharkeyId));
|
||||
reqIds.push(users[i]);
|
||||
}
|
||||
|
||||
const data = await this.client.getRelationships(reqIds);
|
||||
|
@ -93,11 +92,8 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async getStatuses() {
|
||||
try {
|
||||
const data = await this.client.getAccountStatuses(
|
||||
convertId((this.request.params as any).id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(argsToBools(limitToInt(this.request.query as any)))
|
||||
);
|
||||
return data.data.map((status) => convertStatus(status));
|
||||
const data = await this.client.getAccountStatuses((this.request.params as any).id, argsToBools(limitToInt(this.request.query as any)));
|
||||
return await Promise.all(data.data.map(async (status) => await this.mastoconverter.convertStatus(status)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -108,10 +104,10 @@ export class ApiAccountMastodon {
|
|||
public async getFollowers() {
|
||||
try {
|
||||
const data = await this.client.getAccountFollowers(
|
||||
convertId((this.request.params as any).id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(limitToInt(this.request.query as any)),
|
||||
(this.request.params as any).id,
|
||||
limitToInt(this.request.query as any),
|
||||
);
|
||||
return data.data.map((account) => convertAccount(account));
|
||||
return await Promise.all(data.data.map(async (account) => await this.mastoconverter.convertAccount(account)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -122,10 +118,10 @@ export class ApiAccountMastodon {
|
|||
public async getFollowing() {
|
||||
try {
|
||||
const data = await this.client.getAccountFollowing(
|
||||
convertId((this.request.params as any).id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(limitToInt(this.request.query as any)),
|
||||
(this.request.params as any).id,
|
||||
limitToInt(this.request.query as any),
|
||||
);
|
||||
return data.data.map((account) => convertAccount(account));
|
||||
return await Promise.all(data.data.map(async (account) => await this.mastoconverter.convertAccount(account)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -135,7 +131,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async addFollow() {
|
||||
try {
|
||||
const data = await this.client.followAccount( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.followAccount( (this.request.params as any).id );
|
||||
const acct = convertRelationship(data.data);
|
||||
acct.following = true;
|
||||
return acct;
|
||||
|
@ -148,7 +144,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async rmFollow() {
|
||||
try {
|
||||
const data = await this.client.unfollowAccount( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.unfollowAccount( (this.request.params as any).id );
|
||||
const acct = convertRelationship(data.data);
|
||||
acct.following = false;
|
||||
return acct;
|
||||
|
@ -161,7 +157,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async addBlock() {
|
||||
try {
|
||||
const data = await this.client.blockAccount( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.blockAccount( (this.request.params as any).id );
|
||||
return convertRelationship(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -172,7 +168,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async rmBlock() {
|
||||
try {
|
||||
const data = await this.client.unblockAccount( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.unblockAccount( (this.request.params as any).id );
|
||||
return convertRelationship(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -184,7 +180,7 @@ export class ApiAccountMastodon {
|
|||
public async addMute() {
|
||||
try {
|
||||
const data = await this.client.muteAccount(
|
||||
convertId((this.request.params as any).id, IdType.SharkeyId),
|
||||
(this.request.params as any).id,
|
||||
this.request.body as any,
|
||||
);
|
||||
return convertRelationship(data.data);
|
||||
|
@ -197,7 +193,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async rmMute() {
|
||||
try {
|
||||
const data = await this.client.unmuteAccount( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.unmuteAccount( (this.request.params as any).id );
|
||||
return convertRelationship(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -208,8 +204,8 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async getBookmarks() {
|
||||
try {
|
||||
const data = await this.client.getBookmarks( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
|
||||
return data.data.map((status) => convertStatus(status));
|
||||
const data = await this.client.getBookmarks( limitToInt(this.request.query as any) );
|
||||
return data.data.map((status) => this.mastoconverter.convertStatus(status));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -219,8 +215,8 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async getFavourites() {
|
||||
try {
|
||||
const data = await this.client.getFavourites( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
|
||||
return data.data.map((status) => convertStatus(status));
|
||||
const data = await this.client.getFavourites( limitToInt(this.request.query as any) );
|
||||
return data.data.map((status) => this.mastoconverter.convertStatus(status));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -230,8 +226,8 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async getMutes() {
|
||||
try {
|
||||
const data = await this.client.getMutes( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
|
||||
return data.data.map((account) => convertAccount(account));
|
||||
const data = await this.client.getMutes( limitToInt(this.request.query as any) );
|
||||
return data.data.map((account) => this.mastoconverter.convertAccount(account));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -241,8 +237,8 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async getBlocks() {
|
||||
try {
|
||||
const data = await this.client.getBlocks( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
|
||||
return data.data.map((account) => convertAccount(account));
|
||||
const data = await this.client.getBlocks( limitToInt(this.request.query as any) );
|
||||
return data.data.map((account) => this.mastoconverter.convertAccount(account));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -252,7 +248,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async acceptFollow() {
|
||||
try {
|
||||
const data = await this.client.acceptFollowRequest( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.acceptFollowRequest( (this.request.params as any).id );
|
||||
return convertRelationship(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -263,7 +259,7 @@ export class ApiAccountMastodon {
|
|||
|
||||
public async rejectFollow() {
|
||||
try {
|
||||
const data = await this.client.rejectFollowRequest( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.rejectFollowRequest( (this.request.params as any).id );
|
||||
return convertRelationship(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IdConvertType as IdType, convertId, convertFilter } from '../converters.js';
|
||||
import { convertFilter } from '../converters.js';
|
||||
import type { MegalodonInterface } from 'megalodon';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
||||
|
@ -23,7 +23,7 @@ export class ApiFilterMastodon {
|
|||
|
||||
public async getFilter() {
|
||||
try {
|
||||
const data = await this.client.getFilter( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.getFilter( (this.request.params as any).id );
|
||||
return convertFilter(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -45,7 +45,7 @@ export class ApiFilterMastodon {
|
|||
public async updateFilter() {
|
||||
try {
|
||||
const body: any = this.request.body;
|
||||
const data = await this.client.updateFilter(convertId((this.request.params as any).id, IdType.SharkeyId), body.pharse, body.context);
|
||||
const data = await this.client.updateFilter((this.request.params as any).id, body.pharse, body.context);
|
||||
return convertFilter(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -55,7 +55,7 @@ export class ApiFilterMastodon {
|
|||
|
||||
public async rmFilter() {
|
||||
try {
|
||||
const data = await this.client.deleteFilter( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.deleteFilter( (this.request.params as any).id );
|
||||
return data.data;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { IdConvertType as IdType, convertId, convertNotification } from '../converters.js';
|
||||
import { convertTimelinesArgsId } from './timeline.js';
|
||||
import { convertNotification } from '../converters.js';
|
||||
import type { MegalodonInterface, Entity } from 'megalodon';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
||||
|
@ -19,7 +18,7 @@ export class ApiNotifyMastodon {
|
|||
|
||||
public async getNotifications() {
|
||||
try {
|
||||
const data = await this.client.getNotifications( convertTimelinesArgsId(toLimitToInt(this.request.query)) );
|
||||
const data = await this.client.getNotifications( toLimitToInt(this.request.query) );
|
||||
const notifs = data.data;
|
||||
const processed = notifs.map((n: Entity.Notification) => {
|
||||
const convertedn = convertNotification(n);
|
||||
|
@ -39,7 +38,7 @@ export class ApiNotifyMastodon {
|
|||
|
||||
public async getNotification() {
|
||||
try {
|
||||
const data = await this.client.getNotification( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.getNotification( (this.request.params as any).id );
|
||||
const notif = convertNotification(data.data);
|
||||
if (notif.type !== 'follow' && notif.type !== 'follow_request' && notif.type === 'reaction') notif.type = 'favourite';
|
||||
return notif;
|
||||
|
@ -51,7 +50,7 @@ export class ApiNotifyMastodon {
|
|||
|
||||
public async rmNotification() {
|
||||
try {
|
||||
const data = await this.client.dismissNotification( convertId((this.request.params as any).id, IdType.SharkeyId) );
|
||||
const data = await this.client.dismissNotification( (this.request.params as any).id );
|
||||
return data.data;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
|
|
@ -1,69 +1,14 @@
|
|||
import { Converter } from 'megalodon';
|
||||
import { convertAccount, convertStatus } from '../converters.js';
|
||||
import { convertTimelinesArgsId, limitToInt } from './timeline.js';
|
||||
import { MastoConverters } from '../converters.js';
|
||||
import { limitToInt } from './timeline.js';
|
||||
import type { MegalodonInterface } from 'megalodon';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
||||
async function getHighlight(
|
||||
BASE_URL: string,
|
||||
domain: string,
|
||||
accessTokens: string | undefined,
|
||||
) {
|
||||
const accessTokenArr = accessTokens?.split(' ') ?? [null];
|
||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||
try {
|
||||
const apicall = await fetch(`${BASE_URL}/api/notes/featured`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ i: accessToken }),
|
||||
});
|
||||
const api = await apicall.json();
|
||||
const data: MisskeyEntity.Note[] = api;
|
||||
return data.map((note) => Converter.note(note, domain));
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
console.log(e.response.data);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getFeaturedUser( BASE_URL: string, host: string, accessTokens: string | undefined, limit: number ) {
|
||||
const accessTokenArr = accessTokens?.split(' ') ?? [null];
|
||||
const accessToken = accessTokenArr[accessTokenArr.length - 1];
|
||||
try {
|
||||
const apicall = await fetch(`${BASE_URL}/api/users`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ i: accessToken, limit, origin: 'local', sort: '+follower', state: 'alive' }),
|
||||
});
|
||||
const api = await apicall.json();
|
||||
const data: MisskeyEntity.UserDetail[] = api;
|
||||
return data.map((u) => {
|
||||
return {
|
||||
source: 'past_interactions',
|
||||
account: Converter.userDetail(u, host),
|
||||
};
|
||||
});
|
||||
} catch (e: any) {
|
||||
console.log(e);
|
||||
console.log(e.response.data);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
export class ApiSearchMastodon {
|
||||
private request: FastifyRequest;
|
||||
private client: MegalodonInterface;
|
||||
private BASE_URL: string;
|
||||
|
||||
constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string) {
|
||||
constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string, private mastoConverter: MastoConverters) {
|
||||
this.request = request;
|
||||
this.client = client;
|
||||
this.BASE_URL = BASE_URL;
|
||||
|
@ -71,7 +16,7 @@ export class ApiSearchMastodon {
|
|||
|
||||
public async SearchV1() {
|
||||
try {
|
||||
const query: any = convertTimelinesArgsId(limitToInt(this.request.query as any));
|
||||
const query: any = limitToInt(this.request.query as any);
|
||||
const type = query.type || '';
|
||||
const data = await this.client.search(query.q, { type: type, ...query });
|
||||
return data.data;
|
||||
|
@ -83,14 +28,14 @@ export class ApiSearchMastodon {
|
|||
|
||||
public async SearchV2() {
|
||||
try {
|
||||
const query: any = convertTimelinesArgsId(limitToInt(this.request.query as any));
|
||||
const query: any = limitToInt(this.request.query as any);
|
||||
const type = query.type;
|
||||
const acct = !type || type === 'accounts' ? await this.client.search(query.q, { type: 'accounts', ...query }) : null;
|
||||
const stat = !type || type === 'statuses' ? await this.client.search(query.q, { type: 'statuses', ...query }) : null;
|
||||
const tags = !type || type === 'hashtags' ? await this.client.search(query.q, { type: 'hashtags', ...query }) : null;
|
||||
const data = {
|
||||
accounts: acct?.data.accounts.map((account) => convertAccount(account)) ?? [],
|
||||
statuses: stat?.data.statuses.map((status) => convertStatus(status)) ?? [],
|
||||
accounts: await Promise.all(acct?.data.accounts.map(async (account: any) => await this.mastoConverter.convertAccount(account)) ?? []),
|
||||
statuses: await Promise.all(stat?.data.statuses.map(async (status: any) => await this.mastoConverter.convertStatus(status)) ?? []),
|
||||
hashtags: tags?.data.hashtags ?? [],
|
||||
};
|
||||
return data;
|
||||
|
@ -102,30 +47,39 @@ export class ApiSearchMastodon {
|
|||
|
||||
public async getStatusTrends() {
|
||||
try {
|
||||
const data = await getHighlight(
|
||||
this.BASE_URL,
|
||||
this.request.hostname,
|
||||
this.request.headers.authorization,
|
||||
);
|
||||
return data.map((status) => convertStatus(status));
|
||||
const data = await fetch(`${this.BASE_URL}/api/notes/featured`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => data.map((status: any) => this.mastoConverter.convertStatus(status)));
|
||||
return data;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
return e.response.data;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async getSuggestions() {
|
||||
try {
|
||||
const data = await getFeaturedUser(
|
||||
this.BASE_URL,
|
||||
this.request.hostname,
|
||||
this.request.headers.authorization,
|
||||
(this.request.query as any).limit || 20,
|
||||
);
|
||||
return data.map((suggestion) => { suggestion.account = convertAccount(suggestion.account); return suggestion; });
|
||||
const data = await fetch(`${this.BASE_URL}/api/users`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ i: this.request.headers.authorization?.replace('Bearer ', ''), limit: parseInt((this.request.query as any).limit) || 20, origin: 'local', sort: '+follower', state: 'alive' }),
|
||||
}).then((res) => res.json()).then(data => data.map(((entry: any) => { return { source: 'global', account: entry }; })));
|
||||
return Promise.all(data.map(async (suggestion: any) => { suggestion.account = await this.mastoConverter.convertAccount(suggestion.account); return suggestion; }));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
return e.response.data;
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import querystring from 'querystring';
|
||||
import { emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js';
|
||||
import { convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js';
|
||||
import { getClient } from '../MastodonApiServerService.js';
|
||||
import { convertTimelinesArgsId, limitToInt } from './timeline.js';
|
||||
import { limitToInt } from './timeline.js';
|
||||
import type { Entity } from 'megalodon';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import type { Config } from '@/config.js';
|
||||
|
@ -18,9 +18,9 @@ export class ApiStatusMastodon {
|
|||
private fastify: FastifyInstance;
|
||||
private mastoconverter: MastoConverters;
|
||||
|
||||
constructor(fastify: FastifyInstance, config: Config, usersrepo: UsersRepository, notesrepo: NotesRepository, noteeditrepo: NoteEditRepository, userentity: UserEntityService) {
|
||||
constructor(fastify: FastifyInstance, mastoconverter: MastoConverters) {
|
||||
this.fastify = fastify;
|
||||
this.mastoconverter = new MastoConverters(config, usersrepo, notesrepo, noteeditrepo, userentity);
|
||||
this.mastoconverter = mastoconverter;
|
||||
}
|
||||
|
||||
public async getStatus() {
|
||||
|
@ -29,7 +29,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.getStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -44,8 +44,8 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getStatusSource(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(convertStatusSource(data.data));
|
||||
const data = await client.getStatusSource(_request.params.id);
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(_request.is404 ? 404 : 401).send(e.response.data);
|
||||
|
@ -60,10 +60,7 @@ export class ApiStatusMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const query: any = _request.query;
|
||||
try {
|
||||
const data = await client.getStatusContext(
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(limitToInt(query)),
|
||||
);
|
||||
const data = await client.getStatusContext(_request.params.id, limitToInt(query));
|
||||
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);
|
||||
|
@ -77,7 +74,8 @@ export class ApiStatusMastodon {
|
|||
public async getHistory() {
|
||||
this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/history', async (_request, reply) => {
|
||||
try {
|
||||
reply.send([]);
|
||||
const edits = await this.mastoconverter.getEdits(_request.params.id);
|
||||
reply.send(edits);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -91,8 +89,8 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getStatusRebloggedBy(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(data.data.map((account: Entity.Account) => convertAccount(account)));
|
||||
const data = await client.getStatusRebloggedBy(_request.params.id);
|
||||
reply.send(await Promise.all(data.data.map(async (account: Entity.Account) => await this.mastoconverter.convertAccount(account))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -106,8 +104,8 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getStatusFavouritedBy(convertId(_request.params.id, IdType.SharkeyId));
|
||||
reply.send(data.data.map((account: Entity.Account) => convertAccount(account)));
|
||||
const data = await client.getStatusFavouritedBy(_request.params.id);
|
||||
reply.send(await Promise.all(data.data.map(async (account: Entity.Account) => await this.mastoconverter.convertAccount(account))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
reply.code(401).send(e.response.data);
|
||||
|
@ -121,7 +119,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getMedia(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.getMedia(_request.params.id);
|
||||
reply.send(convertAttachment(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -136,7 +134,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.getPoll(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.getPoll(_request.params.id);
|
||||
reply.send(convertPoll(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -152,7 +150,7 @@ export class ApiStatusMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const body: any = _request.body;
|
||||
try {
|
||||
const data = await client.votePoll(convertId(_request.params.id, IdType.SharkeyId), body.choices);
|
||||
const data = await client.votePoll(_request.params.id, body.choices);
|
||||
reply.send(convertPoll(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -168,8 +166,6 @@ export class ApiStatusMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
let body: any = _request.body;
|
||||
try {
|
||||
if (body.in_reply_to_id) body.in_reply_to_id = convertId(body.in_reply_to_id, IdType.SharkeyId);
|
||||
if (body.quote_id) body.quote_id = convertId(body.quote_id, IdType.SharkeyId);
|
||||
if (
|
||||
(!body.poll && body['poll[options][]']) ||
|
||||
(!body.media_ids && body['media_ids[]'])
|
||||
|
@ -201,9 +197,6 @@ export class ApiStatusMastodon {
|
|||
}
|
||||
if (!body.media_ids) body.media_ids = undefined;
|
||||
if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
|
||||
if (body.media_ids) {
|
||||
body.media_ids = (body.media_ids as string[]).map((p) => convertId(p, IdType.SharkeyId));
|
||||
}
|
||||
|
||||
const { sensitive } = body;
|
||||
body.sensitive = typeof sensitive === 'string' ? sensitive === 'true' : sensitive;
|
||||
|
@ -241,10 +234,7 @@ export class ApiStatusMastodon {
|
|||
try {
|
||||
if (!body.media_ids) body.media_ids = undefined;
|
||||
if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
|
||||
if (body.media_ids) {
|
||||
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);
|
||||
const data = await client.editStatus(_request.params.id, body);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -259,10 +249,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = (await client.createEmojiReaction(
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
'❤',
|
||||
)) as any;
|
||||
const data = (await client.createEmojiReaction(_request.params.id, '❤')) as any;
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -277,10 +264,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.deleteEmojiReaction(
|
||||
convertId(_request.params.id, IdType.SharkeyId),
|
||||
'❤',
|
||||
);
|
||||
const data = await client.deleteEmojiReaction(_request.params.id, '❤');
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -295,7 +279,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.reblogStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.reblogStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -310,7 +294,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unreblogStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.unreblogStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -325,7 +309,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.bookmarkStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.bookmarkStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -340,7 +324,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unbookmarkStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.unbookmarkStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -355,7 +339,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.pinStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.pinStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -370,7 +354,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.unpinStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.unpinStatus(_request.params.id);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -385,7 +369,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.createEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
|
||||
const data = await client.createEmojiReaction(_request.params.id, _request.params.name);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -400,7 +384,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.deleteEmojiReaction(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
|
||||
const data = await client.deleteEmojiReaction(_request.params.id, _request.params.name);
|
||||
reply.send(await this.mastoconverter.convertStatus(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -415,7 +399,7 @@ export class ApiStatusMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const data = await client.deleteStatus(convertId(_request.params.id, IdType.SharkeyId));
|
||||
const data = await client.deleteStatus(_request.params.id);
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ParsedUrlQuery } from 'querystring';
|
||||
import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, MastoConverters } from '../converters.js';
|
||||
import { convertConversation, convertList, MastoConverters } from '../converters.js';
|
||||
import { getClient } from '../MastodonApiServerService.js';
|
||||
import type { Entity } from 'megalodon';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
|
@ -32,20 +32,11 @@ export function argsToBools(q: ParsedUrlQuery) {
|
|||
return q;
|
||||
}
|
||||
|
||||
export function convertTimelinesArgsId(q: ParsedUrlQuery) {
|
||||
if (typeof q.min_id === 'string') q.min_id = convertId(q.min_id, IdType.SharkeyId);
|
||||
if (typeof q.max_id === 'string') q.max_id = convertId(q.max_id, IdType.SharkeyId);
|
||||
if (typeof q.since_id === 'string') q.since_id = convertId(q.since_id, IdType.SharkeyId);
|
||||
return q;
|
||||
}
|
||||
|
||||
export class ApiTimelineMastodon {
|
||||
private fastify: FastifyInstance;
|
||||
private mastoconverter: MastoConverters;
|
||||
|
||||
constructor(fastify: FastifyInstance, config: Config, usersRepository: UsersRepository, notesRepository: NotesRepository, noteEditRepository: NoteEditRepository, userEntityService: UserEntityService) {
|
||||
constructor(fastify: FastifyInstance, config: Config, private mastoconverter: MastoConverters) {
|
||||
this.fastify = fastify;
|
||||
this.mastoconverter = new MastoConverters(config, usersRepository, notesRepository, noteEditRepository, userEntityService);
|
||||
}
|
||||
|
||||
public async getTL() {
|
||||
|
@ -56,8 +47,8 @@ export class ApiTimelineMastodon {
|
|||
try {
|
||||
const query: any = _request.query;
|
||||
const data = query.local === 'true'
|
||||
? await client.getLocalTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query))))
|
||||
: await client.getPublicTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query))));
|
||||
? await client.getLocalTimeline(argsToBools(limitToInt(query)))
|
||||
: await client.getPublicTimeline(argsToBools(limitToInt(query)));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -74,7 +65,7 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const query: any = _request.query;
|
||||
const data = await client.getHomeTimeline(convertTimelinesArgsId(limitToInt(query)));
|
||||
const data = await client.getHomeTimeline(limitToInt(query));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -92,7 +83,7 @@ export class ApiTimelineMastodon {
|
|||
try {
|
||||
const query: any = _request.query;
|
||||
const params: any = _request.params;
|
||||
const data = await client.getTagTimeline(params.hashtag, convertTimelinesArgsId(limitToInt(query)));
|
||||
const data = await client.getTagTimeline(params.hashtag, limitToInt(query));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -110,7 +101,7 @@ export class ApiTimelineMastodon {
|
|||
try {
|
||||
const query: any = _request.query;
|
||||
const params: any = _request.params;
|
||||
const data = await client.getListTimeline(convertId(params.id, IdType.SharkeyId), convertTimelinesArgsId(limitToInt(query)));
|
||||
const data = await client.getListTimeline(params.id, limitToInt(query));
|
||||
reply.send(await Promise.all(data.data.map(async (status: Entity.Status) => await this.mastoconverter.convertStatus(status))));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -127,7 +118,7 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
try {
|
||||
const query: any = _request.query;
|
||||
const data = await client.getConversationTimeline(convertTimelinesArgsId(limitToInt(query)));
|
||||
const data = await client.getConversationTimeline(limitToInt(query));
|
||||
reply.send(data.data.map((conversation: Entity.Conversation) => convertConversation(conversation)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -144,7 +135,7 @@ export class ApiTimelineMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
const params: any = _request.params;
|
||||
const data = await client.getList(convertId(params.id, IdType.SharkeyId));
|
||||
const data = await client.getList(params.id);
|
||||
reply.send(convertList(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -160,8 +151,7 @@ export class ApiTimelineMastodon {
|
|||
const BASE_URL = `${_request.protocol}://${_request.hostname}`;
|
||||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
const account = await client.verifyAccountCredentials();
|
||||
const data = await client.getLists(account.data.id);
|
||||
const data = await client.getLists();
|
||||
reply.send(data.data.map((list: Entity.List) => convertList(list)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -178,11 +168,8 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const params: any = _request.params;
|
||||
const query: any = _request.query;
|
||||
const data = await client.getAccountsInList(
|
||||
convertId(params.id, IdType.SharkeyId),
|
||||
convertTimelinesArgsId(query),
|
||||
);
|
||||
reply.send(data.data.map((account: Entity.Account) => convertAccount(account)));
|
||||
const data = await client.getAccountsInList(params.id, query);
|
||||
reply.send(data.data.map((account: Entity.Account) => this.mastoconverter.convertAccount(account)));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
@ -199,10 +186,7 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const params: any = _request.params;
|
||||
const query: any = _request.query;
|
||||
const data = await client.addAccountsToList(
|
||||
convertId(params.id, IdType.SharkeyId),
|
||||
(query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)),
|
||||
);
|
||||
const data = await client.addAccountsToList(params.id, query.accounts_id);
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -220,10 +204,7 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const params: any = _request.params;
|
||||
const query: any = _request.query;
|
||||
const data = await client.deleteAccountsFromList(
|
||||
convertId(params.id, IdType.SharkeyId),
|
||||
(query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)),
|
||||
);
|
||||
const data = await client.deleteAccountsFromList(params.id, query.accounts_id);
|
||||
reply.send(data.data);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -258,7 +239,7 @@ export class ApiTimelineMastodon {
|
|||
const client = getClient(BASE_URL, accessTokens);
|
||||
const body: any = _request.body;
|
||||
const params: any = _request.params;
|
||||
const data = await client.updateList(convertId(params.id, IdType.SharkeyId), body.title);
|
||||
const data = await client.updateList(params.id, body.title);
|
||||
reply.send(convertList(data.data));
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
|
@ -275,8 +256,8 @@ export class ApiTimelineMastodon {
|
|||
const accessTokens = _request.headers.authorization;
|
||||
const client = getClient(BASE_URL, accessTokens);
|
||||
const params: any = _request.params;
|
||||
const data = await client.deleteList(convertId(params.id, IdType.SharkeyId));
|
||||
reply.send(data.data);
|
||||
const data = await client.deleteList(params.id);
|
||||
reply.send({});
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
console.error(e.response.data);
|
||||
|
|
|
@ -64,15 +64,15 @@
|
|||
"socks-proxy-agent": "^8.0.2",
|
||||
"typescript": "5.1.6",
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ws": "8.14.2",
|
||||
"@types/core-js": "^2.5.6",
|
||||
"@types/form-data": "^2.5.0",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/object-assign-deep": "^0.4.1",
|
||||
"@types/parse-link-header": "^2.0.1",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@types/uuid": "^9.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
"eslint": "^8.49.0",
|
||||
|
|
|
@ -5,15 +5,16 @@
|
|||
namespace Entity {
|
||||
export type Account = {
|
||||
id: string
|
||||
fqn?: string
|
||||
username: string
|
||||
acct: string
|
||||
display_name: string
|
||||
locked: boolean
|
||||
discoverable?: boolean
|
||||
group: boolean | null
|
||||
noindex: boolean | null
|
||||
suspended: boolean | null
|
||||
limited: boolean | null
|
||||
group?: boolean | null
|
||||
noindex?: boolean | null
|
||||
suspended?: boolean | null
|
||||
limited?: boolean | null
|
||||
created_at: string
|
||||
followers_count: number
|
||||
following_count: number
|
||||
|
|
|
@ -2,6 +2,6 @@ namespace Entity {
|
|||
export type Field = {
|
||||
name: string
|
||||
value: string
|
||||
verified_at: string | null
|
||||
verified_at?: string | null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ namespace Entity {
|
|||
export type List = {
|
||||
id: string
|
||||
title: string
|
||||
replies_policy: RepliesPolicy | null
|
||||
replies_policy?: RepliesPolicy | null
|
||||
exclusive?: RepliesPolicy | null
|
||||
}
|
||||
|
||||
export type RepliesPolicy = 'followed' | 'list' | 'none'
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Entity {
|
|||
in_reply_to_account_id: string | null
|
||||
reblog: Status | null
|
||||
content: string
|
||||
plain_content: string | null
|
||||
plain_content?: string | null
|
||||
created_at: string
|
||||
emojis: Emoji[]
|
||||
replies_count: number
|
||||
|
|
|
@ -1041,7 +1041,7 @@ export interface MegalodonInterface {
|
|||
*
|
||||
* @return Array of lists.
|
||||
*/
|
||||
getLists(id: string): Promise<Response<Array<Entity.List>>>
|
||||
getLists(id?: string): Promise<Response<Array<Entity.List>>>
|
||||
/**
|
||||
* Show a single list.
|
||||
*
|
||||
|
|
|
@ -238,6 +238,21 @@ export default class Misskey implements MegalodonInterface {
|
|||
description: options.note
|
||||
})
|
||||
}
|
||||
if (options.avatar) {
|
||||
params = Object.assign(params, {
|
||||
avatarId: options.avatar
|
||||
})
|
||||
}
|
||||
if (options.header) {
|
||||
params = Object.assign(params, {
|
||||
bannerId: options.header
|
||||
})
|
||||
}
|
||||
if (options.fields_attributes) {
|
||||
params = Object.assign(params, {
|
||||
fields: options.fields_attributes
|
||||
})
|
||||
}
|
||||
if (options.locked !== undefined) {
|
||||
params = Object.assign(params, {
|
||||
isLocked: options.locked.toString() === 'true' ? true : false
|
||||
|
@ -1148,6 +1163,7 @@ export default class Misskey implements MegalodonInterface {
|
|||
media_ids?: Array<string> | null
|
||||
poll?: { options?: Array<string>; expires_in?: number; multiple?: boolean; hide_totals?: boolean }
|
||||
visibility?: "public" | "unlisted" | "private" | "direct"
|
||||
in_reply_to_id?: string
|
||||
}
|
||||
): Promise<Response<Entity.Status>> {
|
||||
let params = {
|
||||
|
@ -1160,6 +1176,11 @@ export default class Misskey implements MegalodonInterface {
|
|||
fileIds: _options.media_ids
|
||||
})
|
||||
}
|
||||
if (_options.in_reply_to_id) {
|
||||
params = Object.assign(params, {
|
||||
replyId: _options.in_reply_to_id
|
||||
})
|
||||
}
|
||||
if (_options.poll) {
|
||||
let pollParam = {
|
||||
choices: _options.poll.options,
|
||||
|
@ -1871,9 +1892,15 @@ export default class Misskey implements MegalodonInterface {
|
|||
/**
|
||||
* POST /api/users/lists/list
|
||||
*/
|
||||
public async getLists(id: string): Promise<Response<Array<Entity.List>>> {
|
||||
public async getLists(id?: string): Promise<Response<Array<Entity.List>>> {
|
||||
if (id) {
|
||||
return this.client
|
||||
.post<Array<MisskeyAPI.Entity.List>>('/api/users/lists/list', { userId: id })
|
||||
.then(res => ({ ...res, data: res.data.map(l => MisskeyAPI.Converter.list(l)) }))
|
||||
}
|
||||
|
||||
return this.client
|
||||
.post<Array<MisskeyAPI.Entity.List>>('/api/users/lists/list', { userId: id })
|
||||
.post<Array<MisskeyAPI.Entity.List>>('/api/users/lists/list', {})
|
||||
.then(res => ({ ...res, data: res.data.map(l => MisskeyAPI.Converter.list(l)) }))
|
||||
}
|
||||
|
||||
|
@ -2017,7 +2044,7 @@ export default class Misskey implements MegalodonInterface {
|
|||
}
|
||||
if (options.exclude_type) {
|
||||
params = Object.assign(params, {
|
||||
excludeType: options.exclude_type.map(e => MisskeyAPI.Converter.encodeNotificationType(e))
|
||||
excludeTypes: options.exclude_type.map(e => MisskeyAPI.Converter.encodeNotificationType(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,8 +78,10 @@ namespace MisskeyAPI {
|
|||
acct = `${u.username}@${u.host}`;
|
||||
acctUrl = `https://${u.host}/@${u.username}`;
|
||||
}
|
||||
const fqn = `${u.username}@${u.host ?? host}`;
|
||||
return {
|
||||
id: u.id,
|
||||
fqn: fqn,
|
||||
username: u.username,
|
||||
acct: acct,
|
||||
display_name: u.name ? u.name : '',
|
||||
|
@ -389,7 +391,7 @@ namespace MisskeyAPI {
|
|||
export const list = (l: Entity.List): MegalodonEntity.List => ({
|
||||
id: l.id,
|
||||
title: l.name,
|
||||
replies_policy: null
|
||||
exclusive: null
|
||||
})
|
||||
|
||||
export const encodeNotificationType = (
|
||||
|
@ -465,8 +467,8 @@ namespace MisskeyAPI {
|
|||
|
||||
export const stats = (s: Entity.Stats): MegalodonEntity.Stats => {
|
||||
return {
|
||||
user_count: s.usersCount,
|
||||
status_count: s.notesCount,
|
||||
user_count: s.originalUsersCount,
|
||||
status_count: s.originalNotesCount,
|
||||
domain_count: s.instances
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,6 @@ namespace MisskeyEntity {
|
|||
name: string;
|
||||
value: string;
|
||||
verified?: string;
|
||||
verified_at?: string;
|
||||
};
|
||||
}
|
||||
|
|
158
pnpm-lock.yaml
158
pnpm-lock.yaml
|
@ -994,7 +994,7 @@ importers:
|
|||
version: 7.5.0
|
||||
storybook-addon-misskey-theme:
|
||||
specifier: github:misskey-dev/storybook-addon-misskey-theme
|
||||
version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.4.6)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.5.0)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
summaly:
|
||||
specifier: github:misskey-dev/summaly
|
||||
version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
|
||||
|
@ -1016,9 +1016,27 @@ importers:
|
|||
|
||||
packages/megalodon:
|
||||
dependencies:
|
||||
'@types/core-js':
|
||||
specifier: ^2.5.6
|
||||
version: 2.5.6
|
||||
'@types/form-data':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
'@types/jest':
|
||||
specifier: ^29.5.5
|
||||
version: 29.5.5
|
||||
'@types/oauth':
|
||||
specifier: ^0.9.2
|
||||
version: 0.9.2
|
||||
'@types/object-assign-deep':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
'@types/parse-link-header':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
'@types/uuid':
|
||||
specifier: ^9.0.4
|
||||
version: 9.0.4
|
||||
'@types/ws':
|
||||
specifier: ^8.5.5
|
||||
version: 8.5.5
|
||||
|
@ -1056,24 +1074,6 @@ importers:
|
|||
specifier: 8.14.2
|
||||
version: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
|
||||
devDependencies:
|
||||
'@types/core-js':
|
||||
specifier: ^2.5.6
|
||||
version: 2.5.6
|
||||
'@types/form-data':
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
'@types/jest':
|
||||
specifier: ^29.5.5
|
||||
version: 29.5.5
|
||||
'@types/object-assign-deep':
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.1
|
||||
'@types/parse-link-header':
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
'@types/uuid':
|
||||
specifier: ^9.0.4
|
||||
version: 9.0.4
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^6.7.2
|
||||
version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.1.6)
|
||||
|
@ -1794,7 +1794,6 @@ packages:
|
|||
dependencies:
|
||||
'@babel/highlight': 7.22.13
|
||||
chalk: 2.4.2
|
||||
dev: true
|
||||
|
||||
/@babel/compat-data@7.22.9:
|
||||
resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==}
|
||||
|
@ -2049,7 +2048,6 @@ packages:
|
|||
'@babel/helper-validator-identifier': 7.22.15
|
||||
chalk: 2.4.2
|
||||
js-tokens: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@babel/parser@7.21.8:
|
||||
resolution: {integrity: sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==}
|
||||
|
@ -4162,7 +4160,6 @@ packages:
|
|||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
jest-get-type: 29.6.3
|
||||
dev: true
|
||||
|
||||
/@jest/expect@29.7.0:
|
||||
resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==}
|
||||
|
@ -4247,7 +4244,6 @@ packages:
|
|||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
dev: true
|
||||
|
||||
/@jest/source-map@29.6.3:
|
||||
resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==}
|
||||
|
@ -4322,7 +4318,6 @@ packages:
|
|||
'@types/node': 20.8.6
|
||||
'@types/yargs': 17.0.19
|
||||
chalk: 4.1.2
|
||||
dev: true
|
||||
|
||||
/@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.2.2)(vite@4.4.11):
|
||||
resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
|
||||
|
@ -5510,7 +5505,6 @@ packages:
|
|||
|
||||
/@sinclair/typebox@0.27.8:
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
dev: true
|
||||
|
||||
/@sindresorhus/is@4.6.0:
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
|
@ -6466,17 +6460,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@storybook/channels@7.4.6:
|
||||
resolution: {integrity: sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==}
|
||||
dependencies:
|
||||
'@storybook/client-logger': 7.4.6
|
||||
'@storybook/core-events': 7.4.6
|
||||
'@storybook/global': 5.0.0
|
||||
qs: 6.11.1
|
||||
telejson: 7.2.0
|
||||
tiny-invariant: 1.3.1
|
||||
dev: true
|
||||
|
||||
/@storybook/channels@7.5.0:
|
||||
resolution: {integrity: sha512-/7QJS1UA7TX3uhZqCpjv4Ib8nfMnDOJrBWvjiXiUONaRcSk/he5X+W1Zz/c7dgt+wkYuAh+evjc7glIaBhVNVQ==}
|
||||
dependencies:
|
||||
|
@ -6540,12 +6523,6 @@ packages:
|
|||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/@storybook/client-logger@7.4.6:
|
||||
resolution: {integrity: sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==}
|
||||
dependencies:
|
||||
'@storybook/global': 5.0.0
|
||||
dev: true
|
||||
|
||||
/@storybook/client-logger@7.5.0:
|
||||
resolution: {integrity: sha512-JV7J9vc69f9Il4uW62NIeweUU7O38VwFWxtCkhd0bcBA/9RG0go4M2avzxYYEAe9kIOX9IBBk8WGzMacwW4gKQ==}
|
||||
dependencies:
|
||||
|
@ -6573,29 +6550,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@storybook/components@7.4.6(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0)
|
||||
'@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/client-logger': 7.4.6
|
||||
'@storybook/csf': 0.1.0
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/types': 7.4.6
|
||||
memoizerific: 1.11.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||
util-deprecate: 1.0.2
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/react-dom'
|
||||
dev: true
|
||||
|
||||
/@storybook/components@7.5.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-6lmZ6PbS27xN32vTJ/NvgaiKkFIQRzZuBeBIg2u+FoAEgCiCwRXjZKe/O8NZC2Xr0uf97+7U2P0kD4Hwr9SNhw==}
|
||||
peerDependencies:
|
||||
|
@ -6657,12 +6611,6 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@storybook/core-events@7.4.6:
|
||||
resolution: {integrity: sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==}
|
||||
dependencies:
|
||||
ts-dedent: 2.2.0
|
||||
dev: true
|
||||
|
||||
/@storybook/core-events@7.5.0:
|
||||
resolution: {integrity: sha512-FsD+clTzayqprbVllnL8LLch+uCslJFDgsv7Zh99/zoi7OHtHyauoCZkdLBSiDzgc84qS41dY19HqX1/y7cnOw==}
|
||||
dependencies:
|
||||
|
@ -6995,20 +6943,6 @@ packages:
|
|||
ts-dedent: 2.2.0
|
||||
dev: true
|
||||
|
||||
/@storybook/theming@7.4.6(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-HW77iJ9ptCMqhoBOYFjRQw7VBap+38fkJGHP5KylEJCyYCgIAm2dEcQmtWpMVYFssSGcb6djfbtAMhYU4TL4Iw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
|
||||
'@storybook/client-logger': 7.4.6
|
||||
'@storybook/global': 5.0.0
|
||||
memoizerific: 1.11.3
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: true
|
||||
|
||||
/@storybook/theming@7.5.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-uTo97oh+pvmlfsZocFq5qae0zGo0VGk7oiBqNSSw6CiTqE1rIuSxoPrMAY+oCTWCUZV7DjONIGvpnGl2QALsAw==}
|
||||
peerDependencies:
|
||||
|
@ -7023,15 +6957,6 @@ packages:
|
|||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: true
|
||||
|
||||
/@storybook/types@7.4.6:
|
||||
resolution: {integrity: sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==}
|
||||
dependencies:
|
||||
'@storybook/channels': 7.4.6
|
||||
'@types/babel__core': 7.20.0
|
||||
'@types/express': 4.17.17
|
||||
file-system-cache: 2.3.0
|
||||
dev: true
|
||||
|
||||
/@storybook/types@7.5.0:
|
||||
resolution: {integrity: sha512-fiOUnHKFi/UZSfvc53F0WEQCiquqcSqslL3f5EffwQRiXfeXlGavJb0kU03BO+CvOXcliRn6qKSF2dL0Rgb7Xw==}
|
||||
dependencies:
|
||||
|
@ -7087,7 +7012,7 @@ packages:
|
|||
ts-dedent: 2.2.0
|
||||
type-fest: 2.19.0
|
||||
vue: 3.3.4
|
||||
vue-component-type-helpers: 1.8.19
|
||||
vue-component-type-helpers: 1.8.22
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
@ -7694,7 +7619,7 @@ packages:
|
|||
|
||||
/@types/core-js@2.5.6:
|
||||
resolution: {integrity: sha512-zLzoC7avO4EYUUYCSzDaahSP1QJEpZQcPxqs91mPeFdh2NS4hQBcnRoEc9RuXfJ8cdN/KXUWukMmZGcKaWeOvw==}
|
||||
dev: true
|
||||
dev: false
|
||||
|
||||
/@types/cross-spawn@6.0.2:
|
||||
resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
|
||||
|
@ -7786,7 +7711,7 @@ packages:
|
|||
deprecated: This is a stub types definition. form-data provides its own type definitions, so you do not need this installed.
|
||||
dependencies:
|
||||
form-data: 4.0.0
|
||||
dev: true
|
||||
dev: false
|
||||
|
||||
/@types/glob@7.2.0:
|
||||
resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
|
||||
|
@ -7818,19 +7743,16 @@ packages:
|
|||
|
||||
/@types/istanbul-lib-coverage@2.0.4:
|
||||
resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
|
||||
dev: true
|
||||
|
||||
/@types/istanbul-lib-report@3.0.0:
|
||||
resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
|
||||
dependencies:
|
||||
'@types/istanbul-lib-coverage': 2.0.4
|
||||
dev: true
|
||||
|
||||
/@types/istanbul-reports@3.0.1:
|
||||
resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
|
||||
dependencies:
|
||||
'@types/istanbul-lib-report': 3.0.0
|
||||
dev: true
|
||||
|
||||
/@types/jest@28.1.3:
|
||||
resolution: {integrity: sha512-Tsbjk8Y2hkBaY/gJsataeb4q9Mubw9EOz7+4RjPkzD5KjTvHHs7cpws22InaoXxAVAhF5HfFbzJjo6oKWqSZLw==}
|
||||
|
@ -7844,7 +7766,6 @@ packages:
|
|||
dependencies:
|
||||
expect: 29.7.0
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/@types/js-levenshtein@1.1.1:
|
||||
resolution: {integrity: sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==}
|
||||
|
@ -7978,7 +7899,7 @@ packages:
|
|||
|
||||
/@types/object-assign-deep@0.4.1:
|
||||
resolution: {integrity: sha512-uWJatOM1JKDdF6Fwa16124b76BtxvTz5Lv+ORGuI7dwqU4iqExXpeHrHOi1c8BU4FgSJ6PdH0skR9Zmz8+MUqQ==}
|
||||
dev: true
|
||||
dev: false
|
||||
|
||||
/@types/offscreencanvas@2019.3.0:
|
||||
resolution: {integrity: sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q==}
|
||||
|
@ -7992,7 +7913,7 @@ packages:
|
|||
|
||||
/@types/parse-link-header@2.0.1:
|
||||
resolution: {integrity: sha512-BrKNSrRTqn3UkMXvdVtr/znJch0PMBpEvEP8oBkxDx7eEGntuFLI+WpA5HGsNHK4SlqyhaMa+Ks0ViwyixQB5w==}
|
||||
dev: true
|
||||
dev: false
|
||||
|
||||
/@types/pg@8.10.5:
|
||||
resolution: {integrity: sha512-GS3ebGcSJQqKSnq4/WnSH1XQvx0vTDLEmqLENk7onKvTnry9BWPsZiZeUMJlEPw+5bCQDzfxZFhxlUztpNCKgQ==}
|
||||
|
@ -8135,7 +8056,6 @@ packages:
|
|||
|
||||
/@types/stack-utils@2.0.1:
|
||||
resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
|
||||
dev: true
|
||||
|
||||
/@types/throttle-debounce@5.0.0:
|
||||
resolution: {integrity: sha512-Pb7k35iCGFcGPECoNE4DYp3Oyf2xcTd3FbFQxXUI9hEYKUl6YX+KLf7HrBmgVcD05nl50LIH6i+80js4iYmWbw==}
|
||||
|
@ -8159,7 +8079,6 @@ packages:
|
|||
|
||||
/@types/uuid@9.0.4:
|
||||
resolution: {integrity: sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==}
|
||||
dev: true
|
||||
|
||||
/@types/uuid@9.0.5:
|
||||
resolution: {integrity: sha512-xfHdwa1FMJ082prjSJpoEI57GZITiQz10r3vEJCHa2khEFQjKy91aWKz6+zybzssCvXUwE1LQWgWVwZ4nYUvHQ==}
|
||||
|
@ -8202,7 +8121,6 @@ packages:
|
|||
|
||||
/@types/yargs-parser@21.0.0:
|
||||
resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
|
||||
dev: true
|
||||
|
||||
/@types/yargs@16.0.5:
|
||||
resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==}
|
||||
|
@ -8214,7 +8132,6 @@ packages:
|
|||
resolution: {integrity: sha512-cAx3qamwaYX9R0fzOIZAlFpo4A+1uBVCxqpKz9D26uTF4srRXaGTTsikQmaotCtNdbhzyUH7ft6p9ktz9s6UNQ==}
|
||||
dependencies:
|
||||
'@types/yargs-parser': 21.0.0
|
||||
dev: true
|
||||
|
||||
/@types/yauzl@2.10.0:
|
||||
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
|
||||
|
@ -8960,7 +8877,6 @@ packages:
|
|||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
color-convert: 1.9.3
|
||||
dev: true
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
|
@ -8971,7 +8887,6 @@ packages:
|
|||
/ansi-styles@5.2.0:
|
||||
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/ansi-styles@6.2.1:
|
||||
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
|
||||
|
@ -9902,7 +9817,6 @@ packages:
|
|||
ansi-styles: 3.2.1
|
||||
escape-string-regexp: 1.0.5
|
||||
supports-color: 5.5.0
|
||||
dev: true
|
||||
|
||||
/chalk@3.0.0:
|
||||
resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==}
|
||||
|
@ -10054,7 +9968,6 @@ packages:
|
|||
/ci-info@3.7.1:
|
||||
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/cjs-module-lexer@1.2.2:
|
||||
resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
|
||||
|
@ -10172,7 +10085,6 @@ packages:
|
|||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
dependencies:
|
||||
color-name: 1.1.3
|
||||
dev: true
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
|
@ -10182,7 +10094,6 @@ packages:
|
|||
|
||||
/color-name@1.1.3:
|
||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
||||
dev: true
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
@ -10967,7 +10878,6 @@ packages:
|
|||
/diff-sequences@29.6.3:
|
||||
resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/diff@5.1.0:
|
||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||
|
@ -11358,12 +11268,10 @@ packages:
|
|||
/escape-string-regexp@1.0.5:
|
||||
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
|
||||
engines: {node: '>=0.8.0'}
|
||||
dev: true
|
||||
|
||||
/escape-string-regexp@2.0.0:
|
||||
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/escape-string-regexp@4.0.0:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
|
@ -11775,7 +11683,6 @@ packages:
|
|||
jest-matcher-utils: 29.7.0
|
||||
jest-message-util: 29.7.0
|
||||
jest-util: 29.7.0
|
||||
dev: true
|
||||
|
||||
/exponential-backoff@3.1.1:
|
||||
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
|
||||
|
@ -13758,7 +13665,6 @@ packages:
|
|||
diff-sequences: 29.6.3
|
||||
jest-get-type: 29.6.3
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/jest-docblock@29.7.0:
|
||||
resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==}
|
||||
|
@ -13807,7 +13713,6 @@ packages:
|
|||
/jest-get-type@29.6.3:
|
||||
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
dev: true
|
||||
|
||||
/jest-haste-map@29.7.0:
|
||||
resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==}
|
||||
|
@ -13854,7 +13759,6 @@ packages:
|
|||
jest-diff: 29.7.0
|
||||
jest-get-type: 29.6.3
|
||||
pretty-format: 29.7.0
|
||||
dev: true
|
||||
|
||||
/jest-message-util@29.7.0:
|
||||
resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==}
|
||||
|
@ -13869,7 +13773,6 @@ packages:
|
|||
pretty-format: 29.7.0
|
||||
slash: 3.0.0
|
||||
stack-utils: 2.0.6
|
||||
dev: true
|
||||
|
||||
/jest-mock@27.5.1:
|
||||
resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
|
||||
|
@ -14027,7 +13930,6 @@ packages:
|
|||
ci-info: 3.7.1
|
||||
graceful-fs: 4.2.11
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/jest-validate@29.7.0:
|
||||
resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==}
|
||||
|
@ -14136,7 +14038,6 @@ packages:
|
|||
|
||||
/js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
dev: true
|
||||
|
||||
/js-yaml@3.14.1:
|
||||
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
|
||||
|
@ -16601,7 +16502,6 @@ packages:
|
|||
'@jest/schemas': 29.6.3
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 18.2.0
|
||||
dev: true
|
||||
|
||||
/pretty-hrtime@1.0.3:
|
||||
resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
|
||||
|
@ -17090,7 +16990,6 @@ packages:
|
|||
|
||||
/react-is@18.2.0:
|
||||
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
|
||||
dev: true
|
||||
|
||||
/react-refresh@0.14.0:
|
||||
resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==}
|
||||
|
@ -18227,7 +18126,6 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
dev: true
|
||||
|
||||
/stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
@ -19667,8 +19565,8 @@ packages:
|
|||
resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
|
||||
dev: true
|
||||
|
||||
/vue-component-type-helpers@1.8.19:
|
||||
resolution: {integrity: sha512-1OANGSZK4pzHF4uc86usWi+o5Y0zgoDtqWkPg6Am6ot+jHSAmpOah59V/4N82So5xRgivgCxGgK09lBy1XNUfQ==}
|
||||
/vue-component-type-helpers@1.8.22:
|
||||
resolution: {integrity: sha512-LK3wJHs3vJxHG292C8cnsRusgyC5SEZDCzDCD01mdE/AoREFMl2tzLRuzwyuEsOIz13tqgBcnvysN3Lxsa14Fw==}
|
||||
dev: true
|
||||
|
||||
/vue-demi@0.13.11(vue@3.3.4):
|
||||
|
@ -20210,7 +20108,7 @@ packages:
|
|||
sharp: 0.31.3
|
||||
dev: false
|
||||
|
||||
github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.4.6)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.5.0)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
|
||||
id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
|
||||
name: storybook-addon-misskey-theme
|
||||
|
@ -20232,7 +20130,7 @@ packages:
|
|||
optional: true
|
||||
dependencies:
|
||||
'@storybook/blocks': 7.5.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/components': 7.5.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/core-events': 7.5.0
|
||||
'@storybook/manager-api': 7.5.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@storybook/preview-api': 7.5.0
|
||||
|
|
Loading…
Reference in a new issue