Merge pull request #5 from transfem-org/feature/masto-api
Feature/masto api
This commit is contained in:
		
						commit
						018280e3df
					
				
					 251 changed files with 25148 additions and 129 deletions
				
			
		
							
								
								
									
										4
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -58,6 +58,10 @@ ormconfig.json
 | 
			
		|||
temp
 | 
			
		||||
/packages/frontend/src/**/*.stories.ts
 | 
			
		||||
 | 
			
		||||
# Sharkey
 | 
			
		||||
/packages/megalodon/lib
 | 
			
		||||
/packages/megalodon-bk
 | 
			
		||||
 | 
			
		||||
# blender backups
 | 
			
		||||
*.blend1
 | 
			
		||||
*.blend2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ COPY --link ["packages/backend/package.json", "./packages/backend/"]
 | 
			
		|||
COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
 | 
			
		||||
COPY --link ["packages/sw/package.json", "./packages/sw/"]
 | 
			
		||||
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
 | 
			
		||||
COPY --link ["packages/megalodon/package.json", "./packages/megalodon/"]
 | 
			
		||||
 | 
			
		||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
 | 
			
		||||
	pnpm i --frozen-lockfile --aggregate-output
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
<div align="center">
 | 
			
		||||
<a href="https://dev.transfem.social">
 | 
			
		||||
