diff --git a/packages/backend/package.json b/packages/backend/package.json
index 22030d168..d2a4042ff 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -117,7 +117,6 @@
"json5": "2.2.3",
"jsonld": "8.3.1",
"jsrsasign": "10.8.6",
- "megalodon": "workspace:*",
"meilisearch": "0.35.0",
"mfm-js": "0.23.3",
"microformats-parser": "1.5.2",
diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts
index fc5eece01..fa81380f0 100644
--- a/packages/backend/src/server/ServerModule.ts
+++ b/packages/backend/src/server/ServerModule.ts
@@ -23,7 +23,6 @@ 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';
@@ -40,7 +39,6 @@ import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js';
import { ServerStatsChannelService } from './api/stream/channels/server-stats.js';
import { UserListChannelService } from './api/stream/channels/user-list.js';
import { OpenApiServerService } from './api/openapi/OpenApiServerService.js';
-import { MastodonApiServerService } from './api/mastodon/MastodonApiServerService.js';
import { ClientLoggerService } from './web/ClientLoggerService.js';
import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js';
import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
@@ -86,9 +84,7 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
ServerStatsChannelService,
UserListChannelService,
OpenApiServerService,
- MastodonApiServerService,
OAuth2ProviderService,
- MastoConverters,
],
exports: [
ServerService,
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 4d10e0fa4..e2f969eb9 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -31,7 +31,6 @@ import { WellKnownServerService } from './WellKnownServerService.js';
import { FileServerService } from './FileServerService.js';
import { ClientServerService } from './web/ClientServerService.js';
import { OpenApiServerService } from './api/openapi/OpenApiServerService.js';
-import { MastodonApiServerService } from './api/mastodon/MastodonApiServerService.js';
import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
const _dirname = fileURLToPath(new URL('.', import.meta.url));
@@ -105,7 +104,6 @@ export class ServerService implements OnApplicationShutdown {
fastify.register(this.apiServerService.createServer, { prefix: '/api' });
fastify.register(this.openApiServerService.createServer);
- fastify.register(this.mastodonApiServerService.createServer, { prefix: '/api' });
fastify.register(this.fileServerService.createServer);
fastify.register(this.activityPubServerService.createServer);
fastify.register(this.nodeinfoServerService.createServer);
diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts
deleted file mode 100644
index 5706a1778..000000000
--- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts
+++ /dev/null
@@ -1,906 +0,0 @@
-import { Inject, Injectable } from '@nestjs/common';
-import megalodon, { Entity, MegalodonInterface } from 'megalodon';
-import querystring from 'querystring';
-import { IsNull } from 'typeorm';
-import multer from 'fastify-multer';
-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 { 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];
- const accessToken = accessTokenArr[accessTokenArr.length - 1];
- const generator = (megalodon as any).default;
- const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface;
- return client;
-}
-
-@Injectable()
-export class MastodonApiServerService {
- constructor(
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
- @Inject(DI.notesRepository)
- private notesRepository: NotesRepository,
- @Inject(DI.userProfilesRepository)
- 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
- public createServer(fastify: FastifyInstance, _options: FastifyPluginOptions, done: (err?: Error) => void) {
- const upload = multer({
- storage: multer.diskStorage({}),
- limits: {
- fileSize: this.config.maxFileSize || 262144000,
- files: 1,
- },
- });
-
- fastify.addHook('onRequest', (request, reply, done) => {
- reply.header('Access-Control-Allow-Origin', '*');
- done();
- });
-
- fastify.addContentTypeParser('application/x-www-form-urlencoded', (request, payload, done) => {
- let body = '';
- payload.on('data', (data) => {
- body += data;
- });
- payload.on('end', () => {
- try {
- const parsed = querystring.parse(body);
- done(null, parsed);
- } catch (e: any) {
- done(e);
- }
- });
- payload.on('error', done);
- });
-
- fastify.register(multer.contentParser);
-
- fastify.get('/v1/custom_emojis', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getInstanceCustomEmojis();
- reply.send(data.data);
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/instance', 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.getInstance();
- const admin = await this.usersRepository.findOne({
- where: {
- host: IsNull(),
- isRoot: true,
- isDeleted: false,
- isSuspended: false,
- },
- order: { id: 'ASC' },
- });
- 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);
- }
- });
-
- fastify.get('/v1/announcements', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getInstanceAnnouncements();
- reply.send(data.data.map((announcement) => convertAnnouncement(announcement)));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Body: { id: string } }>('/v1/announcements/:id/dismiss', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.dismissInstanceAnnouncement(
- _request.body['id'],
- );
- reply.send(data.data);
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- },
- );
-
- fastify.post('/v1/media', { preHandler: upload.single('file') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const multipartData = await _request.file;
- if (!multipartData) {
- reply.code(401).send({ error: 'No image' });
- return;
- }
- const data = await client.uploadMedia(multipartData);
- reply.send(convertAttachment(data.data as Entity.Attachment));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post('/v2/media', { preHandler: upload.single('file') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const multipartData = await _request.file;
- if (!multipartData) {
- reply.code(401).send({ error: 'No image' });
- return;
- }
- const data = await client.uploadMedia(multipartData, _request.body!);
- reply.send(convertAttachment(data.data as Entity.Attachment));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/filters', 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.getFilters();
- reply.send(data.data.map((filter) => convertFilter(filter)));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/trends', 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/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', { preHandler: upload.single('none') }, 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
- // displayed without being logged in
- try {
- const data = await ApiAuthMastodon(_request, client);
- reply.send(data);
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/preferences', 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.getPreferences();
- reply.send(data.data);
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- //#region Accounts
- fastify.get('/v1/accounts/verify_credentials', 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 account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.verifyCredentials());
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- 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(await this.mastoConverter.convertAccount(data.data));
- } catch (e: any) {
- //console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/accounts/lookup', 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.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(await this.mastoConverter.convertAccount(data.data.accounts[0]));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/accounts/relationships', 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
- let users;
- try {
- 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, this.mastoConverter);
- reply.send(await account.getRelationships(users));
- } catch (e: any) {
- /* console.error(e); */
- const data = e.response.data;
- data.users = users;
- console.error(data);
- reply.code(401).send(data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const sharkId = _request.params.id;
- const data = await client.getAccount(sharkId);
- const account = await this.mastoConverter.convertAccount(data.data);
- reply.send(account);
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/statuses', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getStatuses());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/featured_tags', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getFeaturedTags();
- reply.send(data.data.map((tag) => convertFeaturedTag(tag)));
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/followers', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getFollowers());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/following', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getFollowing());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/accounts/:id/lists', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getAccountLists(_request.params.id);
- reply.send(data.data.map((list) => convertList(list)));
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/follow', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.addFollow());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unfollow', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.rmFollow());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/block', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.addBlock());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unblock', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.rmBlock());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/mute', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.addMute());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/accounts/:id/unmute', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.rmMute());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/followed_tags', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getFollowedTags();
- reply.send(data.data);
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/bookmarks', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getBookmarks());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/favourites', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getFavourites());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/mutes', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getMutes());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/blocks', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.getBlocks());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/follow_requests', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getFollowRequests( ((_request.query as any) || { limit: 20 }).limit );
- 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); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/follow_requests/:id/authorize', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.acceptFollow());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/follow_requests/:id/reject', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const account = new ApiAccountMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await account.rejectFollow());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
- //#endregion
-
- //#region Search
- fastify.get('/v1/search', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await search.SearchV1());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v2/search', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await search.SearchV2());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v1/trends/statuses', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await search.getStatusTrends());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get('/v2/suggestions', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const search = new ApiSearchMastodon(_request, client, BASE_URL, this.mastoConverter);
- reply.send(await search.getSuggestions());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
- //#endregion
-
- //#region Notifications
- fastify.get('/v1/notifications', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const notify = new ApiNotifyMastodon(_request, client);
- reply.send(await notify.getNotifications());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.get<{ Params: { id: string } }>('/v1/notification/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const notify = new ApiNotifyMastodon(_request, client);
- reply.send(await notify.getNotification());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const notify = new ApiNotifyMastodon(_request, client);
- reply.send(await notify.rmNotification());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post('/v1/notifications/clear', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const notify = new ApiNotifyMastodon(_request, client);
- reply.send(await notify.rmNotifications());
- } catch (e: any) {
- /* console.error(e);
- console.error(e.response.data); */
- reply.code(401).send(e.response.data);
- }
- });
- //#endregion
-
- //#region Filters
- fastify.get<{ Params: { id: string } }>('/v1/filters/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const filter = new ApiFilterMastodon(_request, client);
- !_request.params.id ? reply.send(await filter.getFilters()) : reply.send(await filter.getFilter());
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post('/v1/filters', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const filter = new ApiFilterMastodon(_request, client);
- reply.send(await filter.createFilter());
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.post<{ Params: { id: string } }>('/v1/filters/:id', { preHandler: upload.single('none') }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const filter = new ApiFilterMastodon(_request, client);
- reply.send(await filter.updateFilter());
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
-
- fastify.delete<{ Params: { id: string } }>('/v1/filters/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const filter = new ApiFilterMastodon(_request, client);
- reply.send(await filter.rmFilter());
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- //#endregion
-
- //#region Timelines
- const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.mastoConverter);
-
- // GET Endpoints
- TLEndpoint.getTL();
- TLEndpoint.getHomeTl();
- TLEndpoint.getListTL();
- TLEndpoint.getTagTl();
- TLEndpoint.getConversations();
- TLEndpoint.getList();
- TLEndpoint.getLists();
- TLEndpoint.getListAccounts();
-
- // POST Endpoints
- TLEndpoint.createList();
- TLEndpoint.addListAccount();
-
- // PUT Endpoint
- TLEndpoint.updateList();
-
- // DELETE Endpoints
- TLEndpoint.deleteList();
- TLEndpoint.rmListAccount();
- //#endregion
-
- //#region Status
- const NoteEndpoint = new ApiStatusMastodon(fastify, this.mastoConverter);
-
- // GET Endpoints
- NoteEndpoint.getStatus();
- NoteEndpoint.getStatusSource();
- NoteEndpoint.getContext();
- NoteEndpoint.getHistory();
- NoteEndpoint.getReblogged();
- NoteEndpoint.getFavourites();
- NoteEndpoint.getMedia();
- NoteEndpoint.getPoll();
-
- //POST Endpoints
- NoteEndpoint.postStatus();
- NoteEndpoint.addFavourite();
- NoteEndpoint.rmFavourite();
- NoteEndpoint.reblogStatus();
- NoteEndpoint.unreblogStatus();
- NoteEndpoint.bookmarkStatus();
- NoteEndpoint.unbookmarkStatus();
- NoteEndpoint.pinStatus();
- NoteEndpoint.unpinStatus();
- NoteEndpoint.reactStatus();
- NoteEndpoint.unreactStatus();
- NoteEndpoint.votePoll();
-
- // PUT Endpoint
- fastify.put<{ Params: { id: string } }>('/v1/media/:id', { preHandler: upload.none() }, async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.updateMedia(_request.params.id, _request.body!);
- reply.send(convertAttachment(data.data));
- } catch (e: any) {
- /* console.error(e); */
- reply.code(401).send(e.response.data);
- }
- });
- NoteEndpoint.updateStatus();
-
- // DELETE Endpoint
- NoteEndpoint.deleteStatus();
- //#endregion
- done();
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts
deleted file mode 100644
index 9571e14cb..000000000
--- a/packages/backend/src/server/api/mastodon/converters.ts
+++ /dev/null
@@ -1,353 +0,0 @@
-import { Inject, Injectable } from '@nestjs/common';
-import { Entity } from 'megalodon';
-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, 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,
- SharkeyId,
-}
-
-export const escapeMFM = (text: string): string => text
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/`/g, '`')
- .replace(/\r?\n/g, '
');
-
-@Injectable()
-export class MastoConverters {
- constructor(
- @Inject(DI.config)
- private config: Config,
-
- @Inject(DI.usersRepository)
- private usersRepository: UsersRepository,
-
- @Inject(DI.userProfilesRepository)
- private userProfilesRepository: UserProfilesRepository,
-
- @Inject(DI.noteEditRepository)
- private noteEditRepository: NoteEditRepository,
-
- private mfmService: MfmService,
- private getterService: GetterService,
- private customEmojiService: CustomEmojiService,
- private idService: IdService,
- private driveFileEntityService: DriveFileEntityService,
- ) {
- }
-
- 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;
- if (u.host) {
- const info = m.find(r => r.username === u.username && r.host === u.host);
- acct = `${u.username}@${u.host}`;
- acctUrl = `https://${u.host}/@${u.username}`;
- if (info) url = info.url ?? info.uri;
- }
- return {
- id: u.id,
- username: u.username,
- acct: acct,
- url: url ?? acctUrl,
- };
- }
-
- public 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 {
- return this.getterService.getUser(id).then(p => {
- return p;
- });
- }
-
- private async encodeField(f: Entity.Field): Promise {
- 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}`;
- const acctUri = `https://${this.config.host}/users/${user.id}`;
- 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,
- uri: user.uri ?? acctUri,
- 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[] = [];
-
- 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 {
- if (!status) return null;
- return await this.convertStatus(status);
- }
-
- public async convertStatus(status: Entity.Status) {
- 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;
-
- const tags = note.tags.map(tag => {
- return {
- name: tag,
- url: `${this.config.url}/tags/${tag}`,
- } as Entity.Tag;
- });
-
- const isQuote = note.renoteId && note.text ? true : false;
-
- const renote = note.renoteId ? this.getterService.getNote(note.renoteId) : null;
-
- 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: false,
- 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);
- return result;
-}
-
-export function convertAccount(account: Entity.Account) {
- return simpleConvert(account);
-}
-export function convertAnnouncement(announcement: Entity.Announcement) {
- return simpleConvert(announcement);
-}
-export function convertAttachment(attachment: Entity.Attachment) {
- return simpleConvert(attachment);
-}
-export function convertFilter(filter: Entity.Filter) {
- return simpleConvert(filter);
-}
-export function convertList(list: Entity.List) {
- return simpleConvert(list);
-}
-export function convertFeaturedTag(tag: Entity.FeaturedTag) {
- return simpleConvert(tag);
-}
-
-export function convertNotification(notification: Entity.Notification) {
- notification.account = convertAccount(notification.account);
- if (notification.status) notification.status = convertStatus(notification.status);
- return notification;
-}
-
-export function convertPoll(poll: Entity.Poll) {
- return simpleConvert(poll);
-}
-export function convertReaction(reaction: Entity.Reaction) {
- if (reaction.accounts) {
- reaction.accounts = reaction.accounts.map(convertAccount);
- }
- return reaction;
-}
-export function convertRelationship(relationship: Entity.Relationship) {
- return simpleConvert(relationship);
-}
-
-export function convertStatus(status: Entity.Status) {
- status.account = convertAccount(status.account);
- status.media_attachments = status.media_attachments.map((attachment) =>
- convertAttachment(attachment),
- );
- if (status.poll) status.poll = convertPoll(status.poll);
- if (status.reblog) status.reblog = convertStatus(status.reblog);
-
- return status;
-}
-
-export function convertStatusSource(status: Entity.StatusSource) {
- return simpleConvert(status);
-}
-
-export function convertConversation(conversation: Entity.Conversation) {
- conversation.accounts = conversation.accounts.map(convertAccount);
- if (conversation.last_status) {
- conversation.last_status = convertStatus(conversation.last_status);
- }
-
- return conversation;
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints.ts b/packages/backend/src/server/api/mastodon/endpoints.ts
deleted file mode 100644
index 5a7582389..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { ApiAuthMastodon } from './endpoints/auth.js';
-import { ApiAccountMastodon } from './endpoints/account.js';
-import { ApiSearchMastodon } from './endpoints/search.js';
-import { ApiNotifyMastodon } from './endpoints/notifications.js';
-import { ApiFilterMastodon } from './endpoints/filter.js';
-import { ApiTimelineMastodon } from './endpoints/timeline.js';
-import { ApiStatusMastodon } from './endpoints/status.js';
-
-export {
- ApiAccountMastodon,
- ApiAuthMastodon,
- ApiSearchMastodon,
- ApiNotifyMastodon,
- ApiFilterMastodon,
- ApiTimelineMastodon,
- ApiStatusMastodon,
-};
diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts
deleted file mode 100644
index 07d9efb8c..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/account.ts
+++ /dev/null
@@ -1,270 +0,0 @@
-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: '',
- following: false,
- followed_by: false,
- delivery_following: false,
- blocking: false,
- blocked_by: false,
- muting: false,
- muting_notifications: false,
- requested: false,
- domain_blocking: false,
- showing_reblogs: false,
- endorsed: false,
- notifying: false,
- note: '',
-};
-
-@Injectable()
-export class ApiAccountMastodon {
- private request: FastifyRequest;
- private client: MegalodonInterface;
- private 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;
- }
-
- public async verifyCredentials() {
- try {
- const data = await this.client.verifyAccountCredentials();
- 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); */
- return e.response;
- }
- }
-
- public async lookup() {
- try {
- const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' });
- return this.mastoconverter.convertAccount(data.data.accounts[0]);
- } catch (e: any) {
- /* console.error(e)
- console.error(e.response.data); */
- return e.response;
- }
- }
-
- public async getRelationships(users: [string]) {
- try {
- relationshipModel.id = users.toString() || '1';
-
- if (!(users.length > 0)) {
- return [relationshipModel];
- }
-
- const reqIds = [];
- for (let i = 0; i < users.length; i++) {
- reqIds.push(users[i]);
- }
-
- const data = await this.client.getRelationships(reqIds);
- return data.data.map((relationship) => convertRelationship(relationship));
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async getStatuses() {
- try {
- 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);
- return e.response.data;
- }
- }
-
- public async getFollowers() {
- try {
- const data = await this.client.getAccountFollowers(
- (this.request.params as any).id,
- limitToInt(this.request.query as any),
- );
- 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);
- return e.response.data;
- }
- }
-
- public async getFollowing() {
- try {
- const data = await this.client.getAccountFollowing(
- (this.request.params as any).id,
- limitToInt(this.request.query as any),
- );
- 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);
- return e.response.data;
- }
- }
-
- public async addFollow() {
- try {
- const data = await this.client.followAccount( (this.request.params as any).id );
- const acct = convertRelationship(data.data);
- acct.following = true;
- return acct;
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async rmFollow() {
- try {
- const data = await this.client.unfollowAccount( (this.request.params as any).id );
- const acct = convertRelationship(data.data);
- acct.following = false;
- return acct;
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async addBlock() {
- try {
- const data = await this.client.blockAccount( (this.request.params as any).id );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async rmBlock() {
- try {
- const data = await this.client.unblockAccount( (this.request.params as any).id );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async addMute() {
- try {
- const data = await this.client.muteAccount(
- (this.request.params as any).id,
- this.request.body as any,
- );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async rmMute() {
- try {
- const data = await this.client.unmuteAccount( (this.request.params as any).id );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async getBookmarks() {
- try {
- 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);
- return e.response.data;
- }
- }
-
- public async getFavourites() {
- try {
- 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);
- return e.response.data;
- }
- }
-
- public async getMutes() {
- try {
- 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);
- return e.response.data;
- }
- }
-
- public async getBlocks() {
- try {
- 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);
- return e.response.data;
- }
- }
-
- public async acceptFollow() {
- try {
- const data = await this.client.acceptFollowRequest( (this.request.params as any).id );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-
- public async rejectFollow() {
- try {
- const data = await this.client.rejectFollowRequest( (this.request.params as any).id );
- return convertRelationship(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- return e.response.data;
- }
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts
deleted file mode 100644
index 80a5e89ca..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import type { MegalodonInterface } from 'megalodon';
-import type { FastifyRequest } from 'fastify';
-
-const readScope = [
- 'read:account',
- 'read:drive',
- 'read:blocks',
- 'read:favorites',
- 'read:following',
- 'read:messaging',
- 'read:mutes',
- 'read:notifications',
- 'read:reactions',
- 'read:pages',
- 'read:page-likes',
- 'read:user-groups',
- 'read:channels',
- 'read:gallery',
- 'read:gallery-likes',
-];
-
-const writeScope = [
- 'write:account',
- 'write:drive',
- 'write:blocks',
- 'write:favorites',
- 'write:following',
- 'write:messaging',
- 'write:mutes',
- 'write:notes',
- 'write:notifications',
- 'write:reactions',
- 'write:votes',
- 'write:pages',
- 'write:page-likes',
- 'write:user-groups',
- 'write:channels',
- 'write:gallery',
- 'write:gallery-likes',
-];
-
-export async function ApiAuthMastodon(request: FastifyRequest, client: MegalodonInterface) {
- const body: any = request.body || request.query;
- try {
- let scope = body.scopes;
- if (typeof scope === 'string') scope = scope.split(' ') || scope.split('+');
- const pushScope = new Set();
- for (const s of scope) {
- if (s.match(/^read/)) for (const r of readScope) pushScope.add(r);
- if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r);
- }
- const scopeArr = Array.from(pushScope);
-
- const red = body.redirect_uris;
- const appData = await client.registerApp(body.client_name, {
- scopes: scopeArr,
- redirect_uris: red,
- website: body.website,
- });
- const returns = {
- id: Math.floor(Math.random() * 100).toString(),
- name: appData.name,
- website: body.website,
- redirect_uri: red,
- client_id: Buffer.from(appData.url || '').toString('base64'),
- client_secret: appData.clientSecret,
- };
-
- return returns;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/filter.ts b/packages/backend/src/server/api/mastodon/endpoints/filter.ts
deleted file mode 100644
index 212c79b25..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/filter.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { convertFilter } from '../converters.js';
-import type { MegalodonInterface } from 'megalodon';
-import type { FastifyRequest } from 'fastify';
-
-export class ApiFilterMastodon {
- private request: FastifyRequest;
- private client: MegalodonInterface;
-
- constructor(request: FastifyRequest, client: MegalodonInterface) {
- this.request = request;
- this.client = client;
- }
-
- public async getFilters() {
- try {
- const data = await this.client.getFilters();
- return data.data.map((filter) => convertFilter(filter));
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async getFilter() {
- try {
- const data = await this.client.getFilter( (this.request.params as any).id );
- return convertFilter(data.data);
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async createFilter() {
- try {
- const body: any = this.request.body;
- const data = await this.client.createFilter(body.pharse, body.context, body);
- return convertFilter(data.data);
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async updateFilter() {
- try {
- const body: any = this.request.body;
- 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);
- return e.response.data;
- }
- }
-
- public async rmFilter() {
- try {
- const data = await this.client.deleteFilter( (this.request.params as any).id );
- return data.data;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts
deleted file mode 100644
index 61713b341..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Entity } from 'megalodon';
-import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from '@/const.js';
-import type { Config } from '@/config.js';
-import type { MiMeta } from '@/models/Meta.js';
-
-export async function getInstance(
- response: Entity.Instance,
- contact: Entity.Account,
- config: Config,
- meta: MiMeta,
-) {
- return {
- uri: config.url,
- title: meta.name || 'Sharkey',
- short_description:
- meta.description || 'This is a vanilla Sharkey Instance. It doesn\'t seem to have a description.',
- description:
- meta.description ||
- 'This is a vanilla Sharkey Instance. It doesn\'t seem to have a description.',
- email: response.email || '',
- version: `3.0.0 (compatible; Sharkey ${config.version})`,
- urls: response.urls,
- stats: {
- user_count: response.stats.user_count,
- status_count: response.stats.status_count,
- domain_count: response.stats.domain_count,
- },
- thumbnail: meta.backgroundImageUrl || '/static-assets/transparent.png',
- languages: meta.langs,
- registrations: !meta.disableRegistration || response.registrations,
- approval_required: meta.approvalRequiredForSignup,
- invites_enabled: response.registrations,
- configuration: {
- accounts: {
- max_featured_tags: 20,
- },
- statuses: {
- max_characters: MAX_NOTE_TEXT_LENGTH,
- max_media_attachments: 16,
- characters_reserved_per_url: response.uri.length,
- },
- media_attachments: {
- supported_mime_types: FILE_TYPE_BROWSERSAFE,
- image_size_limit: 10485760,
- image_matrix_limit: 16777216,
- video_size_limit: 41943040,
- video_frame_rate_limit: 60,
- video_matrix_limit: 2304000,
- },
- polls: {
- max_options: 10,
- max_characters_per_option: 50,
- min_expiration: 50,
- max_expiration: 2629746,
- },
- reactions: {
- max_reactions: 1,
- },
- },
- contact_account: contact,
- rules: [],
- };
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts
deleted file mode 100644
index c4628b58c..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-import { convertNotification } from '../converters.js';
-import type { MegalodonInterface, Entity } from 'megalodon';
-import type { FastifyRequest } from 'fastify';
-
-function toLimitToInt(q: any) {
- if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10);
- return q;
-}
-
-export class ApiNotifyMastodon {
- private request: FastifyRequest;
- private client: MegalodonInterface;
-
- constructor(request: FastifyRequest, client: MegalodonInterface) {
- this.request = request;
- this.client = client;
- }
-
- public async getNotifications() {
- try {
- 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);
- if (convertedn.type !== 'follow' && convertedn.type !== 'follow_request') {
- if (convertedn.type === 'reaction') convertedn.type = 'favourite';
- return convertedn;
- } else {
- return convertedn;
- }
- });
- return processed;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async getNotification() {
- try {
- 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;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async rmNotification() {
- try {
- const data = await this.client.dismissNotification( (this.request.params as any).id );
- return data.data;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async rmNotifications() {
- try {
- const data = await this.client.dismissNotifications();
- return data.data;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts
deleted file mode 100644
index 500129c90..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/search.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { MastoConverters } from '../converters.js';
-import { limitToInt } from './timeline.js';
-import type { MegalodonInterface } from 'megalodon';
-import type { FastifyRequest } from 'fastify';
-
-export class ApiSearchMastodon {
- private request: FastifyRequest;
- private client: MegalodonInterface;
- private 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;
- }
-
- public async SearchV1() {
- try {
- 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;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async SearchV2() {
- try {
- 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: 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;
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- }
-
- public async getStatusTrends() {
- try {
- 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 [];
- }
- }
-
- public async getSuggestions() {
- try {
- 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 [];
- }
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts
deleted file mode 100644
index fe77646af..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/status.ts
+++ /dev/null
@@ -1,410 +0,0 @@
-import querystring from 'querystring';
-import { emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
-import { convertAttachment, convertPoll, convertStatusSource, MastoConverters } from '../converters.js';
-import { getClient } from '../MastodonApiServerService.js';
-import { limitToInt } from './timeline.js';
-import type { Entity } from 'megalodon';
-import type { FastifyInstance } from 'fastify';
-import type { Config } from '@/config.js';
-import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-
-function normalizeQuery(data: any) {
- const str = querystring.stringify(data);
- return querystring.parse(str);
-}
-
-export class ApiStatusMastodon {
- private fastify: FastifyInstance;
- private mastoconverter: MastoConverters;
-
- constructor(fastify: FastifyInstance, mastoconverter: MastoConverters) {
- this.fastify = fastify;
- this.mastoconverter = mastoconverter;
- }
-
- public async getStatus() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(_request.is404 ? 404 : 401).send(e.response.data);
- }
- });
- }
-
- public async getStatusSource() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/source', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- 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);
- }
- });
- }
-
- public async getContext() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/context', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const query: any = _request.query;
- try {
- 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);
- } catch (e: any) {
- console.error(e);
- reply.code(_request.is404 ? 404 : 401).send(e.response.data);
- }
- });
- }
-
- public async getHistory() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/history', async (_request, reply) => {
- try {
- 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);
- }
- });
- }
-
- public async getReblogged() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/reblogged_by', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- 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);
- }
- });
- }
-
- public async getFavourites() {
- this.fastify.get<{ Params: { id: string } }>('/v1/statuses/:id/favourited_by', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- 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);
- }
- });
- }
-
- public async getMedia() {
- this.fastify.get<{ Params: { id: string } }>('/v1/media/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getMedia(_request.params.id);
- reply.send(convertAttachment(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getPoll() {
- this.fastify.get<{ Params: { id: string } }>('/v1/polls/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.getPoll(_request.params.id);
- reply.send(convertPoll(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async votePoll() {
- this.fastify.post<{ Params: { id: string } }>('/v1/polls/:id/votes', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const body: any = _request.body;
- try {
- const data = await client.votePoll(_request.params.id, body.choices);
- reply.send(convertPoll(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async postStatus() {
- this.fastify.post('/v1/statuses', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- let body: any = _request.body;
- try {
- if (
- (!body.poll && body['poll[options][]']) ||
- (!body.media_ids && body['media_ids[]'])
- ) {
- body = normalizeQuery(body);
- }
- const text = body.status ? body.status : ' ';
- const removed = text.replace(/@\S+/g, '').replace(/\s|/g, '');
- const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed);
- const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed);
- if ((body.in_reply_to_id && isDefaultEmoji) || (body.in_reply_to_id && isCustomEmoji)) {
- const a = await client.createEmojiReaction(
- body.in_reply_to_id,
- removed,
- );
- reply.send(a.data);
- }
- if (body.in_reply_to_id && removed === '/unreact') {
- try {
- const id = body.in_reply_to_id;
- const post = await client.getStatus(id);
- const react = post.data.emoji_reactions.filter((e: any) => e.me)[0].name;
- const data = await client.deleteEmojiReaction(id, react);
- reply.send(data.data);
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- }
- if (!body.media_ids) body.media_ids = undefined;
- if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
-
- const { sensitive } = body;
- body.sensitive = typeof sensitive === 'string' ? sensitive === 'true' : sensitive;
-
- if (body.poll) {
- if (
- body.poll.expires_in != null &&
- typeof body.poll.expires_in === 'string'
- ) body.poll.expires_in = parseInt(body.poll.expires_in);
- if (
- body.poll.multiple != null &&
- typeof body.poll.multiple === 'string'
- ) body.poll.multiple = body.poll.multiple === 'true';
- if (
- body.poll.hide_totals != null &&
- typeof body.poll.hide_totals === 'string'
- ) body.poll.hide_totals = body.poll.hide_totals === 'true';
- }
-
- const data = await client.postStatus(text, body);
- reply.send(await this.mastoconverter.convertStatus(data.data as Entity.Status));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async updateStatus() {
- this.fastify.put<{ Params: { id: string } }>('/v1/statuses/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const body: any = _request.body;
- try {
- if (!body.media_ids) body.media_ids = undefined;
- if (body.media_ids && !body.media_ids.length) body.media_ids = undefined;
- const data = await client.editStatus(_request.params.id, body);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(_request.is404 ? 404 : 401).send(e.response.data);
- }
- });
- }
-
- public async addFavourite() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/favourite', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = (await client.createEmojiReaction(_request.params.id, '❤')) as any;
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async rmFavourite() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/unfavourite', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.deleteEmojiReaction(_request.params.id, '❤');
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async reblogStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/reblog', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.reblogStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async unreblogStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/unreblog', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.unreblogStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async bookmarkStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/bookmark', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.bookmarkStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async unbookmarkStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/unbookmark', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.unbookmarkStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async pinStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/pin', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.pinStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async unpinStatus() {
- this.fastify.post<{ Params: { id: string } }>('/v1/statuses/:id/unpin', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.unpinStatus(_request.params.id);
- reply.send(await this.mastoconverter.convertStatus(data.data));
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async reactStatus() {
- this.fastify.post<{ Params: { id: string, name: string } }>('/v1/statuses/:id/react/:name', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- 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);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async unreactStatus() {
- this.fastify.post<{ Params: { id: string, name: string } }>('/v1/statuses/:id/unreact/:name', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- 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);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async deleteStatus() {
- this.fastify.delete<{ Params: { id: string } }>('/v1/statuses/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const data = await client.deleteStatus(_request.params.id);
- reply.send(data.data);
- } catch (e: any) {
- console.error(e);
- reply.code(401).send(e.response.data);
- }
- });
- }
-}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts
deleted file mode 100644
index f81b63b9a..000000000
--- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts
+++ /dev/null
@@ -1,268 +0,0 @@
-import { ParsedUrlQuery } from 'querystring';
-import { convertConversation, convertList, MastoConverters } from '../converters.js';
-import { getClient } from '../MastodonApiServerService.js';
-import type { Entity } from 'megalodon';
-import type { FastifyInstance } from 'fastify';
-import type { Config } from '@/config.js';
-import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
-import { UserEntityService } from '@/core/entities/UserEntityService.js';
-
-export function limitToInt(q: ParsedUrlQuery) {
- const object: any = q;
- if (q.limit) if (typeof q.limit === 'string') object.limit = parseInt(q.limit, 10);
- if (q.offset) if (typeof q.offset === 'string') object.offset = parseInt(q.offset, 10);
- return object;
-}
-
-export function argsToBools(q: ParsedUrlQuery) {
- // Values taken from https://docs.joinmastodon.org/client/intro/#boolean
- const toBoolean = (value: string) =>
- !['0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].includes(value);
-
- // Keys taken from:
- // - https://docs.joinmastodon.org/methods/accounts/#statuses
- // - https://docs.joinmastodon.org/methods/timelines/#public
- // - https://docs.joinmastodon.org/methods/timelines/#tag
- const object: any = q;
- if (q.only_media) if (typeof q.only_media === 'string') object.only_media = toBoolean(q.only_media);
- if (q.exclude_replies) if (typeof q.exclude_replies === 'string') object.exclude_replies = toBoolean(q.exclude_replies);
- if (q.exclude_reblogs) if (typeof q.exclude_reblogs === 'string') object.exclude_reblogs = toBoolean(q.exclude_reblogs);
- if (q.pinned) if (typeof q.pinned === 'string') object.pinned = toBoolean(q.pinned);
- if (q.local) if (typeof q.local === 'string') object.local = toBoolean(q.local);
- return q;
-}
-
-export class ApiTimelineMastodon {
- private fastify: FastifyInstance;
-
- constructor(fastify: FastifyInstance, config: Config, private mastoconverter: MastoConverters) {
- this.fastify = fastify;
- }
-
- public async getTL() {
- this.fastify.get('/v1/timelines/public', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const query: any = _request.query;
- const data = query.local === 'true'
- ? 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);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getHomeTl() {
- this.fastify.get('/v1/timelines/home', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const query: any = _request.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);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getTagTl() {
- this.fastify.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const query: any = _request.query;
- const params: any = _request.params;
- 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);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getListTL() {
- this.fastify.get<{ Params: { id: string } }>('/v1/timelines/list/:id', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const query: any = _request.query;
- const params: any = _request.params;
- 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);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getConversations() {
- this.fastify.get('/v1/conversations', async (_request, reply) => {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- try {
- const query: any = _request.query;
- const data = await client.getConversationTimeline(limitToInt(query));
- reply.send(data.data.map((conversation: Entity.Conversation) => convertConversation(conversation)));
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getList() {
- this.fastify.get<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const params: any = _request.params;
- const data = await client.getList(params.id);
- reply.send(convertList(data.data));
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async getLists() {
- this.fastify.get('/v1/lists', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const data = await client.getLists();
- reply.send(data.data.map((list: Entity.List) => convertList(list)));
- } catch (e: any) {
- console.error(e);
- return e.response.data;
- }
- });
- }
-
- public async getListAccounts() {
- this.fastify.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const params: any = _request.params;
- const query: any = _request.query;
- 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);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async addListAccount() {
- this.fastify.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const params: any = _request.params;
- const query: any = _request.query;
- const data = await client.addAccountsToList(params.id, query.accounts_id);
- reply.send(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async rmListAccount() {
- this.fastify.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const params: any = _request.params;
- const query: any = _request.query;
- const data = await client.deleteAccountsFromList(params.id, query.accounts_id);
- reply.send(data.data);
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async createList() {
- this.fastify.post('/v1/lists', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const body: any = _request.body;
- const data = await client.createList(body.title);
- reply.send(convertList(data.data));
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async updateList() {
- this.fastify.put<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const body: any = _request.body;
- const params: any = _request.params;
- const data = await client.updateList(params.id, body.title);
- reply.send(convertList(data.data));
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-
- public async deleteList() {
- this.fastify.delete<{ Params: { id: string } }>('/v1/lists/:id', async (_request, reply) => {
- try {
- const BASE_URL = `${_request.protocol}://${_request.hostname}`;
- const accessTokens = _request.headers.authorization;
- const client = getClient(BASE_URL, accessTokens);
- const params: any = _request.params;
- const data = await client.deleteList(params.id);
- reply.send({});
- } catch (e: any) {
- console.error(e);
- console.error(e.response.data);
- reply.code(401).send(e.response.data);
- }
- });
- }
-}
diff --git a/packages/megalodon/.npmignore b/packages/megalodon/.npmignore
deleted file mode 100644
index fd54d1deb..000000000
--- a/packages/megalodon/.npmignore
+++ /dev/null
@@ -1,3 +0,0 @@
-node_modules
-./src
-tsconfig.json
diff --git a/packages/megalodon/package.json b/packages/megalodon/package.json
deleted file mode 100644
index 01c5c1147..000000000
--- a/packages/megalodon/package.json
+++ /dev/null
@@ -1,90 +0,0 @@
-{
- "name": "megalodon",
- "version": "7.0.1",
- "description": "Mastodon API client for node.js and browser",
- "main": "./lib/src/index.js",
- "typings": "./lib/src/index.d.ts",
- "scripts": {
- "build": "tsc -p ./",
- "lint": "eslint --ext .js,.ts src",
- "doc": "typedoc --out ../docs ./src",
- "test": "NODE_ENV=test jest -u --maxWorkers=3"
- },
- "engines": {
- "node": ">=15.0.0"
- },
- "repository": {
- "type": "git",
- "url": "git+https://github.com/h3poteto/megalodon.git"
- },
- "keywords": [
- "mastodon",
- "client",
- "api",
- "streaming",
- "rest",
- "proxy"
- ],
- "author": "h3poteto",
- "license": "MIT",
- "bugs": {
- "url": "https://github.com/h3poteto/megalodon/issues"
- },
- "jest": {
- "moduleFileExtensions": [
- "ts",
- "js"
- ],
- "moduleNameMapper": {
- "^@/(.+)": "/src/$1",
- "^~/(.+)": "/$1"
- },
- "testMatch": [
- "**/test/**/*.spec.ts"
- ],
- "preset": "ts-jest/presets/default",
- "transform": {
- "^.+\\.(ts|tsx)$": [
- "ts-jest",
- {
- "tsconfig": "tsconfig.json"
- }
- ]
- },
- "testEnvironment": "node"
- },
- "homepage": "https://github.com/h3poteto/megalodon#readme",
- "dependencies": {
- "@types/core-js": "^2.5.8",
- "@types/form-data": "^2.5.0",
- "@types/jest": "^29.5.10",
- "@types/oauth": "^0.9.4",
- "@types/object-assign-deep": "^0.4.3",
- "@types/parse-link-header": "^2.0.3",
- "@types/uuid": "^9.0.7",
- "@types/ws": "^8.5.10",
- "axios": "1.6.0",
- "dayjs": "^1.11.10",
- "form-data": "^4.0.0",
- "https-proxy-agent": "^7.0.2",
- "oauth": "^0.10.0",
- "object-assign-deep": "^0.4.0",
- "parse-link-header": "^2.0.0",
- "socks-proxy-agent": "^8.0.2",
- "typescript": "5.1.6",
- "uuid": "^9.0.1",
- "ws": "8.14.2"
- },
- "devDependencies": {
- "@typescript-eslint/eslint-plugin": "^6.12.0",
- "@typescript-eslint/parser": "^6.12.0",
- "eslint": "^8.54.0",
- "eslint-config-prettier": "^9.0.0",
- "jest": "^29.7.0",
- "jest-worker": "^29.7.0",
- "lodash": "4.17.21",
- "prettier": "^3.1.0",
- "ts-jest": "^29.1.1",
- "typedoc": "^0.25.3"
- }
-}
diff --git a/packages/megalodon/src/axios.d.ts b/packages/megalodon/src/axios.d.ts
deleted file mode 100644
index 114cb06aa..000000000
--- a/packages/megalodon/src/axios.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-declare module 'axios/lib/adapters/http'
diff --git a/packages/megalodon/src/cancel.ts b/packages/megalodon/src/cancel.ts
deleted file mode 100644
index 3b905a492..000000000
--- a/packages/megalodon/src/cancel.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-export class RequestCanceledError extends Error {
- public isCancel: boolean
-
- constructor(msg: string) {
- super(msg)
- this.isCancel = true
- Object.setPrototypeOf(this, RequestCanceledError)
- }
-}
-
-export const isCancel = (value: any): boolean => {
- return value && value.isCancel
-}
diff --git a/packages/megalodon/src/converter.ts b/packages/megalodon/src/converter.ts
deleted file mode 100644
index f768fc930..000000000
--- a/packages/megalodon/src/converter.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import MisskeyAPI from "./misskey/api_client";
-
-export default MisskeyAPI.Converter;
\ No newline at end of file
diff --git a/packages/megalodon/src/default.ts b/packages/megalodon/src/default.ts
deleted file mode 100644
index 0194b3dcc..000000000
--- a/packages/megalodon/src/default.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export const NO_REDIRECT = 'urn:ietf:wg:oauth:2.0:oob'
-export const DEFAULT_SCOPE = ['read', 'write', 'follow']
-export const DEFAULT_UA = 'megalodon'
diff --git a/packages/megalodon/src/detector.ts b/packages/megalodon/src/detector.ts
deleted file mode 100644
index 31f34d72f..000000000
--- a/packages/megalodon/src/detector.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import axios, { AxiosRequestConfig } from 'axios'
-import proxyAgent, { ProxyConfig } from './proxy_config'
-import { NodeinfoError } from './megalodon'
-
-const NODEINFO_10 = 'http://nodeinfo.diaspora.software/ns/schema/1.0'
-const NODEINFO_20 = 'http://nodeinfo.diaspora.software/ns/schema/2.0'
-const NODEINFO_21 = 'http://nodeinfo.diaspora.software/ns/schema/2.1'
-
-type Links = {
- links: Array
-}
-
-type Link = {
- href: string
- rel: string
-}
-
-type Nodeinfo10 = {
- software: Software
- metadata: Metadata
-}
-
-type Nodeinfo20 = {
- software: Software
- metadata: Metadata
-}
-
-type Nodeinfo21 = {
- software: Software
- metadata: Metadata
-}
-
-type Software = {
- name: string
-}
-
-type Metadata = {
- upstream?: {
- name: string
- }
-}
-
-/**
- * Detect SNS type.
- * Now support Mastodon, Pleroma and Pixelfed. Throws an error when no known platform can be detected.
- *
- * @param url Base URL of SNS.
- * @param proxyConfig Proxy setting, or set false if don't use proxy.
- * @return SNS name.
- */
-export const detector = async (
- url: string,
- proxyConfig: ProxyConfig | false = false
-): Promise<'mastodon' | 'pleroma' | 'misskey' | 'friendica'> => {
- let options: AxiosRequestConfig = {
- timeout: 20000
- }
- if (proxyConfig) {
- options = Object.assign(options, {
- httpsAgent: proxyAgent(proxyConfig)
- })
- }
-
- const res = await axios.get(url + '/.well-known/nodeinfo', options)
- const link = res.data.links.find(l => l.rel === NODEINFO_20 || l.rel === NODEINFO_21)
- if (!link) throw new NodeinfoError('Could not find nodeinfo')
- switch (link.rel) {
- case NODEINFO_10: {
- const res = await axios.get(link.href, options)
- switch (res.data.software.name) {
- case 'pleroma':
- return 'pleroma'
- case 'akkoma':
- return 'pleroma'
- case 'mastodon':
- return 'mastodon'
- case "wildebeest":
- return "mastodon"
- case 'misskey':
- return 'misskey'
- case 'friendica':
- return 'friendica'
- default:
- if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
- return 'mastodon'
- }
- throw new NodeinfoError('Unknown SNS')
- }
- }
- case NODEINFO_20: {
- const res = await axios.get(link.href, options)
- switch (res.data.software.name) {
- case 'pleroma':
- return 'pleroma'
- case 'akkoma':
- return 'pleroma'
- case 'mastodon':
- return 'mastodon'
- case "wildebeest":
- return "mastodon"
- case 'misskey':
- return 'misskey'
- case 'friendica':
- return 'friendica'
- default:
- if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
- return 'mastodon'
- }
- throw new NodeinfoError('Unknown SNS')
- }
- }
- case NODEINFO_21: {
- const res = await axios.get(link.href, options)
- switch (res.data.software.name) {
- case 'pleroma':
- return 'pleroma'
- case 'akkoma':
- return 'pleroma'
- case 'mastodon':
- return 'mastodon'
- case "wildebeest":
- return "mastodon"
- case 'misskey':
- return 'misskey'
- case 'friendica':
- return 'friendica'
- default:
- if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
- return 'mastodon'
- }
- throw new NodeinfoError('Unknown SNS')
- }
- }
- default:
- throw new NodeinfoError('Could not find nodeinfo')
- }
-}
diff --git a/packages/megalodon/src/entities/account.ts b/packages/megalodon/src/entities/account.ts
deleted file mode 100644
index e2219dd04..000000000
--- a/packages/megalodon/src/entities/account.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-///
-///
-///
-///
-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
- created_at: string
- followers_count: number
- following_count: number
- statuses_count: number
- note: string
- url: string
- avatar: string
- avatar_static: string
- header: string
- header_static: string
- emojis: Array
- moved: Account | null
- fields: Array
- bot: boolean | null
- source?: Source
- role?: Role
- mute_expires_at?: string
- }
-}
diff --git a/packages/megalodon/src/entities/activity.ts b/packages/megalodon/src/entities/activity.ts
deleted file mode 100644
index 2494916a9..000000000
--- a/packages/megalodon/src/entities/activity.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Entity {
- export type Activity = {
- week: string
- statuses: string
- logins: string
- registrations: string
- }
-}
diff --git a/packages/megalodon/src/entities/announcement.ts b/packages/megalodon/src/entities/announcement.ts
deleted file mode 100644
index 0db9c23bb..000000000
--- a/packages/megalodon/src/entities/announcement.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-///
-
-namespace Entity {
- export type Announcement = {
- id: string
- content: string
- starts_at: string | null
- ends_at: string | null
- published: boolean
- all_day: boolean
- published_at: string
- updated_at: string | null
- read: boolean | null
- mentions: Array
- statuses: Array
- tags: Array
- emojis: Array
- reactions: Array
- }
-
- export type AnnouncementAccount = {
- id: string
- username: string
- url: string
- acct: string
- }
-
- export type AnnouncementStatus = {
- id: string
- url: string
- }
-
- export type AnnouncementReaction = {
- name: string
- count: number
- me: boolean | null
- url: string | null
- static_url: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/application.ts b/packages/megalodon/src/entities/application.ts
deleted file mode 100644
index 3af64fcf9..000000000
--- a/packages/megalodon/src/entities/application.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Entity {
- export type Application = {
- name: string
- website?: string | null
- vapid_key?: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/async_attachment.ts b/packages/megalodon/src/entities/async_attachment.ts
deleted file mode 100644
index b383f90c5..000000000
--- a/packages/megalodon/src/entities/async_attachment.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-///
-namespace Entity {
- export type AsyncAttachment = {
- id: string
- type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio'
- url: string | null
- remote_url: string | null
- preview_url: string
- text_url: string | null
- meta: Meta | null
- description: string | null
- blurhash: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/attachment.ts b/packages/megalodon/src/entities/attachment.ts
deleted file mode 100644
index aab1deade..000000000
--- a/packages/megalodon/src/entities/attachment.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace Entity {
- export type Sub = {
- // For Image, Gifv, and Video
- width?: number
- height?: number
- size?: string
- aspect?: number
-
- // For Gifv and Video
- frame_rate?: string
-
- // For Audio, Gifv, and Video
- duration?: number
- bitrate?: number
- }
-
- export type Focus = {
- x: number
- y: number
- }
-
- export type Meta = {
- original?: Sub
- small?: Sub
- focus?: Focus
- length?: string
- duration?: number
- fps?: number
- size?: string
- width?: number
- height?: number
- aspect?: number
- audio_encode?: string
- audio_bitrate?: string
- audio_channel?: string
- }
-
- export type Attachment = {
- id: string
- type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio'
- url: string
- remote_url: string | null
- preview_url: string | null
- text_url: string | null
- meta: Meta | null
- description: string | null
- blurhash: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/card.ts b/packages/megalodon/src/entities/card.ts
deleted file mode 100644
index 1ef6f5e4d..000000000
--- a/packages/megalodon/src/entities/card.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace Entity {
- export type Card = {
- url: string
- title: string
- description: string
- type: 'link' | 'photo' | 'video' | 'rich'
- image: string | null
- author_name: string | null
- author_url: string | null
- provider_name: string | null
- provider_url: string | null
- html: string | null
- width: number | null
- height: number | null
- embed_url: string | null
- blurhash: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/context.ts b/packages/megalodon/src/entities/context.ts
deleted file mode 100644
index 3f2eda58f..000000000
--- a/packages/megalodon/src/entities/context.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-
-namespace Entity {
- export type Context = {
- ancestors: Array
- descendants: Array
- }
-}
diff --git a/packages/megalodon/src/entities/conversation.ts b/packages/megalodon/src/entities/conversation.ts
deleted file mode 100644
index cdadf1e0f..000000000
--- a/packages/megalodon/src/entities/conversation.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-///
-///
-
-namespace Entity {
- export type Conversation = {
- id: string
- accounts: Array
- last_status: Status | null
- unread: boolean
- }
-}
diff --git a/packages/megalodon/src/entities/emoji.ts b/packages/megalodon/src/entities/emoji.ts
deleted file mode 100644
index 546ef818f..000000000
--- a/packages/megalodon/src/entities/emoji.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Entity {
- export type Emoji = {
- shortcode: string
- static_url: string
- url: string
- visible_in_picker: boolean
- category?: string
- }
-}
diff --git a/packages/megalodon/src/entities/featured_tag.ts b/packages/megalodon/src/entities/featured_tag.ts
deleted file mode 100644
index 06ae6d7a9..000000000
--- a/packages/megalodon/src/entities/featured_tag.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Entity {
- export type FeaturedTag = {
- id: string
- name: string
- statuses_count: number
- last_status_at: string
- }
-}
diff --git a/packages/megalodon/src/entities/field.ts b/packages/megalodon/src/entities/field.ts
deleted file mode 100644
index 71ce3c0cf..000000000
--- a/packages/megalodon/src/entities/field.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Entity {
- export type Field = {
- name: string
- value: string
- verified_at?: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/filter.ts b/packages/megalodon/src/entities/filter.ts
deleted file mode 100644
index ffbacb728..000000000
--- a/packages/megalodon/src/entities/filter.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Entity {
- export type Filter = {
- id: string
- phrase: string
- context: Array
- expires_at: string | null
- irreversible: boolean
- whole_word: boolean
- }
-
- export type FilterContext = string
-}
diff --git a/packages/megalodon/src/entities/follow_request.ts b/packages/megalodon/src/entities/follow_request.ts
deleted file mode 100644
index 84ea4d02c..000000000
--- a/packages/megalodon/src/entities/follow_request.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-///
-///
-
-namespace Entity {
- export type FollowRequest = {
- id: number
- username: string
- acct: string
- display_name: string
- locked: boolean
- bot: boolean
- discoverable?: boolean
- group: boolean
- created_at: string
- note: string
- url: string
- avatar: string
- avatar_static: string
- header: string
- header_static: string
- followers_count: number
- following_count: number
- statuses_count: number
- emojis: Array
- fields: Array
- }
-}
diff --git a/packages/megalodon/src/entities/history.ts b/packages/megalodon/src/entities/history.ts
deleted file mode 100644
index 070969426..000000000
--- a/packages/megalodon/src/entities/history.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Entity {
- export type History = {
- day: string
- uses: number
- accounts: number
- }
-}
diff --git a/packages/megalodon/src/entities/identity_proof.ts b/packages/megalodon/src/entities/identity_proof.ts
deleted file mode 100644
index ff857addb..000000000
--- a/packages/megalodon/src/entities/identity_proof.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Entity {
- export type IdentityProof = {
- provider: string
- provider_username: string
- updated_at: string
- proof_url: string
- profile_url: string
- }
-}
diff --git a/packages/megalodon/src/entities/instance.ts b/packages/megalodon/src/entities/instance.ts
deleted file mode 100644
index 8f4808be8..000000000
--- a/packages/megalodon/src/entities/instance.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-///
-///
-///
-
-namespace Entity {
- export type Instance = {
- uri: string
- title: string
- description: string
- email: string
- version: string
- thumbnail: string | null
- urls: URLs | null
- stats: Stats
- languages: Array
- registrations: boolean
- approval_required: boolean
- invites_enabled?: boolean
- configuration: {
- statuses: {
- max_characters: number
- max_media_attachments?: number
- characters_reserved_per_url?: number
- }
- polls?: {
- max_options: number
- max_characters_per_option: number
- min_expiration: number
- max_expiration: number
- }
- }
- contact_account?: Account
- rules?: Array
- }
-
- export type InstanceRule = {
- id: string
- text: string
- }
-}
diff --git a/packages/megalodon/src/entities/list.ts b/packages/megalodon/src/entities/list.ts
deleted file mode 100644
index 281f02b11..000000000
--- a/packages/megalodon/src/entities/list.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Entity {
- export type List = {
- id: string
- title: string
- replies_policy?: RepliesPolicy | null
- exclusive?: RepliesPolicy | null
- }
-
- export type RepliesPolicy = 'followed' | 'list' | 'none'
-}
diff --git a/packages/megalodon/src/entities/marker.ts b/packages/megalodon/src/entities/marker.ts
deleted file mode 100644
index 33cb98a10..000000000
--- a/packages/megalodon/src/entities/marker.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Entity {
- export type Marker = {
- home?: {
- last_read_id: string
- version: number
- updated_at: string
- }
- notifications?: {
- last_read_id: string
- version: number
- updated_at: string
- unread_count?: number
- }
- }
-}
diff --git a/packages/megalodon/src/entities/mention.ts b/packages/megalodon/src/entities/mention.ts
deleted file mode 100644
index 046912971..000000000
--- a/packages/megalodon/src/entities/mention.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Entity {
- export type Mention = {
- id: string
- username: string
- url: string
- acct: string
- }
-}
diff --git a/packages/megalodon/src/entities/notification.ts b/packages/megalodon/src/entities/notification.ts
deleted file mode 100644
index 653d235d9..000000000
--- a/packages/megalodon/src/entities/notification.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-///
-///
-
-namespace Entity {
- export type Notification = {
- account: Account
- created_at: string
- id: string
- status?: Status
- emoji?: string
- type: NotificationType
- target?: Account
- }
-
- export type NotificationType = string
-}
diff --git a/packages/megalodon/src/entities/poll.ts b/packages/megalodon/src/entities/poll.ts
deleted file mode 100644
index 42e7f69ac..000000000
--- a/packages/megalodon/src/entities/poll.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-///
-
-namespace Entity {
- export type Poll = {
- id: string
- expires_at: string | null
- expired: boolean
- multiple: boolean
- votes_count: number
- options: Array
- voted: boolean
- emojis?: []
- own_votes?: Array
- }
-}
diff --git a/packages/megalodon/src/entities/poll_option.ts b/packages/megalodon/src/entities/poll_option.ts
deleted file mode 100644
index ae4c63849..000000000
--- a/packages/megalodon/src/entities/poll_option.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Entity {
- export type PollOption = {
- title: string
- votes_count: number | null
- }
-}
diff --git a/packages/megalodon/src/entities/preferences.ts b/packages/megalodon/src/entities/preferences.ts
deleted file mode 100644
index cb5797c4c..000000000
--- a/packages/megalodon/src/entities/preferences.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Entity {
- export type Preferences = {
- 'posting:default:visibility': 'public' | 'unlisted' | 'private' | 'direct'
- 'posting:default:sensitive': boolean
- 'posting:default:language': string | null
- 'reading:expand:media': 'default' | 'show_all' | 'hide_all'
- 'reading:expand:spoilers': boolean
- }
-}
diff --git a/packages/megalodon/src/entities/push_subscription.ts b/packages/megalodon/src/entities/push_subscription.ts
deleted file mode 100644
index fe7464e8e..000000000
--- a/packages/megalodon/src/entities/push_subscription.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Entity {
- export type Alerts = {
- follow: boolean
- favourite: boolean
- mention: boolean
- reblog: boolean
- poll: boolean
- }
-
- export type PushSubscription = {
- id: string
- endpoint: string
- server_key: string
- alerts: Alerts
- }
-}
diff --git a/packages/megalodon/src/entities/reaction.ts b/packages/megalodon/src/entities/reaction.ts
deleted file mode 100644
index 8c626f9e8..000000000
--- a/packages/megalodon/src/entities/reaction.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-///
-
-namespace Entity {
- export type Reaction = {
- count: number
- me: boolean
- name: string
- accounts?: Array
- }
-}
diff --git a/packages/megalodon/src/entities/relationship.ts b/packages/megalodon/src/entities/relationship.ts
deleted file mode 100644
index 283a1158c..000000000
--- a/packages/megalodon/src/entities/relationship.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Entity {
- export type Relationship = {
- id: string
- following: boolean
- followed_by: boolean
- blocking: boolean
- blocked_by: boolean
- muting: boolean
- muting_notifications: boolean
- requested: boolean
- domain_blocking: boolean
- showing_reblogs: boolean
- endorsed: boolean
- notifying: boolean
- note: string | null
- }
-}
diff --git a/packages/megalodon/src/entities/report.ts b/packages/megalodon/src/entities/report.ts
deleted file mode 100644
index 353886a34..000000000
--- a/packages/megalodon/src/entities/report.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-///
-
-namespace Entity {
- export type Report = {
- id: string
- action_taken: boolean
- action_taken_at: string | null
- status_ids: Array | null
- rule_ids: Array | null
- // These parameters don't exist in Pleroma
- category: Category | null
- comment: string | null
- forwarded: boolean | null
- target_account?: Account | null
- }
-
- export type Category = 'spam' | 'violation' | 'other'
-}
diff --git a/packages/megalodon/src/entities/results.ts b/packages/megalodon/src/entities/results.ts
deleted file mode 100644
index fe168de67..000000000
--- a/packages/megalodon/src/entities/results.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-///
-///
-///
-
-namespace Entity {
- export type Results = {
- accounts: Array
- statuses: Array
- hashtags: Array
- }
-}
diff --git a/packages/megalodon/src/entities/role.ts b/packages/megalodon/src/entities/role.ts
deleted file mode 100644
index caaae9ea1..000000000
--- a/packages/megalodon/src/entities/role.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace Entity {
- export type Role = {
- name: string
- }
-}
diff --git a/packages/megalodon/src/entities/scheduled_status.ts b/packages/megalodon/src/entities/scheduled_status.ts
deleted file mode 100644
index 561a5b9f2..000000000
--- a/packages/megalodon/src/entities/scheduled_status.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-///
-///
-namespace Entity {
- export type ScheduledStatus = {
- id: string
- scheduled_at: string
- params: StatusParams
- media_attachments: Array | null
- }
-}
diff --git a/packages/megalodon/src/entities/source.ts b/packages/megalodon/src/entities/source.ts
deleted file mode 100644
index d87cf55d8..000000000
--- a/packages/megalodon/src/entities/source.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-///
-namespace Entity {
- export type Source = {
- privacy: string | null
- sensitive: boolean | null
- language: string | null
- note: string
- fields: Array
- }
-}
diff --git a/packages/megalodon/src/entities/stats.ts b/packages/megalodon/src/entities/stats.ts
deleted file mode 100644
index 76f0bad34..000000000
--- a/packages/megalodon/src/entities/stats.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Entity {
- export type Stats = {
- user_count: number
- status_count: number
- domain_count: number
- }
-}
diff --git a/packages/megalodon/src/entities/status.ts b/packages/megalodon/src/entities/status.ts
deleted file mode 100644
index da36a0471..000000000
--- a/packages/megalodon/src/entities/status.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-///
-///
-///
-///
-///
-///
-///
-///
-
-namespace Entity {
- export type Status = {
- id: string
- uri: string
- url: string
- account: Account
- in_reply_to_id: string | null
- in_reply_to_account_id: string | null
- reblog: Status | null
- content: string
- plain_content?: string | null
- created_at: string
- emojis: Emoji[]
- replies_count: number
- reblogs_count: number
- favourites_count: number
- reblogged: boolean | null
- favourited: boolean | null
- muted: boolean | null
- sensitive: boolean
- spoiler_text: string
- visibility: 'public' | 'unlisted' | 'private' | 'direct'
- media_attachments: Array
- mentions: Array
- tags: Array
- card: Card | null
- poll: Poll | null
- application: Application | null
- language: string | null
- pinned: boolean | null
- emoji_reactions: Array
- quote: Status | boolean
- bookmarked: boolean
- }
-
- export type StatusTag = {
- name: string
- url: string
- }
-}
diff --git a/packages/megalodon/src/entities/status_edit.ts b/packages/megalodon/src/entities/status_edit.ts
deleted file mode 100644
index 4040b4ff9..000000000
--- a/packages/megalodon/src/entities/status_edit.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-///
-///
-///
-///
-///
-///
-///
-///
-///
-
-namespace Entity {
- export type StatusEdit = {
- account: Account;
- content: string;
- plain_content: string | null;
- created_at: string;
- emojis: Emoji[];
- sensitive: boolean;
- spoiler_text: string;
- media_attachments: Array;
- poll: Poll | null;
- };
-}
diff --git a/packages/megalodon/src/entities/status_params.ts b/packages/megalodon/src/entities/status_params.ts
deleted file mode 100644
index 82d789086..000000000
--- a/packages/megalodon/src/entities/status_params.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Entity {
- export type StatusParams = {
- text: string
- in_reply_to_id: string | null
- media_ids: Array | null
- sensitive: boolean | null
- spoiler_text: string | null
- visibility: 'public' | 'unlisted' | 'private' | 'direct' | null
- scheduled_at: string | null
- application_id: number | null
- }
-}
diff --git a/packages/megalodon/src/entities/status_source.ts b/packages/megalodon/src/entities/status_source.ts
deleted file mode 100644
index 0de7030ed..000000000
--- a/packages/megalodon/src/entities/status_source.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Entity {
- export type StatusSource = {
- id: string
- text: string
- spoiler_text: string
- }
-}
diff --git a/packages/megalodon/src/entities/tag.ts b/packages/megalodon/src/entities/tag.ts
deleted file mode 100644
index ddc5fe92b..000000000
--- a/packages/megalodon/src/entities/tag.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-///
-
-namespace Entity {
- export type Tag = {
- name: string
- url: string
- history: Array
- following?: boolean
- }
-}
diff --git a/packages/megalodon/src/entities/token.ts b/packages/megalodon/src/entities/token.ts
deleted file mode 100644
index 6fa28e39b..000000000
--- a/packages/megalodon/src/entities/token.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Entity {
- export type Token = {
- access_token: string
- token_type: string
- scope: string
- created_at: number
- }
-}
diff --git a/packages/megalodon/src/entities/urls.ts b/packages/megalodon/src/entities/urls.ts
deleted file mode 100644
index 4a980d589..000000000
--- a/packages/megalodon/src/entities/urls.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace Entity {
- export type URLs = {
- streaming_api: string
- }
-}
diff --git a/packages/megalodon/src/entity.ts b/packages/megalodon/src/entity.ts
deleted file mode 100644
index 387981cec..000000000
--- a/packages/megalodon/src/entity.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-
-export default Entity
diff --git a/packages/megalodon/src/filter_context.ts b/packages/megalodon/src/filter_context.ts
deleted file mode 100644
index c69be98cd..000000000
--- a/packages/megalodon/src/filter_context.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import Entity from './entity'
-
-namespace FilterContext {
- export const Home: Entity.FilterContext = 'home'
- export const Notifications: Entity.FilterContext = 'notifications'
- export const Public: Entity.FilterContext = 'public'
- export const Thread: Entity.FilterContext = 'thread'
- export const Account: Entity.FilterContext = 'account'
-}
-
-export default FilterContext
diff --git a/packages/megalodon/src/friendica.ts b/packages/megalodon/src/friendica.ts
deleted file mode 100644
index c5ee9d59c..000000000
--- a/packages/megalodon/src/friendica.ts
+++ /dev/null
@@ -1,2868 +0,0 @@
-import { OAuth2 } from 'oauth'
-import FormData from 'form-data'
-import parseLinkHeader from 'parse-link-header'
-
-import FriendicaAPI from './friendica/api_client'
-import WebSocket from './friendica/web_socket'
-import { MegalodonInterface, NoImplementedError } from './megalodon'
-import Response from './response'
-import Entity from './entity'
-import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default'
-import { ProxyConfig } from './proxy_config'
-import OAuth from './oauth'
-import { UnknownNotificationTypeError } from './notification'
-
-export default class Friendica implements MegalodonInterface {
- public client: FriendicaAPI.Interface
- public baseUrl: string
-
- /**
- * @param baseUrl hostname or base URL
- * @param accessToken access token from OAuth2 authorization
- * @param userAgent UserAgent is specified in header on request.
- * @param proxyConfig Proxy setting, or set false if don't use proxy.
- */
- constructor(
- baseUrl: string,
- accessToken: string | null = null,
- userAgent: string | null = DEFAULT_UA,
- proxyConfig: ProxyConfig | false = false
- ) {
- let token = ''
- if (accessToken) {
- token = accessToken
- }
- let agent: string = DEFAULT_UA
- if (userAgent) {
- agent = userAgent
- }
- this.client = new FriendicaAPI.Client(baseUrl, token, agent, proxyConfig)
- this.baseUrl = baseUrl
- }
-
- public cancel(): void {
- return this.client.cancel()
- }
-
- /**
- * First, call createApp to get client_id and client_secret.
- * Next, call generateAuthUrl to get authorization url.
- * @param client_name Form Data, which is sent to /api/v1/apps
- * @param options Form Data, which is sent to /api/v1/apps. and properties should be **snake_case**
- */
- public async registerApp(
- client_name: string,
- options: Partial<{ scopes: Array; redirect_uris: string; website: string }>
- ): Promise {
- const scopes = options.scopes || DEFAULT_SCOPE
- return this.createApp(client_name, options).then(async appData => {
- return this.generateAuthUrl(appData.client_id, appData.client_secret, {
- scope: scopes,
- redirect_uri: appData.redirect_uri
- }).then(url => {
- appData.url = url
- return appData
- })
- })
- }
-
- /**
- * Call /api/v1/apps
- *
- * Create an application.
- * @param client_name your application's name
- * @param options Form Data
- */
- public async createApp(
- client_name: string,
- options: Partial<{ scopes: Array; redirect_uris: string; website: string }>
- ): Promise {
- const scopes = options.scopes || DEFAULT_SCOPE
- const redirect_uris = options.redirect_uris || NO_REDIRECT
-
- const params: {
- client_name: string
- redirect_uris: string
- scopes: string
- website?: string
- } = {
- client_name: client_name,
- redirect_uris: redirect_uris,
- scopes: scopes.join(' ')
- }
- if (options.website) params.website = options.website
-
- return this.client
- .post('/api/v1/apps', params)
- .then((res: Response) => OAuth.AppData.from(res.data))
- }
-
- /**
- * Generate authorization url using OAuth2.
- *
- * @param clientId your OAuth app's client ID
- * @param clientSecret your OAuth app's client Secret
- * @param options as property, redirect_uri and scope are available, and must be the same as when you register your app
- */
- public generateAuthUrl(
- clientId: string,
- clientSecret: string,
- options: Partial<{ scope: Array; redirect_uri: string }>
- ): Promise {
- const scope = options.scope || DEFAULT_SCOPE
- const redirect_uri = options.redirect_uri || NO_REDIRECT
- return new Promise(resolve => {
- const oauth = new OAuth2(clientId, clientSecret, this.baseUrl, undefined, '/oauth/token')
- const url = oauth.getAuthorizeUrl({
- redirect_uri: redirect_uri,
- response_type: 'code',
- client_id: clientId,
- scope: scope.join(' ')
- })
- resolve(url)
- })
- }
-
- // ======================================
- // apps
- // ======================================
- /**
- * GET /api/v1/apps/verify_credentials
- *
- * @return An Application
- */
- public verifyAppCredentials(): Promise> {
- return this.client.get('/api/v1/apps/verify_credentials')
- }
-
- // ======================================
- // apps/oauth
- // ======================================
- /**
- * POST /oauth/token
- *
- * Fetch OAuth access token.
- * Get an access token based client_id and client_secret and authorization code.
- * @param client_id will be generated by #createApp or #registerApp
- * @param client_secret will be generated by #createApp or #registerApp
- * @param code will be generated by the link of #generateAuthUrl or #registerApp
- * @param redirect_uri must be the same uri as the time when you register your OAuth application
- */
- public async fetchAccessToken(
- client_id: string | null,
- client_secret: string,
- code: string,
- redirect_uri: string = NO_REDIRECT
- ): Promise {
- if (!client_id) {
- throw new Error('client_id is required')
- }
- return this.client
- .post('/oauth/token', {
- client_id,
- client_secret,
- code,
- redirect_uri,
- grant_type: 'authorization_code'
- })
- .then((res: Response) => OAuth.TokenData.from(res.data))
- }
-
- /**
- * POST /oauth/token
- *
- * Refresh OAuth access token.
- * Send refresh token and get new access token.
- * @param client_id will be generated by #createApp or #registerApp
- * @param client_secret will be generated by #createApp or #registerApp
- * @param refresh_token will be get #fetchAccessToken
- */
- public async refreshToken(client_id: string, client_secret: string, refresh_token: string): Promise {
- return this.client
- .post('/oauth/token', {
- client_id,
- client_secret,
- refresh_token,
- grant_type: 'refresh_token'
- })
- .then((res: Response) => OAuth.TokenData.from(res.data))
- }
-
- /**
- * POST /oauth/revoke
- *
- * Revoke an OAuth token.
- * @param client_id will be generated by #createApp or #registerApp
- * @param client_secret will be generated by #createApp or #registerApp
- * @param token will be get #fetchAccessToken
- */
- public async revokeToken(client_id: string, client_secret: string, token: string): Promise>> {
- return this.client.post>('/oauth/revoke', {
- client_id,
- client_secret,
- token
- })
- }
-
- // ======================================
- // accounts
- // ======================================
- public async registerAccount(
- _username: string,
- _email: string,
- _password: string,
- _agreement: boolean,
- _locale: string,
- _reason?: string | null
- ): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * GET /api/v1/accounts/verify_credentials
- *
- * @return Account.
- */
- public async verifyAccountCredentials(): Promise> {
- return this.client.get('/api/v1/accounts/verify_credentials').then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.account(res.data)
- })
- })
- }
-
- public async updateCredentials(_options?: {
- discoverable?: boolean
- bot?: boolean
- display_name?: string
- note?: string
- avatar?: string
- header?: string
- locked?: boolean
- source?: {
- privacy?: string
- sensitive?: boolean
- language?: string
- }
- fields_attributes?: Array<{ name: string; value: string }>
- }): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * GET /api/v1/accounts/:id
- *
- * @param id The account ID.
- * @return An account.
- */
- public async getAccount(id: string): Promise> {
- return this.client.get(`/api/v1/accounts/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.account(res.data)
- })
- })
- }
-
- /**
- * GET /api/v1/accounts/:id/statuses
- *
- * @param id The account ID.
-
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID but starting with most recent.
- * @param options.min_id Return results newer than ID.
- * @param options.pinned Return statuses which include pinned statuses.
- * @param options.exclude_replies Return statuses which exclude replies.
- * @param options.exclude_reblogs Return statuses which exclude reblogs.
- * @param options.only_media Show only statuses with media attached? Defaults to false.
- * @return Account's statuses.
- */
- public async getAccountStatuses(
- id: string,
- options?: {
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- pinned?: boolean
- exclude_replies?: boolean
- exclude_reblogs?: boolean
- only_media: boolean
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.pinned) {
- params = Object.assign(params, {
- pinned: options.pinned
- })
- }
- if (options.exclude_replies) {
- params = Object.assign(params, {
- exclude_replies: options.exclude_replies
- })
- }
- if (options.exclude_reblogs) {
- params = Object.assign(params, {
- exclude_reblogs: options.exclude_reblogs
- })
- }
- if (options.only_media) {
- params = Object.assign(params, {
- only_media: options.only_media
- })
- }
- }
-
- return this.client.get>(`/api/v1/accounts/${id}/statuses`, params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/follow
- *
- * @param id Target account ID.
- * @return Relationship.
- */
- public async subscribeAccount(id: string): Promise> {
- const params = {
- notify: true
- }
- return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/follow
- *
- * @param id Target account ID.
- * @return Relationship.
- */
- public async unsubscribeAccount(id: string): Promise> {
- const params = {
- notify: false
- }
- return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- public getAccountFavourites(
- _id: string,
- _options?: {
- limit?: number
- max_id?: string
- since_id?: string
- }
- ): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * GET /api/v1/accounts/:id/followers
- *
- * @param id The account ID.
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @return The array of accounts.
- */
- public async getAccountFollowers(
- id: string,
- options?: {
- limit?: number
- max_id?: string
- since_id?: string
- get_all?: boolean
- sleep_ms?: number
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.urlToAccounts(`/api/v1/accounts/${id}/followers`, params, options?.get_all || false, options?.sleep_ms || 0)
- }
-
- /**
- * GET /api/v1/accounts/:id/following
- *
- * @param id The account ID.
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @return The array of accounts.
- */
- public async getAccountFollowing(
- id: string,
- options?: {
- limit?: number
- max_id?: string
- since_id?: string
- get_all?: boolean
- sleep_ms?: number
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.urlToAccounts(`/api/v1/accounts/${id}/following`, params, options?.get_all || false, options?.sleep_ms || 0)
- }
-
- /** Helper function to optionally follow Link headers as pagination */
- private async urlToAccounts(url: string, params: Record, get_all: boolean, sleep_ms: number) {
- const res = await this.client.get>(url, params)
- let converted = Object.assign({}, res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- if (get_all && converted.headers.link) {
- let parsed = parseLinkHeader(converted.headers.link)
- while (parsed?.next) {
- const nextRes = await this.client.get>(parsed?.next.url, undefined, undefined, true)
- converted = Object.assign({}, converted, {
- data: [...converted.data, ...nextRes.data.map(a => FriendicaAPI.Converter.account(a))]
- })
- parsed = parseLinkHeader(nextRes.headers.link)
- if (sleep_ms) {
- await new Promise(converted => setTimeout(converted, sleep_ms))
- }
- }
- }
- return converted
- }
-
- /**
- * GET /api/v1/accounts/:id/lists
- *
- * @param id The account ID.
- * @return The array of lists.
- */
- public async getAccountLists(id: string): Promise>> {
- return this.client.get>(`/api/v1/accounts/${id}/lists`).then(res => {
- return Object.assign(res, {
- data: res.data.map(l => FriendicaAPI.Converter.list(l))
- })
- })
- }
-
- /**
- * GET /api/v1/accounts/:id/identity_proofs
- *
- * @param id The account ID.
- * @return Array of IdentityProof
- */
- public async getIdentityProof(id: string): Promise>> {
- return this.client.get>(`/api/v1/accounts/${id}/identity_proofs`).then(res => {
- return Object.assign(res, {
- data: res.data.map(i => FriendicaAPI.Converter.identity_proof(i))
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/follow
- *
- * @param id The account ID.
- * @param reblog Receive this account's reblogs in home timeline.
- * @return Relationship
- */
- public async followAccount(id: string, options?: { reblog?: boolean }): Promise> {
- let params = {}
- if (options) {
- if (options.reblog !== undefined) {
- params = Object.assign(params, {
- reblog: options.reblog
- })
- }
- }
- return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/unfollow
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async unfollowAccount(id: string): Promise> {
- return this.client.post(`/api/v1/accounts/${id}/unfollow`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/block
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async blockAccount(id: string): Promise> {
- return this.client.post(`/api/v1/accounts/${id}/block`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/unblock
- *
- * @param id The account ID.
- * @return RElationship
- */
- public async unblockAccount(id: string): Promise> {
- return this.client.post(`/api/v1/accounts/${id}/unblock`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/mute
- *
- * @param id The account ID.
- * @param notifications Mute notifications in addition to statuses.
- * @return Relationship
- */
- public async muteAccount(id: string, notifications = true): Promise> {
- return this.client
- .post(`/api/v1/accounts/${id}/mute`, {
- notifications: notifications
- })
- .then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/unmute
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async unmuteAccount(id: string): Promise> {
- return this.client.post(`/api/v1/accounts/${id}/unmute`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/pin
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async pinAccount(_id: string): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * POST /api/v1/accounts/:id/unpin
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async unpinAccount(_id: string): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * GET /api/v1/accounts/relationships
- *
- * @param id The account ID.
- * @return Relationship
- */
- public async getRelationship(id: string): Promise> {
- return this.client
- .get>('/api/v1/accounts/relationships', {
- id: [id]
- })
- .then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data[0])
- })
- })
- }
-
- /**
- * Get multiple relationships in one method
- *
- * @param ids Array of account IDs.
- * @return Array of Relationship.
- */
- public async getRelationships(ids: Array): Promise>> {
- return this.client
- .get>('/api/v1/accounts/relationships', {
- id: ids
- })
- .then(res => {
- return Object.assign(res, {
- data: res.data.map(r => FriendicaAPI.Converter.relationship(r))
- })
- })
- }
-
- /**
- * GET /api/v1/accounts/search
- *
- * @param q Search query.
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @return The array of accounts.
- */
- public async searchAccount(
- q: string,
- options?: {
- following?: boolean
- resolve?: boolean
- limit?: number
- max_id?: string
- since_id?: string
- }
- ): Promise>> {
- let params = { q: q }
- if (options) {
- if (options.following !== undefined && options.following !== null) {
- params = Object.assign(params, {
- following: options.following
- })
- }
- if (options.resolve !== undefined && options.resolve !== null) {
- params = Object.assign(params, {
- resolve: options.resolve
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/accounts/search', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- // ======================================
- // accounts/bookmarks
- // ======================================
- /**
- * GET /api/v1/bookmarks
- *
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getBookmarks(options?: {
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }): Promise>> {
- let params = {}
- if (options) {
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- }
- return this.client.get>('/api/v1/bookmarks', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- // ======================================
- // accounts/favourites
- // ======================================
- /**
- * GET /api/v1/favourites
- *
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
- let params = {}
- if (options) {
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/favourites', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- // ======================================
- // accounts/mutes
- // ======================================
- /**
- * GET /api/v1/mutes
- *
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of accounts.
- */
- public async getMutes(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
- let params = {}
- if (options) {
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/mutes', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- // ======================================
- // accounts/blocks
- // ======================================
- /**
- * GET /api/v1/blocks
- *
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of accounts.
- */
- public async getBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
- let params = {}
- if (options) {
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/blocks', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- // ======================================
- // accounts/domain_blocks
- // ======================================
- public async getDomainBlocks(_options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public blockDomain(_domain: string): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public unblockDomain(_domain: string): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // accounts/filters
- // ======================================
- /**
- * GET /api/v1/filters
- *
- * @return Array of filters.
- */
- public async getFilters(): Promise>> {
- return this.client.get>('/api/v1/filters').then(res => {
- return Object.assign(res, {
- data: res.data.map(f => FriendicaAPI.Converter.filter(f))
- })
- })
- }
-
- public async getFilter(_id: string): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public async createFilter(
- _phrase: string,
- _context: Array,
- _options?: {
- irreversible?: boolean
- whole_word?: boolean
- expires_in?: string
- }
- ): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public async updateFilter(
- _id: string,
- _phrase: string,
- _context: Array,
- _options?: {
- irreversible?: boolean
- whole_word?: boolean
- expires_in?: string
- }
- ): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public async deleteFilter(_id: string): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // accounts/reports
- // ======================================
- public async report(
- _account_id: string,
- _options?: {
- status_ids?: Array
- comment: string
- forward?: boolean
- category?: Entity.Category
- rule_ids?: Array
- }
- ): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // accounts/follow_requests
- // ======================================
- /**
- * GET /api/v1/follow_requests
- *
- * @param limit Maximum number of results.
- * @return Array of FollowRequest.
- */
- public async getFollowRequests(limit?: number): Promise>> {
- if (limit) {
- return this.client
- .get>('/api/v1/follow_requests', {
- limit: limit
- })
- .then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.follow_request(a))
- })
- })
- } else {
- return this.client.get>('/api/v1/follow_requests').then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.follow_request(a))
- })
- })
- }
- }
-
- /**
- * POST /api/v1/follow_requests/:id/authorize
- *
- * @param id The FollowRequest ID.
- * @return Relationship.
- */
- public async acceptFollowRequest(id: string): Promise> {
- return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/follow_requests/:id/reject
- *
- * @param id The FollowRequest ID.
- * @return Relationship.
- */
- public async rejectFollowRequest(id: string): Promise> {
- return this.client.post(`/api/v1/follow_requests/${id}/reject`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.relationship(res.data)
- })
- })
- }
-
- // ======================================
- // accounts/endorsements
- // ======================================
- /**
- * GET /api/v1/endorsements
- *
- * @param options.limit Max number of results to return. Defaults to 40.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @return Array of accounts.
- */
- public async getEndorsements(options?: { limit?: number; max_id?: string; since_id?: string }): Promise>> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- }
- return this.client.get>('/api/v1/endorsements', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- // ======================================
- // accounts/featured_tags
- // ======================================
- public async getFeaturedTags(): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public async createFeaturedTag(_name: string): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public deleteFeaturedTag(_id: string): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- public async getSuggestedTags(): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // accounts/preferences
- // ======================================
- /**
- * GET /api/v1/preferences
- *
- * @return Preferences.
- */
- public async getPreferences(): Promise> {
- return this.client.get('/api/v1/preferences').then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.preferences(res.data)
- })
- })
- }
-
- // ======================================
- // accounts/followed_tags
- // ======================================
- public async getFollowedTags(): Promise>> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // accounts/suggestions
- // ======================================
- /**
- * GET /api/v1/suggestions
- *
- * @param limit Maximum number of results.
- * @return Array of accounts.
- */
- public async getSuggestions(limit?: number): Promise>> {
- if (limit) {
- return this.client
- .get>('/api/v1/suggestions', {
- limit: limit
- })
- .then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- } else {
- return this.client.get>('/api/v1/suggestions').then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
- }
-
- // ======================================
- // accounts/tags
- // ======================================
- /**
- * GET /api/v1/tags/:id
- *
- * @param id Target hashtag id.
- * @return Tag
- */
- public async getTag(id: string): Promise> {
- return this.client.get(`/api/v1/tags/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.tag(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/tags/:id/follow
- *
- * @param id Target hashtag id.
- * @return Tag
- */
- public async followTag(id: string): Promise> {
- return this.client.post(`/api/v1/tags/${id}/follow`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.tag(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/tags/:id/unfollow
- *
- * @param id Target hashtag id.
- * @return Tag
- */
- public async unfollowTag(id: string): Promise> {
- return this.client.post(`/api/v1/tags/${id}/unfollow`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.tag(res.data)
- })
- })
- }
-
- // ======================================
- // statuses
- // ======================================
- /**
- * POST /api/v1/statuses
- *
- * @param status Text content of status.
- * @param options.media_ids Array of Attachment ids.
- * @param options.poll Poll object.
- * @param options.in_reply_to_id ID of the status being replied to, if status is a reply.
- * @param options.sensitive Mark status and attached media as sensitive?
- * @param options.spoiler_text Text to be shown as a warning or subject before the actual content.
- * @param options.visibility Visibility of the posted status.
- * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status.
- * @param options.language ISO 639 language code for this status.
- * @param options.quote_id ID of the status being quoted to, if status is a quote.
- * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead.
- */
- public async postStatus(
- status: string,
- options: {
- media_ids?: Array
- poll?: { options: Array; expires_in: number; multiple?: boolean; hide_totals?: boolean }
- in_reply_to_id?: string
- sensitive?: boolean
- spoiler_text?: string
- visibility?: 'public' | 'unlisted' | 'private' | 'direct'
- scheduled_at?: string
- language?: string
- quote_id?: string
- }
- ): Promise> {
- let params = {
- status: status
- }
- if (options) {
- if (options.media_ids) {
- params = Object.assign(params, {
- media_ids: options.media_ids
- })
- }
- if (options.poll) {
- let pollParam = {
- options: options.poll.options,
- expires_in: options.poll.expires_in
- }
- if (options.poll.multiple !== undefined) {
- pollParam = Object.assign(pollParam, {
- multiple: options.poll.multiple
- })
- }
- if (options.poll.hide_totals !== undefined) {
- pollParam = Object.assign(pollParam, {
- hide_totals: options.poll.hide_totals
- })
- }
- params = Object.assign(params, {
- poll: pollParam
- })
- }
- if (options.in_reply_to_id) {
- params = Object.assign(params, {
- in_reply_to_id: options.in_reply_to_id
- })
- }
- if (options.sensitive !== undefined) {
- params = Object.assign(params, {
- sensitive: options.sensitive
- })
- }
- if (options.spoiler_text) {
- params = Object.assign(params, {
- spoiler_text: options.spoiler_text
- })
- }
- if (options.visibility) {
- params = Object.assign(params, {
- visibility: options.visibility
- })
- }
- if (options.scheduled_at) {
- params = Object.assign(params, {
- scheduled_at: options.scheduled_at
- })
- }
- if (options.language) {
- params = Object.assign(params, {
- language: options.language
- })
- }
- if (options.quote_id) {
- params = Object.assign(params, {
- quote_id: options.quote_id
- })
- }
- }
- if (options.scheduled_at) {
- return this.client.post('/api/v1/statuses', params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.scheduled_status(res.data)
- })
- })
- }
- return this.client.post('/api/v1/statuses', params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
- /**
- * GET /api/v1/statuses/:id
- *
- * @param id The target status id.
- * @return Status
- */
- public async getStatus(id: string): Promise> {
- return this.client.get(`/api/v1/statuses/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- PUT /api/v1/statuses/:id
- *
- * @param id The target status id.
- * @return Status
- */
- public async editStatus(
- id: string,
- options: {
- status?: string
- spoiler_text?: string
- sensitive?: boolean
- media_ids?: Array
- poll?: { options?: Array; expires_in?: number; multiple?: boolean; hide_totals?: boolean }
- }
- ): Promise> {
- let params = {}
- if (options.status) {
- params = Object.assign(params, {
- status: options.status
- })
- }
- if (options.spoiler_text) {
- params = Object.assign(params, {
- spoiler_text: options.spoiler_text
- })
- }
- if (options.sensitive) {
- params = Object.assign(params, {
- sensitive: options.sensitive
- })
- }
- if (options.media_ids) {
- params = Object.assign(params, {
- media_ids: options.media_ids
- })
- }
- if (options.poll) {
- let pollParam = {}
- if (options.poll.options !== undefined) {
- pollParam = Object.assign(pollParam, {
- options: options.poll.options
- })
- }
- if (options.poll.expires_in !== undefined) {
- pollParam = Object.assign(pollParam, {
- expires_in: options.poll.expires_in
- })
- }
- if (options.poll.multiple !== undefined) {
- pollParam = Object.assign(pollParam, {
- multiple: options.poll.multiple
- })
- }
- if (options.poll.hide_totals !== undefined) {
- pollParam = Object.assign(pollParam, {
- hide_totals: options.poll.hide_totals
- })
- }
- params = Object.assign(params, {
- poll: pollParam
- })
- }
- return this.client.put(`/api/v1/statuses/${id}`, params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * DELETE /api/v1/statuses/:id
- *
- * @param id The target status id.
- * @return Status
- */
- public async deleteStatus(id: string): Promise> {
- return this.client.del(`/api/v1/statuses/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * GET /api/v1/statuses/:id/context
- *
- * Get parent and child statuses.
- * @param id The target status id.
- * @return Context
- */
- public async getStatusContext(
- id: string,
- options?: { limit?: number; max_id?: string; since_id?: string }
- ): Promise> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- }
- return this.client.get(`/api/v1/statuses/${id}/context`, params).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.context(res.data)
- })
- })
- }
-
- /**
- * GET /api/v1/statuses/:id/source
- *
- * Obtain the source properties for a status so that it can be edited.
- * @param id The target status id.
- * @return StatusSource
- */
- public async getStatusSource(id: string): Promise> {
- return this.client.get(`/api/v1/statuses/${id}/source`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status_source(res.data)
- })
- })
- }
-
- /**
- * GET /api/v1/statuses/:id/reblogged_by
- *
- * @param id The target status id.
- * @return Array of accounts.
- */
- public async getStatusRebloggedBy(id: string): Promise>> {
- return this.client.get>(`/api/v1/statuses/${id}/reblogged_by`).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- /**
- * GET /api/v1/statuses/:id/favourited_by
- *
- * @param id The target status id.
- * @return Array of accounts.
- */
- public async getStatusFavouritedBy(id: string): Promise>> {
- return this.client.get>(`/api/v1/statuses/${id}/favourited_by`).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/favourite
- *
- * @param id The target status id.
- * @return Status.
- */
- public async favouriteStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/favourite`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/unfavourite
- *
- * @param id The target status id.
- * @return Status.
- */
- public async unfavouriteStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/reblog
- *
- * @param id The target status id.
- * @return Status.
- */
- public async reblogStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/reblog`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/unreblog
- *
- * @param id The target status id.
- * @return Status.
- */
- public async unreblogStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/unreblog`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/bookmark
- *
- * @param id The target status id.
- * @return Status.
- */
- public async bookmarkStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/bookmark`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/unbookmark
- *
- * @param id The target status id.
- * @return Status.
- */
- public async unbookmarkStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/mute
- *
- * @param id The target status id.
- * @return Status
- */
- public async muteStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/mute`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/unmute
- *
- * @param id The target status id.
- * @return Status
- */
- public async unmuteStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/unmute`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/pin
- * @param id The target status id.
- * @return Status
- */
- public async pinStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/pin`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/statuses/:id/unpin
- *
- * @param id The target status id.
- * @return Status
- */
- public async unpinStatus(id: string): Promise> {
- return this.client.post(`/api/v1/statuses/${id}/unpin`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.status(res.data)
- })
- })
- }
-
- // ======================================
- // statuses/media
- // ======================================
- /**
- * POST /api/v2/media
- *
- * @param file The file to be attached, using multipart form data.
- * @param options.description A plain-text description of the media.
- * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0.
- * @return Attachment
- */
- public async uploadMedia(
- file: any,
- options?: { description?: string; focus?: string }
- ): Promise> {
- const formData = new FormData()
- formData.append('file', file)
- if (options) {
- if (options.description) {
- formData.append('description', options.description)
- }
- if (options.focus) {
- formData.append('focus', options.focus)
- }
- }
- return this.client.postForm('/api/v2/media', formData).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.async_attachment(res.data)
- })
- })
- }
-
- /**
- * GET /api/v1/media/:id
- *
- * @param id Target media ID.
- * @return Attachment
- */
- public async getMedia(id: string): Promise> {
- const res = await this.client.get(`/api/v1/media/${id}`)
-
- return Object.assign(res, {
- data: FriendicaAPI.Converter.attachment(res.data)
- })
- }
-
- /**
- * PUT /api/v1/media/:id
- *
- * @param id Target media ID.
- * @param options.file The file to be attached, using multipart form data.
- * @param options.description A plain-text description of the media.
- * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0.
- * @param options.is_sensitive Whether the media is sensitive.
- * @return Attachment
- */
- public async updateMedia(
- id: string,
- options?: {
- file?: any
- description?: string
- focus?: string
- }
- ): Promise> {
- const formData = new FormData()
- if (options) {
- if (options.file) {
- formData.append('file', options.file)
- }
- if (options.description) {
- formData.append('description', options.description)
- }
- if (options.focus) {
- formData.append('focus', options.focus)
- }
- }
- return this.client.putForm(`/api/v1/media/${id}`, formData).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.attachment(res.data)
- })
- })
- }
-
- // ======================================
- // statuses/polls
- // ======================================
- /**
- * GET /api/v1/polls/:id
- *
- * @param id Target poll ID.
- * @return Poll
- */
- public async getPoll(id: string): Promise> {
- return this.client.get(`/api/v1/polls/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.poll(res.data)
- })
- })
- }
-
- public async votePoll(_id: string, _choices: Array): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- // ======================================
- // statuses/scheduled_statuses
- // ======================================
- /**
- * GET /api/v1/scheduled_statuses
- *
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of scheduled statuses.
- */
- public async getScheduledStatuses(options?: {
- limit?: number | null
- max_id?: string | null
- since_id?: string | null
- min_id?: string | null
- }): Promise>> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- }
- return this.client.get>('/api/v1/scheduled_statuses', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.scheduled_status(s))
- })
- })
- }
-
- /**
- * GET /api/v1/scheduled_statuses/:id
- *
- * @param id Target status ID.
- * @return ScheduledStatus.
- */
- public async getScheduledStatus(id: string): Promise> {
- return this.client.get(`/api/v1/scheduled_statuses/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.scheduled_status(res.data)
- })
- })
- }
-
- public async scheduleStatus(_id: string, _scheduled_at?: string | null): Promise> {
- return new Promise((_, reject) => {
- const err = new NoImplementedError('friendica does not support')
- reject(err)
- })
- }
-
- /**
- * DELETE /api/v1/scheduled_statuses/:id
- *
- * @param id Target scheduled status ID.
- */
- public cancelScheduledStatus(id: string): Promise>> {
- return this.client.del>(`/api/v1/scheduled_statuses/${id}`)
- }
-
- // ======================================
- // timelines
- // ======================================
- /**
- * GET /api/v1/timelines/public
- *
- * @param options.only_media Show only statuses with media attached? Defaults to false.
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getPublicTimeline(options?: {
- only_media?: boolean
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }): Promise>> {
- let params = {
- local: false
- }
- if (options) {
- if (options.only_media !== undefined) {
- params = Object.assign(params, {
- only_media: options.only_media
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/timelines/public', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- /**
- * GET /api/v1/timelines/public
- *
- * @param options.only_media Show only statuses with media attached? Defaults to false.
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getLocalTimeline(options?: {
- only_media?: boolean
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }): Promise>> {
- let params = {
- local: true
- }
- if (options) {
- if (options.only_media !== undefined) {
- params = Object.assign(params, {
- only_media: options.only_media
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/timelines/public', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- /**
- * GET /api/v1/timelines/tag/:hashtag
- *
- * @param hashtag Content of a #hashtag, not including # symbol.
- * @param options.local Show only local statuses? Defaults to false.
- * @param options.only_media Show only statuses with media attached? Defaults to false.
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getTagTimeline(
- hashtag: string,
- options?: {
- local?: boolean
- only_media?: boolean
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.local !== undefined) {
- params = Object.assign(params, {
- local: options.local
- })
- }
- if (options.only_media !== undefined) {
- params = Object.assign(params, {
- only_media: options.only_media
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>(`/api/v1/timelines/tag/${hashtag}`, params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- /**
- * GET /api/v1/timelines/home
- *
- * @param options.local Show only local statuses? Defaults to false.
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getHomeTimeline(options?: {
- local?: boolean
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }): Promise>> {
- let params = {}
- if (options) {
- if (options.local !== undefined) {
- params = Object.assign(params, {
- local: options.local
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/timelines/home', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- /**
- * GET /api/v1/timelines/list/:list_id
- *
- * @param list_id Local ID of the list in the database.
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getListTimeline(
- list_id: string,
- options?: {
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>(`/api/v1/timelines/list/${list_id}`, params).then(res => {
- return Object.assign(res, {
- data: res.data.map(s => FriendicaAPI.Converter.status(s))
- })
- })
- }
-
- // ======================================
- // timelines/conversations
- // ======================================
- /**
- * GET /api/v1/conversations
- *
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of statuses.
- */
- public async getConversationTimeline(options?: {
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- }): Promise>> {
- let params = {}
- if (options) {
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- }
- return this.client.get>('/api/v1/conversations', params).then(res => {
- return Object.assign(res, {
- data: res.data.map(c => FriendicaAPI.Converter.conversation(c))
- })
- })
- }
-
- /**
- * DELETE /api/v1/conversations/:id
- *
- * @param id Target conversation ID.
- */
- public deleteConversation(id: string): Promise>> {
- return this.client.del>(`/api/v1/conversations/${id}`)
- }
-
- /**
- * POST /api/v1/conversations/:id/read
- *
- * @param id Target conversation ID.
- * @return Conversation.
- */
- public async readConversation(id: string): Promise> {
- return this.client.post(`/api/v1/conversations/${id}/read`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.conversation(res.data)
- })
- })
- }
-
- // ======================================
- // timelines/lists
- // ======================================
- /**
- * GET /api/v1/lists
- *
- * @return Array of lists.
- */
- public async getLists(): Promise>> {
- return this.client.get>('/api/v1/lists').then(res => {
- return Object.assign(res, {
- data: res.data.map(l => FriendicaAPI.Converter.list(l))
- })
- })
- }
-
- /**
- * GET /api/v1/lists/:id
- *
- * @param id Target list ID.
- * @return List.
- */
- public async getList(id: string): Promise> {
- return this.client.get(`/api/v1/lists/${id}`).then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.list(res.data)
- })
- })
- }
-
- /**
- * POST /api/v1/lists
- *
- * @param title List name.
- * @return List.
- */
- public async createList(title: string): Promise> {
- return this.client
- .post('/api/v1/lists', {
- title: title
- })
- .then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.list(res.data)
- })
- })
- }
-
- /**
- * PUT /api/v1/lists/:id
- *
- * @param id Target list ID.
- * @param title New list name.
- * @return List.
- */
- public async updateList(id: string, title: string): Promise> {
- return this.client
- .put(`/api/v1/lists/${id}`, {
- title: title
- })
- .then(res => {
- return Object.assign(res, {
- data: FriendicaAPI.Converter.list(res.data)
- })
- })
- }
-
- /**
- * DELETE /api/v1/lists/:id
- *
- * @param id Target list ID.
- */
- public deleteList(id: string): Promise>> {
- return this.client.del>(`/api/v1/lists/${id}`)
- }
-
- /**
- * GET /api/v1/lists/:id/accounts
- *
- * @param id Target list ID.
- * @param options.limit Max number of results to return.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @return Array of accounts.
- */
- public async getAccountsInList(
- id: string,
- options?: {
- limit?: number
- max_id?: string
- since_id?: string
- }
- ): Promise>> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- }
- return this.client.get>(`/api/v1/lists/${id}/accounts`, params).then(res => {
- return Object.assign(res, {
- data: res.data.map(a => FriendicaAPI.Converter.account(a))
- })
- })
- }
-
- /**
- * POST /api/v1/lists/:id/accounts
- *
- * @param id Target list ID.
- * @param account_ids Array of account IDs to add to the list.
- */
- public addAccountsToList(id: string, account_ids: Array): Promise>> {
- return this.client.post>(`/api/v1/lists/${id}/accounts`, {
- account_ids: account_ids
- })
- }
-
- /**
- * DELETE /api/v1/lists/:id/accounts
- *
- * @param id Target list ID.
- * @param account_ids Array of account IDs to add to the list.
- */
- public deleteAccountsFromList(id: string, account_ids: Array): Promise>> {
- return this.client.del>(`/api/v1/lists/${id}/accounts`, {
- account_ids: account_ids
- })
- }
-
- // ======================================
- // timelines/markers
- // ======================================
- public async getMarkers(_timeline: Array): Promise>> {
- return new Promise(resolve => {
- const res: Response = {
- data: {},
- status: 200,
- statusText: '200',
- headers: {}
- }
- resolve(res)
- })
- }
-
- public async saveMarkers(_options?: {
- home?: { last_read_id: string }
- notifications?: { last_read_id: string }
- }): Promise> {
- return new Promise(resolve => {
- const res: Response = {
- data: {},
- status: 200,
- statusText: '200',
- headers: {}
- }
- resolve(res)
- })
- }
-
- // ======================================
- // notifications
- // ======================================
- /**
- * GET /api/v1/notifications
- *
- * @param options.limit Max number of results to return. Defaults to 20.
- * @param options.max_id Return results older than ID.
- * @param options.since_id Return results newer than ID.
- * @param options.min_id Return results immediately newer than ID.
- * @param options.exclude_types Array of types to exclude.
- * @param options.account_id Return only notifications received from this account.
- * @return Array of notifications.
- */
- public async getNotifications(options?: {
- limit?: number
- max_id?: string
- since_id?: string
- min_id?: string
- exclude_types?: Array
- account_id?: string
- }): Promise>> {
- let params = {}
- if (options) {
- if (options.limit) {
- params = Object.assign(params, {
- limit: options.limit
- })
- }
- if (options.max_id) {
- params = Object.assign(params, {
- max_id: options.max_id
- })
- }
- if (options.since_id) {
- params = Object.assign(params, {
- since_id: options.since_id
- })
- }
- if (options.min_id) {
- params = Object.assign(params, {
- min_id: options.min_id
- })
- }
- if (options.exclude_types) {
- params = Object.assign(params, {
- exclude_types: options.exclude_types.map(e => FriendicaAPI.Converter.encodeNotificationType(e))
- })
- }
- if (options.account_id) {
- params = Object.assign(params, {
- account_id: options.account_id
- })
- }
- }
- return this.client.get>('/api/v1/notifications', params).then(res => {
- return Object.assign(res, {
- data: res.data.flatMap(n => {
- const notify = FriendicaAPI.Converter.notification(n)
- if (notify instanceof UnknownNotificationTypeError) return []
- return notify
- })
- })
- })
- }
-
- /**
- * GET /api/v1/notifications/:id
- *
- * @param id Target notification ID.
- * @return Notification.
- */
- public async getNotification(id: string): Promise> {
- const res = await this.client.get