<a href="https://test.transfem.social">
 | 
			
		||||
	<img src="https://cdn.transfem.social/files/06eb1052-fd80-448a-803e-3adf7a2d03a3.png" alt="Sharky logo" style="border-radius:50%" width="400"/>
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
**🌎 **[Sharky](https://dev.transfem.social/)** is an open source, decentralized social media platform that's free forever! 🚀**
 | 
			
		||||
**🌎 **[Sharky](https://test.transfem.social/)** is an open source, decentralized social media platform that's free forever! 🚀**
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,13 +15,19 @@
 | 
			
		|||
 | 
			
		||||
<div>
 | 
			
		||||
 | 
			
		||||
<a href="https://dev.transfem.social/"><img src="https://cdn.transfem.social/files/dce7b668-fa9e-44e9-8e53-60fd743681aa.png" align="right" height="320px"/></a>
 | 
			
		||||
<a href="https://test.transfem.social/"><img src="https://cdn.transfem.social/files/dce7b668-fa9e-44e9-8e53-60fd743681aa.png" align="right" height="320px"/></a>
 | 
			
		||||
 | 
			
		||||
## ✨ Features
 | 
			
		||||
- **ActivityPub support**\
 | 
			
		||||
Not on Sharky? No problem! Not only can Sharky instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
 | 
			
		||||
- **Reactions**\
 | 
			
		||||
You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
 | 
			
		||||
- **Post Editing**\
 | 
			
		||||
In Sharkey you can edit your post, this is not possible in normal Misskey
 | 
			
		||||
- **Mastodon API**\
 | 
			
		||||
Sharkey implements the mastodon api unlike normal Misskey
 | 
			
		||||
- **UI/UX Improvements**\
 | 
			
		||||
Sharkey makes some Ui/UX improvements to make it easier to navigate
 | 
			
		||||
- **Drive**\
 | 
			
		||||
With Sharky's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
 | 
			
		||||
- **Rich Web UI**\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,10 @@
 | 
			
		|||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"name": "sharkey",
 | 
			
		||||
	"version": "2023.9.0-beta.10",
 | 
			
		||||
	"codename": "nasubi",
 | 
			
		||||
	"repository": {
 | 
			
		||||
		"type": "git",
 | 
			
		||||
		"url": "https://github.com/misskey-dev/misskey.git"
 | 
			
		||||
		"url": "https://github.com/transfem-org/sharkey.git"
 | 
			
		||||
	},
 | 
			
		||||
	"packageManager": "pnpm@8.7.6",
 | 
			
		||||
	"workspaces": [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
		"watch:swc": "swc src -d built -D -w",
 | 
			
		||||
		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
 | 
			
		||||
		"watch": "node watch.mjs",
 | 
			
		||||
		"typecheck": "tsc --noEmit",
 | 
			
		||||
		"typecheck": "pnpm --filter megalodon build && tsc --noEmit",
 | 
			
		||||
		"eslint": "eslint --quiet \"src/**/*.ts\"",
 | 
			
		||||
		"lint": "pnpm typecheck && pnpm eslint",
 | 
			
		||||
		"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit",
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +99,7 @@
 | 
			
		|||
		"date-fns": "2.30.0",
 | 
			
		||||
		"deep-email-validator": "0.1.21",
 | 
			
		||||
		"fastify": "4.23.2",
 | 
			
		||||
		"fastify-multer": "^2.0.3",
 | 
			
		||||
		"feed": "4.2.2",
 | 
			
		||||
		"file-type": "18.5.0",
 | 
			
		||||
		"fluent-ffmpeg": "2.1.2",
 | 
			
		||||
| 
						 | 
				
			
			@ -116,6 +117,7 @@
 | 
			
		|||
		"json5": "2.2.3",
 | 
			
		||||
		"jsonld": "8.3.1",
 | 
			
		||||
		"jsrsasign": "10.8.6",
 | 
			
		||||
		"megalodon": "workspace:*",
 | 
			
		||||
		"meilisearch": "0.34.2",
 | 
			
		||||
		"mfm-js": "0.23.3",
 | 
			
		||||
		"microformats-parser": "1.5.2",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
						 | 
				
			
			@ -39,6 +39,7 @@ 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';
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +85,7 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
 | 
			
		|||
		ServerStatsChannelService,
 | 
			
		||||
		UserListChannelService,
 | 
			
		||||
		OpenApiServerService,
 | 
			
		||||
		MastodonApiServerService,
 | 
			
		||||
		OAuth2ProviderService,
 | 
			
		||||
	],
 | 
			
		||||
	exports: [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ 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));
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +57,7 @@ export class ServerService implements OnApplicationShutdown {
 | 
			
		|||
		private userEntityService: UserEntityService,
 | 
			
		||||
		private apiServerService: ApiServerService,
 | 
			
		||||
		private openApiServerService: OpenApiServerService,
 | 
			
		||||
		private mastodonApiServerService: MastodonApiServerService,
 | 
			
		||||
		private streamingApiServerService: StreamingApiServerService,
 | 
			
		||||
		private activityPubServerService: ActivityPubServerService,
 | 
			
		||||
		private wellKnownServerService: WellKnownServerService,
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +97,7 @@ 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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,783 @@
 | 
			
		|||
import { Inject, Injectable } from '@nestjs/common';
 | 
			
		||||
import megalodon, { Entity, MegalodonInterface } from 'megalodon';
 | 
			
		||||
import { IsNull } from 'typeorm';
 | 
			
		||||
import multer from 'fastify-multer';
 | 
			
		||||
import type { UsersRepository } from '@/models/_.js';
 | 
			
		||||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
import type { Config } from '@/config.js';
 | 
			
		||||
import { MetaService } from '@/core/MetaService.js';
 | 
			
		||||
import { convertId, IdConvertType as IdType, convertAccount, convertAnnouncement, convertFilter, convertAttachment, convertFeaturedTag, convertList } from './converters.js';
 | 
			
		||||
import { getInstance } from './endpoints/meta.js';
 | 
			
		||||
import { ApiAuthMastodon, ApiAccountMastodon, ApiFilterMastodon, ApiNotifyMastodon, ApiSearchMastodon, ApiTimelineMastodon, ApiStatusMastodon } from './endpoints.js';
 | 
			
		||||
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
 | 
			
		||||
 | 
			
		||||
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.config)
 | 
			
		||||
        private config: Config,
 | 
			
		||||
        private metaService: MetaService,
 | 
			
		||||
	) { }
 | 
			
		||||
 | 
			
		||||
	@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.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 : convertAccount((await client.getAccount(admin.id)).data);
 | 
			
		||||
				reply.send(await getInstance(data.data, contact, 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(
 | 
			
		||||
					convertId(_request.body['id'], IdType.SharkeyId),
 | 
			
		||||
				);
 | 
			
		||||
				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.post('/v1/apps', async (_request, reply) => {
 | 
			
		||||
			const BASE_URL = `${_request.protocol}://${_request.hostname}`;
 | 
			
		||||
			const client = getClient(BASE_URL, ''); // we are using this here, because in private mode some info isnt
 | 
			
		||||
			// 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);
 | 
			
		||||
				reply.send(await account.verifyCredentials());
 | 
			
		||||
			} catch (e: any) {
 | 
			
		||||
				console.error(e);
 | 
			
		||||
				reply.code(401).send(e.response.data);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		fastify.patch('/v1/accounts/update_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);
 | 
			
		||||
				reply.send(await account.updateCredentials());
 | 
			
		||||
			} 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 account = new ApiAccountMastodon(_request, client, BASE_URL);
 | 
			
		||||
				reply.send(await account.lookup());
 | 
			
		||||
			} 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[]'] : null;
 | 
			
		||||
				if (typeof ids === 'string') {
 | 
			
		||||
					ids = [ids];
 | 
			
		||||
				}
 | 
			
		||||
				users = ids;
 | 
			
		||||
				const account = new ApiAccountMastodon(_request, client, BASE_URL);
 | 
			
		||||
				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 = convertId(_request.params.id, IdType.SharkeyId);
 | 
			
		||||
				const data = await client.getAccount(sharkId);
 | 
			
		||||
				reply.send(convertAccount(data.data));
 | 
			
		||||
			} 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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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(data.data.map((account) => 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', 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);
 | 
			
		||||
				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', 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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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);
 | 
			
		||||
				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', 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', 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', 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', 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);
 | 
			
		||||
 | 
			
		||||
		// 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);
 | 
			
		||||
 | 
			
		||||
		// GET Endpoints
 | 
			
		||||
		NoteEndpoint.getStatus();
 | 
			
		||||
		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
 | 
			
		||||
		NoteEndpoint.updateMedia();
 | 
			
		||||
 | 
			
		||||
		// DELETE Endpoint
 | 
			
		||||
		NoteEndpoint.deleteStatus();
 | 
			
		||||
		//#endregion
 | 
			
		||||
		done();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										132
									
								
								packages/backend/src/server/api/mastodon/converters.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								packages/backend/src/server/api/mastodon/converters.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
import { Entity } from 'megalodon';
 | 
			
		||||
 | 
			
		||||
const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz';
 | 
			
		||||
 | 
			
		||||
export enum IdConvertType {
 | 
			
		||||
    MastodonId,
 | 
			
		||||
    SharkeyId,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function convertId(in_id: string, id_convert_type: IdConvertType): string {
 | 
			
		||||
	switch (id_convert_type) {
 | 
			
		||||
		case IdConvertType.MastodonId: {
 | 
			
		||||
			let out = BigInt(0);
 | 
			
		||||
			const lowerCaseId = in_id.toLowerCase();
 | 
			
		||||
			for (let i = 0; i < lowerCaseId.length; i++) {
 | 
			
		||||
				const charValue = numFromChar(lowerCaseId.charAt(i));
 | 
			
		||||
				out += BigInt(charValue) * BigInt(36) ** BigInt(i);
 | 
			
		||||
			}
 | 
			
		||||
			return out.toString();
 | 
			
		||||
		}
 | 
			
		||||
    
 | 
			
		||||
		case IdConvertType.SharkeyId: {
 | 
			
		||||
			let input = BigInt(in_id);
 | 
			
		||||
			let outStr = '';
 | 
			
		||||
			while (input > BigInt(0)) {
 | 
			
		||||
				const remainder = Number(input % BigInt(36));
 | 
			
		||||
				outStr = charFromNum(remainder) + outStr;
 | 
			
		||||
				input /= BigInt(36);
 | 
			
		||||
			}
 | 
			
		||||
			const ReversedoutStr = outStr.split('').reduce((acc, char) => char + acc, '');
 | 
			
		||||
			return ReversedoutStr;
 | 
			
		||||
		}
 | 
			
		||||
    
 | 
			
		||||
		default:
 | 
			
		||||
			throw new Error('Invalid ID conversion type');
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function numFromChar(character: string): number {
 | 
			
		||||
	for (let i = 0; i < CHAR_COLLECTION.length; i++) {
 | 
			
		||||
		if (CHAR_COLLECTION.charAt(i) === character) {
 | 
			
		||||
			return i;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	throw new Error('Invalid character in parsed base36 id');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function charFromNum(number: number): string {
 | 
			
		||||
	if (number >= 0 && number < CHAR_COLLECTION.length) {
 | 
			
		||||
		return CHAR_COLLECTION.charAt(number);
 | 
			
		||||
	} else {
 | 
			
		||||
		throw new Error('Invalid number for base-36 encoding');
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function simpleConvert(data: any) {
 | 
			
		||||
	// copy the object to bypass weird pass by reference bugs
 | 
			
		||||
	const result = Object.assign({}, data);
 | 
			
		||||
	result.id = convertId(data.id, IdConvertType.MastodonId);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
	notification.id = convertId(notification.id, IdConvertType.MastodonId);
 | 
			
		||||
	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.id = convertId(status.id, IdConvertType.MastodonId);
 | 
			
		||||
	if (status.in_reply_to_account_id) status.in_reply_to_account_id = convertId(
 | 
			
		||||
		status.in_reply_to_account_id,
 | 
			
		||||
		IdConvertType.MastodonId,
 | 
			
		||||
	);
 | 
			
		||||
	if (status.in_reply_to_id) status.in_reply_to_id = convertId(status.in_reply_to_id, IdConvertType.MastodonId);
 | 
			
		||||
	status.media_attachments = status.media_attachments.map((attachment) =>
 | 
			
		||||
		convertAttachment(attachment),
 | 
			
		||||
	);
 | 
			
		||||
	status.mentions = status.mentions.map((mention) => ({
 | 
			
		||||
		...mention,
 | 
			
		||||
		id: convertId(mention.id, IdConvertType.MastodonId),
 | 
			
		||||
	}));
 | 
			
		||||
	if (status.poll) status.poll = convertPoll(status.poll);
 | 
			
		||||
	if (status.reblog) status.reblog = convertStatus(status.reblog);
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function convertConversation(conversation: Entity.Conversation) {
 | 
			
		||||
	conversation.id = convertId(conversation.id, IdConvertType.MastodonId);
 | 
			
		||||
	conversation.accounts = conversation.accounts.map(convertAccount);
 | 
			
		||||
	if (conversation.last_status) {
 | 
			
		||||
		conversation.last_status = convertStatus(conversation.last_status);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return conversation;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/backend/src/server/api/mastodon/endpoints.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/backend/src/server/api/mastodon/endpoints.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
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,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										285
									
								
								packages/backend/src/server/api/mastodon/endpoints/account.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								packages/backend/src/server/api/mastodon/endpoints/account.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,285 @@
 | 
			
		|||
import { convertId, IdConvertType as IdType, convertAccount, convertRelationship, convertStatus } from '../converters.js';
 | 
			
		||||
import { argsToBools, convertTimelinesArgsId, limitToInt } from './timeline.js';
 | 
			
		||||
import type { MegalodonInterface } from 'megalodon';
 | 
			
		||||
import type { FastifyRequest } from 'fastify';
 | 
			
		||||
 | 
			
		||||
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: '',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class ApiAccountMastodon {
 | 
			
		||||
	private request: FastifyRequest;
 | 
			
		||||
	private client: MegalodonInterface;
 | 
			
		||||
	private BASE_URL: string;
 | 
			
		||||
 | 
			
		||||
	constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string) {
 | 
			
		||||
		this.request = request;
 | 
			
		||||
		this.client = client;
 | 
			
		||||
		this.BASE_URL = BASE_URL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async verifyCredentials() {
 | 
			
		||||
		try {
 | 
			
		||||
			const data = await this.client.verifyAccountCredentials();
 | 
			
		||||
			const acct = data.data;
 | 
			
		||||
			acct.id = convertId(acct.id, IdType.MastodonId);
 | 
			
		||||
			acct.display_name = acct.display_name || acct.username;
 | 
			
		||||
			acct.url = `${this.BASE_URL}/@${acct.url}`;
 | 
			
		||||
			acct.note = acct.note || '';
 | 
			
		||||
			acct.avatar_static = acct.avatar;
 | 
			
		||||
			acct.header = acct.header || '/static-assets/transparent.png';
 | 
			
		||||
			acct.header_static = acct.header || '/static-assets/transparent.png';
 | 
			
		||||
			acct.source = {
 | 
			
		||||
				note: acct.note,
 | 
			
		||||
				fields: acct.fields,
 | 
			
		||||
				privacy: '',
 | 
			
		||||
				sensitive: false,
 | 
			
		||||
				language: '',
 | 
			
		||||
			};
 | 
			
		||||
			console.log(acct);
 | 
			
		||||
			return acct;
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			console.error(e.response.data);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateCredentials() {
 | 
			
		||||
		try {
 | 
			
		||||
			const data = await this.client.updateCredentials(this.request.body as any);
 | 
			
		||||
			return convertAccount(data.data);
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			console.error(e.response.data);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async lookup() {
 | 
			
		||||
		try {
 | 
			
		||||
			const data = await this.client.search((this.request.query as any).acct, { type: 'accounts' });
 | 
			
		||||
			return convertAccount(data.data.accounts[0]);
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			console.error(e.response.data);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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(convertId(users[i], IdType.SharkeyId));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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(
 | 
			
		||||
				convertId((this.request.params as any).id, IdType.SharkeyId), 
 | 
			
		||||
				convertTimelinesArgsId(argsToBools(limitToInt(this.request.query as any))),
 | 
			
		||||
			);
 | 
			
		||||
			return data.data.map((status) => 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(
 | 
			
		||||
				convertId((this.request.params as any).id, IdType.SharkeyId), 
 | 
			
		||||
				convertTimelinesArgsId(limitToInt(this.request.query as any)),
 | 
			
		||||
			);
 | 
			
		||||
			return data.data.map((account) => 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(
 | 
			
		||||
				convertId((this.request.params as any).id, IdType.SharkeyId), 
 | 
			
		||||
				convertTimelinesArgsId(limitToInt(this.request.query as any)),
 | 
			
		||||
			);
 | 
			
		||||
			return data.data.map((account) => 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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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(
 | 
			
		||||
				convertId((this.request.params as any).id, IdType.SharkeyId),
 | 
			
		||||
                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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
 | 
			
		||||
			return data.data.map((status) => 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( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
 | 
			
		||||
			return data.data.map((status) => 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( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
 | 
			
		||||
			return data.data.map((account) => 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( convertTimelinesArgsId(limitToInt(this.request.query as any)) );
 | 
			
		||||
			return data.data.map((account) => 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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			return convertRelationship(data.data);
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			console.error(e.response.data);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								packages/backend/src/server/api/mastodon/endpoints/auth.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								packages/backend/src/server/api/mastodon/endpoints/auth.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
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(' ');
 | 
			
		||||
		const pushScope = new Set<string>();
 | 
			
		||||
		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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								packages/backend/src/server/api/mastodon/endpoints/filter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								packages/backend/src/server/api/mastodon/endpoints/filter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
import { IdConvertType as IdType, convertId, 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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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(convertId((this.request.params as any).id, IdType.SharkeyId), 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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			return data.data;
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								packages/backend/src/server/api/mastodon/endpoints/meta.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								packages/backend/src/server/api/mastodon/endpoints/meta.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
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?.substring(0, 50) || 'See real server website',
 | 
			
		||||
		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: !response.registrations,
 | 
			
		||||
		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: [],
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,71 @@
 | 
			
		|||
import { IdConvertType as IdType, convertId, convertNotification } from '../converters.js';
 | 
			
		||||
import { convertTimelinesArgsId } from './timeline.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( convertTimelinesArgsId(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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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( convertId((this.request.params as any).id, IdType.SharkeyId) );
 | 
			
		||||
			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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								packages/backend/src/server/api/mastodon/endpoints/search.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								packages/backend/src/server/api/mastodon/endpoints/search.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
import { Converter } from 'megalodon';
 | 
			
		||||
import { convertAccount, convertStatus } from '../converters.js';
 | 
			
		||||
import { convertTimelinesArgsId, limitToInt } from './timeline.js';
 | 
			
		||||
import type { MegalodonInterface } from 'megalodon';
 | 
			
		||||
import type { FastifyRequest } from 'fastify';
 | 
			
		||||
 | 
			
		||||
async function getHighlight(
 | 
			
		||||
	BASE_URL: string,
 | 
			
		||||
	domain: string,
 | 
			
		||||
	accessTokens: string | undefined,
 | 
			
		||||
) {
 | 
			
		||||
	const accessTokenArr = accessTokens?.split(' ') ?? [null];
 | 
			
		||||
	const accessToken = accessTokenArr[accessTokenArr.length - 1];
 | 
			
		||||
	try {
 | 
			
		||||
		const apicall = await fetch(`${BASE_URL}/api/notes/featured`,
 | 
			
		||||
			{
 | 
			
		||||
				method: 'POST',
 | 
			
		||||
				headers: {
 | 
			
		||||
					'Accept': 'application/json, text/plain, */*',
 | 
			
		||||
					'Content-Type': 'application/json',
 | 
			
		||||
				},
 | 
			
		||||
				body: JSON.stringify({ i: accessToken }),
 | 
			
		||||
			});
 | 
			
		||||
		const api = await apicall.json();
 | 
			
		||||
		const data: MisskeyEntity.Note[] = api;
 | 
			
		||||
		return data.map((note) => Converter.note(note, domain));
 | 
			
		||||
	} catch (e: any) {
 | 
			
		||||
		console.log(e);
 | 
			
		||||
		console.log(e.response.data);
 | 
			
		||||
		return [];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getFeaturedUser( BASE_URL: string, host: string, accessTokens: string | undefined, limit: number ) {
 | 
			
		||||
	const accessTokenArr = accessTokens?.split(' ') ?? [null];
 | 
			
		||||
	const accessToken = accessTokenArr[accessTokenArr.length - 1];
 | 
			
		||||
	try {
 | 
			
		||||
		const apicall = await fetch(`${BASE_URL}/api/users`,
 | 
			
		||||
			{
 | 
			
		||||
				method: 'POST',
 | 
			
		||||
				headers: {
 | 
			
		||||
					'Accept': 'application/json, text/plain, */*',
 | 
			
		||||
					'Content-Type': 'application/json',
 | 
			
		||||
				},
 | 
			
		||||
				body: JSON.stringify({ i: accessToken, limit, origin: 'local', sort: '+follower', state: 'alive' }),
 | 
			
		||||
			});
 | 
			
		||||
		const api = await apicall.json();
 | 
			
		||||
		const data: MisskeyEntity.UserDetail[] = api;
 | 
			
		||||
		return data.map((u) => {
 | 
			
		||||
			return {
 | 
			
		||||
				source: 'past_interactions',
 | 
			
		||||
				account: Converter.userDetail(u, host),
 | 
			
		||||
			};
 | 
			
		||||
		});
 | 
			
		||||
	} catch (e: any) {
 | 
			
		||||
		console.log(e);
 | 
			
		||||
		console.log(e.response.data);
 | 
			
		||||
		return [];
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
export class ApiSearchMastodon {
 | 
			
		||||
	private request: FastifyRequest;
 | 
			
		||||
	private client: MegalodonInterface;
 | 
			
		||||
	private BASE_URL: string;
 | 
			
		||||
 | 
			
		||||
	constructor(request: FastifyRequest, client: MegalodonInterface, BASE_URL: string) {
 | 
			
		||||
		this.request = request;
 | 
			
		||||
		this.client = client;
 | 
			
		||||
		this.BASE_URL = BASE_URL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async SearchV1() {
 | 
			
		||||
		try {
 | 
			
		||||
			const query: any = convertTimelinesArgsId(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 = convertTimelinesArgsId(limitToInt(this.request.query as any));
 | 
			
		||||
			const type = query.type;
 | 
			
		||||
			const acct = !type || type === 'accounts' ? await this.client.search(query.q, { type: 'accounts', ...query }) : null;
 | 
			
		||||
			const stat = !type || type === 'statuses' ? await this.client.search(query.q, { type: 'statuses', ...query }) : null;
 | 
			
		||||
			const tags = !type || type === 'hashtags' ? await this.client.search(query.q, { type: 'hashtags', ...query }) : null;
 | 
			
		||||
			const data = {
 | 
			
		||||
				accounts: acct?.data.accounts.map((account) => convertAccount(account)) ?? [],
 | 
			
		||||
				statuses: stat?.data.statuses.map((status) => convertStatus(status)) ?? [],
 | 
			
		||||
				hashtags: tags?.data.hashtags ?? [],
 | 
			
		||||
			};
 | 
			
		||||
			return data;
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async getStatusTrends() {
 | 
			
		||||
		try {
 | 
			
		||||
			const data = await getHighlight(
 | 
			
		||||
				this.BASE_URL,
 | 
			
		||||
				this.request.hostname,
 | 
			
		||||
				this.request.headers.authorization,
 | 
			
		||||
			);
 | 
			
		||||
			return data.map((status) => convertStatus(status));
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async getSuggestions() {
 | 
			
		||||
		try {
 | 
			
		||||
			const data = await getFeaturedUser(
 | 
			
		||||
				this.BASE_URL,
 | 
			
		||||
				this.request.hostname,
 | 
			
		||||
				this.request.headers.authorization,
 | 
			
		||||
				(this.request.query as any).limit || 20,
 | 
			
		||||
			);
 | 
			
		||||
			return data.map((suggestion) => { suggestion.account = convertAccount(suggestion.account); return suggestion; });
 | 
			
		||||
		} catch (e: any) {
 | 
			
		||||
			console.error(e);
 | 
			
		||||
			return e.response.data;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										400
									
								
								packages/backend/src/server/api/mastodon/endpoints/status.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								packages/backend/src/server/api/mastodon/endpoints/status.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,400 @@
 | 
			
		|||
import querystring from 'querystring';
 | 
			
		||||
import { emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js';
 | 
			
		||||
import { convertId, IdConvertType as IdType, convertAccount, convertAttachment, convertPoll, convertStatus } from '../converters.js';
 | 
			
		||||
import { getClient } from '../MastodonApiServerService.js';
 | 
			
		||||
import { convertTimelinesArgsId, limitToInt } from './timeline.js';
 | 
			
		||||
import type { Entity } from 'megalodon';
 | 
			
		||||
import type { FastifyInstance } from 'fastify';
 | 
			
		||||
 | 
			
		||||
function normalizeQuery(data: any) {
 | 
			
		||||
	const str = querystring.stringify(data);
 | 
			
		||||
	return querystring.parse(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ApiStatusMastodon {
 | 
			
		||||
	private fastify: FastifyInstance;
 | 
			
		||||
 | 
			
		||||
	constructor(fastify: FastifyInstance) {
 | 
			
		||||
		this.fastify = fastify;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(convertStatus(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(
 | 
			
		||||
					convertId(_request.params.id, IdType.SharkeyId),
 | 
			
		||||
					convertTimelinesArgsId(limitToInt(query)),
 | 
			
		||||
				);
 | 
			
		||||
				data.data.ancestors = data.data.ancestors.map((status: Entity.Status) => convertStatus(status));
 | 
			
		||||
				data.data.descendants = data.data.descendants.map((status: Entity.Status) => convertStatus(status));
 | 
			
		||||
				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 {
 | 
			
		||||
				reply.code(401).send({ message: 'Not Implemented' });
 | 
			
		||||
			} 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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(data.data.map((account: Entity.Account) => 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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(data.data.map((account: Entity.Account) => 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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				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(convertId(_request.params.id, IdType.SharkeyId), 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.in_reply_to_id) body.in_reply_to_id = convertId(body.in_reply_to_id, IdType.SharkeyId);
 | 
			
		||||
				if (body.quote_id) body.quote_id = convertId(body.quote_id, IdType.SharkeyId);
 | 
			
		||||
				if (
 | 
			
		||||
					(!body.poll && body['poll[options][]']) ||
 | 
			
		||||
                    (!body.media_ids && body['media_ids[]'])
 | 
			
		||||
				) {
 | 
			
		||||
					body = normalizeQuery(body);
 | 
			
		||||
				}
 | 
			
		||||
				const text = 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) || 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;
 | 
			
		||||
				if (body.media_ids) {
 | 
			
		||||
					body.media_ids = (body.media_ids as string[]).map((p) => convertId(p, IdType.SharkeyId));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				const { sensitive } = body;
 | 
			
		||||
				body.sensitive = typeof sensitive === 'string' ? sensitive === 'true' : sensitive;
 | 
			
		||||
 | 
			
		||||
				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(convertStatus(data.data as Entity.Status));
 | 
			
		||||
			} catch (e: any) {
 | 
			
		||||
				console.error(e);
 | 
			
		||||
				reply.code(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(
 | 
			
		||||
					convertId(_request.params.id, IdType.SharkeyId),
 | 
			
		||||
					'⭐',
 | 
			
		||||
				)) as any;
 | 
			
		||||
				reply.send(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(
 | 
			
		||||
					convertId(_request.params.id, IdType.SharkeyId),
 | 
			
		||||
					'⭐',
 | 
			
		||||
				);
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
 | 
			
		||||
				reply.send(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(convertId(_request.params.id, IdType.SharkeyId), _request.params.name);
 | 
			
		||||
				reply.send(convertStatus(data.data));
 | 
			
		||||
			} catch (e: any) {
 | 
			
		||||
				console.error(e);
 | 
			
		||||
				reply.code(401).send(e.response.data);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async updateMedia() {
 | 
			
		||||
		this.fastify.put<{ 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.updateMedia(convertId(_request.params.id, IdType.SharkeyId), _request.body as any);
 | 
			
		||||
				reply.send(convertAttachment(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(convertId(_request.params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(data.data);
 | 
			
		||||
			} catch (e: any) {
 | 
			
		||||
				console.error(e);
 | 
			
		||||
				reply.code(401).send(e.response.data);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										282
									
								
								packages/backend/src/server/api/mastodon/endpoints/timeline.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								packages/backend/src/server/api/mastodon/endpoints/timeline.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,282 @@
 | 
			
		|||
import { ParsedUrlQuery } from 'querystring';
 | 
			
		||||
import { convertId, IdConvertType as IdType, convertAccount, convertConversation, convertList, convertStatus } from '../converters.js';
 | 
			
		||||
import { getClient } from '../MastodonApiServerService.js';
 | 
			
		||||
import type { Entity } from 'megalodon';
 | 
			
		||||
import type { FastifyInstance } from 'fastify';
 | 
			
		||||
 | 
			
		||||
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 function convertTimelinesArgsId(q: ParsedUrlQuery) {
 | 
			
		||||
	if (typeof q.min_id === 'string') q.min_id = convertId(q.min_id, IdType.SharkeyId);
 | 
			
		||||
	if (typeof q.max_id === 'string') q.max_id = convertId(q.max_id, IdType.SharkeyId);
 | 
			
		||||
	if (typeof q.since_id === 'string') q.since_id = convertId(q.since_id, IdType.SharkeyId);
 | 
			
		||||
	return q;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class ApiTimelineMastodon {
 | 
			
		||||
	private fastify: FastifyInstance;
 | 
			
		||||
 | 
			
		||||
	constructor(fastify: FastifyInstance) {
 | 
			
		||||
		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(convertTimelinesArgsId(argsToBools(limitToInt(query))))
 | 
			
		||||
					: await client.getPublicTimeline(convertTimelinesArgsId(argsToBools(limitToInt(query))));
 | 
			
		||||
				reply.send(data.data.map((status: Entity.Status) => 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(convertTimelinesArgsId(limitToInt(query)));
 | 
			
		||||
				reply.send(data.data.map((status: Entity.Status) => 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, convertTimelinesArgsId(limitToInt(query)));
 | 
			
		||||
				reply.send(data.data.map((status: Entity.Status) => 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(convertId(params.id, IdType.SharkeyId), convertTimelinesArgsId(limitToInt(query)));
 | 
			
		||||
				reply.send(data.data.map((status: Entity.Status) => 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(convertTimelinesArgsId(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(convertId(params.id, IdType.SharkeyId));
 | 
			
		||||
				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 account = await client.verifyAccountCredentials();
 | 
			
		||||
				const data = await client.getLists(account.data.id);
 | 
			
		||||
				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(
 | 
			
		||||
					convertId(params.id, IdType.SharkeyId),
 | 
			
		||||
					convertTimelinesArgsId(query),
 | 
			
		||||
				);
 | 
			
		||||
				reply.send(data.data.map((account: Entity.Account) => 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(
 | 
			
		||||
					convertId(params.id, IdType.SharkeyId),
 | 
			
		||||
					(query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)),
 | 
			
		||||
				);
 | 
			
		||||
				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(
 | 
			
		||||
					convertId(params.id, IdType.SharkeyId),
 | 
			
		||||
					(query.accounts_id as string[]).map((id) => convertId(id, IdType.SharkeyId)),
 | 
			
		||||
				);
 | 
			
		||||
				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(convertId(params.id, IdType.SharkeyId), 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(convertId(params.id, IdType.SharkeyId));
 | 
			
		||||
				reply.send(data.data);
 | 
			
		||||
			} catch (e: any) {
 | 
			
		||||
				console.error(e);
 | 
			
		||||
				console.error(e.response.data);
 | 
			
		||||
				reply.code(401).send(e.response.data);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/megalodon/.npmignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/megalodon/.npmignore
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
node_modules
 | 
			
		||||
./src
 | 
			
		||||
tsconfig.json
 | 
			
		||||
							
								
								
									
										87
									
								
								packages/megalodon/package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								packages/megalodon/package.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,87 @@
 | 
			
		|||
{
 | 
			
		||||
  "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": {
 | 
			
		||||
      "^@/(.+)": "<rootDir>/src/$1",
 | 
			
		||||
      "^~/(.+)": "<rootDir>/$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/oauth": "^0.9.2",
 | 
			
		||||
    "@types/ws": "^8.5.5",
 | 
			
		||||
    "axios": "1.5.0",
 | 
			
		||||
    "dayjs": "^1.11.9",
 | 
			
		||||
    "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": {
 | 
			
		||||
    "@types/core-js": "^2.5.6",
 | 
			
		||||
    "@types/form-data": "^2.5.0",
 | 
			
		||||
    "@types/jest": "^29.5.5",
 | 
			
		||||
    "@types/object-assign-deep": "^0.4.1",
 | 
			
		||||
    "@types/parse-link-header": "^2.0.1",
 | 
			
		||||
    "@types/uuid": "^9.0.4",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.7.2",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.7.2",
 | 
			
		||||
    "eslint": "^8.49.0",
 | 
			
		||||
    "eslint-config-prettier": "^9.0.0",
 | 
			
		||||
    "jest": "^29.7.0",
 | 
			
		||||
    "jest-worker": "^29.7.0",
 | 
			
		||||
    "lodash": "^4.17.14",
 | 
			
		||||
    "prettier": "^3.0.3",
 | 
			
		||||
    "ts-jest": "^29.1.1",
 | 
			
		||||
    "typedoc": "^0.25.1"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								packages/megalodon/src/axios.d.ts
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/megalodon/src/axios.d.ts
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
declare module 'axios/lib/adapters/http'
 | 
			
		||||
							
								
								
									
										13
									
								
								packages/megalodon/src/cancel.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/megalodon/src/cancel.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/megalodon/src/converter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/megalodon/src/converter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
import MisskeyAPI from "./misskey/api_client";
 | 
			
		||||
 | 
			
		||||
export default MisskeyAPI.Converter;
 | 
			
		||||
							
								
								
									
										3
									
								
								packages/megalodon/src/default.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/megalodon/src/default.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export const NO_REDIRECT = 'urn:ietf:wg:oauth:2.0:oob'
 | 
			
		||||
export const DEFAULT_SCOPE = ['read', 'write', 'follow']
 | 
			
		||||
export const DEFAULT_UA = 'megalodon'
 | 
			
		||||
							
								
								
									
										137
									
								
								packages/megalodon/src/detector.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								packages/megalodon/src/detector.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,137 @@
 | 
			
		|||
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<Link>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<Links>(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<Nodeinfo10>(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<Nodeinfo20>(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<Nodeinfo21>(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')
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								packages/megalodon/src/entities/account.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								packages/megalodon/src/entities/account.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="source.ts" />
 | 
			
		||||
/// <reference path="field.ts" />
 | 
			
		||||
/// <reference path="role.ts" />
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Account = {
 | 
			
		||||
    id: 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<Emoji>
 | 
			
		||||
    moved: Account | null
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
    bot: boolean | null
 | 
			
		||||
    source?: Source
 | 
			
		||||
    role?: Role
 | 
			
		||||
    mute_expires_at?: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/entities/activity.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/entities/activity.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Activity = {
 | 
			
		||||
    week: string
 | 
			
		||||
    statuses: string
 | 
			
		||||
    logins: string
 | 
			
		||||
    registrations: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								packages/megalodon/src/entities/announcement.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/megalodon/src/entities/announcement.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
/// <reference path="emoji.ts" />
 | 
			
		||||
 | 
			
		||||
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<AnnouncementAccount>
 | 
			
		||||
    statuses: Array<AnnouncementStatus>
 | 
			
		||||
    tags: Array<StatusTag>
 | 
			
		||||
    emojis: Array<Emoji>
 | 
			
		||||
    reactions: Array<AnnouncementReaction>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/entities/application.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/entities/application.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Application = {
 | 
			
		||||
    name: string
 | 
			
		||||
    website?: string | null
 | 
			
		||||
    vapid_key?: string | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								packages/megalodon/src/entities/async_attachment.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/megalodon/src/entities/async_attachment.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
/// <reference path="attachment.ts" />
 | 
			
		||||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								packages/megalodon/src/entities/attachment.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								packages/megalodon/src/entities/attachment.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								packages/megalodon/src/entities/card.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/megalodon/src/entities/card.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/entities/context.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/entities/context.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Context = {
 | 
			
		||||
    ancestors: Array<Status>
 | 
			
		||||
    descendants: Array<Status>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/megalodon/src/entities/conversation.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/megalodon/src/entities/conversation.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Conversation = {
 | 
			
		||||
    id: string
 | 
			
		||||
    accounts: Array<Account>
 | 
			
		||||
    last_status: Status | null
 | 
			
		||||
    unread: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/entities/emoji.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/entities/emoji.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Emoji = {
 | 
			
		||||
    shortcode: string
 | 
			
		||||
    static_url: string
 | 
			
		||||
    url: string
 | 
			
		||||
    visible_in_picker: boolean
 | 
			
		||||
    category?: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/entities/featured_tag.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/entities/featured_tag.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type FeaturedTag = {
 | 
			
		||||
    id: string
 | 
			
		||||
    name: string
 | 
			
		||||
    statuses_count: number
 | 
			
		||||
    last_status_at: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/entities/field.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/entities/field.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Field = {
 | 
			
		||||
    name: string
 | 
			
		||||
    value: string
 | 
			
		||||
    verified_at: string | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								packages/megalodon/src/entities/filter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								packages/megalodon/src/entities/filter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Filter = {
 | 
			
		||||
    id: string
 | 
			
		||||
    phrase: string
 | 
			
		||||
    context: Array<FilterContext>
 | 
			
		||||
    expires_at: string | null
 | 
			
		||||
    irreversible: boolean
 | 
			
		||||
    whole_word: boolean
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type FilterContext = string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								packages/megalodon/src/entities/follow_request.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								packages/megalodon/src/entities/follow_request.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="field.ts" />
 | 
			
		||||
 | 
			
		||||
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<Emoji>
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/entities/history.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/entities/history.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type History = {
 | 
			
		||||
    day: string
 | 
			
		||||
    uses: number
 | 
			
		||||
    accounts: number
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/entities/identity_proof.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/entities/identity_proof.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type IdentityProof = {
 | 
			
		||||
    provider: string
 | 
			
		||||
    provider_username: string
 | 
			
		||||
    updated_at: string
 | 
			
		||||
    proof_url: string
 | 
			
		||||
    profile_url: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								packages/megalodon/src/entities/instance.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/megalodon/src/entities/instance.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="urls.ts" />
 | 
			
		||||
/// <reference path="stats.ts" />
 | 
			
		||||
 | 
			
		||||
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<string>
 | 
			
		||||
    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<InstanceRule>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type InstanceRule = {
 | 
			
		||||
    id: string
 | 
			
		||||
    text: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/entities/list.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/entities/list.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type List = {
 | 
			
		||||
    id: string
 | 
			
		||||
    title: string
 | 
			
		||||
    replies_policy: RepliesPolicy | null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type RepliesPolicy = 'followed' | 'list' | 'none'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								packages/megalodon/src/entities/marker.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/megalodon/src/entities/marker.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
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
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/entities/mention.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/entities/mention.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Mention = {
 | 
			
		||||
    id: string
 | 
			
		||||
    username: string
 | 
			
		||||
    url: string
 | 
			
		||||
    acct: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/megalodon/src/entities/notification.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/megalodon/src/entities/notification.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Notification = {
 | 
			
		||||
    account: Account
 | 
			
		||||
    created_at: string
 | 
			
		||||
    id: string
 | 
			
		||||
    status?: Status
 | 
			
		||||
    emoji?: string
 | 
			
		||||
    type: NotificationType
 | 
			
		||||
    target?: Account
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type NotificationType = string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								packages/megalodon/src/entities/poll.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/megalodon/src/entities/poll.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
/// <reference path="poll_option.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Poll = {
 | 
			
		||||
    id: string
 | 
			
		||||
    expires_at: string | null
 | 
			
		||||
    expired: boolean
 | 
			
		||||
    multiple: boolean
 | 
			
		||||
    votes_count: number
 | 
			
		||||
    options: Array<PollOption>
 | 
			
		||||
    voted: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/megalodon/src/entities/poll_option.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/megalodon/src/entities/poll_option.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type PollOption = {
 | 
			
		||||
    title: string
 | 
			
		||||
    votes_count: number | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/entities/preferences.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/entities/preferences.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/megalodon/src/entities/push_subscription.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/megalodon/src/entities/push_subscription.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/megalodon/src/entities/reaction.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/megalodon/src/entities/reaction.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Reaction = {
 | 
			
		||||
    count: number
 | 
			
		||||
    me: boolean
 | 
			
		||||
    name: string
 | 
			
		||||
    accounts?: Array<Account>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/megalodon/src/entities/relationship.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/megalodon/src/entities/relationship.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								packages/megalodon/src/entities/report.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								packages/megalodon/src/entities/report.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Report = {
 | 
			
		||||
    id: string
 | 
			
		||||
    action_taken: boolean
 | 
			
		||||
    action_taken_at: string | null
 | 
			
		||||
    status_ids: Array<string> | null
 | 
			
		||||
    rule_ids: Array<string> | 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'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/megalodon/src/entities/results.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/megalodon/src/entities/results.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
/// <reference path="tag.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Results = {
 | 
			
		||||
    accounts: Array<Account>
 | 
			
		||||
    statuses: Array<Status>
 | 
			
		||||
    hashtags: Array<Tag>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								packages/megalodon/src/entities/role.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/megalodon/src/entities/role.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Role = {
 | 
			
		||||
    name: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/megalodon/src/entities/scheduled_status.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/megalodon/src/entities/scheduled_status.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="attachment.ts" />
 | 
			
		||||
/// <reference path="status_params.ts" />
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type ScheduledStatus = {
 | 
			
		||||
    id: string
 | 
			
		||||
    scheduled_at: string
 | 
			
		||||
    params: StatusParams
 | 
			
		||||
    media_attachments: Array<Attachment> | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/megalodon/src/entities/source.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/megalodon/src/entities/source.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="field.ts" />
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Source = {
 | 
			
		||||
    privacy: string | null
 | 
			
		||||
    sensitive: boolean | null
 | 
			
		||||
    language: string | null
 | 
			
		||||
    note: string
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/entities/stats.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/entities/stats.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Stats = {
 | 
			
		||||
    user_count: number
 | 
			
		||||
    status_count: number
 | 
			
		||||
    domain_count: number
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								packages/megalodon/src/entities/status.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								packages/megalodon/src/entities/status.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="application.ts" />
 | 
			
		||||
/// <reference path="mention.ts" />
 | 
			
		||||
/// <reference path="attachment.ts" />
 | 
			
		||||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="card.ts" />
 | 
			
		||||
/// <reference path="poll.ts" />
 | 
			
		||||
/// <reference path="reaction.ts" />
 | 
			
		||||
 | 
			
		||||
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<Attachment>
 | 
			
		||||
    mentions: Array<Mention>
 | 
			
		||||
    tags: Array<StatusTag>
 | 
			
		||||
    card: Card | null
 | 
			
		||||
    poll: Poll | null
 | 
			
		||||
    application: Application | null
 | 
			
		||||
    language: string | null
 | 
			
		||||
    pinned: boolean | null
 | 
			
		||||
    emoji_reactions: Array<Reaction>
 | 
			
		||||
    quote: boolean
 | 
			
		||||
    bookmarked: boolean
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type StatusTag = {
 | 
			
		||||
    name: string
 | 
			
		||||
    url: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								packages/megalodon/src/entities/status_edit.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/megalodon/src/entities/status_edit.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="application.ts" />
 | 
			
		||||
/// <reference path="mention.ts" />
 | 
			
		||||
/// <reference path="tag.ts" />
 | 
			
		||||
/// <reference path="attachment.ts" />
 | 
			
		||||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="card.ts" />
 | 
			
		||||
/// <reference path="poll.ts" />
 | 
			
		||||
/// <reference path="reaction.ts" />
 | 
			
		||||
 | 
			
		||||
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<Attachment>;
 | 
			
		||||
		poll: Poll | null;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								packages/megalodon/src/entities/status_params.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								packages/megalodon/src/entities/status_params.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type StatusParams = {
 | 
			
		||||
    text: string
 | 
			
		||||
    in_reply_to_id: string | null
 | 
			
		||||
    media_ids: Array<string> | null
 | 
			
		||||
    sensitive: boolean | null
 | 
			
		||||
    spoiler_text: string | null
 | 
			
		||||
    visibility: 'public' | 'unlisted' | 'private' | 'direct' | null
 | 
			
		||||
    scheduled_at: string | null
 | 
			
		||||
    application_id: number | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/entities/status_source.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/entities/status_source.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type StatusSource = {
 | 
			
		||||
    id: string
 | 
			
		||||
    text: string
 | 
			
		||||
    spoiler_text: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/megalodon/src/entities/tag.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/megalodon/src/entities/tag.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="history.ts" />
 | 
			
		||||
 | 
			
		||||
namespace Entity {
 | 
			
		||||
  export type Tag = {
 | 
			
		||||
    name: string
 | 
			
		||||
    url: string
 | 
			
		||||
    history: Array<History>
 | 
			
		||||
    following?: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/entities/token.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/entities/token.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type Token = {
 | 
			
		||||
    access_token: string
 | 
			
		||||
    token_type: string
 | 
			
		||||
    scope: string
 | 
			
		||||
    created_at: number
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								packages/megalodon/src/entities/urls.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								packages/megalodon/src/entities/urls.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
namespace Entity {
 | 
			
		||||
  export type URLs = {
 | 
			
		||||
    streaming_api: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								packages/megalodon/src/entity.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								packages/megalodon/src/entity.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
/// <reference path="./entities/account.ts" />
 | 
			
		||||
/// <reference path="./entities/activity.ts" />
 | 
			
		||||
/// <reference path="./entities/announcement.ts" />
 | 
			
		||||
/// <reference path="./entities/application.ts" />
 | 
			
		||||
/// <reference path="./entities/async_attachment.ts" />
 | 
			
		||||
/// <reference path="./entities/attachment.ts" />
 | 
			
		||||
/// <reference path="./entities/card.ts" />
 | 
			
		||||
/// <reference path="./entities/context.ts" />
 | 
			
		||||
/// <reference path="./entities/conversation.ts" />
 | 
			
		||||
/// <reference path="./entities/emoji.ts" />
 | 
			
		||||
/// <reference path="./entities/featured_tag.ts" />
 | 
			
		||||
/// <reference path="./entities/field.ts" />
 | 
			
		||||
/// <reference path="./entities/filter.ts" />
 | 
			
		||||
/// <reference path="./entities/follow_request.ts" />
 | 
			
		||||
/// <reference path="./entities/history.ts" />
 | 
			
		||||
/// <reference path="./entities/identity_proof.ts" />
 | 
			
		||||
/// <reference path="./entities/instance.ts" />
 | 
			
		||||
/// <reference path="./entities/list.ts" />
 | 
			
		||||
/// <reference path="./entities/marker.ts" />
 | 
			
		||||
/// <reference path="./entities/mention.ts" />
 | 
			
		||||
/// <reference path="./entities/notification.ts" />
 | 
			
		||||
/// <reference path="./entities/poll.ts" />
 | 
			
		||||
/// <reference path="./entities/poll_option.ts" />
 | 
			
		||||
/// <reference path="./entities/preferences.ts" />
 | 
			
		||||
/// <reference path="./entities/push_subscription.ts" />
 | 
			
		||||
/// <reference path="./entities/reaction.ts" />
 | 
			
		||||
/// <reference path="./entities/relationship.ts" />
 | 
			
		||||
/// <reference path="./entities/report.ts" />
 | 
			
		||||
/// <reference path="./entities/results.ts" />
 | 
			
		||||
/// <reference path="./entities/scheduled_status.ts" />
 | 
			
		||||
/// <reference path="./entities/source.ts" />
 | 
			
		||||
/// <reference path="./entities/stats.ts" />
 | 
			
		||||
/// <reference path="./entities/status.ts" />
 | 
			
		||||
/// <reference path="./entities/status_params.ts" />
 | 
			
		||||
/// <reference path="./entities/status_source.ts" />
 | 
			
		||||
/// <reference path="./entities/tag.ts" />
 | 
			
		||||
/// <reference path="./entities/token.ts" />
 | 
			
		||||
/// <reference path="./entities/urls.ts" />
 | 
			
		||||
 | 
			
		||||
export default Entity
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/megalodon/src/filter_context.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/megalodon/src/filter_context.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
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
 | 
			
		||||
							
								
								
									
										2868
									
								
								packages/megalodon/src/friendica.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2868
									
								
								packages/megalodon/src/friendica.ts
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										768
									
								
								packages/megalodon/src/friendica/api_client.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										768
									
								
								packages/megalodon/src/friendica/api_client.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,768 @@
 | 
			
		|||
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
 | 
			
		||||
import objectAssignDeep from 'object-assign-deep'
 | 
			
		||||
 | 
			
		||||
import WebSocket from './web_socket'
 | 
			
		||||
import Response from '../response'
 | 
			
		||||
import { RequestCanceledError } from '../cancel'
 | 
			
		||||
import proxyAgent, { ProxyConfig } from '../proxy_config'
 | 
			
		||||
import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from '../default'
 | 
			
		||||
import FriendicaEntity from './entity'
 | 
			
		||||
import MegalodonEntity from '../entity'
 | 
			
		||||
import NotificationType, { UnknownNotificationTypeError } from '../notification'
 | 
			
		||||
import FriendicaNotificationType from './notification'
 | 
			
		||||
 | 
			
		||||
namespace FriendicaAPI {
 | 
			
		||||
  /**
 | 
			
		||||
   * Interface
 | 
			
		||||
   */
 | 
			
		||||
  export interface Interface {
 | 
			
		||||
    get<T = any>(path: string, params?: any, headers?: { [key: string]: string }, pathIsFullyQualified?: boolean): Promise<Response<T>>
 | 
			
		||||
    put<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    putForm<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    patch<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    patchForm<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    post<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    postForm<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    del<T = any>(path: string, params?: any, headers?: { [key: string]: string }): Promise<Response<T>>
 | 
			
		||||
    cancel(): void
 | 
			
		||||
    socket(path: string, stream: string, params?: string): WebSocket
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Friendica API client.
 | 
			
		||||
   *
 | 
			
		||||
   * Using axios for request, you will handle promises.
 | 
			
		||||
   */
 | 
			
		||||
  export class Client implements Interface {
 | 
			
		||||
    static DEFAULT_SCOPE = DEFAULT_SCOPE
 | 
			
		||||
    static DEFAULT_URL = 'https://mastodon.social'
 | 
			
		||||
    static NO_REDIRECT = NO_REDIRECT
 | 
			
		||||
 | 
			
		||||
    private accessToken: string | null
 | 
			
		||||
    private baseUrl: string
 | 
			
		||||
    private userAgent: string
 | 
			
		||||
    private abortController: AbortController
 | 
			
		||||
    private proxyConfig: ProxyConfig | false = false
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @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 = DEFAULT_UA,
 | 
			
		||||
      proxyConfig: ProxyConfig | false = false
 | 
			
		||||
    ) {
 | 
			
		||||
      this.accessToken = accessToken
 | 
			
		||||
      this.baseUrl = baseUrl
 | 
			
		||||
      this.userAgent = userAgent
 | 
			
		||||
      this.proxyConfig = proxyConfig
 | 
			
		||||
      this.abortController = new AbortController()
 | 
			
		||||
      axios.defaults.signal = this.abortController.signal
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * GET request to mastodon REST API.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Query parameters
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async get<T>(
 | 
			
		||||
      path: string,
 | 
			
		||||
      params = {},
 | 
			
		||||
      headers: { [key: string]: string } = {},
 | 
			
		||||
      pathIsFullyQualified = false
 | 
			
		||||
    ): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        params: params,
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .get<T>((pathIsFullyQualified ? '' : this.baseUrl) + path, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse<T>) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * PUT request to mastodon REST API.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data. If you want to post file, please use FormData()
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async put<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .put<T>(this.baseUrl + path, params, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse<T>) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * PUT request to mastodon REST API for multipart.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data. If you want to post file, please use FormData()
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async putForm<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .putForm<T>(this.baseUrl + path, params, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse<T>) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * PATCH request to mastodon REST API.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data. If you want to post file, please use FormData()
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async patch<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .patch<T>(this.baseUrl + path, params, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse<T>) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * PATCH request to mastodon REST API for multipart.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data. If you want to post file, please use FormData()
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async patchForm<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .patchForm<T>(this.baseUrl + path, params, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse<T>) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * POST request to mastodon REST API.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async post<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios.post<T>(this.baseUrl + path, params, options).then((resp: AxiosResponse<T>) => {
 | 
			
		||||
        const res: Response<T> = {
 | 
			
		||||
          data: resp.data,
 | 
			
		||||
          status: resp.status,
 | 
			
		||||
          statusText: resp.statusText,
 | 
			
		||||
          headers: resp.headers
 | 
			
		||||
        }
 | 
			
		||||
        return res
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * POST request to mastodon REST API for multipart.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async postForm<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios.postForm<T>(this.baseUrl + path, params, options).then((resp: AxiosResponse<T>) => {
 | 
			
		||||
        const res: Response<T> = {
 | 
			
		||||
          data: resp.data,
 | 
			
		||||
          status: resp.status,
 | 
			
		||||
          statusText: resp.statusText,
 | 
			
		||||
          headers: resp.headers
 | 
			
		||||
        }
 | 
			
		||||
        return res
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * DELETE request to mastodon REST API.
 | 
			
		||||
     * @param path relative path from baseUrl
 | 
			
		||||
     * @param params Form data
 | 
			
		||||
     * @param headers Request header object
 | 
			
		||||
     */
 | 
			
		||||
    public async del<T>(path: string, params = {}, headers: { [key: string]: string } = {}): Promise<Response<T>> {
 | 
			
		||||
      let options: AxiosRequestConfig = {
 | 
			
		||||
        data: params,
 | 
			
		||||
        headers: headers,
 | 
			
		||||
        maxContentLength: Infinity,
 | 
			
		||||
        maxBodyLength: Infinity
 | 
			
		||||
      }
 | 
			
		||||
      if (this.accessToken) {
 | 
			
		||||
        options = objectAssignDeep({}, options, {
 | 
			
		||||
          headers: {
 | 
			
		||||
            Authorization: `Bearer ${this.accessToken}`
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      if (this.proxyConfig) {
 | 
			
		||||
        options = Object.assign(options, {
 | 
			
		||||
          httpAgent: proxyAgent(this.proxyConfig),
 | 
			
		||||
          httpsAgent: proxyAgent(this.proxyConfig)
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      return axios
 | 
			
		||||
        .delete(this.baseUrl + path, options)
 | 
			
		||||
        .catch((err: Error) => {
 | 
			
		||||
          if (axios.isCancel(err)) {
 | 
			
		||||
            throw new RequestCanceledError(err.message)
 | 
			
		||||
          } else {
 | 
			
		||||
            throw err
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .then((resp: AxiosResponse) => {
 | 
			
		||||
          const res: Response<T> = {
 | 
			
		||||
            data: resp.data,
 | 
			
		||||
            status: resp.status,
 | 
			
		||||
            statusText: resp.statusText,
 | 
			
		||||
            headers: resp.headers
 | 
			
		||||
          }
 | 
			
		||||
          return res
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Cancel all requests in this instance.
 | 
			
		||||
     * @returns void
 | 
			
		||||
     */
 | 
			
		||||
    public cancel(): void {
 | 
			
		||||
      return this.abortController.abort()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get connection and receive websocket connection for Pleroma API.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path relative path from baseUrl: normally it is `/streaming`.
 | 
			
		||||
     * @param stream Stream name, please refer: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/web/mastodon_api/mastodon_socket.ex#L19-28
 | 
			
		||||
     * @returns WebSocket, which inherits from EventEmitter
 | 
			
		||||
     */
 | 
			
		||||
    public socket(path: string, stream: string, params?: string): WebSocket {
 | 
			
		||||
      if (!this.accessToken) {
 | 
			
		||||
        throw new Error('accessToken is required')
 | 
			
		||||
      }
 | 
			
		||||
      const url = this.baseUrl + path
 | 
			
		||||
      const streaming = new WebSocket(url, stream, params, this.accessToken, this.userAgent, this.proxyConfig)
 | 
			
		||||
      process.nextTick(() => {
 | 
			
		||||
        streaming.start()
 | 
			
		||||
      })
 | 
			
		||||
      return streaming
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export namespace Entity {
 | 
			
		||||
    export type Account = FriendicaEntity.Account
 | 
			
		||||
    export type Activity = FriendicaEntity.Activity
 | 
			
		||||
    export type Application = FriendicaEntity.Application
 | 
			
		||||
    export type AsyncAttachment = FriendicaEntity.AsyncAttachment
 | 
			
		||||
    export type Attachment = FriendicaEntity.Attachment
 | 
			
		||||
    export type Card = FriendicaEntity.Card
 | 
			
		||||
    export type Context = FriendicaEntity.Context
 | 
			
		||||
    export type Conversation = FriendicaEntity.Conversation
 | 
			
		||||
    export type Emoji = FriendicaEntity.Emoji
 | 
			
		||||
    export type FeaturedTag = FriendicaEntity.FeaturedTag
 | 
			
		||||
    export type Field = FriendicaEntity.Field
 | 
			
		||||
    export type Filter = FriendicaEntity.Filter
 | 
			
		||||
    export type FollowRequest = FriendicaEntity.FollowRequest
 | 
			
		||||
    export type History = FriendicaEntity.History
 | 
			
		||||
    export type IdentityProof = FriendicaEntity.IdentityProof
 | 
			
		||||
    export type Instance = FriendicaEntity.Instance
 | 
			
		||||
    export type List = FriendicaEntity.List
 | 
			
		||||
    export type Marker = FriendicaEntity.Marker
 | 
			
		||||
    export type Mention = FriendicaEntity.Mention
 | 
			
		||||
    export type Notification = FriendicaEntity.Notification
 | 
			
		||||
    export type Poll = FriendicaEntity.Poll
 | 
			
		||||
    export type PollOption = FriendicaEntity.PollOption
 | 
			
		||||
    export type Preferences = FriendicaEntity.Preferences
 | 
			
		||||
    export type PushSubscription = FriendicaEntity.PushSubscription
 | 
			
		||||
    export type Relationship = FriendicaEntity.Relationship
 | 
			
		||||
    export type Report = FriendicaEntity.Report
 | 
			
		||||
    export type Results = FriendicaEntity.Results
 | 
			
		||||
    export type ScheduledStatus = FriendicaEntity.ScheduledStatus
 | 
			
		||||
    export type Source = FriendicaEntity.Source
 | 
			
		||||
    export type Stats = FriendicaEntity.Stats
 | 
			
		||||
    export type Status = FriendicaEntity.Status
 | 
			
		||||
    export type StatusParams = FriendicaEntity.StatusParams
 | 
			
		||||
    export type StatusSource = FriendicaEntity.StatusSource
 | 
			
		||||
    export type Tag = FriendicaEntity.Tag
 | 
			
		||||
    export type Token = FriendicaEntity.Token
 | 
			
		||||
    export type URLs = FriendicaEntity.URLs
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export namespace Converter {
 | 
			
		||||
    export const encodeNotificationType = (
 | 
			
		||||
      t: MegalodonEntity.NotificationType
 | 
			
		||||
    ): FriendicaEntity.NotificationType | UnknownNotificationTypeError => {
 | 
			
		||||
      switch (t) {
 | 
			
		||||
        case NotificationType.Follow:
 | 
			
		||||
          return FriendicaNotificationType.Follow
 | 
			
		||||
        case NotificationType.Favourite:
 | 
			
		||||
          return FriendicaNotificationType.Favourite
 | 
			
		||||
        case NotificationType.Reblog:
 | 
			
		||||
          return FriendicaNotificationType.Reblog
 | 
			
		||||
        case NotificationType.Mention:
 | 
			
		||||
          return FriendicaNotificationType.Mention
 | 
			
		||||
        case NotificationType.FollowRequest:
 | 
			
		||||
          return FriendicaNotificationType.FollowRequest
 | 
			
		||||
        case NotificationType.Status:
 | 
			
		||||
          return FriendicaNotificationType.Status
 | 
			
		||||
        case NotificationType.PollExpired:
 | 
			
		||||
          return FriendicaNotificationType.Poll
 | 
			
		||||
        case NotificationType.Update:
 | 
			
		||||
          return FriendicaNotificationType.Update
 | 
			
		||||
        default:
 | 
			
		||||
          return new UnknownNotificationTypeError()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export const decodeNotificationType = (
 | 
			
		||||
      t: FriendicaEntity.NotificationType
 | 
			
		||||
    ): MegalodonEntity.NotificationType | UnknownNotificationTypeError => {
 | 
			
		||||
      switch (t) {
 | 
			
		||||
        case FriendicaNotificationType.Follow:
 | 
			
		||||
          return NotificationType.Follow
 | 
			
		||||
        case FriendicaNotificationType.Favourite:
 | 
			
		||||
          return NotificationType.Favourite
 | 
			
		||||
        case FriendicaNotificationType.Mention:
 | 
			
		||||
          return NotificationType.Mention
 | 
			
		||||
        case FriendicaNotificationType.Reblog:
 | 
			
		||||
          return NotificationType.Reblog
 | 
			
		||||
        case FriendicaNotificationType.FollowRequest:
 | 
			
		||||
          return NotificationType.FollowRequest
 | 
			
		||||
        case FriendicaNotificationType.Status:
 | 
			
		||||
          return NotificationType.Status
 | 
			
		||||
        case FriendicaNotificationType.Poll:
 | 
			
		||||
          return NotificationType.PollExpired
 | 
			
		||||
        case FriendicaNotificationType.Update:
 | 
			
		||||
          return NotificationType.Update
 | 
			
		||||
        default:
 | 
			
		||||
          return new UnknownNotificationTypeError()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export const account = (a: Entity.Account): MegalodonEntity.Account => ({
 | 
			
		||||
      id: a.id,
 | 
			
		||||
      username: a.username,
 | 
			
		||||
      acct: a.acct,
 | 
			
		||||
      display_name: a.display_name,
 | 
			
		||||
      locked: a.locked,
 | 
			
		||||
      discoverable: a.discoverable,
 | 
			
		||||
      group: a.group,
 | 
			
		||||
      noindex: null,
 | 
			
		||||
      suspended: null,
 | 
			
		||||
      limited: null,
 | 
			
		||||
      created_at: a.created_at,
 | 
			
		||||
      followers_count: a.followers_count,
 | 
			
		||||
      following_count: a.following_count,
 | 
			
		||||
      statuses_count: a.statuses_count,
 | 
			
		||||
      note: a.note,
 | 
			
		||||
      url: a.url,
 | 
			
		||||
      avatar: a.avatar,
 | 
			
		||||
      avatar_static: a.avatar_static,
 | 
			
		||||
      header: a.header,
 | 
			
		||||
      header_static: a.header_static,
 | 
			
		||||
      emojis: a.emojis.map(e => emoji(e)),
 | 
			
		||||
      moved: a.moved ? account(a.moved) : null,
 | 
			
		||||
      fields: a.fields.map(f => field(f)),
 | 
			
		||||
      bot: a.bot,
 | 
			
		||||
      source: a.source ? source(a.source) : undefined
 | 
			
		||||
    })
 | 
			
		||||
    export const activity = (a: Entity.Activity): MegalodonEntity.Activity => a
 | 
			
		||||
    export const application = (a: Entity.Application): MegalodonEntity.Application => a
 | 
			
		||||
    export const attachment = (a: Entity.Attachment): MegalodonEntity.Attachment => a
 | 
			
		||||
    export const async_attachment = (a: Entity.AsyncAttachment) => {
 | 
			
		||||
      if (a.url) {
 | 
			
		||||
        return {
 | 
			
		||||
          id: a.id,
 | 
			
		||||
          type: a.type,
 | 
			
		||||
          url: a.url,
 | 
			
		||||
          remote_url: a.remote_url,
 | 
			
		||||
          preview_url: a.preview_url,
 | 
			
		||||
          text_url: a.text_url,
 | 
			
		||||
          meta: a.meta,
 | 
			
		||||
          description: a.description,
 | 
			
		||||
          blurhash: a.blurhash
 | 
			
		||||
        } as MegalodonEntity.Attachment
 | 
			
		||||
      } else {
 | 
			
		||||
        return a as MegalodonEntity.AsyncAttachment
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    export const card = (c: Entity.Card): MegalodonEntity.Card => ({
 | 
			
		||||
      url: c.url,
 | 
			
		||||
      title: c.title,
 | 
			
		||||
      description: c.description,
 | 
			
		||||
      type: c.type,
 | 
			
		||||
      image: c.image,
 | 
			
		||||
      author_name: c.author_name,
 | 
			
		||||
      author_url: c.author_url,
 | 
			
		||||
      provider_name: c.provider_name,
 | 
			
		||||
      provider_url: c.provider_url,
 | 
			
		||||
      html: c.html,
 | 
			
		||||
      width: c.width,
 | 
			
		||||
      height: c.height,
 | 
			
		||||
      embed_url: null,
 | 
			
		||||
      blurhash: c.blurhash
 | 
			
		||||
    })
 | 
			
		||||
    export const context = (c: Entity.Context): MegalodonEntity.Context => ({
 | 
			
		||||
      ancestors: Array.isArray(c.ancestors) ? c.ancestors.map(a => status(a)) : [],
 | 
			
		||||
      descendants: Array.isArray(c.descendants) ? c.descendants.map(d => status(d)) : []
 | 
			
		||||
    })
 | 
			
		||||
    export const conversation = (c: Entity.Conversation): MegalodonEntity.Conversation => ({
 | 
			
		||||
      id: c.id,
 | 
			
		||||
      accounts: Array.isArray(c.accounts) ? c.accounts.map(a => account(a)) : [],
 | 
			
		||||
      last_status: c.last_status ? status(c.last_status) : null,
 | 
			
		||||
      unread: c.unread
 | 
			
		||||
    })
 | 
			
		||||
    export const emoji = (e: Entity.Emoji): MegalodonEntity.Emoji => ({
 | 
			
		||||
      shortcode: e.shortcode,
 | 
			
		||||
      static_url: e.static_url,
 | 
			
		||||
      url: e.url,
 | 
			
		||||
      visible_in_picker: e.visible_in_picker
 | 
			
		||||
    })
 | 
			
		||||
    export const featured_tag = (e: Entity.FeaturedTag): MegalodonEntity.FeaturedTag => e
 | 
			
		||||
    export const field = (f: Entity.Field): MegalodonEntity.Field => f
 | 
			
		||||
    export const filter = (f: Entity.Filter): MegalodonEntity.Filter => f
 | 
			
		||||
    export const follow_request = (f: Entity.FollowRequest): MegalodonEntity.FollowRequest => ({
 | 
			
		||||
      id: f.id,
 | 
			
		||||
      username: f.username,
 | 
			
		||||
      acct: f.acct,
 | 
			
		||||
      display_name: f.display_name,
 | 
			
		||||
      locked: f.locked,
 | 
			
		||||
      bot: f.bot,
 | 
			
		||||
      discoverable: f.discoverable,
 | 
			
		||||
      group: f.group,
 | 
			
		||||
      created_at: f.created_at,
 | 
			
		||||
      note: f.note,
 | 
			
		||||
      url: f.url,
 | 
			
		||||
      avatar: f.avatar,
 | 
			
		||||
      avatar_static: f.avatar_static,
 | 
			
		||||
      header: f.header,
 | 
			
		||||
      header_static: f.header_static,
 | 
			
		||||
      followers_count: f.followers_count,
 | 
			
		||||
      following_count: f.following_count,
 | 
			
		||||
      statuses_count: f.statuses_count,
 | 
			
		||||
      emojis: f.emojis.map(e => emoji(e)),
 | 
			
		||||
      fields: f.fields.map(f => field(f))
 | 
			
		||||
    })
 | 
			
		||||
    export const history = (h: Entity.History): MegalodonEntity.History => h
 | 
			
		||||
    export const identity_proof = (i: Entity.IdentityProof): MegalodonEntity.IdentityProof => i
 | 
			
		||||
    export const instance = (i: Entity.Instance): MegalodonEntity.Instance => {
 | 
			
		||||
      return {
 | 
			
		||||
        uri: i.uri,
 | 
			
		||||
        title: i.title,
 | 
			
		||||
        description: i.description,
 | 
			
		||||
        email: i.email,
 | 
			
		||||
        version: i.version,
 | 
			
		||||
        thumbnail: i.thumbnail,
 | 
			
		||||
        urls: i.urls ? urls(i.urls) : null,
 | 
			
		||||
        stats: stats(i.stats),
 | 
			
		||||
        languages: i.languages,
 | 
			
		||||
        registrations: i.registrations,
 | 
			
		||||
        approval_required: i.approval_required,
 | 
			
		||||
        invites_enabled: i.invites_enabled,
 | 
			
		||||
        configuration: {
 | 
			
		||||
          statuses: {
 | 
			
		||||
            max_characters: i.max_toot_chars
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        contact_account: account(i.contact_account),
 | 
			
		||||
        rules: i.rules
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    export const list = (l: Entity.List): MegalodonEntity.List => l
 | 
			
		||||
    export const marker = (m: Entity.Marker): MegalodonEntity.Marker => m
 | 
			
		||||
    export const mention = (m: Entity.Mention): MegalodonEntity.Mention => m
 | 
			
		||||
    export const notification = (n: Entity.Notification): MegalodonEntity.Notification | UnknownNotificationTypeError => {
 | 
			
		||||
      const notificationType = decodeNotificationType(n.type)
 | 
			
		||||
      if (notificationType instanceof UnknownNotificationTypeError) return notificationType
 | 
			
		||||
      if (n.status) {
 | 
			
		||||
        return {
 | 
			
		||||
          account: account(n.account),
 | 
			
		||||
          created_at: n.created_at,
 | 
			
		||||
          id: n.id,
 | 
			
		||||
          status: status(n.status),
 | 
			
		||||
          type: notificationType
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return {
 | 
			
		||||
          account: account(n.account),
 | 
			
		||||
          created_at: n.created_at,
 | 
			
		||||
          id: n.id,
 | 
			
		||||
          type: notificationType
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    export const poll = (p: Entity.Poll): MegalodonEntity.Poll => p
 | 
			
		||||
    export const poll_option = (p: Entity.PollOption): MegalodonEntity.PollOption => p
 | 
			
		||||
    export const preferences = (p: Entity.Preferences): MegalodonEntity.Preferences => p
 | 
			
		||||
    export const push_subscription = (p: Entity.PushSubscription): MegalodonEntity.PushSubscription => p
 | 
			
		||||
    export const relationship = (r: Entity.Relationship): MegalodonEntity.Relationship => r
 | 
			
		||||
    export const report = (r: Entity.Report): MegalodonEntity.Report => ({
 | 
			
		||||
      id: r.id,
 | 
			
		||||
      action_taken: r.action_taken,
 | 
			
		||||
      action_taken_at: null,
 | 
			
		||||
      category: r.category,
 | 
			
		||||
      comment: r.comment,
 | 
			
		||||
      forwarded: r.forwarded,
 | 
			
		||||
      status_ids: r.status_ids,
 | 
			
		||||
      rule_ids: r.rule_ids,
 | 
			
		||||
      target_account: account(r.target_account)
 | 
			
		||||
    })
 | 
			
		||||
    export const results = (r: Entity.Results): MegalodonEntity.Results => ({
 | 
			
		||||
      accounts: Array.isArray(r.accounts) ? r.accounts.map(a => account(a)) : [],
 | 
			
		||||
      statuses: Array.isArray(r.statuses) ? r.statuses.map(s => status(s)) : [],
 | 
			
		||||
      hashtags: Array.isArray(r.hashtags) ? r.hashtags.map(h => tag(h)) : []
 | 
			
		||||
    })
 | 
			
		||||
    export const scheduled_status = (s: Entity.ScheduledStatus): MegalodonEntity.ScheduledStatus => {
 | 
			
		||||
      return {
 | 
			
		||||
        id: s.id,
 | 
			
		||||
        scheduled_at: s.scheduled_at,
 | 
			
		||||
        params: status_params(s.params),
 | 
			
		||||
        media_attachments: s.media_attachments ? s.media_attachments.map(a => attachment(a)) : null
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    export const source = (s: Entity.Source): MegalodonEntity.Source => s
 | 
			
		||||
    export const stats = (s: Entity.Stats): MegalodonEntity.Stats => s
 | 
			
		||||
    export const status = (s: Entity.Status): MegalodonEntity.Status => ({
 | 
			
		||||
      id: s.id,
 | 
			
		||||
      uri: s.uri,
 | 
			
		||||
      url: s.url,
 | 
			
		||||
      account: account(s.account),
 | 
			
		||||
      in_reply_to_id: s.in_reply_to_id,
 | 
			
		||||
      in_reply_to_account_id: s.in_reply_to_account_id,
 | 
			
		||||
      reblog: s.reblog ? status(s.reblog) : s.quote ? status(s.quote) : null,
 | 
			
		||||
      content: s.content,
 | 
			
		||||
      plain_content: null,
 | 
			
		||||
      created_at: s.created_at,
 | 
			
		||||
      emojis: Array.isArray(s.emojis) ? s.emojis.map(e => emoji(e)) : [],
 | 
			
		||||
      replies_count: s.replies_count,
 | 
			
		||||
      reblogs_count: s.reblogs_count,
 | 
			
		||||
      favourites_count: s.favourites_count,
 | 
			
		||||
      reblogged: s.reblogged,
 | 
			
		||||
      favourited: s.favourited,
 | 
			
		||||
      muted: s.muted,
 | 
			
		||||
      sensitive: s.sensitive,
 | 
			
		||||
      spoiler_text: s.spoiler_text,
 | 
			
		||||
      visibility: s.visibility,
 | 
			
		||||
      media_attachments: Array.isArray(s.media_attachments) ? s.media_attachments.map(m => attachment(m)) : [],
 | 
			
		||||
      mentions: Array.isArray(s.mentions) ? s.mentions.map(m => mention(m)) : [],
 | 
			
		||||
      tags: s.tags,
 | 
			
		||||
      card: s.card ? card(s.card) : null,
 | 
			
		||||
      poll: s.poll ? poll(s.poll) : null,
 | 
			
		||||
      application: s.application ? application(s.application) : null,
 | 
			
		||||
      language: s.language,
 | 
			
		||||
      pinned: s.pinned,
 | 
			
		||||
      emoji_reactions: [],
 | 
			
		||||
      bookmarked: s.bookmarked ? s.bookmarked : false,
 | 
			
		||||
      quote: false
 | 
			
		||||
    })
 | 
			
		||||
    export const status_params = (s: Entity.StatusParams): MegalodonEntity.StatusParams => {
 | 
			
		||||
      return {
 | 
			
		||||
        text: s.text,
 | 
			
		||||
        in_reply_to_id: s.in_reply_to_id,
 | 
			
		||||
        media_ids: s.media_ids,
 | 
			
		||||
        sensitive: s.sensitive,
 | 
			
		||||
        spoiler_text: s.spoiler_text,
 | 
			
		||||
        visibility: s.visibility,
 | 
			
		||||
        scheduled_at: s.scheduled_at,
 | 
			
		||||
        application_id: parseInt(s.application_id)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    export const status_source = (s: Entity.StatusSource): MegalodonEntity.StatusSource => s
 | 
			
		||||
    export const tag = (t: Entity.Tag): MegalodonEntity.Tag => t
 | 
			
		||||
    export const token = (t: Entity.Token): MegalodonEntity.Token => t
 | 
			
		||||
    export const urls = (u: Entity.URLs): MegalodonEntity.URLs => u
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
export default FriendicaAPI
 | 
			
		||||
							
								
								
									
										29
									
								
								packages/megalodon/src/friendica/entities/account.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								packages/megalodon/src/friendica/entities/account.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="source.ts" />
 | 
			
		||||
/// <reference path="field.ts" />
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Account = {
 | 
			
		||||
    id: string
 | 
			
		||||
    username: string
 | 
			
		||||
    acct: string
 | 
			
		||||
    display_name: string
 | 
			
		||||
    locked: boolean
 | 
			
		||||
    discoverable?: boolean
 | 
			
		||||
    group: 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<Emoji>
 | 
			
		||||
    moved: Account | null
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
    bot: boolean
 | 
			
		||||
    source?: Source
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/friendica/entities/activity.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/friendica/entities/activity.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Activity = {
 | 
			
		||||
    week: string
 | 
			
		||||
    statuses: string
 | 
			
		||||
    logins: string
 | 
			
		||||
    registrations: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/friendica/entities/application.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/friendica/entities/application.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Application = {
 | 
			
		||||
    name: string
 | 
			
		||||
    website?: string | null
 | 
			
		||||
    vapid_key?: string | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
/// <reference path="attachment.ts" />
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								packages/megalodon/src/friendica/entities/attachment.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								packages/megalodon/src/friendica/entities/attachment.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/megalodon/src/friendica/entities/card.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/megalodon/src/friendica/entities/card.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Card = {
 | 
			
		||||
    url: string
 | 
			
		||||
    title: string
 | 
			
		||||
    description: string
 | 
			
		||||
    type: 'link' | 'photo' | 'video' | 'rich'
 | 
			
		||||
    image: string | null
 | 
			
		||||
    author_name: string
 | 
			
		||||
    author_url: string
 | 
			
		||||
    provider_name: string
 | 
			
		||||
    provider_url: string
 | 
			
		||||
    html: string
 | 
			
		||||
    width: number
 | 
			
		||||
    height: number
 | 
			
		||||
    blurhash: string | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/friendica/entities/context.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/friendica/entities/context.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Context = {
 | 
			
		||||
    ancestors: Array<Status>
 | 
			
		||||
    descendants: Array<Status>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/megalodon/src/friendica/entities/conversation.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/megalodon/src/friendica/entities/conversation.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Conversation = {
 | 
			
		||||
    id: string
 | 
			
		||||
    accounts: Array<Account>
 | 
			
		||||
    last_status: Status | null
 | 
			
		||||
    unread: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/friendica/entities/emoji.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/friendica/entities/emoji.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Emoji = {
 | 
			
		||||
    shortcode: string
 | 
			
		||||
    static_url: string
 | 
			
		||||
    url: string
 | 
			
		||||
    visible_in_picker: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type FeaturedTag = {
 | 
			
		||||
    id: string
 | 
			
		||||
    name: string
 | 
			
		||||
    statuses_count: number
 | 
			
		||||
    last_status_at: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/friendica/entities/field.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/friendica/entities/field.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Field = {
 | 
			
		||||
    name: string
 | 
			
		||||
    value: string
 | 
			
		||||
    verified_at: string | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								packages/megalodon/src/friendica/entities/filter.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								packages/megalodon/src/friendica/entities/filter.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Filter = {
 | 
			
		||||
    id: string
 | 
			
		||||
    phrase: string
 | 
			
		||||
    context: Array<FilterContext>
 | 
			
		||||
    expires_at: string | null
 | 
			
		||||
    irreversible: boolean
 | 
			
		||||
    whole_word: boolean
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type FilterContext = string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								packages/megalodon/src/friendica/entities/follow_request.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								packages/megalodon/src/friendica/entities/follow_request.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
/// <reference path="emoji.ts" />
 | 
			
		||||
/// <reference path="field.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  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<Emoji>
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/friendica/entities/history.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/friendica/entities/history.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type History = {
 | 
			
		||||
    day: string
 | 
			
		||||
    uses: number
 | 
			
		||||
    accounts: number
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type IdentityProof = {
 | 
			
		||||
    provider: string
 | 
			
		||||
    provider_username: string
 | 
			
		||||
    updated_at: string
 | 
			
		||||
    proof_url: string
 | 
			
		||||
    profile_url: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								packages/megalodon/src/friendica/entities/instance.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								packages/megalodon/src/friendica/entities/instance.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="urls.ts" />
 | 
			
		||||
/// <reference path="stats.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Instance = {
 | 
			
		||||
    uri: string
 | 
			
		||||
    title: string
 | 
			
		||||
    description: string
 | 
			
		||||
    email: string
 | 
			
		||||
    version: string
 | 
			
		||||
    thumbnail: string | null
 | 
			
		||||
    urls: URLs | null
 | 
			
		||||
    stats: Stats
 | 
			
		||||
    languages: Array<string>
 | 
			
		||||
    registrations: boolean
 | 
			
		||||
    approval_required: boolean
 | 
			
		||||
    invites_enabled: boolean
 | 
			
		||||
    max_toot_chars: number
 | 
			
		||||
    contact_account: Account
 | 
			
		||||
    rules: Array<InstanceRule>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type InstanceRule = {
 | 
			
		||||
    id: string
 | 
			
		||||
    text: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/friendica/entities/list.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/friendica/entities/list.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type List = {
 | 
			
		||||
    id: string
 | 
			
		||||
    title: string
 | 
			
		||||
    replies_policy: RepliesPolicy
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type RepliesPolicy = 'followed' | 'list' | 'none'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								packages/megalodon/src/friendica/entities/marker.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/megalodon/src/friendica/entities/marker.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Marker = {
 | 
			
		||||
    home: {
 | 
			
		||||
      last_read_id: string
 | 
			
		||||
      version: number
 | 
			
		||||
      updated_at: string
 | 
			
		||||
    }
 | 
			
		||||
    notifications: {
 | 
			
		||||
      last_read_id: string
 | 
			
		||||
      version: number
 | 
			
		||||
      updated_at: string
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								packages/megalodon/src/friendica/entities/mention.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								packages/megalodon/src/friendica/entities/mention.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Mention = {
 | 
			
		||||
    id: string
 | 
			
		||||
    username: string
 | 
			
		||||
    url: string
 | 
			
		||||
    acct: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								packages/megalodon/src/friendica/entities/notification.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								packages/megalodon/src/friendica/entities/notification.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Notification = {
 | 
			
		||||
    account: Account
 | 
			
		||||
    created_at: string
 | 
			
		||||
    id: string
 | 
			
		||||
    status?: Status
 | 
			
		||||
    type: NotificationType
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type NotificationType = string
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								packages/megalodon/src/friendica/entities/poll.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/megalodon/src/friendica/entities/poll.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
/// <reference path="poll_option.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Poll = {
 | 
			
		||||
    id: string
 | 
			
		||||
    expires_at: string | null
 | 
			
		||||
    expired: boolean
 | 
			
		||||
    multiple: boolean
 | 
			
		||||
    votes_count: number
 | 
			
		||||
    options: Array<PollOption>
 | 
			
		||||
    voted: boolean
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								packages/megalodon/src/friendica/entities/poll_option.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/megalodon/src/friendica/entities/poll_option.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type PollOption = {
 | 
			
		||||
    title: string
 | 
			
		||||
    votes_count: number | null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								packages/megalodon/src/friendica/entities/preferences.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								packages/megalodon/src/friendica/entities/preferences.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								packages/megalodon/src/friendica/entities/relationship.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								packages/megalodon/src/friendica/entities/relationship.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								packages/megalodon/src/friendica/entities/report.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								packages/megalodon/src/friendica/entities/report.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Report = {
 | 
			
		||||
    id: string
 | 
			
		||||
    action_taken: boolean
 | 
			
		||||
    category: Category
 | 
			
		||||
    comment: string
 | 
			
		||||
    forwarded: boolean
 | 
			
		||||
    status_ids: Array<string> | null
 | 
			
		||||
    rule_ids: Array<string> | null
 | 
			
		||||
    target_account: Account
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export type Category = 'spam' | 'violation' | 'other'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								packages/megalodon/src/friendica/entities/results.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/megalodon/src/friendica/entities/results.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
/// <reference path="account.ts" />
 | 
			
		||||
/// <reference path="status.ts" />
 | 
			
		||||
/// <reference path="tag.ts" />
 | 
			
		||||
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Results = {
 | 
			
		||||
    accounts: Array<Account>
 | 
			
		||||
    statuses: Array<Status>
 | 
			
		||||
    hashtags: Array<Tag>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="attachment.ts" />
 | 
			
		||||
/// <reference path="status_params.ts" />
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type ScheduledStatus = {
 | 
			
		||||
    id: string
 | 
			
		||||
    scheduled_at: string
 | 
			
		||||
    params: StatusParams
 | 
			
		||||
    media_attachments: Array<Attachment>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								packages/megalodon/src/friendica/entities/source.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								packages/megalodon/src/friendica/entities/source.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
/// <reference path="field.ts" />
 | 
			
		||||
namespace FriendicaEntity {
 | 
			
		||||
  export type Source = {
 | 
			
		||||
    privacy: string | null
 | 
			
		||||
    sensitive: boolean | null
 | 
			
		||||
    language: string | null
 | 
			
		||||
    note: string
 | 
			
		||||
    fields: Array<Field>
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								packages/megalodon/src/friendica/entities/stats.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/megalodon/src/friendica/entities/stats.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace FriendicaEntity {
 | 
			
		||||
  export type Stats = {
 | 
			
		||||
    user_count: number
 | 
			
		||||
    status_count: number
 | 
			
		||||
    domain_count: number
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue