diff --git a/packages/backend/src/server/api/mastodon/MastodonClientService.ts b/packages/backend/src/server/api/mastodon/MastodonClientService.ts index 82f9b7bfa9..474aaefb35 100644 --- a/packages/backend/src/server/api/mastodon/MastodonClientService.ts +++ b/packages/backend/src/server/api/mastodon/MastodonClientService.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { megalodon, MegalodonInterface } from 'megalodon'; +import { Misskey } from 'megalodon'; import { Injectable } from '@nestjs/common'; import { MiLocalUser } from '@/models/User.js'; import { AuthenticateService } from '@/server/api/AuthenticateService.js'; @@ -18,7 +18,7 @@ export class MastodonClientService { /** * Gets the authenticated user and API client for a request. */ - public async getAuthClient(request: FastifyRequest, accessToken?: string | null): Promise<{ client: MegalodonInterface, me: MiLocalUser | null }> { + public async getAuthClient(request: FastifyRequest, accessToken?: string | null): Promise<{ client: Misskey, me: MiLocalUser | null }> { const authorization = request.headers.authorization; accessToken = accessToken !== undefined ? accessToken : getAccessToken(authorization); @@ -41,14 +41,14 @@ export class MastodonClientService { /** * Creates an authenticated API client for a request. */ - public getClient(request: FastifyRequest, accessToken?: string | null): MegalodonInterface { + public getClient(request: FastifyRequest, accessToken?: string | null): Misskey { const authorization = request.headers.authorization; accessToken = accessToken !== undefined ? accessToken : getAccessToken(authorization); // TODO pass agent? const baseUrl = this.getBaseUrl(request); const userAgent = request.headers['user-agent']; - return megalodon('misskey', baseUrl, accessToken, userAgent); + return new Misskey(baseUrl, accessToken, userAgent); } /** diff --git a/packages/megalodon/src/entities/instance.ts b/packages/megalodon/src/entities/instance.ts index 8f4808be8f..7849a94aa7 100644 --- a/packages/megalodon/src/entities/instance.ts +++ b/packages/megalodon/src/entities/instance.ts @@ -10,7 +10,7 @@ namespace Entity { email: string version: string thumbnail: string | null - urls: URLs | null + urls: URLs stats: Stats languages: Array registrations: boolean diff --git a/packages/megalodon/src/friendica.ts b/packages/megalodon/src/friendica.ts deleted file mode 100644 index c5ee9d59ce..0000000000 --- a/packages/megalodon/src/friendica.ts +++ /dev/null @@ -1,2868 +0,0 @@ -import { OAuth2 } from 'oauth' -import FormData from 'form-data' -import parseLinkHeader from 'parse-link-header' - -import FriendicaAPI from './friendica/api_client' -import WebSocket from './friendica/web_socket' -import { MegalodonInterface, NoImplementedError } from './megalodon' -import Response from './response' -import Entity from './entity' -import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default' -import { ProxyConfig } from './proxy_config' -import OAuth from './oauth' -import { UnknownNotificationTypeError } from './notification' - -export default class Friendica implements MegalodonInterface { - public client: FriendicaAPI.Interface - public baseUrl: string - - /** - * @param baseUrl hostname or base URL - * @param accessToken access token from OAuth2 authorization - * @param userAgent UserAgent is specified in header on request. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - */ - constructor( - baseUrl: string, - accessToken: string | null = null, - userAgent: string | null = DEFAULT_UA, - proxyConfig: ProxyConfig | false = false - ) { - let token = '' - if (accessToken) { - token = accessToken - } - let agent: string = DEFAULT_UA - if (userAgent) { - agent = userAgent - } - this.client = new FriendicaAPI.Client(baseUrl, token, agent, proxyConfig) - this.baseUrl = baseUrl - } - - public cancel(): void { - return this.client.cancel() - } - - /** - * First, call createApp to get client_id and client_secret. - * Next, call generateAuthUrl to get authorization url. - * @param client_name Form Data, which is sent to /api/v1/apps - * @param options Form Data, which is sent to /api/v1/apps. and properties should be **snake_case** - */ - public async registerApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - return this.createApp(client_name, options).then(async appData => { - return this.generateAuthUrl(appData.client_id, appData.client_secret, { - scope: scopes, - redirect_uri: appData.redirect_uri - }).then(url => { - appData.url = url - return appData - }) - }) - } - - /** - * Call /api/v1/apps - * - * Create an application. - * @param client_name your application's name - * @param options Form Data - */ - public async createApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - const redirect_uris = options.redirect_uris || NO_REDIRECT - - const params: { - client_name: string - redirect_uris: string - scopes: string - website?: string - } = { - client_name: client_name, - redirect_uris: redirect_uris, - scopes: scopes.join(' ') - } - if (options.website) params.website = options.website - - return this.client - .post('/api/v1/apps', params) - .then((res: Response) => OAuth.AppData.from(res.data)) - } - - /** - * Generate authorization url using OAuth2. - * - * @param clientId your OAuth app's client ID - * @param clientSecret your OAuth app's client Secret - * @param options as property, redirect_uri and scope are available, and must be the same as when you register your app - */ - public generateAuthUrl( - clientId: string, - clientSecret: string, - options: Partial<{ scope: Array; redirect_uri: string }> - ): Promise { - const scope = options.scope || DEFAULT_SCOPE - const redirect_uri = options.redirect_uri || NO_REDIRECT - return new Promise(resolve => { - const oauth = new OAuth2(clientId, clientSecret, this.baseUrl, undefined, '/oauth/token') - const url = oauth.getAuthorizeUrl({ - redirect_uri: redirect_uri, - response_type: 'code', - client_id: clientId, - scope: scope.join(' ') - }) - resolve(url) - }) - } - - // ====================================== - // apps - // ====================================== - /** - * GET /api/v1/apps/verify_credentials - * - * @return An Application - */ - public verifyAppCredentials(): Promise> { - return this.client.get('/api/v1/apps/verify_credentials') - } - - // ====================================== - // apps/oauth - // ====================================== - /** - * POST /oauth/token - * - * Fetch OAuth access token. - * Get an access token based client_id and client_secret and authorization code. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param code will be generated by the link of #generateAuthUrl or #registerApp - * @param redirect_uri must be the same uri as the time when you register your OAuth application - */ - public async fetchAccessToken( - client_id: string | null, - client_secret: string, - code: string, - redirect_uri: string = NO_REDIRECT - ): Promise { - if (!client_id) { - throw new Error('client_id is required') - } - return this.client - .post('/oauth/token', { - client_id, - client_secret, - code, - redirect_uri, - grant_type: 'authorization_code' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/token - * - * Refresh OAuth access token. - * Send refresh token and get new access token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param refresh_token will be get #fetchAccessToken - */ - public async refreshToken(client_id: string, client_secret: string, refresh_token: string): Promise { - return this.client - .post('/oauth/token', { - client_id, - client_secret, - refresh_token, - grant_type: 'refresh_token' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/revoke - * - * Revoke an OAuth token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param token will be get #fetchAccessToken - */ - public async revokeToken(client_id: string, client_secret: string, token: string): Promise>> { - return this.client.post>('/oauth/revoke', { - client_id, - client_secret, - token - }) - } - - // ====================================== - // accounts - // ====================================== - public async registerAccount( - _username: string, - _email: string, - _password: string, - _agreement: boolean, - _locale: string, - _reason?: string | null - ): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * GET /api/v1/accounts/verify_credentials - * - * @return Account. - */ - public async verifyAccountCredentials(): Promise> { - return this.client.get('/api/v1/accounts/verify_credentials').then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.account(res.data) - }) - }) - } - - public async updateCredentials(_options?: { - discoverable?: boolean - bot?: boolean - display_name?: string - note?: string - avatar?: string - header?: string - locked?: boolean - source?: { - privacy?: string - sensitive?: boolean - language?: string - } - fields_attributes?: Array<{ name: string; value: string }> - }): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * GET /api/v1/accounts/:id - * - * @param id The account ID. - * @return An account. - */ - public async getAccount(id: string): Promise> { - return this.client.get(`/api/v1/accounts/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.account(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/statuses - * - * @param id The account ID. - - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID but starting with most recent. - * @param options.min_id Return results newer than ID. - * @param options.pinned Return statuses which include pinned statuses. - * @param options.exclude_replies Return statuses which exclude replies. - * @param options.exclude_reblogs Return statuses which exclude reblogs. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @return Account's statuses. - */ - public async getAccountStatuses( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - pinned?: boolean - exclude_replies?: boolean - exclude_reblogs?: boolean - only_media: boolean - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.pinned) { - params = Object.assign(params, { - pinned: options.pinned - }) - } - if (options.exclude_replies) { - params = Object.assign(params, { - exclude_replies: options.exclude_replies - }) - } - if (options.exclude_reblogs) { - params = Object.assign(params, { - exclude_reblogs: options.exclude_reblogs - }) - } - if (options.only_media) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - } - - return this.client.get>(`/api/v1/accounts/${id}/statuses`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id Target account ID. - * @return Relationship. - */ - public async subscribeAccount(id: string): Promise> { - const params = { - notify: true - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id Target account ID. - * @return Relationship. - */ - public async unsubscribeAccount(id: string): Promise> { - const params = { - notify: false - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - public getAccountFavourites( - _id: string, - _options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * GET /api/v1/accounts/:id/followers - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowers( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - get_all?: boolean - sleep_ms?: number - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.urlToAccounts(`/api/v1/accounts/${id}/followers`, params, options?.get_all || false, options?.sleep_ms || 0) - } - - /** - * GET /api/v1/accounts/:id/following - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowing( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - get_all?: boolean - sleep_ms?: number - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.urlToAccounts(`/api/v1/accounts/${id}/following`, params, options?.get_all || false, options?.sleep_ms || 0) - } - - /** Helper function to optionally follow Link headers as pagination */ - private async urlToAccounts(url: string, params: Record, get_all: boolean, sleep_ms: number) { - const res = await this.client.get>(url, params) - let converted = Object.assign({}, res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - if (get_all && converted.headers.link) { - let parsed = parseLinkHeader(converted.headers.link) - while (parsed?.next) { - const nextRes = await this.client.get>(parsed?.next.url, undefined, undefined, true) - converted = Object.assign({}, converted, { - data: [...converted.data, ...nextRes.data.map(a => FriendicaAPI.Converter.account(a))] - }) - parsed = parseLinkHeader(nextRes.headers.link) - if (sleep_ms) { - await new Promise(converted => setTimeout(converted, sleep_ms)) - } - } - } - return converted - } - - /** - * GET /api/v1/accounts/:id/lists - * - * @param id The account ID. - * @return The array of lists. - */ - public async getAccountLists(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/lists`).then(res => { - return Object.assign(res, { - data: res.data.map(l => FriendicaAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/identity_proofs - * - * @param id The account ID. - * @return Array of IdentityProof - */ - public async getIdentityProof(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/identity_proofs`).then(res => { - return Object.assign(res, { - data: res.data.map(i => FriendicaAPI.Converter.identity_proof(i)) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id The account ID. - * @param reblog Receive this account's reblogs in home timeline. - * @return Relationship - */ - public async followAccount(id: string, options?: { reblog?: boolean }): Promise> { - let params = {} - if (options) { - if (options.reblog !== undefined) { - params = Object.assign(params, { - reblog: options.reblog - }) - } - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unfollow - * - * @param id The account ID. - * @return Relationship - */ - public async unfollowAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unfollow`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/block - * - * @param id The account ID. - * @return Relationship - */ - public async blockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/block`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unblock - * - * @param id The account ID. - * @return RElationship - */ - public async unblockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unblock`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/mute - * - * @param id The account ID. - * @param notifications Mute notifications in addition to statuses. - * @return Relationship - */ - public async muteAccount(id: string, notifications = true): Promise> { - return this.client - .post(`/api/v1/accounts/${id}/mute`, { - notifications: notifications - }) - .then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unmute - * - * @param id The account ID. - * @return Relationship - */ - public async unmuteAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unmute`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/pin - * - * @param id The account ID. - * @return Relationship - */ - public async pinAccount(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * POST /api/v1/accounts/:id/unpin - * - * @param id The account ID. - * @return Relationship - */ - public async unpinAccount(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * GET /api/v1/accounts/relationships - * - * @param id The account ID. - * @return Relationship - */ - public async getRelationship(id: string): Promise> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: [id] - }) - .then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data[0]) - }) - }) - } - - /** - * Get multiple relationships in one method - * - * @param ids Array of account IDs. - * @return Array of Relationship. - */ - public async getRelationships(ids: Array): Promise>> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: ids - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(r => FriendicaAPI.Converter.relationship(r)) - }) - }) - } - - /** - * GET /api/v1/accounts/search - * - * @param q Search query. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async searchAccount( - q: string, - options?: { - following?: boolean - resolve?: boolean - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = { q: q } - if (options) { - if (options.following !== undefined && options.following !== null) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.resolve !== undefined && options.resolve !== null) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/accounts/search', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/bookmarks - // ====================================== - /** - * GET /api/v1/bookmarks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getBookmarks(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/bookmarks', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/favourites - // ====================================== - /** - * GET /api/v1/favourites - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/favourites', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/mutes - // ====================================== - /** - * GET /api/v1/mutes - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getMutes(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/mutes', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/blocks - // ====================================== - /** - * GET /api/v1/blocks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/blocks', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/domain_blocks - // ====================================== - public async getDomainBlocks(_options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public blockDomain(_domain: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public unblockDomain(_domain: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // accounts/filters - // ====================================== - /** - * GET /api/v1/filters - * - * @return Array of filters. - */ - public async getFilters(): Promise>> { - return this.client.get>('/api/v1/filters').then(res => { - return Object.assign(res, { - data: res.data.map(f => FriendicaAPI.Converter.filter(f)) - }) - }) - } - - public async getFilter(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async createFilter( - _phrase: string, - _context: Array, - _options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async updateFilter( - _id: string, - _phrase: string, - _context: Array, - _options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async deleteFilter(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // accounts/reports - // ====================================== - public async report( - _account_id: string, - _options?: { - status_ids?: Array - comment: string - forward?: boolean - category?: Entity.Category - rule_ids?: Array - } - ): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // accounts/follow_requests - // ====================================== - /** - * GET /api/v1/follow_requests - * - * @param limit Maximum number of results. - * @return Array of FollowRequest. - */ - public async getFollowRequests(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/follow_requests', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.follow_request(a)) - }) - }) - } else { - return this.client.get>('/api/v1/follow_requests').then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.follow_request(a)) - }) - }) - } - } - - /** - * POST /api/v1/follow_requests/:id/authorize - * - * @param id The FollowRequest ID. - * @return Relationship. - */ - public async acceptFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/follow_requests/:id/reject - * - * @param id The FollowRequest ID. - * @return Relationship. - */ - public async rejectFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/reject`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.relationship(res.data) - }) - }) - } - - // ====================================== - // accounts/endorsements - // ====================================== - /** - * GET /api/v1/endorsements - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return Array of accounts. - */ - public async getEndorsements(options?: { limit?: number; max_id?: string; since_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>('/api/v1/endorsements', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/featured_tags - // ====================================== - public async getFeaturedTags(): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async createFeaturedTag(_name: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public deleteFeaturedTag(_id: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async getSuggestedTags(): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // accounts/preferences - // ====================================== - /** - * GET /api/v1/preferences - * - * @return Preferences. - */ - public async getPreferences(): Promise> { - return this.client.get('/api/v1/preferences').then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.preferences(res.data) - }) - }) - } - - // ====================================== - // accounts/followed_tags - // ====================================== - public async getFollowedTags(): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // accounts/suggestions - // ====================================== - /** - * GET /api/v1/suggestions - * - * @param limit Maximum number of results. - * @return Array of accounts. - */ - public async getSuggestions(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/suggestions', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } else { - return this.client.get>('/api/v1/suggestions').then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - } - - // ====================================== - // accounts/tags - // ====================================== - /** - * GET /api/v1/tags/:id - * - * @param id Target hashtag id. - * @return Tag - */ - public async getTag(id: string): Promise> { - return this.client.get(`/api/v1/tags/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.tag(res.data) - }) - }) - } - - /** - * POST /api/v1/tags/:id/follow - * - * @param id Target hashtag id. - * @return Tag - */ - public async followTag(id: string): Promise> { - return this.client.post(`/api/v1/tags/${id}/follow`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.tag(res.data) - }) - }) - } - - /** - * POST /api/v1/tags/:id/unfollow - * - * @param id Target hashtag id. - * @return Tag - */ - public async unfollowTag(id: string): Promise> { - return this.client.post(`/api/v1/tags/${id}/unfollow`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.tag(res.data) - }) - }) - } - - // ====================================== - // statuses - // ====================================== - /** - * POST /api/v1/statuses - * - * @param status Text content of status. - * @param options.media_ids Array of Attachment ids. - * @param options.poll Poll object. - * @param options.in_reply_to_id ID of the status being replied to, if status is a reply. - * @param options.sensitive Mark status and attached media as sensitive? - * @param options.spoiler_text Text to be shown as a warning or subject before the actual content. - * @param options.visibility Visibility of the posted status. - * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status. - * @param options.language ISO 639 language code for this status. - * @param options.quote_id ID of the status being quoted to, if status is a quote. - * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead. - */ - public async postStatus( - status: string, - options: { - media_ids?: Array - poll?: { options: Array; expires_in: number; multiple?: boolean; hide_totals?: boolean } - in_reply_to_id?: string - sensitive?: boolean - spoiler_text?: string - visibility?: 'public' | 'unlisted' | 'private' | 'direct' - scheduled_at?: string - language?: string - quote_id?: string - } - ): Promise> { - let params = { - status: status - } - if (options) { - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = { - options: options.poll.options, - expires_in: options.poll.expires_in - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - if (options.in_reply_to_id) { - params = Object.assign(params, { - in_reply_to_id: options.in_reply_to_id - }) - } - if (options.sensitive !== undefined) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.visibility) { - params = Object.assign(params, { - visibility: options.visibility - }) - } - if (options.scheduled_at) { - params = Object.assign(params, { - scheduled_at: options.scheduled_at - }) - } - if (options.language) { - params = Object.assign(params, { - language: options.language - }) - } - if (options.quote_id) { - params = Object.assign(params, { - quote_id: options.quote_id - }) - } - } - if (options.scheduled_at) { - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.scheduled_status(res.data) - }) - }) - } - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - /** - * GET /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async getStatus(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - PUT /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async editStatus( - id: string, - options: { - status?: string - spoiler_text?: string - sensitive?: boolean - media_ids?: Array - poll?: { options?: Array; expires_in?: number; multiple?: boolean; hide_totals?: boolean } - } - ): Promise> { - let params = {} - if (options.status) { - params = Object.assign(params, { - status: options.status - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.sensitive) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = {} - if (options.poll.options !== undefined) { - pollParam = Object.assign(pollParam, { - options: options.poll.options - }) - } - if (options.poll.expires_in !== undefined) { - pollParam = Object.assign(pollParam, { - expires_in: options.poll.expires_in - }) - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - return this.client.put(`/api/v1/statuses/${id}`, params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async deleteStatus(id: string): Promise> { - return this.client.del(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/context - * - * Get parent and child statuses. - * @param id The target status id. - * @return Context - */ - public async getStatusContext( - id: string, - options?: { limit?: number; max_id?: string; since_id?: string } - ): Promise> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get(`/api/v1/statuses/${id}/context`, params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.context(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/source - * - * Obtain the source properties for a status so that it can be edited. - * @param id The target status id. - * @return StatusSource - */ - public async getStatusSource(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}/source`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status_source(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/reblogged_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusRebloggedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/reblogged_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/favourited_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusFavouritedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/favourited_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/favourite - * - * @param id The target status id. - * @return Status. - */ - public async favouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/favourite`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unfavourite - * - * @param id The target status id. - * @return Status. - */ - public async unfavouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/reblog - * - * @param id The target status id. - * @return Status. - */ - public async reblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/reblog`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unreblog - * - * @param id The target status id. - * @return Status. - */ - public async unreblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unreblog`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/bookmark - * - * @param id The target status id. - * @return Status. - */ - public async bookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/bookmark`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unbookmark - * - * @param id The target status id. - * @return Status. - */ - public async unbookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/mute - * - * @param id The target status id. - * @return Status - */ - public async muteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/mute`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unmute - * - * @param id The target status id. - * @return Status - */ - public async unmuteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unmute`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/pin - * @param id The target status id. - * @return Status - */ - public async pinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/pin`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unpin - * - * @param id The target status id. - * @return Status - */ - public async unpinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unpin`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.status(res.data) - }) - }) - } - - // ====================================== - // statuses/media - // ====================================== - /** - * POST /api/v2/media - * - * @param file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @return Attachment - */ - public async uploadMedia( - file: any, - options?: { description?: string; focus?: string } - ): Promise> { - const formData = new FormData() - formData.append('file', file) - if (options) { - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.postForm('/api/v2/media', formData).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.async_attachment(res.data) - }) - }) - } - - /** - * GET /api/v1/media/:id - * - * @param id Target media ID. - * @return Attachment - */ - public async getMedia(id: string): Promise> { - const res = await this.client.get(`/api/v1/media/${id}`) - - return Object.assign(res, { - data: FriendicaAPI.Converter.attachment(res.data) - }) - } - - /** - * PUT /api/v1/media/:id - * - * @param id Target media ID. - * @param options.file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @param options.is_sensitive Whether the media is sensitive. - * @return Attachment - */ - public async updateMedia( - id: string, - options?: { - file?: any - description?: string - focus?: string - } - ): Promise> { - const formData = new FormData() - if (options) { - if (options.file) { - formData.append('file', options.file) - } - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.putForm(`/api/v1/media/${id}`, formData).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.attachment(res.data) - }) - }) - } - - // ====================================== - // statuses/polls - // ====================================== - /** - * GET /api/v1/polls/:id - * - * @param id Target poll ID. - * @return Poll - */ - public async getPoll(id: string): Promise> { - return this.client.get(`/api/v1/polls/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.poll(res.data) - }) - }) - } - - public async votePoll(_id: string, _choices: Array): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // statuses/scheduled_statuses - // ====================================== - /** - * GET /api/v1/scheduled_statuses - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of scheduled statuses. - */ - public async getScheduledStatuses(options?: { - limit?: number | null - max_id?: string | null - since_id?: string | null - min_id?: string | null - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/scheduled_statuses', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.scheduled_status(s)) - }) - }) - } - - /** - * GET /api/v1/scheduled_statuses/:id - * - * @param id Target status ID. - * @return ScheduledStatus. - */ - public async getScheduledStatus(id: string): Promise> { - return this.client.get(`/api/v1/scheduled_statuses/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.scheduled_status(res.data) - }) - }) - } - - public async scheduleStatus(_id: string, _scheduled_at?: string | null): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * DELETE /api/v1/scheduled_statuses/:id - * - * @param id Target scheduled status ID. - */ - public cancelScheduledStatus(id: string): Promise>> { - return this.client.del>(`/api/v1/scheduled_statuses/${id}`) - } - - // ====================================== - // timelines - // ====================================== - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getPublicTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: false - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getLocalTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: true - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/tag/:hashtag - * - * @param hashtag Content of a #hashtag, not including # symbol. - * @param options.local Show only local statuses? Defaults to false. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getTagTimeline( - hashtag: string, - options?: { - local?: boolean - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/tag/${hashtag}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/home - * - * @param options.local Show only local statuses? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getHomeTimeline(options?: { - local?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/home', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/list/:list_id - * - * @param list_id Local ID of the list in the database. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getListTimeline( - list_id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/list/${list_id}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => FriendicaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // timelines/conversations - // ====================================== - /** - * GET /api/v1/conversations - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getConversationTimeline(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/conversations', params).then(res => { - return Object.assign(res, { - data: res.data.map(c => FriendicaAPI.Converter.conversation(c)) - }) - }) - } - - /** - * DELETE /api/v1/conversations/:id - * - * @param id Target conversation ID. - */ - public deleteConversation(id: string): Promise>> { - return this.client.del>(`/api/v1/conversations/${id}`) - } - - /** - * POST /api/v1/conversations/:id/read - * - * @param id Target conversation ID. - * @return Conversation. - */ - public async readConversation(id: string): Promise> { - return this.client.post(`/api/v1/conversations/${id}/read`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.conversation(res.data) - }) - }) - } - - // ====================================== - // timelines/lists - // ====================================== - /** - * GET /api/v1/lists - * - * @return Array of lists. - */ - public async getLists(): Promise>> { - return this.client.get>('/api/v1/lists').then(res => { - return Object.assign(res, { - data: res.data.map(l => FriendicaAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/lists/:id - * - * @param id Target list ID. - * @return List. - */ - public async getList(id: string): Promise> { - return this.client.get(`/api/v1/lists/${id}`).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.list(res.data) - }) - }) - } - - /** - * POST /api/v1/lists - * - * @param title List name. - * @return List. - */ - public async createList(title: string): Promise> { - return this.client - .post('/api/v1/lists', { - title: title - }) - .then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.list(res.data) - }) - }) - } - - /** - * PUT /api/v1/lists/:id - * - * @param id Target list ID. - * @param title New list name. - * @return List. - */ - public async updateList(id: string, title: string): Promise> { - return this.client - .put(`/api/v1/lists/${id}`, { - title: title - }) - .then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.list(res.data) - }) - }) - } - - /** - * DELETE /api/v1/lists/:id - * - * @param id Target list ID. - */ - public deleteList(id: string): Promise>> { - return this.client.del>(`/api/v1/lists/${id}`) - } - - /** - * GET /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param options.limit Max number of results to return. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getAccountsInList( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>(`/api/v1/lists/${id}/accounts`, params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public addAccountsToList(id: string, account_ids: Array): Promise>> { - return this.client.post>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - /** - * DELETE /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public deleteAccountsFromList(id: string, account_ids: Array): Promise>> { - return this.client.del>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - // ====================================== - // timelines/markers - // ====================================== - public async getMarkers(_timeline: Array): Promise>> { - return new Promise(resolve => { - const res: Response = { - data: {}, - status: 200, - statusText: '200', - headers: {} - } - resolve(res) - }) - } - - public async saveMarkers(_options?: { - home?: { last_read_id: string } - notifications?: { last_read_id: string } - }): Promise> { - return new Promise(resolve => { - const res: Response = { - data: {}, - status: 200, - statusText: '200', - headers: {} - } - resolve(res) - }) - } - - // ====================================== - // notifications - // ====================================== - /** - * GET /api/v1/notifications - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @param options.exclude_types Array of types to exclude. - * @param options.account_id Return only notifications received from this account. - * @return Array of notifications. - */ - public async getNotifications(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - exclude_types?: Array - account_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.exclude_types) { - params = Object.assign(params, { - exclude_types: options.exclude_types.map(e => FriendicaAPI.Converter.encodeNotificationType(e)) - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - } - return this.client.get>('/api/v1/notifications', params).then(res => { - return Object.assign(res, { - data: res.data.flatMap(n => { - const notify = FriendicaAPI.Converter.notification(n) - if (notify instanceof UnknownNotificationTypeError) return [] - return notify - }) - }) - }) - } - - /** - * GET /api/v1/notifications/:id - * - * @param id Target notification ID. - * @return Notification. - */ - public async getNotification(id: string): Promise> { - const res = await this.client.get(`/api/v1/notifications/${id}`) - const notify = FriendicaAPI.Converter.notification(res.data) - if (notify instanceof UnknownNotificationTypeError) { - throw new UnknownNotificationTypeError() - } - return { ...res, data: notify } - } - - /** - * POST /api/v1/notifications/clear - */ - public dismissNotifications(): Promise>> { - return this.client.post>('/api/v1/notifications/clear') - } - - /** - * POST /api/v1/notifications/:id/dismiss - * - * @param id Target notification ID. - */ - public dismissNotification(id: string): Promise>> { - return this.client.post>(`/api/v1/notifications/${id}/dismiss`) - } - - public readNotifications(_options: { - id?: string - max_id?: string - }): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // notifications/push - // ====================================== - /** - * POST /api/v1/push/subscription - * - * @param subscription[endpoint] Endpoint URL that is called when a notification event occurs. - * @param subscription[keys][p256dh] User agent public key. Base64 encoded string of public key of ECDH key using prime256v1 curve. - * @param subscription[keys] Auth secret. Base64 encoded string of 16 bytes of random data. - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async subscribePushNotification( - subscription: { endpoint: string; keys: { p256dh: string; auth: string } }, - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = { - subscription - } - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.post('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * GET /api/v1/push/subscription - * - * @return PushSubscription. - */ - public async getPushSubscription(): Promise> { - return this.client.get('/api/v1/push/subscription').then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * PUT /api/v1/push/subscription - * - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async updatePushSubscription( - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = {} - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.put('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * DELETE /api/v1/push/subscription - */ - public deletePushSubscription(): Promise>> { - return this.client.del>('/api/v1/push/subscription') - } - - // ====================================== - // search - // ====================================== - /** - * GET /api/v2/search - * - * @param q The search query. - * @param options.type Enum of search target. - * @param options.limit Maximum number of results to load, per type. Defaults to 20. Max 40. - * @param options.max_id Return results older than this id. - * @param options.min_id Return results immediately newer than this id. - * @param options.resolve Attempt WebFinger lookup. Defaults to false. - * @param options.following Only include accounts that the user is following. Defaults to false. - * @param options.account_id If provided, statuses returned will be authored only by this account. - * @param options.exclude_unreviewed Filter out unreviewed tags? Defaults to false. - * @return Results. - */ - public async search( - q: string, - options?: { - type?: 'accounts' | 'hashtags' | 'statuses' - limit?: number - max_id?: string - min_id?: string - resolve?: boolean - offset?: number - following?: boolean - account_id?: string - exclude_unreviewed?: boolean - } - ): Promise> { - let params = { - q - } - if (options) { - if (options.type) { - params = Object.assign(params, { - type: options.type - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.resolve !== undefined) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.following !== undefined) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - if (options.exclude_unreviewed) { - params = Object.assign(params, { - exclude_unreviewed: options.exclude_unreviewed - }) - } - } - return this.client.get('/api/v2/search', params).then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.results(res.data) - }) - }) - } - - // ====================================== - // instance - // ====================================== - /** - * GET /api/v1/instance - */ - public async getInstance(): Promise> { - return this.client.get('/api/v1/instance').then(res => { - return Object.assign(res, { - data: FriendicaAPI.Converter.instance(res.data) - }) - }) - } - - /** - * GET /api/v1/instance/peers - */ - public getInstancePeers(): Promise>> { - return this.client.get>('/api/v1/instance/peers') - } - - /** - * GET /api/v1/instance/activity - */ - public async getInstanceActivity(): Promise>> { - return this.client.get>('/api/v1/instance/activity').then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.activity(a)) - }) - }) - } - - // ====================================== - // instance/trends - // ====================================== - /** - * GET /api/v1/trends - * - * @param limit Maximum number of results to return. Defaults to 10. - */ - public async getInstanceTrends(limit?: number | null): Promise>> { - let params = {} - if (limit) { - params = Object.assign(params, { - limit - }) - } - return this.client.get>('/api/v1/trends', params).then(res => { - return Object.assign(res, { - data: res.data.map(t => FriendicaAPI.Converter.tag(t)) - }) - }) - } - - // ====================================== - // instance/directory - // ====================================== - /** - * GET /api/v1/directory - * - * @param options.limit How many accounts to load. Default 40. - * @param options.offset How many accounts to skip before returning results. Default 0. - * @param options.order Order of results. - * @param options.local Only return local accounts. - * @return Array of accounts. - */ - public async getInstanceDirectory(options?: { - limit?: number - offset?: number - order?: 'active' | 'new' - local?: boolean - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.order) { - params = Object.assign(params, { - order: options.order - }) - } - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - } - return this.client.get>('/api/v1/directory', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => FriendicaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // instance/custom_emojis - // ====================================== - /** - * GET /api/v1/custom_emojis - * - * @return Array of emojis. - */ - public async getInstanceCustomEmojis(): Promise>> { - return this.client.get>('/api/v1/custom_emojis').then(res => { - return Object.assign(res, { - data: res.data.map(e => FriendicaAPI.Converter.emoji(e)) - }) - }) - } - - // ====================================== - // instance/announcements - // ====================================== - /** - * GET /api/v1/announcements - * - * @return Array of announcements. - */ - public async getInstanceAnnouncements(): Promise>> { - return new Promise(resolve => { - resolve({ - data: [], - status: 200, - statusText: '200', - headers: null - }) - }) - } - - /** - * POST /api/v1/announcements/:id/dismiss - * - * @param id The ID of the Announcement in the database. - */ - public async dismissInstanceAnnouncement(_id: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * PUT /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async addReactionToAnnouncement(_id: string, _name: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - /** - * DELETE /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async removeReactionFromAnnouncement(_id: string, _name: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // Emoji reactions - // ====================================== - public async createEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async deleteEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async getEmojiReactions(_id: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - public async getEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('friendica does not support') - reject(err) - }) - } - - // ====================================== - // WebSocket - // ====================================== - public userSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'user') - } - - public publicSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public') - } - - public localSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public:local') - } - - public tagSocket(tag: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'hashtag', `tag=${tag}`) - } - - public listSocket(list_id: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'list', `list=${list_id}`) - } - - public directSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'direct') - } -} diff --git a/packages/megalodon/src/friendica/api_client.ts b/packages/megalodon/src/friendica/api_client.ts deleted file mode 100644 index b0d3399784..0000000000 --- a/packages/megalodon/src/friendica/api_client.ts +++ /dev/null @@ -1,769 +0,0 @@ -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(path: string, params?: any, headers?: { [key: string]: string }, pathIsFullyQualified?: boolean): Promise> - put(path: string, params?: any, headers?: { [key: string]: string }): Promise> - putForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patch(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patchForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - post(path: string, params?: any, headers?: { [key: string]: string }): Promise> - postForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - del(path: string, params?: any, headers?: { [key: string]: string }): Promise> - 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( - path: string, - params = {}, - headers: { [key: string]: string } = {}, - pathIsFullyQualified = false - ): Promise> { - 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((pathIsFullyQualified ? '' : 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 = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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 = { - 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, - edited_at: s.edited_at || null, - 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 diff --git a/packages/megalodon/src/friendica/entities/account.ts b/packages/megalodon/src/friendica/entities/account.ts deleted file mode 100644 index 670a583712..0000000000 --- a/packages/megalodon/src/friendica/entities/account.ts +++ /dev/null @@ -1,29 +0,0 @@ -/// -/// -/// -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 - moved: Account | null - fields: Array - bot: boolean - source?: Source - } -} diff --git a/packages/megalodon/src/friendica/entities/activity.ts b/packages/megalodon/src/friendica/entities/activity.ts deleted file mode 100644 index 4db360d233..0000000000 --- a/packages/megalodon/src/friendica/entities/activity.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace FriendicaEntity { - export type Activity = { - week: string - statuses: string - logins: string - registrations: string - } -} diff --git a/packages/megalodon/src/friendica/entities/application.ts b/packages/megalodon/src/friendica/entities/application.ts deleted file mode 100644 index 5e54ce82d8..0000000000 --- a/packages/megalodon/src/friendica/entities/application.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace FriendicaEntity { - export type Application = { - name: string - website?: string | null - vapid_key?: string | null - } -} diff --git a/packages/megalodon/src/friendica/entities/async_attachment.ts b/packages/megalodon/src/friendica/entities/async_attachment.ts deleted file mode 100644 index 76934af66a..0000000000 --- a/packages/megalodon/src/friendica/entities/async_attachment.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/attachment.ts b/packages/megalodon/src/friendica/entities/attachment.ts deleted file mode 100644 index 04be0e72d2..0000000000 --- a/packages/megalodon/src/friendica/entities/attachment.ts +++ /dev/null @@ -1,49 +0,0 @@ -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/card.ts b/packages/megalodon/src/friendica/entities/card.ts deleted file mode 100644 index c23471983b..0000000000 --- a/packages/megalodon/src/friendica/entities/card.ts +++ /dev/null @@ -1,17 +0,0 @@ -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/context.ts b/packages/megalodon/src/friendica/entities/context.ts deleted file mode 100644 index 9c977544a7..0000000000 --- a/packages/megalodon/src/friendica/entities/context.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// - -namespace FriendicaEntity { - export type Context = { - ancestors: Array - descendants: Array - } -} diff --git a/packages/megalodon/src/friendica/entities/conversation.ts b/packages/megalodon/src/friendica/entities/conversation.ts deleted file mode 100644 index 550ae70817..0000000000 --- a/packages/megalodon/src/friendica/entities/conversation.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// -/// - -namespace FriendicaEntity { - export type Conversation = { - id: string - accounts: Array - last_status: Status | null - unread: boolean - } -} diff --git a/packages/megalodon/src/friendica/entities/emoji.ts b/packages/megalodon/src/friendica/entities/emoji.ts deleted file mode 100644 index a0d92e6bc7..0000000000 --- a/packages/megalodon/src/friendica/entities/emoji.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace FriendicaEntity { - export type Emoji = { - shortcode: string - static_url: string - url: string - visible_in_picker: boolean - } -} diff --git a/packages/megalodon/src/friendica/entities/featured_tag.ts b/packages/megalodon/src/friendica/entities/featured_tag.ts deleted file mode 100644 index 14dd1a8263..0000000000 --- a/packages/megalodon/src/friendica/entities/featured_tag.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace FriendicaEntity { - export type FeaturedTag = { - id: string - name: string - statuses_count: number - last_status_at: string - } -} diff --git a/packages/megalodon/src/friendica/entities/field.ts b/packages/megalodon/src/friendica/entities/field.ts deleted file mode 100644 index 299ca0a456..0000000000 --- a/packages/megalodon/src/friendica/entities/field.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace FriendicaEntity { - export type Field = { - name: string - value: string - verified_at: string | null - } -} diff --git a/packages/megalodon/src/friendica/entities/filter.ts b/packages/megalodon/src/friendica/entities/filter.ts deleted file mode 100644 index a71a936ab1..0000000000 --- a/packages/megalodon/src/friendica/entities/filter.ts +++ /dev/null @@ -1,12 +0,0 @@ -namespace FriendicaEntity { - export type Filter = { - id: string - phrase: string - context: Array - expires_at: string | null - irreversible: boolean - whole_word: boolean - } - - export type FilterContext = string -} diff --git a/packages/megalodon/src/friendica/entities/follow_request.ts b/packages/megalodon/src/friendica/entities/follow_request.ts deleted file mode 100644 index 83f5bf9ba9..0000000000 --- a/packages/megalodon/src/friendica/entities/follow_request.ts +++ /dev/null @@ -1,27 +0,0 @@ -/// -/// - -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 - fields: Array - } -} diff --git a/packages/megalodon/src/friendica/entities/history.ts b/packages/megalodon/src/friendica/entities/history.ts deleted file mode 100644 index 8f9cd6bd6b..0000000000 --- a/packages/megalodon/src/friendica/entities/history.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace FriendicaEntity { - export type History = { - day: string - uses: number - accounts: number - } -} diff --git a/packages/megalodon/src/friendica/entities/identity_proof.ts b/packages/megalodon/src/friendica/entities/identity_proof.ts deleted file mode 100644 index fb6166c65f..0000000000 --- a/packages/megalodon/src/friendica/entities/identity_proof.ts +++ /dev/null @@ -1,9 +0,0 @@ -namespace FriendicaEntity { - export type IdentityProof = { - provider: string - provider_username: string - updated_at: string - proof_url: string - profile_url: string - } -} diff --git a/packages/megalodon/src/friendica/entities/instance.ts b/packages/megalodon/src/friendica/entities/instance.ts deleted file mode 100644 index a86390eb0b..0000000000 --- a/packages/megalodon/src/friendica/entities/instance.ts +++ /dev/null @@ -1,28 +0,0 @@ -/// -/// -/// - -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 - registrations: boolean - approval_required: boolean - invites_enabled: boolean - max_toot_chars: number - contact_account: Account - rules: Array - } - - export type InstanceRule = { - id: string - text: string - } -} diff --git a/packages/megalodon/src/friendica/entities/list.ts b/packages/megalodon/src/friendica/entities/list.ts deleted file mode 100644 index 90487aec28..0000000000 --- a/packages/megalodon/src/friendica/entities/list.ts +++ /dev/null @@ -1,9 +0,0 @@ -namespace FriendicaEntity { - export type List = { - id: string - title: string - replies_policy: RepliesPolicy - } - - export type RepliesPolicy = 'followed' | 'list' | 'none' -} diff --git a/packages/megalodon/src/friendica/entities/marker.ts b/packages/megalodon/src/friendica/entities/marker.ts deleted file mode 100644 index 4ec41a07d6..0000000000 --- a/packages/megalodon/src/friendica/entities/marker.ts +++ /dev/null @@ -1,14 +0,0 @@ -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 - } - } -} diff --git a/packages/megalodon/src/friendica/entities/mention.ts b/packages/megalodon/src/friendica/entities/mention.ts deleted file mode 100644 index 0e93333fe8..0000000000 --- a/packages/megalodon/src/friendica/entities/mention.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace FriendicaEntity { - export type Mention = { - id: string - username: string - url: string - acct: string - } -} diff --git a/packages/megalodon/src/friendica/entities/notification.ts b/packages/megalodon/src/friendica/entities/notification.ts deleted file mode 100644 index acdbfb9276..0000000000 --- a/packages/megalodon/src/friendica/entities/notification.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// -/// - -namespace FriendicaEntity { - export type Notification = { - account: Account - created_at: string - id: string - status?: Status - type: NotificationType - } - - export type NotificationType = string -} diff --git a/packages/megalodon/src/friendica/entities/poll.ts b/packages/megalodon/src/friendica/entities/poll.ts deleted file mode 100644 index 4ac2262c5e..0000000000 --- a/packages/megalodon/src/friendica/entities/poll.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -namespace FriendicaEntity { - export type Poll = { - id: string - expires_at: string | null - expired: boolean - multiple: boolean - votes_count: number - options: Array - voted: boolean - } -} diff --git a/packages/megalodon/src/friendica/entities/poll_option.ts b/packages/megalodon/src/friendica/entities/poll_option.ts deleted file mode 100644 index f9628ddd80..0000000000 --- a/packages/megalodon/src/friendica/entities/poll_option.ts +++ /dev/null @@ -1,6 +0,0 @@ -namespace FriendicaEntity { - export type PollOption = { - title: string - votes_count: number | null - } -} diff --git a/packages/megalodon/src/friendica/entities/preferences.ts b/packages/megalodon/src/friendica/entities/preferences.ts deleted file mode 100644 index dec8b511be..0000000000 --- a/packages/megalodon/src/friendica/entities/preferences.ts +++ /dev/null @@ -1,9 +0,0 @@ -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/push_subscription.ts b/packages/megalodon/src/friendica/entities/push_subscription.ts deleted file mode 100644 index 857a98f27e..0000000000 --- a/packages/megalodon/src/friendica/entities/push_subscription.ts +++ /dev/null @@ -1,16 +0,0 @@ -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/relationship.ts b/packages/megalodon/src/friendica/entities/relationship.ts deleted file mode 100644 index bba3099a82..0000000000 --- a/packages/megalodon/src/friendica/entities/relationship.ts +++ /dev/null @@ -1,17 +0,0 @@ -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 - } -} diff --git a/packages/megalodon/src/friendica/entities/report.ts b/packages/megalodon/src/friendica/entities/report.ts deleted file mode 100644 index f20d6d2db1..0000000000 --- a/packages/megalodon/src/friendica/entities/report.ts +++ /dev/null @@ -1,16 +0,0 @@ -/// - -namespace FriendicaEntity { - export type Report = { - id: string - action_taken: boolean - category: Category - comment: string - forwarded: boolean - status_ids: Array | null - rule_ids: Array | null - target_account: Account - } - - export type Category = 'spam' | 'violation' | 'other' -} diff --git a/packages/megalodon/src/friendica/entities/results.ts b/packages/megalodon/src/friendica/entities/results.ts deleted file mode 100644 index 7af2356574..0000000000 --- a/packages/megalodon/src/friendica/entities/results.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// -/// -/// - -namespace FriendicaEntity { - export type Results = { - accounts: Array - statuses: Array - hashtags: Array - } -} diff --git a/packages/megalodon/src/friendica/entities/scheduled_status.ts b/packages/megalodon/src/friendica/entities/scheduled_status.ts deleted file mode 100644 index da292f7008..0000000000 --- a/packages/megalodon/src/friendica/entities/scheduled_status.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// -/// -namespace FriendicaEntity { - export type ScheduledStatus = { - id: string - scheduled_at: string - params: StatusParams - media_attachments: Array - } -} diff --git a/packages/megalodon/src/friendica/entities/source.ts b/packages/megalodon/src/friendica/entities/source.ts deleted file mode 100644 index 4033e911e8..0000000000 --- a/packages/megalodon/src/friendica/entities/source.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// -namespace FriendicaEntity { - export type Source = { - privacy: string | null - sensitive: boolean | null - language: string | null - note: string - fields: Array - } -} diff --git a/packages/megalodon/src/friendica/entities/stats.ts b/packages/megalodon/src/friendica/entities/stats.ts deleted file mode 100644 index 8ef290b7bc..0000000000 --- a/packages/megalodon/src/friendica/entities/stats.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace FriendicaEntity { - export type Stats = { - user_count: number - status_count: number - domain_count: number - } -} diff --git a/packages/megalodon/src/friendica/entities/status.ts b/packages/megalodon/src/friendica/entities/status.ts deleted file mode 100644 index 014da84ee1..0000000000 --- a/packages/megalodon/src/friendica/entities/status.ts +++ /dev/null @@ -1,49 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// - -namespace FriendicaEntity { - 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 - created_at: string - edited_at?: string | null - emojis: Emoji[] - replies_count: number - reblogs_count: number - favourites_count: number - reblogged: boolean | null - favourited: boolean | null - muted: boolean | null - sensitive: boolean - spoiler_text: string - visibility: 'public' | 'unlisted' | 'private' | 'direct' - media_attachments: Array - mentions: Array - tags: Array - card: Card | null - poll: Poll | null - application: Application | null - language: string | null - pinned: boolean | null - bookmarked?: boolean - // These parameters are unique parameters in fedibird.com for quote. - quote_id?: string - quote?: Status | null - } - - export type StatusTag = { - name: string - url: string - } -} diff --git a/packages/megalodon/src/friendica/entities/status_params.ts b/packages/megalodon/src/friendica/entities/status_params.ts deleted file mode 100644 index 6a14af837a..0000000000 --- a/packages/megalodon/src/friendica/entities/status_params.ts +++ /dev/null @@ -1,12 +0,0 @@ -namespace FriendicaEntity { - export type StatusParams = { - text: string - in_reply_to_id: string | null - media_ids: Array | null - sensitive: boolean | null - spoiler_text: string | null - visibility: 'public' | 'unlisted' | 'private' | null - scheduled_at: string | null - application_id: string - } -} diff --git a/packages/megalodon/src/friendica/entities/status_source.ts b/packages/megalodon/src/friendica/entities/status_source.ts deleted file mode 100644 index 2b5ee9bd0f..0000000000 --- a/packages/megalodon/src/friendica/entities/status_source.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace FriendicaEntity { - export type StatusSource = { - id: string - text: string - spoiler_text: string - } -} diff --git a/packages/megalodon/src/friendica/entities/tag.ts b/packages/megalodon/src/friendica/entities/tag.ts deleted file mode 100644 index f7998d22fd..0000000000 --- a/packages/megalodon/src/friendica/entities/tag.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -namespace FriendicaEntity { - export type Tag = { - name: string - url: string - history: Array - following?: boolean - } -} diff --git a/packages/megalodon/src/friendica/entities/token.ts b/packages/megalodon/src/friendica/entities/token.ts deleted file mode 100644 index 904d68651f..0000000000 --- a/packages/megalodon/src/friendica/entities/token.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace FriendicaEntity { - export type Token = { - access_token: string - token_type: string - scope: string - created_at: number - } -} diff --git a/packages/megalodon/src/friendica/entities/urls.ts b/packages/megalodon/src/friendica/entities/urls.ts deleted file mode 100644 index 8c736b9ef4..0000000000 --- a/packages/megalodon/src/friendica/entities/urls.ts +++ /dev/null @@ -1,5 +0,0 @@ -namespace FriendicaEntity { - export type URLs = { - streaming_api: string - } -} diff --git a/packages/megalodon/src/friendica/entity.ts b/packages/megalodon/src/friendica/entity.ts deleted file mode 100644 index 6d64f061ce..0000000000 --- a/packages/megalodon/src/friendica/entity.ts +++ /dev/null @@ -1,38 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// - -export default FriendicaEntity diff --git a/packages/megalodon/src/friendica/notification.ts b/packages/megalodon/src/friendica/notification.ts deleted file mode 100644 index 78701c46bc..0000000000 --- a/packages/megalodon/src/friendica/notification.ts +++ /dev/null @@ -1,14 +0,0 @@ -import FriendicaEntity from './entity' - -namespace FriendicaNotificationType { - export const Mention: FriendicaEntity.NotificationType = 'mention' - export const Reblog: FriendicaEntity.NotificationType = 'reblog' - export const Favourite: FriendicaEntity.NotificationType = 'favourite' - export const Follow: FriendicaEntity.NotificationType = 'follow' - export const Poll: FriendicaEntity.NotificationType = 'poll' - export const FollowRequest: FriendicaEntity.NotificationType = 'follow_request' - export const Status: FriendicaEntity.NotificationType = 'status' - export const Update: FriendicaEntity.NotificationType = 'update' -} - -export default FriendicaNotificationType diff --git a/packages/megalodon/src/friendica/web_socket.ts b/packages/megalodon/src/friendica/web_socket.ts deleted file mode 100644 index ca16f24a5f..0000000000 --- a/packages/megalodon/src/friendica/web_socket.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { WebSocketInterface } from '../megalodon' -import { EventEmitter } from 'events' -import { ProxyConfig } from '../proxy_config' - -export default class WebSocket extends EventEmitter implements WebSocketInterface { - constructor( - _url: string, - _stream: string, - _params: string | undefined, - _accessToken: string, - _userAgent: string, - _proxyConfig: ProxyConfig | false = false - ) { - super() - } - public start() {} - public stop() {} -} diff --git a/packages/megalodon/src/index.ts b/packages/megalodon/src/index.ts index 070c397d2d..621f007ccf 100644 --- a/packages/megalodon/src/index.ts +++ b/packages/megalodon/src/index.ts @@ -2,15 +2,14 @@ import Response from './response' import OAuth from './oauth' import { isCancel, RequestCanceledError } from './cancel' import { ProxyConfig } from './proxy_config' -import generator, { MegalodonInterface, WebSocketInterface } from './megalodon' +import { MegalodonInterface, WebSocketInterface } from './megalodon' import { detector } from './detector' -import Mastodon from './mastodon' -import Pleroma from './pleroma' import Misskey from './misskey' import Entity from './entity' import NotificationType from './notification' import FilterContext from './filter_context' import Converter from './converter' +import MastodonEntity from './mastodon/entity'; export { Response, @@ -23,14 +22,8 @@ export { WebSocketInterface, NotificationType, FilterContext, - Mastodon, - Pleroma, Misskey, Entity, Converter, - generator, + MastodonEntity, } - -export const megalodon = generator; - -export default generator diff --git a/packages/megalodon/src/mastodon.ts b/packages/megalodon/src/mastodon.ts deleted file mode 100644 index 4a8b1fc1ea..0000000000 --- a/packages/megalodon/src/mastodon.ts +++ /dev/null @@ -1,3169 +0,0 @@ -import { OAuth2 } from 'oauth' -import FormData from 'form-data' -import parseLinkHeader from 'parse-link-header' - -import MastodonAPI from './mastodon/api_client' -import WebSocket from './mastodon/web_socket' -import { MegalodonInterface, NoImplementedError } from './megalodon' -import Response from './response' -import Entity from './entity' -import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default' -import { ProxyConfig } from './proxy_config' -import OAuth from './oauth' -import { UnknownNotificationTypeError } from './notification' - -export default class Mastodon implements MegalodonInterface { - public client: MastodonAPI.Interface - public baseUrl: string - - /** - * @param baseUrl hostname or base URL - * @param accessToken access token from OAuth2 authorization - * @param userAgent UserAgent is specified in header on request. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - */ - constructor( - baseUrl: string, - accessToken: string | null = null, - userAgent: string | null = DEFAULT_UA, - proxyConfig: ProxyConfig | false = false - ) { - let token: string = '' - if (accessToken) { - token = accessToken - } - let agent: string = DEFAULT_UA - if (userAgent) { - agent = userAgent - } - this.client = new MastodonAPI.Client(baseUrl, token, agent, proxyConfig) - this.baseUrl = baseUrl - } - - public cancel(): void { - return this.client.cancel() - } - - /** - * Call /api/v1/apps - * - * Create an application. - * @param client_name your application's name - * @param options Form Data - */ - public async registerApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - return this.createApp(client_name, options).then(async appData => { - return this.generateAuthUrl(appData.client_id, appData.client_secret, { - scope: scopes, - redirect_uri: appData.redirect_uri - }).then(url => { - appData.url = url - return appData - }) - }) - } - - /** - * Call /api/v1/apps - * - * Create an application. - * @param client_name your application's name - * @param options Form Data - */ - public async createApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - const redirect_uris = options.redirect_uris || NO_REDIRECT - - const params: { - client_name: string - redirect_uris: string - scopes: string - website?: string - } = { - client_name: client_name, - redirect_uris: redirect_uris, - scopes: scopes.join(' ') - } - if (options.website) params.website = options.website - - return this.client - .post('/api/v1/apps', params) - .then((res: Response) => OAuth.AppData.from(res.data)) - } - - /** - * Generate authorization url using OAuth2. - * - * @param clientId your OAuth app's client ID - * @param clientSecret your OAuth app's client Secret - * @param options as property, redirect_uri and scope are available, and must be the same as when you register your app - */ - public generateAuthUrl( - clientId: string, - clientSecret: string, - options: Partial<{ scope: Array; redirect_uri: string }> - ): Promise { - const scope = options.scope || DEFAULT_SCOPE - const redirect_uri = options.redirect_uri || NO_REDIRECT - return new Promise(resolve => { - const oauth = new OAuth2(clientId, clientSecret, this.baseUrl, undefined, '/oauth/token') - const url = oauth.getAuthorizeUrl({ - redirect_uri: redirect_uri, - response_type: 'code', - client_id: clientId, - scope: scope.join(' ') - }) - resolve(url) - }) - } - - // ====================================== - // apps - // ====================================== - /** - * GET /api/v1/apps/verify_credentials - * - * @return An Application - */ - public verifyAppCredentials(): Promise> { - return this.client.get('/api/v1/apps/verify_credentials') - } - - // ====================================== - // apps/oauth - // ====================================== - /** - * POST /oauth/token - * - * Fetch OAuth access token. - * Get an access token based client_id and client_secret and authorization code. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param code will be generated by the link of #generateAuthUrl or #registerApp - * @param redirect_uri must be the same uri as the time when you register your OAuth application - */ - public async fetchAccessToken( - client_id: string | null, - client_secret: string, - code: string, - redirect_uri: string = NO_REDIRECT - ): Promise { - if (!client_id) { - throw new Error('client_id is required') - } - return this.client - .post('/oauth/token', { - client_id, - client_secret, - code, - redirect_uri, - grant_type: 'authorization_code' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/revoke - * - * Revoke an OAuth token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param token will be get #fetchAccessToken - */ - public async refreshToken(client_id: string, client_secret: string, refresh_token: string): Promise { - return this.client - .post('/oauth/token', { - client_id, - client_secret, - refresh_token, - grant_type: 'refresh_token' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/revoke - * - * Revoke an OAuth token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param token will be get #fetchAccessToken - */ - public async revokeToken(client_id: string, client_secret: string, token: string): Promise> { - return this.client.post<{}>('/oauth/revoke', { - client_id, - client_secret, - token - }) - } - - // ====================================== - // accounts - // ====================================== - /** - * POST /api/v1/accounts - * - * @param username Username for the account. - * @param email Email for the account. - * @param password Password for the account. - * @param agreement Whether the user agrees to the local rules, terms, and policies. - * @param locale The language of the confirmation email that will be sent - * @param reason Text that will be reviewed by moderators if registrations require manual approval. - * @return An account token. - */ - public async registerAccount( - username: string, - email: string, - password: string, - agreement: boolean, - locale: string, - reason?: string | null - ): Promise> { - let params = { - username: username, - email: email, - password: password, - agreement: agreement, - locale: locale - } - if (reason) { - params = Object.assign(params, { - reason: reason - }) - } - return this.client.post('/api/v1/accounts', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.token(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/verify_credentials - * - * @return Account. - */ - public async verifyAccountCredentials(): Promise> { - return this.client.get('/api/v1/accounts/verify_credentials').then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.account(res.data) - }) - }) - } - - /** - * PATCH /api/v1/accounts/update_credentials - * - * @return An account. - */ - public async updateCredentials(options?: { - discoverable?: boolean - bot?: boolean - display_name?: string - note?: string - avatar?: string - header?: string - locked?: boolean - source?: { - privacy?: string - sensitive?: boolean - language?: string - } - fields_attributes?: Array<{ name: string; value: string }> - }): Promise> { - let params = {} - if (options) { - if (options.discoverable !== undefined) { - params = Object.assign(params, { - discoverable: options.discoverable - }) - } - if (options.bot !== undefined) { - params = Object.assign(params, { - bot: options.bot - }) - } - if (options.display_name) { - params = Object.assign(params, { - display_name: options.display_name - }) - } - if (options.note) { - params = Object.assign(params, { - note: options.note - }) - } - if (options.avatar) { - params = Object.assign(params, { - avatar: options.avatar - }) - } - if (options.header) { - params = Object.assign(params, { - header: options.header - }) - } - if (options.locked !== undefined) { - params = Object.assign(params, { - locked: options.locked - }) - } - if (options.source) { - params = Object.assign(params, { - source: options.source - }) - } - if (options.fields_attributes) { - params = Object.assign(params, { - fields_attributes: options.fields_attributes - }) - } - } - return this.client.patch('/api/v1/accounts/update_credentials', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.account(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id - * - * @param id The account ID. - * @return An account. - */ - public async getAccount(id: string): Promise> { - return this.client.get(`/api/v1/accounts/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.account(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/statuses - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID but starting with most recent. - * @param options.min_id Return results newer than ID. - * @param options.pinned Return statuses which include pinned statuses. - * @param options.exclude_replies Return statuses which exclude replies. - * @param options.exclude_reblogs Return statuses which exclude reblogs. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @return Account's statuses. - */ - public async getAccountStatuses( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - pinned?: boolean - exclude_replies?: boolean - exclude_reblogs?: boolean - only_media: boolean - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.pinned) { - params = Object.assign(params, { - pinned: options.pinned - }) - } - if (options.exclude_replies) { - params = Object.assign(params, { - exclude_replies: options.exclude_replies - }) - } - if (options.exclude_reblogs) { - params = Object.assign(params, { - exclude_reblogs: options.exclude_reblogs - }) - } - if (options.only_media) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - } - return this.client.get>(`/api/v1/accounts/${id}/statuses`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - public getAccountFavourites( - _id: string, - _options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id Target account ID. - * @return Relationship. - */ - public async subscribeAccount(id: string): Promise> { - const params = { - notify: true - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id Target account ID. - * @return Relationship. - */ - public async unsubscribeAccount(id: string): Promise> { - const params = { - notify: false - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/followers - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowers( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - get_all?: boolean - sleep_ms?: number - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.urlToAccounts(`/api/v1/accounts/${id}/followers`, params, options?.get_all || false, options?.sleep_ms || 0) - } - - /** - * GET /api/v1/accounts/:id/following - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowing( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - get_all?: boolean - sleep_ms?: number - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.urlToAccounts(`/api/v1/accounts/${id}/following`, params, options?.get_all || false, options?.sleep_ms || 0) - } - - /** Helper function to optionally follow Link headers as pagination */ - private async urlToAccounts(url: string, params: Record, get_all: boolean, sleep_ms: number) { - const res = await this.client.get>(url, params) - let converted = Object.assign({}, res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - if (get_all && converted.headers.link) { - let parsed = parseLinkHeader(converted.headers.link) - while (parsed?.next) { - const nextRes = await this.client.get>(parsed?.next.url, undefined, undefined, true) - converted = Object.assign({}, converted, { - data: [...converted.data, ...nextRes.data.map(a => MastodonAPI.Converter.account(a))] - }) - parsed = parseLinkHeader(nextRes.headers.link) - if (sleep_ms) { - await new Promise(converted => setTimeout(converted, sleep_ms)) - } - } - } - return converted - } - - /** - * GET /api/v1/accounts/:id/lists - * - * @param id The account ID. - * @return The array of lists. - */ - public async getAccountLists(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/lists`).then(res => { - return Object.assign(res, { - data: res.data.map(l => MastodonAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/identity_proofs - * - * @param id The account ID. - * @return Array of IdentityProof - */ - public async getIdentityProof(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/identity_proofs`).then(res => { - return Object.assign(res, { - data: res.data.map(i => MastodonAPI.Converter.identity_proof(i)) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id The account ID. - * @param reblog Receive this account's reblogs in home timeline. - * @return Relationship - */ - public async followAccount(id: string, options?: { reblog?: boolean }): Promise> { - let params = {} - if (options) { - if (options.reblog !== undefined) { - params = Object.assign(params, { - reblog: options.reblog - }) - } - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unfollow - * - * @param id The account ID. - * @return Relationship - */ - public async unfollowAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unfollow`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/block - * - * @param id The account ID. - * @return Relationship - */ - public async blockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/block`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unblock - * - * @param id The account ID. - * @return RElationship - */ - public async unblockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unblock`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/mute - * - * @param id The account ID. - * @param notifications Mute notifications in addition to statuses. - * @return Relationship - */ - public async muteAccount(id: string, notifications: boolean = true): Promise> { - return this.client - .post(`/api/v1/accounts/${id}/mute`, { - notifications: notifications - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unmute - * - * @param id The account ID. - * @return Relationship - */ - public async unmuteAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unmute`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/pin - * - * @param id The account ID. - * @return Relationship - */ - public async pinAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/pin`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unpin - * - * @param id The account ID. - * @return Relationship - */ - public async unpinAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unpin`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/relationships - * - * @param id The account ID. - * @return Relationship - */ - public async getRelationship(id: string): Promise> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: [id] - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data[0]) - }) - }) - } - - /** - * GET /api/v1/accounts/relationships - * - * @param ids Array of account IDs. - * @return Array of Relationship. - */ - public async getRelationships(ids: Array): Promise>> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: ids - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(r => MastodonAPI.Converter.relationship(r)) - }) - }) - } - - /** - * GET /api/v1/accounts/search - * - * @param q Search query. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async searchAccount( - q: string, - options?: { - following?: boolean - resolve?: boolean - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = { q: q } - if (options) { - if (options.following !== undefined && options.following !== null) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.resolve !== undefined && options.resolve !== null) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/accounts/search', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/bookmarks - // ====================================== - - /** - * GET /api/v1/bookmarks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getBookmarks(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/bookmarks', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/favourites - // ====================================== - - /** - * GET /api/v1/favourites - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/favourites', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/mutes - // ====================================== - /** - * GET /api/v1/mutes - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getMutes(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/mutes', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/blocks - // ====================================== - /** - * GET /api/v1/blocks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/blocks', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/domain_blocks - // ====================================== - /** - * GET /api/v1/domain_blocks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of domain name. - */ - public async getDomainBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/domain_blocks', params) - } - - /** - * POST/api/v1/domain_blocks - * - * @param domain Domain to block. - */ - public blockDomain(domain: string): Promise> { - return this.client.post<{}>('/api/v1/domain_blocks', { - domain: domain - }) - } - - /** - * DELETE /api/v1/domain_blocks - * - * @param domain Domain to unblock - */ - public unblockDomain(domain: string): Promise> { - return this.client.del<{}>('/api/v1/domain_blocks', { - domain: domain - }) - } - - // ====================================== - // accounts/filters - // ====================================== - /** - * GET /api/v1/filters - * - * @return Array of filters. - */ - public async getFilters(): Promise>> { - return this.client.get>('/api/v1/filters').then(res => { - return Object.assign(res, { - data: res.data.map(f => MastodonAPI.Converter.filter(f)) - }) - }) - } - - /** - * GET /api/v1/filters/:id - * - * @param id The filter ID. - * @return Filter. - */ - public async getFilter(id: string): Promise> { - return this.client.get(`/api/v1/filters/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.filter(res.data) - }) - }) - } - - /** - * POST /api/v1/filters - * - * @param phrase Text to be filtered. - * @param context Array of enumerable strings home, notifications, public, thread, account. At least one context must be specified. - * @param options.irreversible Should the server irreversibly drop matching entities from home and notifications? - * @param options.whole_word Consider word boundaries? - * @param options.expires_in ISO 8601 Datetime for when the filter expires. - * @return Filter - */ - public async createFilter( - phrase: string, - context: Array, - options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - let params = { - phrase: phrase, - context: context - } - if (options) { - if (options.irreversible !== undefined) { - params = Object.assign(params, { - irreversible: options.irreversible - }) - } - if (options.whole_word !== undefined) { - params = Object.assign(params, { - whole_word: options.whole_word - }) - } - if (options.expires_in) { - params = Object.assign(params, { - expires_in: options.expires_in - }) - } - } - return this.client.post('/api/v1/filters', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.filter(res.data) - }) - }) - } - - /** - * PUT /api/v1/filters/:id - * - * @param id The filter ID. - * @param phrase Text to be filtered. - * @param context Array of enumerable strings home, notifications, public, thread, account. At least one context must be specified. - * @param options.irreversible Should the server irreversibly drop matching entities from home and notifications? - * @param options.whole_word Consider word boundaries? - * @param options.expires_in ISO 8601 Datetime for when the filter expires. - * @return Filter - */ - public async updateFilter( - id: string, - phrase: string, - context: Array, - options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - let params = { - phrase: phrase, - context: context - } - if (options) { - if (options.irreversible !== undefined) { - params = Object.assign(params, { - irreversible: options.irreversible - }) - } - if (options.whole_word !== undefined) { - params = Object.assign(params, { - whole_word: options.whole_word - }) - } - if (options.expires_in) { - params = Object.assign(params, { - expires_in: options.expires_in - }) - } - } - return this.client.put(`/api/v1/filters/${id}`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.filter(res.data) - }) - }) - } - - /** - * DELETE /api/v1/filters/:id - * - * @param id The filter ID. - * @return Removed filter. - */ - public async deleteFilter(id: string): Promise> { - return this.client.del(`/api/v1/filters/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.filter(res.data) - }) - }) - } - - // ====================================== - // accounts/reports - // ====================================== - /** - * POST /api/v1/reports - * - * @param account_id Target account ID. - * @param options.status_ids Array of Statuses ids to attach to the report. - * @param options.comment The reason for the report. Default maximum of 1000 characters. - * @param options.forward If the account is remote, should the report be forwarded to the remote admin? - * @param options.category Specify if the report is due to spam, violation of enumerated instance rules, or some other reason. Defaults to other. Will be set to violation if rule_ids[] is provided (regardless of any category value you provide). - * @param options.rule_ids For violation category reports, specify the ID of the exact rules broken. Rules and their IDs are available via GET /api/v1/instance/rules and GET /api/v1/instance. - * @return Report - */ - public async report( - account_id: string, - options?: { - status_ids?: Array - comment: string - forward?: boolean - category?: Entity.Category - rule_ids?: Array - } - ): Promise> { - let params = { - account_id: account_id - } - if (options) { - if (options.status_ids) { - params = Object.assign(params, { - status_ids: options.status_ids - }) - } - if (options.comment) { - params = Object.assign(params, { - comment: options.comment - }) - } - if (options.forward !== undefined) { - params = Object.assign(params, { - forward: options.forward - }) - } - if (options.category) { - params = Object.assign(params, { - category: options.category - }) - } - if (options.rule_ids) { - params = Object.assign(params, { - rule_ids: options.rule_ids - }) - } - } - return this.client.post('/api/v1/reports', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.report(res.data) - }) - }) - } - - // ====================================== - // accounts/follow_requests - // ====================================== - /** - * GET /api/v1/follow_requests - * - * @param limit Maximum number of results. - * @return Array of account. - */ - public async getFollowRequests(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/follow_requests', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } else { - return this.client.get>('/api/v1/follow_requests').then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - } - - /** - * POST /api/v1/follow_requests/:id/authorize - * - * @param id Target account ID. - * @return Relationship. - */ - public async acceptFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/follow_requests/:id/reject - * - * @param id Target account ID. - * @return Relationship. - */ - public async rejectFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/reject`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.relationship(res.data) - }) - }) - } - - // ====================================== - // accounts/endorsements - // ====================================== - /** - * GET /api/v1/endorsements - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return Array of accounts. - */ - public async getEndorsements(options?: { limit?: number; max_id?: string; since_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>('/api/v1/endorsements', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/featured_tags - // ====================================== - /** - * GET /api/v1/featured_tags - * - * @return Array of featured tag. - */ - public async getFeaturedTags(): Promise>> { - return this.client.get>('/api/v1/featured_tags').then(res => { - return Object.assign(res, { - data: res.data.map(f => MastodonAPI.Converter.featured_tag(f)) - }) - }) - } - - /** - * POST /api/v1/featured_tags - * - * @param name Target hashtag name. - * @return FeaturedTag. - */ - public async createFeaturedTag(name: string): Promise> { - return this.client - .post('/api/v1/featured_tags', { - name: name - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.featured_tag(res.data) - }) - }) - } - - /** - * DELETE /api/v1/featured_tags/:id - * - * @param id Target featured tag id. - * @return Empty - */ - public deleteFeaturedTag(id: string): Promise> { - return this.client.del<{}>(`/api/v1/featured_tags/${id}`) - } - - /** - * GET /api/v1/featured_tags/suggestions - * - * @return Array of tag. - */ - public async getSuggestedTags(): Promise>> { - return this.client.get>('/api/v1/featured_tags/suggestions').then(res => { - return Object.assign(res, { - data: res.data.map(t => MastodonAPI.Converter.tag(t)) - }) - }) - } - - // ====================================== - // accounts/preferences - // ====================================== - /** - * GET /api/v1/preferences - * - * @return Preferences. - */ - public async getPreferences(): Promise> { - return this.client.get('/api/v1/preferences').then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.preferences(res.data) - }) - }) - } - - // ====================================== - // accounts/followed_tags - // ====================================== - /** - * GET /api/v1/followed_tags - * - * @return Array of Tag. - */ - public async getFollowedTags(): Promise>> { - return this.client.get>('/api/v1/followed_tags').then(res => { - return Object.assign(res, { - data: res.data.map(tag => MastodonAPI.Converter.tag(tag)) - }) - }) - } - - // ====================================== - // accounts/suggestions - // ====================================== - /** - * GET /api/v1/suggestions - * - * @param limit Maximum number of results. - * @return Array of accounts. - */ - public async getSuggestions(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/suggestions', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } else { - return this.client.get>('/api/v1/suggestions').then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - } - - // ====================================== - // accounts/tags - // ====================================== - /** - * GET /api/v1/tags/:id - * - * @param id Target hashtag id. - * @return Tag - */ - public async getTag(id: string): Promise> { - return this.client.get(`/api/v1/tags/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.tag(res.data) - }) - }) - } - - /** - * POST /api/v1/tags/:id/follow - * - * @param id Target hashtag id. - * @return Tag - */ - public async followTag(id: string): Promise> { - return this.client.post(`/api/v1/tags/${id}/follow`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.tag(res.data) - }) - }) - } - - /** - * POST /api/v1/tags/:id/unfollow - * - * @param id Target hashtag id. - * @return Tag - */ - public async unfollowTag(id: string): Promise> { - return this.client.post(`/api/v1/tags/${id}/unfollow`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.tag(res.data) - }) - }) - } - - // ====================================== - // statuses - // ====================================== - /** - * POST /api/v1/statuses - * - * @param status Text content of status. - * @param options.media_ids Array of Attachment ids. - * @param options.poll Poll object. - * @param options.in_reply_to_id ID of the status being replied to, if status is a reply. - * @param options.sensitive Mark status and attached media as sensitive? - * @param options.spoiler_text Text to be shown as a warning or subject before the actual content. - * @param options.visibility Visibility of the posted status. - * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status. - * @param options.language ISO 639 language code for this status. - * @param options.quote_id ID of the status being quoted to, if status is a quote. - * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead. - */ - public async postStatus( - status: string, - options: { - media_ids?: Array - poll?: { options: Array; expires_in: number; multiple?: boolean; hide_totals?: boolean } - in_reply_to_id?: string - sensitive?: boolean - spoiler_text?: string - visibility?: 'public' | 'unlisted' | 'private' | 'direct' - scheduled_at?: string - language?: string - quote_id?: string - } - ): Promise> { - let params = { - status: status - } - if (options) { - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = { - options: options.poll.options, - expires_in: options.poll.expires_in - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - if (options.in_reply_to_id) { - params = Object.assign(params, { - in_reply_to_id: options.in_reply_to_id - }) - } - if (options.sensitive !== undefined) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.visibility) { - params = Object.assign(params, { - visibility: options.visibility - }) - } - if (options.scheduled_at) { - params = Object.assign(params, { - scheduled_at: options.scheduled_at - }) - } - if (options.language) { - params = Object.assign(params, { - language: options.language - }) - } - if (options.quote_id) { - params = Object.assign(params, { - quote_id: options.quote_id - }) - } - } - if (options && options.scheduled_at) { - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.scheduled_status(res.data) - }) - }) - } - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async getStatus(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - PUT /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async editStatus( - id: string, - options: { - status?: string - spoiler_text?: string - sensitive?: boolean - media_ids?: Array - poll?: { options?: Array; expires_in?: number; multiple?: boolean; hide_totals?: boolean } - } - ): Promise> { - let params = {} - if (options.status) { - params = Object.assign(params, { - status: options.status - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.sensitive) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = {} - if (options.poll.options !== undefined) { - pollParam = Object.assign(pollParam, { - options: options.poll.options - }) - } - if (options.poll.expires_in !== undefined) { - pollParam = Object.assign(pollParam, { - expires_in: options.poll.expires_in - }) - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - return this.client.put(`/api/v1/statuses/${id}`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async deleteStatus(id: string): Promise> { - return this.client.del(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/context - * - * Get parent and child statuses. - * @param id The target status id. - * @return Context - */ - public async getStatusContext( - id: string, - options?: { limit?: number; max_id?: string; since_id?: string } - ): Promise> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get(`/api/v1/statuses/${id}/context`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.context(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/source - * - * Obtain the source properties for a status so that it can be edited. - * @param id The target status id. - * @return StatusSource - */ - public async getStatusSource(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}/source`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status_source(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/reblogged_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusRebloggedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/reblogged_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/favourited_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusFavouritedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/favourited_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/favourite - * - * @param id The target status id. - * @return Status. - */ - public async favouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/favourite`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unfavourite - * - * @param id The target status id. - * @return Status. - */ - public async unfavouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/reblog - * - * @param id The target status id. - * @return Status. - */ - public async reblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/reblog`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unreblog - * - * @param id The target status id. - * @return Status. - */ - public async unreblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unreblog`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/bookmark - * - * @param id The target status id. - * @return Status. - */ - public async bookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/bookmark`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unbookmark - * - * @param id The target status id. - * @return Status. - */ - public async unbookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/mute - * - * @param id The target status id. - * @return Status - */ - public async muteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/mute`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unmute - * - * @param id The target status id. - * @return Status - */ - public async unmuteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unmute`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/pin - * @param id The target status id. - * @return Status - */ - public async pinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/pin`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unpin - * - * @param id The target status id. - * @return Status - */ - public async unpinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unpin`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.status(res.data) - }) - }) - } - - // ====================================== - // statuses/media - // ====================================== - /** - * POST /api/v2/media - * - * @param file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @return Attachment - */ - public async uploadMedia( - file: any, - options?: { description?: string; focus?: string } - ): Promise> { - const formData = new FormData() - formData.append('file', file) - if (options) { - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.postForm('/api/v2/media', formData).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.async_attachment(res.data) - }) - }) - } - - /** - * GET /api/v1/media/:id - * - * @param id Target media ID. - * @return Attachment - */ - public async getMedia(id: string): Promise> { - const res = await this.client.get(`/api/v1/media/${id}`) - - return Object.assign(res, { - data: MastodonAPI.Converter.attachment(res.data) - }) - } - - /** - * PUT /api/v1/media/:id - * - * @param id Target media ID. - * @param options.file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @param options.is_sensitive Whether the media is sensitive. - * @return Attachment - */ - public async updateMedia( - id: string, - options?: { - file?: any - description?: string - focus?: string - } - ): Promise> { - const formData = new FormData() - if (options) { - if (options.file) { - formData.append('file', options.file) - } - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.putForm(`/api/v1/media/${id}`, formData).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.attachment(res.data) - }) - }) - } - - // ====================================== - // statuses/polls - // ====================================== - /** - * GET /api/v1/polls/:id - * - * @param id Target poll ID. - * @return Poll - */ - public async getPoll(id: string): Promise> { - return this.client.get(`/api/v1/polls/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.poll(res.data) - }) - }) - } - - /** - * POST /api/v1/polls/:id/votes - * - * @param id Target poll ID. - * @param choices Array of own votes containing index for each option (starting from 0). - * @return Poll - */ - public async votePoll(id: string, choices: Array): Promise> { - return this.client - .post(`/api/v1/polls/${id}/votes`, { - choices: choices - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.poll(res.data) - }) - }) - } - - // ====================================== - // statuses/scheduled_statuses - // ====================================== - /** - * GET /api/v1/scheduled_statuses - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of scheduled statuses. - */ - public async getScheduledStatuses(options?: { - limit?: number | null - max_id?: string | null - since_id?: string | null - min_id?: string | null - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/scheduled_statuses', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.scheduled_status(s)) - }) - }) - } - - /** - * GET /api/v1/scheduled_statuses/:id - * - * @param id Target status ID. - * @return ScheduledStatus. - */ - public async getScheduledStatus(id: string): Promise> { - return this.client.get(`/api/v1/scheduled_statuses/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.scheduled_status(res.data) - }) - }) - } - - /** - * PUT /api/v1/scheduled_statuses/:id - * - * @param id Target scheduled status ID. - * @param scheduled_at ISO 8601 Datetime at which the status will be published. - * @return ScheduledStatus. - */ - public async scheduleStatus(id: string, scheduled_at?: string | null): Promise> { - let params = {} - if (scheduled_at) { - params = Object.assign(params, { - scheduled_at: scheduled_at - }) - } - return this.client.put(`/api/v1/scheduled_statuses/${id}`, params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.scheduled_status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/scheduled_statuses/:id - * - * @param id Target scheduled status ID. - */ - public cancelScheduledStatus(id: string): Promise> { - return this.client.del<{}>(`/api/v1/scheduled_statuses/${id}`) - } - - // ====================================== - // timelines - // ====================================== - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getPublicTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: false - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getLocalTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: true - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/tag/:hashtag - * - * @param hashtag Content of a #hashtag, not including # symbol. - * @param options.local Show only local statuses? Defaults to false. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getTagTimeline( - hashtag: string, - options?: { - local?: boolean - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/tag/${hashtag}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/home - * - * @param options.local Show only local statuses? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getHomeTimeline(options?: { - local?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/home', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/list/:list_id - * - * @param list_id Local ID of the list in the database. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getListTimeline( - list_id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/list/${list_id}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => MastodonAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // timelines/conversations - // ====================================== - /** - * GET /api/v1/conversations - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getConversationTimeline(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/conversations', params).then(res => { - return Object.assign(res, { - data: res.data.map(c => MastodonAPI.Converter.conversation(c)) - }) - }) - } - - /** - * DELETE /api/v1/conversations/:id - * - * @param id Target conversation ID. - */ - public deleteConversation(id: string): Promise> { - return this.client.del<{}>(`/api/v1/conversations/${id}`) - } - - /** - * POST /api/v1/conversations/:id/read - * - * @param id Target conversation ID. - * @return Conversation. - */ - public async readConversation(id: string): Promise> { - return this.client.post(`/api/v1/conversations/${id}/read`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.conversation(res.data) - }) - }) - } - - // ====================================== - // timelines/lists - // ====================================== - /** - * GET /api/v1/lists - * - * @return Array of lists. - */ - public async getLists(): Promise>> { - return this.client.get>('/api/v1/lists').then(res => { - return Object.assign(res, { - data: res.data.map(l => MastodonAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/lists/:id - * - * @param id Target list ID. - * @return List. - */ - public async getList(id: string): Promise> { - return this.client.get(`/api/v1/lists/${id}`).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.list(res.data) - }) - }) - } - - /** - * POST /api/v1/lists - * - * @param title List name. - * @return List. - */ - public async createList(title: string): Promise> { - return this.client - .post('/api/v1/lists', { - title: title - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.list(res.data) - }) - }) - } - - /** - * PUT /api/v1/lists/:id - * - * @param id Target list ID. - * @param title New list name. - * @return List. - */ - public async updateList(id: string, title: string): Promise> { - return this.client - .put(`/api/v1/lists/${id}`, { - title: title - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.list(res.data) - }) - }) - } - - /** - * DELETE /api/v1/lists/:id - * - * @param id Target list ID. - */ - public deleteList(id: string): Promise> { - return this.client.del<{}>(`/api/v1/lists/${id}`) - } - - /** - * GET /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param options.limit Max number of results to return. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getAccountsInList( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>(`/api/v1/lists/${id}/accounts`, params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public addAccountsToList(id: string, account_ids: Array): Promise> { - return this.client.post<{}>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - /** - * DELETE /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public deleteAccountsFromList(id: string, account_ids: Array): Promise> { - return this.client.del<{}>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - // ====================================== - // timelines/markers - // ====================================== - /** - * GET /api/v1/markers - * - * @param timelines Array of timeline names, String enum anyOf home, notifications. - * @return Marker or empty object. - */ - public async getMarkers(timeline: Array): Promise>> { - return this.client - .get>('/api/v1/markers', { - timeline: timeline - }) - .then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.marker(res.data) - }) - }) - } - - /** - * POST /api/v1/markers - * - * @param options.home Marker position of the last read status ID in home timeline. - * @param options.notifications Marker position of the last read notification ID in notifications. - * @return Marker. - */ - public async saveMarkers(options?: { - home?: { last_read_id: string } - notifications?: { last_read_id: string } - }): Promise> { - let params = {} - if (options) { - if (options.home) { - params = Object.assign(params, { - home: options.home - }) - } - if (options.notifications) { - params = Object.assign(params, { - notifications: options.notifications - }) - } - } - return this.client.post('/api/v1/markers', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.marker(res.data) - }) - }) - } - - // ====================================== - // notifications - // ====================================== - /** - * GET /api/v1/notifications - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @param options.exclude_types Array of types to exclude. - * @param options.account_id Return only notifications received from this account. - * @return Array of notifications. - */ - public async getNotifications(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - exclude_types?: Array - account_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.exclude_types) { - params = Object.assign(params, { - exclude_types: options.exclude_types.map(e => MastodonAPI.Converter.encodeNotificationType(e)) - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - } - return this.client.get>('/api/v1/notifications', params).then(res => { - return Object.assign(res, { - data: res.data.flatMap(n => { - const notify = MastodonAPI.Converter.notification(n) - if (notify instanceof UnknownNotificationTypeError) return [] - return notify - }) - }) - }) - } - - /** - * GET /api/v1/notifications/:id - * - * @param id Target notification ID. - * @return Notification. - */ - public async getNotification(id: string): Promise> { - const res = await this.client.get(`/api/v1/notifications/${id}`) - const notify = MastodonAPI.Converter.notification(res.data) - if (notify instanceof UnknownNotificationTypeError) { - throw new UnknownNotificationTypeError() - } - return { ...res, data: notify } - } - - /** - * POST /api/v1/notifications/clear - */ - public dismissNotifications(): Promise> { - return this.client.post<{}>('/api/v1/notifications/clear') - } - - /** - * POST /api/v1/notifications/:id/dismiss - * - * @param id Target notification ID. - */ - public dismissNotification(id: string): Promise> { - return this.client.post<{}>(`/api/v1/notifications/${id}/dismiss`) - } - - public readNotifications(_options: { - id?: string - max_id?: string - }): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - // ====================================== - // notifications/push - // ====================================== - /** - * POST /api/v1/push/subscription - * - * @param subscription[endpoint] Endpoint URL that is called when a notification event occurs. - * @param subscription[keys][p256dh] User agent public key. Base64 encoded string of public key of ECDH key using prime256v1 curve. - * @param subscription[keys] Auth secret. Base64 encoded string of 16 bytes of random data. - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async subscribePushNotification( - subscription: { endpoint: string; keys: { p256dh: string; auth: string } }, - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = { - subscription - } - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.post('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * GET /api/v1/push/subscription - * - * @return PushSubscription. - */ - public async getPushSubscription(): Promise> { - return this.client.get('/api/v1/push/subscription').then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * PUT /api/v1/push/subscription - * - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async updatePushSubscription( - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = {} - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.put('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * DELETE /api/v1/push/subscription - */ - public deletePushSubscription(): Promise> { - return this.client.del<{}>('/api/v1/push/subscription') - } - - // ====================================== - // search - // ====================================== - /** - * GET /api/v2/search - * - * @param q The search query. - * @param type Enum of search target. - * @param options.limit Maximum number of results to load, per type. Defaults to 20. Max 40. - * @param options.max_id Return results older than this id. - * @param options.min_id Return results immediately newer than this id. - * @param options.resolve Attempt WebFinger lookup. Defaults to false. - * @param options.following Only include accounts that the user is following. Defaults to false. - * @param options.account_id If provided, statuses returned will be authored only by this account. - * @param options.exclude_unreviewed Filter out unreviewed tags? Defaults to false. - * @return Results. - */ - public async search( - q: string, - options?: { - type?: 'accounts' | 'hashtags' | 'statuses' - limit?: number - max_id?: string - min_id?: string - resolve?: boolean - offset?: number - following?: boolean - account_id?: string - exclude_unreviewed?: boolean - } - ): Promise> { - let params = { - q - } - if (options) { - if (options.type) { - params = Object.assign(params, { - type: options.type - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.resolve !== undefined) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.following !== undefined) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - if (options.exclude_unreviewed) { - params = Object.assign(params, { - exclude_unreviewed: options.exclude_unreviewed - }) - } - } - return this.client.get('/api/v2/search', params).then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.results(res.data) - }) - }) - } - - // ====================================== - // instance - // ====================================== - /** - * GET /api/v1/instance - */ - public async getInstance(): Promise> { - return this.client.get('/api/v1/instance').then(res => { - return Object.assign(res, { - data: MastodonAPI.Converter.instance(res.data) - }) - }) - } - - /** - * GET /api/v1/instance/peers - */ - public getInstancePeers(): Promise>> { - return this.client.get>('/api/v1/instance/peers') - } - - /** - * GET /api/v1/instance/activity - */ - public async getInstanceActivity(): Promise>> { - return this.client.get>('/api/v1/instance/activity').then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.activity(a)) - }) - }) - } - - // ====================================== - // instance/trends - // ====================================== - /** - * GET /api/v1/trends - * - * @param limit Maximum number of results to return. Defaults to 10. - */ - public async getInstanceTrends(limit?: number | null): Promise>> { - let params = {} - if (limit) { - params = Object.assign(params, { - limit - }) - } - return this.client.get>('/api/v1/trends', params).then(res => { - return Object.assign(res, { - data: res.data.map(t => MastodonAPI.Converter.tag(t)) - }) - }) - } - - // ====================================== - // instance/directory - // ====================================== - /** - * GET /api/v1/directory - * - * @param options.limit How many accounts to load. Default 40. - * @param options.offset How many accounts to skip before returning results. Default 0. - * @param options.order Order of results. - * @param options.local Only return local accounts. - * @return Array of accounts. - */ - public async getInstanceDirectory(options?: { - limit?: number - offset?: number - order?: 'active' | 'new' - local?: boolean - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.order) { - params = Object.assign(params, { - order: options.order - }) - } - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - } - return this.client.get>('/api/v1/directory', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // instance/custom_emojis - // ====================================== - /** - * GET /api/v1/custom_emojis - * - * @return Array of emojis. - */ - public async getInstanceCustomEmojis(): Promise>> { - return this.client.get>('/api/v1/custom_emojis').then(res => { - return Object.assign(res, { - data: res.data.map(e => MastodonAPI.Converter.emoji(e)) - }) - }) - } - - // ====================================== - // instance/announcements - // ====================================== - /** - * GET /api/v1/announcements - * - * @return Array of announcements. - */ - public async getInstanceAnnouncements(): Promise>> { - return this.client.get>('/api/v1/announcements').then(res => { - return Object.assign(res, { - data: res.data.map(a => MastodonAPI.Converter.announcement(a)) - }) - }) - } - - /** - * POST /api/v1/announcements/:id/dismiss - * - * @param id The ID of the Announcement in the database. - */ - public async dismissInstanceAnnouncement(id: string): Promise>> { - return this.client.post>(`/api/v1/announcements/${id}/dismiss`) - } - - /** - * PUT /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async addReactionToAnnouncement(id: string, name: string): Promise>> { - return this.client.put>(`/api/v1/announcements/${id}/reactions/${name}`) - } - - /** - * DELETE /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async removeReactionFromAnnouncement(id: string, name: string): Promise>> { - return this.client.del>(`/api/v1/announcements/${id}/reactions/${name}`) - } - - // ====================================== - // Emoji reactions - // ====================================== - public async createEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - public async deleteEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - public async getEmojiReactions(_id: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - public async getEmojiReaction(_id: string, _emoji: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('mastodon does not support') - reject(err) - }) - } - - // ====================================== - // WebSocket - // ====================================== - public userSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'user') - } - - public publicSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public') - } - - public localSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public:local') - } - - public tagSocket(tag: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'hashtag', `tag=${tag}`) - } - - public listSocket(list_id: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'list', `list=${list_id}`) - } - - public directSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'direct') - } -} diff --git a/packages/megalodon/src/mastodon/api_client.ts b/packages/megalodon/src/mastodon/api_client.ts deleted file mode 100644 index ba4bd36ead..0000000000 --- a/packages/megalodon/src/mastodon/api_client.ts +++ /dev/null @@ -1,662 +0,0 @@ -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 MastodonEntity from './entity' -import MegalodonEntity from '../entity' -import NotificationType, { UnknownNotificationTypeError } from '../notification' -import MastodonNotificationType from './notification' - -namespace MastodonAPI { - /** - * Interface - */ - export interface Interface { - get(path: string, params?: any, headers?: { [key: string]: string }, pathIsFullyQualified?: boolean): Promise> - put(path: string, params?: any, headers?: { [key: string]: string }): Promise> - putForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patch(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patchForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - post(path: string, params?: any, headers?: { [key: string]: string }): Promise> - postForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - del(path: string, params?: any, headers?: { [key: string]: string }): Promise> - cancel(): void - socket(path: string, stream: string, params?: string): WebSocket - } - - /** - * Mastodon 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( - path: string, - params = {}, - headers: { [key: string]: string } = {}, - pathIsFullyQualified = false - ): Promise> { - 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((pathIsFullyQualified ? '' : 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 = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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 = { - 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 = MastodonEntity.Account - export type Activity = MastodonEntity.Activity - export type Announcement = MastodonEntity.Announcement - export type Application = MastodonEntity.Application - export type AsyncAttachment = MegalodonEntity.AsyncAttachment - export type Attachment = MastodonEntity.Attachment - export type Card = MastodonEntity.Card - export type Context = MastodonEntity.Context - export type Conversation = MastodonEntity.Conversation - export type Emoji = MastodonEntity.Emoji - export type FeaturedTag = MastodonEntity.FeaturedTag - export type Field = MastodonEntity.Field - export type Filter = MastodonEntity.Filter - export type History = MastodonEntity.History - export type IdentityProof = MastodonEntity.IdentityProof - export type Instance = MastodonEntity.Instance - export type List = MastodonEntity.List - export type Marker = MastodonEntity.Marker - export type Mention = MastodonEntity.Mention - export type Notification = MastodonEntity.Notification - export type Poll = MastodonEntity.Poll - export type PollOption = MastodonEntity.PollOption - export type Preferences = MastodonEntity.Preferences - export type PushSubscription = MastodonEntity.PushSubscription - export type Relationship = MastodonEntity.Relationship - export type Report = MastodonEntity.Report - export type Results = MastodonEntity.Results - export type Role = MastodonEntity.Role - export type ScheduledStatus = MastodonEntity.ScheduledStatus - export type Source = MastodonEntity.Source - export type Stats = MastodonEntity.Stats - export type Status = MastodonEntity.Status - export type StatusParams = MastodonEntity.StatusParams - export type StatusSource = MastodonEntity.StatusSource - export type Tag = MastodonEntity.Tag - export type Token = MastodonEntity.Token - export type URLs = MastodonEntity.URLs - } - - export namespace Converter { - export const encodeNotificationType = ( - t: MegalodonEntity.NotificationType - ): MastodonEntity.NotificationType | UnknownNotificationTypeError => { - switch (t) { - case NotificationType.Follow: - return MastodonNotificationType.Follow - case NotificationType.Favourite: - return MastodonNotificationType.Favourite - case NotificationType.Reblog: - return MastodonNotificationType.Reblog - case NotificationType.Mention: - return MastodonNotificationType.Mention - case NotificationType.FollowRequest: - return MastodonNotificationType.FollowRequest - case NotificationType.Status: - return MastodonNotificationType.Status - case NotificationType.PollExpired: - return MastodonNotificationType.Poll - case NotificationType.Update: - return MastodonNotificationType.Update - case NotificationType.AdminSignup: - return MastodonNotificationType.AdminSignup - case NotificationType.AdminReport: - return MastodonNotificationType.AdminReport - default: - return new UnknownNotificationTypeError() - } - } - - export const decodeNotificationType = ( - t: MastodonEntity.NotificationType - ): MegalodonEntity.NotificationType | UnknownNotificationTypeError => { - switch (t) { - case MastodonNotificationType.Follow: - return NotificationType.Follow - case MastodonNotificationType.Favourite: - return NotificationType.Favourite - case MastodonNotificationType.Mention: - return NotificationType.Mention - case MastodonNotificationType.Reblog: - return NotificationType.Reblog - case MastodonNotificationType.FollowRequest: - return NotificationType.FollowRequest - case MastodonNotificationType.Status: - return NotificationType.Status - case MastodonNotificationType.Poll: - return NotificationType.PollExpired - case MastodonNotificationType.Update: - return NotificationType.Update - case MastodonNotificationType.AdminSignup: - return NotificationType.AdminSignup - case MastodonNotificationType.AdminReport: - return NotificationType.AdminReport - default: - return new UnknownNotificationTypeError() - } - } - - export const account = (a: Entity.Account): MegalodonEntity.Account => a - export const activity = (a: Entity.Activity): MegalodonEntity.Activity => a - export const announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => 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 => c - 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 => e - 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 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 => i - export const list = (l: Entity.List): MegalodonEntity.List => l - export const marker = (m: Entity.Marker | Record): MegalodonEntity.Marker | Record => 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 => r - 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 => s - 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, - edited_at: s.edited_at || null, - 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, - // Now quote is supported only fedibird.com. - quote: s.quote !== undefined && s.quote !== null - }) - export const status_params = (s: Entity.StatusParams): MegalodonEntity.StatusParams => s - 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 MastodonAPI diff --git a/packages/megalodon/src/mastodon/entities/instance.ts b/packages/megalodon/src/mastodon/entities/instance.ts index 842e2c6bbf..ab0875a273 100644 --- a/packages/megalodon/src/mastodon/entities/instance.ts +++ b/packages/megalodon/src/mastodon/entities/instance.ts @@ -37,8 +37,15 @@ namespace MastodonEntity { min_expiration: number max_expiration: number } + accounts: { + max_featured_tags: number; + max_pinned_statuses: number; + } + reactions: { + max_reactions: number, + } } - contact_account: Account + contact_account: Account | null rules: Array } diff --git a/packages/megalodon/src/mastodon/notification.ts b/packages/megalodon/src/mastodon/notification.ts deleted file mode 100644 index b7551a019e..0000000000 --- a/packages/megalodon/src/mastodon/notification.ts +++ /dev/null @@ -1,16 +0,0 @@ -import MastodonEntity from './entity' - -namespace MastodonNotificationType { - export const Mention: MastodonEntity.NotificationType = 'mention' - export const Reblog: MastodonEntity.NotificationType = 'reblog' - export const Favourite: MastodonEntity.NotificationType = 'favourite' - export const Follow: MastodonEntity.NotificationType = 'follow' - export const Poll: MastodonEntity.NotificationType = 'poll' - export const FollowRequest: MastodonEntity.NotificationType = 'follow_request' - export const Status: MastodonEntity.NotificationType = 'status' - export const Update: MastodonEntity.NotificationType = 'update' - export const AdminSignup: MastodonEntity.NotificationType = 'admin.sign_up' - export const AdminReport: MastodonEntity.NotificationType = 'admin.report' -} - -export default MastodonNotificationType diff --git a/packages/megalodon/src/mastodon/web_socket.ts b/packages/megalodon/src/mastodon/web_socket.ts deleted file mode 100644 index 28bf38a666..0000000000 --- a/packages/megalodon/src/mastodon/web_socket.ts +++ /dev/null @@ -1,348 +0,0 @@ -import WS from 'ws' -import dayjs, { Dayjs } from 'dayjs' -import { EventEmitter } from 'events' -import proxyAgent, { ProxyConfig } from '../proxy_config' -import { WebSocketInterface } from '../megalodon' -import MastodonAPI from './api_client' -import { UnknownNotificationTypeError } from '../notification' - -/** - * WebSocket - * Pleroma is not support streaming. It is support websocket instead of streaming. - * So this class connect to Phoenix websocket for Pleroma. - */ -export default class WebSocket extends EventEmitter implements WebSocketInterface { - public url: string - public stream: string - public params: string | null - public parser: Parser - public headers: { [key: string]: string } - public proxyConfig: ProxyConfig | false = false - private _accessToken: string - private _reconnectInterval: number - private _reconnectMaxAttempts: number - private _reconnectCurrentAttempts: number - private _connectionClosed: boolean - private _client: WS | null - private _pongReceivedTimestamp: Dayjs - private _heartbeatInterval: number = 60000 - private _pongWaiting: boolean = false - - /** - * @param url Full url of websocket: e.g. https://pleroma.io/api/v1/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 - * @param accessToken The access token. - * @param userAgent The specified User Agent. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - */ - constructor( - url: string, - stream: string, - params: string | undefined, - accessToken: string, - userAgent: string, - proxyConfig: ProxyConfig | false = false - ) { - super() - this.url = url - this.stream = stream - if (params === undefined) { - this.params = null - } else { - this.params = params - } - this.parser = new Parser() - this.headers = { - 'User-Agent': userAgent - } - this.proxyConfig = proxyConfig - this._accessToken = accessToken - this._reconnectInterval = 10000 - this._reconnectMaxAttempts = Infinity - this._reconnectCurrentAttempts = 0 - this._connectionClosed = false - this._client = null - this._pongReceivedTimestamp = dayjs() - } - - /** - * Start websocket connection. - */ - public start() { - this._connectionClosed = false - this._resetRetryParams() - this._startWebSocketConnection() - } - - /** - * Reset connection and start new websocket connection. - */ - private _startWebSocketConnection() { - this._resetConnection() - this._setupParser() - this._client = this._connect(this.url, this.stream, this.params, this._accessToken, this.headers, this.proxyConfig) - this._bindSocket(this._client) - } - - /** - * Stop current connection. - */ - public stop() { - this._connectionClosed = true - this._resetConnection() - this._resetRetryParams() - } - - /** - * Clean up current connection, and listeners. - */ - private _resetConnection() { - if (this._client) { - this._client.close(1000) - this._client.removeAllListeners() - this._client = null - } - - if (this.parser) { - this.parser.removeAllListeners() - } - } - - /** - * Resets the parameters used in reconnect. - */ - private _resetRetryParams() { - this._reconnectCurrentAttempts = 0 - } - - /** - * Reconnects to the same endpoint. - */ - private _reconnect() { - setTimeout(() => { - // Skip reconnect when client is connecting. - // https://github.com/websockets/ws/blob/7.2.1/lib/websocket.js#L365 - if (this._client && this._client.readyState === WS.CONNECTING) { - return - } - - if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) { - this._reconnectCurrentAttempts++ - this._clearBinding() - if (this._client) { - // In reconnect, we want to close the connection immediately, - // because recoonect is necessary when some problems occur. - this._client.terminate() - } - // Call connect methods - console.log('Reconnecting') - this._client = this._connect(this.url, this.stream, this.params, this._accessToken, this.headers, this.proxyConfig) - this._bindSocket(this._client) - } - }, this._reconnectInterval) - } - - /** - * @param url Base url of streaming endpoint. - * @param stream The specified stream name. - * @param accessToken Access token. - * @param headers The specified headers. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - * @return A WebSocket instance. - */ - private _connect( - url: string, - stream: string, - params: string | null, - accessToken: string, - headers: { [key: string]: string }, - proxyConfig: ProxyConfig | false - ): WS { - const parameter: Array = [`stream=${stream}`] - - if (params) { - parameter.push(params) - } - - if (accessToken !== null) { - parameter.push(`access_token=${accessToken}`) - } - const requestURL: string = `${url}/?${parameter.join('&')}` - let options: WS.ClientOptions = { - headers: headers - } - if (proxyConfig) { - options = Object.assign(options, { - agent: proxyAgent(proxyConfig) - }) - } - - const cli: WS = new WS(requestURL, options) - return cli - } - - /** - * Clear binding event for web socket client. - */ - private _clearBinding() { - if (this._client) { - this._client.removeAllListeners('close') - this._client.removeAllListeners('pong') - this._client.removeAllListeners('open') - this._client.removeAllListeners('message') - this._client.removeAllListeners('error') - } - } - - /** - * Bind event for web socket client. - * @param client A WebSocket instance. - */ - private _bindSocket(client: WS) { - client.on('close', (code: number, _reason: Buffer) => { - // Refer the code: https://tools.ietf.org/html/rfc6455#section-7.4 - if (code === 1000) { - this.emit('close', {}) - } else { - console.log(`Closed connection with ${code}`) - // If already called close method, it does not retry. - if (!this._connectionClosed) { - this._reconnect() - } - } - }) - client.on('pong', () => { - this._pongWaiting = false - this.emit('pong', {}) - this._pongReceivedTimestamp = dayjs() - // It is required to anonymous function since get this scope in checkAlive. - setTimeout(() => this._checkAlive(this._pongReceivedTimestamp), this._heartbeatInterval) - }) - client.on('open', () => { - this.emit('connect', {}) - // Call first ping event. - setTimeout(() => { - client.ping('') - }, 10000) - }) - client.on('message', (data: WS.Data, isBinary: boolean) => { - this.parser.parse(data, isBinary) - }) - client.on('error', (err: Error) => { - this.emit('error', err) - }) - } - - /** - * Set up parser when receive message. - */ - private _setupParser() { - this.parser.on('update', (status: MastodonAPI.Entity.Status) => { - this.emit('update', MastodonAPI.Converter.status(status)) - }) - this.parser.on('notification', (notification: MastodonAPI.Entity.Notification) => { - const n = MastodonAPI.Converter.notification(notification) - if (n instanceof UnknownNotificationTypeError) { - console.warn(`Unknown notification event has received: ${notification}`) - } else { - this.emit('notification', n) - } - }) - this.parser.on('delete', (id: string) => { - this.emit('delete', id) - }) - this.parser.on('conversation', (conversation: MastodonAPI.Entity.Conversation) => { - this.emit('conversation', MastodonAPI.Converter.conversation(conversation)) - }) - this.parser.on('status_update', (status: MastodonAPI.Entity.Status) => { - this.emit('status_update', MastodonAPI.Converter.status(status)) - }) - this.parser.on('error', (err: Error) => { - this.emit('parser-error', err) - }) - this.parser.on('heartbeat', _ => { - this.emit('heartbeat', 'heartbeat') - }) - } - - /** - * Call ping and wait to pong. - */ - private _checkAlive(timestamp: Dayjs) { - const now: Dayjs = dayjs() - // Block multiple calling, if multiple pong event occur. - // It the duration is less than interval, through ping. - if (now.diff(timestamp) > this._heartbeatInterval - 1000 && !this._connectionClosed) { - // Skip ping when client is connecting. - // https://github.com/websockets/ws/blob/7.2.1/lib/websocket.js#L289 - if (this._client && this._client.readyState !== WS.CONNECTING) { - this._pongWaiting = true - this._client.ping('') - setTimeout(() => { - if (this._pongWaiting) { - this._pongWaiting = false - this._reconnect() - } - }, 10000) - } - } - } -} - -/** - * Parser - * This class provides parser for websocket message. - */ -export class Parser extends EventEmitter { - /** - * @param message Message body of websocket. - */ - public parse(data: WS.Data, isBinary: boolean) { - const message = isBinary ? data : data.toString() - if (typeof message !== 'string') { - this.emit('heartbeat', {}) - return - } - - if (message === '') { - this.emit('heartbeat', {}) - return - } - - let event = '' - let payload = '' - let mes = {} - try { - const obj = JSON.parse(message) - event = obj.event - payload = obj.payload - mes = JSON.parse(payload) - } catch (err) { - // delete event does not have json object - if (event !== 'delete') { - this.emit('error', new Error(`Error parsing websocket reply: ${message}, error message: ${err}`)) - return - } - } - - switch (event) { - case 'update': - this.emit('update', mes as MastodonAPI.Entity.Status) - break - case 'notification': - this.emit('notification', mes as MastodonAPI.Entity.Notification) - break - case 'conversation': - this.emit('conversation', mes as MastodonAPI.Entity.Conversation) - break - case 'delete': - this.emit('delete', payload) - break - case 'status.update': - this.emit('status_update', mes as MastodonAPI.Entity.Status) - break - default: - this.emit('error', new Error(`Unknown event has received: ${message}`)) - } - } -} diff --git a/packages/megalodon/src/megalodon.ts b/packages/megalodon/src/megalodon.ts index e2245f7c21..4328f41f1c 100644 --- a/packages/megalodon/src/megalodon.ts +++ b/packages/megalodon/src/megalodon.ts @@ -1,11 +1,6 @@ import Response from './response' import OAuth from './oauth' -import Pleroma from './pleroma' -import { ProxyConfig } from './proxy_config' -import Mastodon from './mastodon' import Entity from './entity' -import Misskey from './misskey' -import Friendica from './friendica' export interface WebSocketInterface { start(): void @@ -1413,42 +1408,3 @@ export class NodeinfoError extends Error { Object.setPrototypeOf(this, new.target.prototype) } } - -/** - * Get client for each SNS according to megalodon interface. - * - * @param sns Name of your SNS, `mastodon` or `pleroma`. - * @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. - * @return Client instance for each SNS you specified. - */ -const generator = ( - sns: 'mastodon' | 'pleroma' | 'misskey' | 'friendica', - baseUrl: string, - accessToken: string | null = null, - userAgent: string | null = null, - proxyConfig: ProxyConfig | false = false -): MegalodonInterface => { - switch (sns) { - case 'pleroma': { - const pleroma = new Pleroma(baseUrl, accessToken, userAgent, proxyConfig) - return pleroma - } - case 'misskey': { - const misskey = new Misskey(baseUrl, accessToken, userAgent, proxyConfig) - return misskey - } - case 'friendica': { - const friendica = new Friendica(baseUrl, accessToken, userAgent, proxyConfig) - return friendica - } - case 'mastodon': { - const mastodon = new Mastodon(baseUrl, accessToken, userAgent, proxyConfig) - return mastodon - } - } -} - -export default generator diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index 01d5652650..c9a33e3130 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -303,8 +303,8 @@ export default class Misskey implements MegalodonInterface { max_id?: string since_id?: string pinned?: boolean - exclude_replies: boolean - exclude_reblogs: boolean + exclude_replies?: boolean + exclude_reblogs?: boolean only_media?: boolean } ): Promise>> { @@ -2352,6 +2352,18 @@ export default class Misskey implements MegalodonInterface { } })) } + default: { + return { + status: 400, + statusText: 'bad request', + headers: {}, + data: { + accounts: [], + statuses: [], + hashtags: [], + } + } + } } } diff --git a/packages/megalodon/src/pleroma.ts b/packages/megalodon/src/pleroma.ts deleted file mode 100644 index 265c7d3c0b..0000000000 --- a/packages/megalodon/src/pleroma.ts +++ /dev/null @@ -1,3217 +0,0 @@ -import { OAuth2 } from 'oauth' -import FormData from 'form-data' - -import PleromaAPI from './pleroma/api_client' -import WebSocket from './pleroma/web_socket' -import { MegalodonInterface, NoImplementedError, ArgumentError } from './megalodon' -import Response from './response' -import Entity from './entity' -import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default' -import { ProxyConfig } from './proxy_config' -import OAuth from './oauth' -import { UnknownNotificationTypeError } from './notification' - -export default class Pleroma implements MegalodonInterface { - public client: PleromaAPI.Interface - public baseUrl: string - - /** - * @param baseUrl hostname or base URL - * @param accessToken access token from OAuth2 authorization - * @param userAgent UserAgent is specified in header on request. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - */ - constructor( - baseUrl: string, - accessToken: string | null = null, - userAgent: string | null = DEFAULT_UA, - proxyConfig: ProxyConfig | false = false - ) { - let token: string = '' - if (accessToken) { - token = accessToken - } - let agent: string = DEFAULT_UA - if (userAgent) { - agent = userAgent - } - this.client = new PleromaAPI.Client(baseUrl, token, agent, proxyConfig) - this.baseUrl = baseUrl - } - - public cancel(): void { - return this.client.cancel() - } - - /** - * First, call createApp to get client_id and client_secret. - * Next, call generateAuthUrl to get authorization url. - * @param client_name Form Data, which is sent to /api/v1/apps - * @param options Form Data, which is sent to /api/v1/apps. and properties should be **snake_case** - */ - public async registerApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - return this.createApp(client_name, options).then(async appData => { - return this.generateAuthUrl(appData.client_id, appData.client_secret, { - scope: scopes, - redirect_uri: appData.redirect_uri - }).then(url => { - appData.url = url - return appData - }) - }) - } - - /** - * Call /api/v1/apps - * - * Create an application. - * @param client_name your application's name - * @param options Form Data - */ - public async createApp( - client_name: string, - options: Partial<{ scopes: Array; redirect_uris: string; website: string }> - ): Promise { - const scopes = options.scopes || DEFAULT_SCOPE - const redirect_uris = options.redirect_uris || NO_REDIRECT - - const params: { - client_name: string - redirect_uris: string - scopes: string - website?: string - } = { - client_name: client_name, - redirect_uris: redirect_uris, - scopes: scopes.join(' ') - } - if (options.website) params.website = options.website - - return this.client - .post('/api/v1/apps', params) - .then((res: Response) => OAuth.AppData.from(res.data)) - } - - /** - * Generate authorization url using OAuth2. - * - * @param clientId your OAuth app's client ID - * @param clientSecret your OAuth app's client Secret - * @param options as property, redirect_uri and scope are available, and must be the same as when you register your app - */ - public generateAuthUrl( - clientId: string, - clientSecret: string, - options: Partial<{ scope: Array; redirect_uri: string }> - ): Promise { - const scope = options.scope || DEFAULT_SCOPE - const redirect_uri = options.redirect_uri || NO_REDIRECT - return new Promise(resolve => { - const oauth = new OAuth2(clientId, clientSecret, this.baseUrl, undefined, '/oauth/token') - const url = oauth.getAuthorizeUrl({ - redirect_uri: redirect_uri, - response_type: 'code', - client_id: clientId, - scope: scope.join(' ') - }) - resolve(url) - }) - } - - // ====================================== - // apps - // ====================================== - /** - * GET /api/v1/apps/verify_credentials - * - * @return An Application - */ - public verifyAppCredentials(): Promise> { - return this.client.get('/api/v1/apps/verify_credentials') - } - - // ====================================== - // apps/oauth - // ====================================== - /** - * POST /oauth/token - * - * Fetch OAuth access token. - * Get an access token based client_id and client_secret and authorization code. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param code will be generated by the link of #generateAuthUrl or #registerApp - * @param redirect_uri must be the same uri as the time when you register your OAuth application - */ - public async fetchAccessToken( - client_id: string | null, - client_secret: string, - code: string, - redirect_uri: string = NO_REDIRECT - ): Promise { - if (!client_id) { - throw new Error('client_id is required') - } - return this.client - .post('/oauth/token', { - client_id, - client_secret, - code, - redirect_uri, - grant_type: 'authorization_code' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/token - * - * Refresh OAuth access token. - * Send refresh token and get new access token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param refresh_token will be get #fetchAccessToken - */ - public async refreshToken(client_id: string, client_secret: string, refresh_token: string): Promise { - return this.client - .post('/oauth/token', { - client_id, - client_secret, - refresh_token, - grant_type: 'refresh_token' - }) - .then((res: Response) => OAuth.TokenData.from(res.data)) - } - - /** - * POST /oauth/revoke - * - * Revoke an OAuth token. - * @param client_id will be generated by #createApp or #registerApp - * @param client_secret will be generated by #createApp or #registerApp - * @param token will be get #fetchAccessToken - */ - public async revokeToken(client_id: string, client_secret: string, token: string): Promise> { - return this.client.post<{}>('/oauth/revoke', { - client_id, - client_secret, - token - }) - } - - // ====================================== - // accounts - // ====================================== - /** - * POST /api/v1/accounts - * - * @param username Username for the account. - * @param email Email for the account. - * @param password Password for the account. - * @param agreement Whether the user agrees to the local rules, terms, and policies. - * @param locale The language of the confirmation email that will be sent - * @param reason Text that will be reviewed by moderators if registrations require manual approval. - * @return An account token. - */ - public async registerAccount( - username: string, - email: string, - password: string, - agreement: boolean, - locale: string, - reason?: string | null - ): Promise> { - let params = { - username: username, - email: email, - password: password, - agreement: agreement, - locale: locale - } - if (reason) { - params = Object.assign(params, { - reason: reason - }) - } - return this.client.post('/api/v1/accounts', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.token(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/verify_credentials - * - * @return Account. - */ - public async verifyAccountCredentials(): Promise> { - return this.client.get('/api/v1/accounts/verify_credentials').then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.account(res.data) - }) - }) - } - - /** - * PATCH /api/v1/accounts/update_credentials - * - * @return An account. - */ - public async updateCredentials(options?: { - discoverable?: boolean - bot?: boolean - display_name?: string - note?: string - avatar?: string - header?: string - locked?: boolean - source?: { - privacy?: string - sensitive?: boolean - language?: string - } - fields_attributes?: Array<{ name: string; value: string }> - }): Promise> { - let params = {} - if (options) { - if (options.discoverable !== undefined) { - params = Object.assign(params, { - discoverable: options.discoverable - }) - } - if (options.bot !== undefined) { - params = Object.assign(params, { - bot: options.bot - }) - } - if (options.display_name) { - params = Object.assign(params, { - display_name: options.display_name - }) - } - if (options.note) { - params = Object.assign(params, { - note: options.note - }) - } - if (options.avatar) { - params = Object.assign(params, { - avatar: options.avatar - }) - } - if (options.header) { - params = Object.assign(params, { - header: options.header - }) - } - if (options.locked !== undefined) { - params = Object.assign(params, { - locked: options.locked - }) - } - if (options.source) { - params = Object.assign(params, { - source: options.source - }) - } - if (options.fields_attributes) { - params = Object.assign(params, { - fields_attributes: options.fields_attributes - }) - } - } - return this.client.patch('/api/v1/accounts/update_credentials', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.account(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id - * - * @param id The account ID. - * @return An account. - */ - public async getAccount(id: string): Promise> { - return this.client.get(`/api/v1/accounts/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.account(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/statuses - * - * @param id The account ID. - - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID but starting with most recent. - * @param options.min_id Return results newer than ID. - * @param options.pinned Return statuses which include pinned statuses. - * @param options.exclude_replies Return statuses which exclude replies. - * @param options.exclude_reblogs Return statuses which exclude reblogs. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @return Account's statuses. - */ - public async getAccountStatuses( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - pinned?: boolean - exclude_replies?: boolean - exclude_reblogs?: boolean - only_media?: boolean - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.pinned) { - params = Object.assign(params, { - pinned: options.pinned - }) - } - if (options.exclude_replies) { - params = Object.assign(params, { - exclude_replies: options.exclude_replies - }) - } - if (options.exclude_reblogs) { - params = Object.assign(params, { - exclude_reblogs: options.exclude_reblogs - }) - } - if (options.only_media) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - } - return this.client.get>(`/api/v1/accounts/${id}/statuses`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/pleroma/accounts/:id/favourites - * - * @param id Target account ID. - * @param options.limit Max number of results to return. - * @param options.max_id Return results order than ID. - * @param options.since_id Return results newer than ID. - * @return Array of statuses. - */ - public async getAccountFavourites( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>(`/api/v1/pleroma/accounts/${id}/favourites`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * POST /api/v1/pleroma/accounts/:id/subscribe - * - * @param id Target account ID. - * @return Relationship. - */ - public async subscribeAccount(id: string): Promise> { - return this.client.post(`/api/v1/pleroma/accounts/${id}/subscribe`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/pleroma/accounts/:id/unsubscribe - * - * @param id Target account ID. - * @return Relationship. - */ - public async unsubscribeAccount(id: string): Promise> { - return this.client.post(`/api/v1/pleroma/accounts/${id}/unsubscribe`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/followers - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowers( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/accounts/${id}/followers`, params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/following - * - * @param id The account ID. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async getAccountFollowing( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/accounts/${id}/following`, params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/lists - * - * @param id The account ID. - * @return The array of lists. - */ - public async getAccountLists(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/lists`).then(res => { - return Object.assign(res, { - data: res.data.map(l => PleromaAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/accounts/:id/identity_proofs - * - * @param id The account ID. - * @return Array of IdentityProof - */ - public async getIdentityProof(id: string): Promise>> { - return this.client.get>(`/api/v1/accounts/${id}/identity_proofs`).then(res => { - return Object.assign(res, { - data: res.data.map(i => PleromaAPI.Converter.identity_proof(i)) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/follow - * - * @param id The account ID. - * @param reblog Receive this account's reblogs in home timeline. - * @return Relationship - */ - public async followAccount(id: string, options?: { reblog?: boolean }): Promise> { - let params = {} - if (options) { - if (options.reblog !== undefined) { - params = Object.assign(params, { - reblog: options.reblog - }) - } - } - return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unfollow - * - * @param id The account ID. - * @return Relationship - */ - public async unfollowAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unfollow`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/block - * - * @param id The account ID. - * @return Relationship - */ - public async blockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/block`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unblock - * - * @param id The account ID. - * @return RElationship - */ - public async unblockAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unblock`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/mute - * - * @param id The account ID. - * @param notifications Mute notifications in addition to statuses. - * @return Relationship - */ - public async muteAccount(id: string, notifications: boolean = true): Promise> { - return this.client - .post(`/api/v1/accounts/${id}/mute`, { - notifications: notifications - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unmute - * - * @param id The account ID. - * @return Relationship - */ - public async unmuteAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unmute`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/pin - * - * @param id The account ID. - * @return Relationship - */ - public async pinAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/pin`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/accounts/:id/unpin - * - * @param id The account ID. - * @return Relationship - */ - public async unpinAccount(id: string): Promise> { - return this.client.post(`/api/v1/accounts/${id}/unpin`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * GET /api/v1/accounts/relationships - * - * @param id The account ID. - * @return Relationship - */ - public async getRelationship(id: string): Promise> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: [id] - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data[0]) - }) - }) - } - - /** - * Get multiple relationships in one method - * - * @param ids Array of account IDs. - * @return Array of Relationship. - */ - public async getRelationships(ids: Array): Promise>> { - return this.client - .get>('/api/v1/accounts/relationships', { - id: ids - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(r => PleromaAPI.Converter.relationship(r)) - }) - }) - } - - /** - * GET /api/v1/accounts/search - * - * @param q Search query. - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return The array of accounts. - */ - public async searchAccount( - q: string, - options?: { - following?: boolean - resolve?: boolean - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = { q: q } - if (options) { - if (options.following !== undefined && options.following !== null) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.resolve !== undefined && options.resolve !== null) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/accounts/search', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/bookmarks - // ====================================== - /** - * GET /api/v1/bookmarks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getBookmarks(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/bookmarks', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/favourites - // ====================================== - /** - * GET /api/v1/favourites - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/favourites', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // accounts/mutes - // ====================================== - /** - * GET /api/v1/mutes - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getMutes(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/mutes', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/blocks - // ====================================== - /** - * GET /api/v1/blocks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/blocks', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/domain_blocks - // ====================================== - /** - * GET /api/v1/domain_blocks - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of domain name. - */ - public async getDomainBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/domain_blocks', params) - } - - /** - * POST/api/v1/domain_blocks - * - * @param domain Domain to block. - */ - public blockDomain(domain: string): Promise> { - return this.client.post<{}>('/api/v1/domain_blocks', { - domain: domain - }) - } - - /** - * DELETE /api/v1/domain_blocks - * - * @param domain Domain to unblock - */ - public unblockDomain(domain: string): Promise> { - return this.client.del<{}>('/api/v1/domain_blocks', { - domain: domain - }) - } - - // ====================================== - // accounts/filters - // ====================================== - /** - * GET /api/v1/filters - * - * @return Array of filters. - */ - public async getFilters(): Promise>> { - return this.client.get>('/api/v1/filters').then(res => { - return Object.assign(res, { - data: res.data.map(f => PleromaAPI.Converter.filter(f)) - }) - }) - } - - /** - * GET /api/v1/filters/:id - * - * @param id The filter ID. - * @return Filter. - */ - public async getFilter(id: string): Promise> { - return this.client.get(`/api/v1/filters/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.filter(res.data) - }) - }) - } - - /** - * POST /api/v1/filters - * - * @param phrase Text to be filtered. - * @param context Array of enumerable strings home, notifications, public, thread, account. At least one context must be specified. - * @param options.irreversible Should the server irreversibly drop matching entities from home and notifications? - * @param options.whole_word Consider word boundaries? - * @param options.expires_in ISO 8601 Datetime for when the filter expires. - * @return Filter - */ - public async createFilter( - phrase: string, - context: Array, - options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - let params = { - phrase: phrase, - context: context - } - if (options) { - if (options.irreversible !== undefined) { - params = Object.assign(params, { - irreversible: options.irreversible - }) - } - if (options.whole_word !== undefined) { - params = Object.assign(params, { - whole_word: options.whole_word - }) - } - if (options.expires_in) { - params = Object.assign(params, { - expires_in: options.expires_in - }) - } - } - return this.client.post('/api/v1/filters', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.filter(res.data) - }) - }) - } - - /** - * PUT /api/v1/filters/:id - * - * @param id The filter ID. - * @param phrase Text to be filtered. - * @param context Array of enumerable strings home, notifications, public, thread, account. At least one context must be specified. - * @param options.irreversible Should the server irreversibly drop matching entities from home and notifications? - * @param options.whole_word Consider word boundaries? - * @param options.expires_in ISO 8601 Datetime for when the filter expires. - * @return Filter - */ - public async updateFilter( - id: string, - phrase: string, - context: Array, - options?: { - irreversible?: boolean - whole_word?: boolean - expires_in?: string - } - ): Promise> { - let params = { - phrase: phrase, - context: context - } - if (options) { - if (options.irreversible !== undefined) { - params = Object.assign(params, { - irreversible: options.irreversible - }) - } - if (options.whole_word !== undefined) { - params = Object.assign(params, { - whole_word: options.whole_word - }) - } - if (options.expires_in) { - params = Object.assign(params, { - expires_in: options.expires_in - }) - } - } - return this.client.put(`/api/v1/filters/${id}`, params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.filter(res.data) - }) - }) - } - - /** - * DELETE /api/v1/filters/:id - * - * @param id The filter ID. - * @return Removed filter. - */ - public async deleteFilter(id: string): Promise> { - return this.client.del(`/api/v1/filters/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.filter(res.data) - }) - }) - } - - // ====================================== - // accounts/reports - // ====================================== - /** - * POST /api/v1/reports - * - * @param account_id Target account ID. - * @param options.status_ids Array of Statuses ids to attach to the report. - * @param options.comment The reason for the report. Default maximum of 1000 characters. - * @param options.forward If the account is remote, should the report be forwarded to the remote admin? - * @param options.category Specify if the report is due to spam, violation of enumerated instance rules, or some other reason. Defaults to other. Will be set to violation if rule_ids[] is provided (regardless of any category value you provide). - * @param options.rule_ids For violation category reports, specify the ID of the exact rules broken. Rules and their IDs are available via GET /api/v1/instance/rules and GET /api/v1/instance. - * @return Report - */ - public async report( - account_id: string, - options?: { - status_ids?: Array - comment: string - forward?: boolean - category?: Entity.Category - rule_ids?: Array - } - ): Promise> { - let params = { - account_id: account_id - } - if (options) { - if (options.status_ids) { - params = Object.assign(params, { - status_ids: options.status_ids - }) - } - if (options.comment) { - params = Object.assign(params, { - comment: options.comment - }) - } - if (options.forward !== undefined) { - params = Object.assign(params, { - forward: options.forward - }) - } - if (options.category) { - params = Object.assign(params, { - category: options.category - }) - } - if (options.rule_ids) { - params = Object.assign(params, { - rule_ids: options.rule_ids - }) - } - } - return this.client.post('/api/v1/reports', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.report(res.data) - }) - }) - } - - // ====================================== - // accounts/follow_requests - // ====================================== - /** - * GET /api/v1/follow_requests - * - * @param limit Maximum number of results. - * @return Array of account. - */ - public async getFollowRequests(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/follow_requests', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } else { - return this.client.get>('/api/v1/follow_requests').then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - } - - /** - * POST /api/v1/follow_requests/:id/authorize - * - * @param id Target account ID. - * @return Relationship. - */ - public async acceptFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - /** - * POST /api/v1/follow_requests/:id/reject - * - * @param id Target account ID. - * @return Relationship. - */ - public async rejectFollowRequest(id: string): Promise> { - return this.client.post(`/api/v1/follow_requests/${id}/reject`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.relationship(res.data) - }) - }) - } - - // ====================================== - // accounts/endorsements - // ====================================== - /** - * GET /api/v1/endorsements - * - * @param options.limit Max number of results to return. Defaults to 40. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @return Array of accounts. - */ - public async getEndorsements(options?: { limit?: number; max_id?: string; since_id?: string }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>('/api/v1/endorsements', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // accounts/featured_tags - // ====================================== - /** - * GET /api/v1/featured_tags - * - * @return Array of featured tag. - */ - public async getFeaturedTags(): Promise>> { - return this.client.get>('/api/v1/featured_tags').then(res => { - return Object.assign(res, { - data: res.data.map(f => PleromaAPI.Converter.featured_tag(f)) - }) - }) - } - - /** - * POST /api/v1/featured_tags - * - * @param name Target hashtag name. - * @return FeaturedTag. - */ - public async createFeaturedTag(name: string): Promise> { - return this.client - .post('/api/v1/featured_tags', { - name: name - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.featured_tag(res.data) - }) - }) - } - - /** - * DELETE /api/v1/featured_tags/:id - * - * @param id Target featured tag id. - * @return Empty - */ - public deleteFeaturedTag(id: string): Promise> { - return this.client.del<{}>(`/api/v1/featured_tags/${id}`) - } - - /** - * GET /api/v1/featured_tags/suggestions - * - * @return Array of tag. - */ - public async getSuggestedTags(): Promise>> { - return this.client.get>('/api/v1/featured_tags/suggestions').then(res => { - return Object.assign(res, { - data: res.data.map(t => PleromaAPI.Converter.tag(t)) - }) - }) - } - - // ====================================== - // accounts/preferences - // ====================================== - /** - * GET /api/v1/preferences - * - * @return Preferences. - */ - public async getPreferences(): Promise> { - return this.client.get('/api/v1/preferences').then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.preferences(res.data) - }) - }) - } - - // ====================================== - // accounts/followed_tags - // ====================================== - public async getFollowedTags(): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - // ====================================== - // accounts/suggestions - // ====================================== - /** - * GET /api/v1/suggestions - * - * @param limit Maximum number of results. - * @return Array of accounts. - */ - public async getSuggestions(limit?: number): Promise>> { - if (limit) { - return this.client - .get>('/api/v1/suggestions', { - limit: limit - }) - .then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } else { - return this.client.get>('/api/v1/suggestions').then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - } - - // ====================================== - // accounts/tags - // ====================================== - /** - * GET /api/v1/tags/:id - * - * @param id Target hashtag id. - * @return Tag - */ - public async getTag(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - /** - * POST /api/v1/tags/:id/follow - * - * @param id Target hashtag id. - * @return Tag - */ - public async followTag(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - /** - * POST /api/v1/tags/:id/unfollow - * - * @param id Target hashtag id. - * @return Tag - */ - public async unfollowTag(_id: string): Promise> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - // ====================================== - // statuses - // ====================================== - /** - * POST /api/v1/statuses - * - * @param status Text content of status. - * @param options.media_ids Array of Attachment ids. - * @param options.poll Poll object. - * @param options.in_reply_to_id ID of the status being replied to, if status is a reply. - * @param options.sensitive Mark status and attached media as sensitive? - * @param options.spoiler_text Text to be shown as a warning or subject before the actual content. - * @param options.visibility Visibility of the posted status. - * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status. - * @param options.language ISO 639 language code for this status. - * @param options.quote_id ID of the status being quoted to, if status is a quote. - * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead. - */ - public async postStatus( - status: string, - options: { - media_ids?: Array - poll?: { options: Array; expires_in: number; multiple?: boolean; hide_totals?: boolean } - in_reply_to_id?: string - sensitive?: boolean - spoiler_text?: string - visibility?: 'public' | 'unlisted' | 'private' | 'direct' - scheduled_at?: string - language?: string - quote_id?: string - } - ): Promise> { - let params = { - status: status - } - if (options) { - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = { - options: options.poll.options, - expires_in: options.poll.expires_in - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - if (options.in_reply_to_id) { - params = Object.assign(params, { - in_reply_to_id: options.in_reply_to_id - }) - } - if (options.sensitive !== undefined) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.visibility) { - params = Object.assign(params, { - visibility: options.visibility - }) - } - if (options.scheduled_at) { - params = Object.assign(params, { - scheduled_at: options.scheduled_at - }) - } - if (options.language) { - params = Object.assign(params, { - language: options.language - }) - } - if (options.quote_id) { - params = Object.assign(params, { - quote_id: options.quote_id - }) - } - } - if (options && options.scheduled_at) { - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.scheduled_status(res.data) - }) - }) - } - return this.client.post('/api/v1/statuses', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async getStatus(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - PUT /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async editStatus( - id: string, - options: { - status?: string - spoiler_text?: string - sensitive?: boolean - media_ids?: Array - poll?: { options?: Array; expires_in?: number; multiple?: boolean; hide_totals?: boolean } - } - ): Promise> { - let params = {} - if (options.status) { - params = Object.assign(params, { - status: options.status - }) - } - if (options.spoiler_text) { - params = Object.assign(params, { - spoiler_text: options.spoiler_text - }) - } - if (options.sensitive) { - params = Object.assign(params, { - sensitive: options.sensitive - }) - } - if (options.media_ids) { - params = Object.assign(params, { - media_ids: options.media_ids - }) - } - if (options.poll) { - let pollParam = {} - if (options.poll.options !== undefined) { - pollParam = Object.assign(pollParam, { - options: options.poll.options - }) - } - if (options.poll.expires_in !== undefined) { - pollParam = Object.assign(pollParam, { - expires_in: options.poll.expires_in - }) - } - if (options.poll.multiple !== undefined) { - pollParam = Object.assign(pollParam, { - multiple: options.poll.multiple - }) - } - if (options.poll.hide_totals !== undefined) { - pollParam = Object.assign(pollParam, { - hide_totals: options.poll.hide_totals - }) - } - params = Object.assign(params, { - poll: pollParam - }) - } - return this.client.put(`/api/v1/statuses/${id}`, params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/statuses/:id - * - * @param id The target status id. - * @return Status - */ - public async deleteStatus(id: string): Promise> { - return this.client.del(`/api/v1/statuses/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/context - * - * Get parent and child statuses. - * @param id The target status id. - * @return Context - */ - public async getStatusContext( - id: string, - options?: { limit?: number; max_id?: string; since_id?: string } - ): Promise> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get(`/api/v1/statuses/${id}/context`, params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.context(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/source - * - * Obtain the source properties for a status so that it can be edited. - * @param id The target status id. - * @return StatusSource - */ - public async getStatusSource(id: string): Promise> { - return this.client.get(`/api/v1/statuses/${id}/source`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status_source(res.data) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/reblogged_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusRebloggedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/reblogged_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - /** - * GET /api/v1/statuses/:id/favourited_by - * - * @param id The target status id. - * @return Array of accounts. - */ - public async getStatusFavouritedBy(id: string): Promise>> { - return this.client.get>(`/api/v1/statuses/${id}/favourited_by`).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/favourite - * - * @param id The target status id. - * @return Status. - */ - public async favouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/favourite`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unfavourite - * - * @param id The target status id. - * @return Status. - */ - public async unfavouriteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/reblog - * - * @param id The target status id. - * @return Status. - */ - public async reblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/reblog`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unreblog - * - * @param id The target status id. - * @return Status. - */ - public async unreblogStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unreblog`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/bookmark - * - * @param id The target status id. - * @return Status. - */ - public async bookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/bookmark`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unbookmark - * - * @param id The target status id. - * @return Status. - */ - public async unbookmarkStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/mute - * - * @param id The target status id. - * @return Status - */ - public async muteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/mute`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unmute - * - * @param id The target status id. - * @return Status - */ - public async unmuteStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unmute`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/pin - * @param id The target status id. - * @return Status - */ - public async pinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/pin`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * POST /api/v1/statuses/:id/unpin - * - * @param id The target status id. - * @return Status - */ - public async unpinStatus(id: string): Promise> { - return this.client.post(`/api/v1/statuses/${id}/unpin`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - // ====================================== - // statuses/media - // ====================================== - /** - * POST /api/v2/media - * - * @param file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @return Attachment - */ - public async uploadMedia( - file: any, - options?: { description?: string; focus?: string } - ): Promise> { - const formData = new FormData() - formData.append('file', file) - if (options) { - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.postForm('/api/v2/media', formData).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.async_attachment(res.data) - }) - }) - } - - /** - * GET /api/v1/media/:id - * - * @param id Target media ID. - * @return Attachment - */ - public async getMedia(id: string): Promise> { - const res = await this.client.get(`/api/v1/media/${id}`) - - return Object.assign(res, { - data: PleromaAPI.Converter.attachment(res.data) - }) - } - - /** - * PUT /api/v1/media/:id - * - * @param id Target media ID. - * @param options.file The file to be attached, using multipart form data. - * @param options.description A plain-text description of the media. - * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. - * @param options.is_sensitive Whether the media is sensitive. - * @return Attachment - */ - public async updateMedia( - id: string, - options?: { - file?: any - description?: string - focus?: string - } - ): Promise> { - const formData = new FormData() - if (options) { - if (options.file) { - formData.append('file', options.file) - } - if (options.description) { - formData.append('description', options.description) - } - if (options.focus) { - formData.append('focus', options.focus) - } - } - return this.client.putForm(`/api/v1/media/${id}`, formData).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.attachment(res.data) - }) - }) - } - - // ====================================== - // statuses/polls - // ====================================== - /** - * GET /api/v1/polls/:id - * - * @param id Target poll ID. - * @return Poll - */ - public async getPoll(id: string): Promise> { - return this.client.get(`/api/v1/polls/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.poll(res.data) - }) - }) - } - - /** - * POST /api/v1/polls/:id/votes - * - * @param id Target poll ID. - * @param choices Array of own votes containing index for each option (starting from 0). - * @return Poll - */ - public async votePoll(id: string, choices: Array): Promise> { - return this.client - .post(`/api/v1/polls/${id}/votes`, { - choices: choices - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.poll(res.data) - }) - }) - } - - // ====================================== - // statuses/scheduled_statuses - // ====================================== - /** - * GET /api/v1/scheduled_statuses - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of scheduled statuses. - */ - public async getScheduledStatuses(options?: { - limit?: number | null - max_id?: string | null - since_id?: string | null - min_id?: string | null - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - } - return this.client.get>('/api/v1/scheduled_statuses', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.scheduled_status(s)) - }) - }) - } - - /** - * GET /api/v1/scheduled_statuses/:id - * - * @param id Target status ID. - * @return ScheduledStatus. - */ - public async getScheduledStatus(id: string): Promise> { - return this.client.get(`/api/v1/scheduled_statuses/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.scheduled_status(res.data) - }) - }) - } - - /** - * PUT /api/v1/scheduled_statuses/:id - * - * @param id Target scheduled status ID. - * @param scheduled_at ISO 8601 Datetime at which the status will be published. - * @return ScheduledStatus. - */ - public async scheduleStatus(id: string, scheduled_at?: string | null): Promise> { - let params = {} - if (scheduled_at) { - params = Object.assign(params, { - scheduled_at: scheduled_at - }) - } - return this.client.put(`/api/v1/scheduled_statuses/${id}`, params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.scheduled_status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/scheduled_statuses/:id - * - * @param id Target scheduled status ID. - */ - public cancelScheduledStatus(id: string): Promise> { - return this.client.del<{}>(`/api/v1/scheduled_statuses/${id}`) - } - - // ====================================== - // timelines - // ====================================== - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getPublicTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: false - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/public - * - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getLocalTimeline(options?: { - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = { - local: true - } - if (options) { - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/public', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/tag/:hashtag - * - * @param hashtag Content of a #hashtag, not including # symbol. - * @param options.local Show only local statuses? Defaults to false. - * @param options.only_media Show only statuses with media attached? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getTagTimeline( - hashtag: string, - options?: { - local?: boolean - only_media?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.only_media !== undefined) { - params = Object.assign(params, { - only_media: options.only_media - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/tag/${hashtag}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/home - * - * @param options.local Show only local statuses? Defaults to false. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getHomeTimeline(options?: { - local?: boolean - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/timelines/home', params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - /** - * GET /api/v1/timelines/list/:list_id - * - * @param list_id Local ID of the list in the database. - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getListTimeline( - list_id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>(`/api/v1/timelines/list/${list_id}`, params).then(res => { - return Object.assign(res, { - data: res.data.map(s => PleromaAPI.Converter.status(s)) - }) - }) - } - - // ====================================== - // timelines/conversations - // ====================================== - /** - * GET /api/v1/conversations - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of statuses. - */ - public async getConversationTimeline(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - } - return this.client.get>('/api/v1/conversations', params).then(res => { - return Object.assign(res, { - data: res.data.map(c => PleromaAPI.Converter.conversation(c)) - }) - }) - } - - /** - * DELETE /api/v1/conversations/:id - * - * @param id Target conversation ID. - */ - public deleteConversation(id: string): Promise> { - return this.client.del<{}>(`/api/v1/conversations/${id}`) - } - - /** - * POST /api/v1/conversations/:id/read - * - * @param id Target conversation ID. - * @return Conversation. - */ - public async readConversation(id: string): Promise> { - return this.client.post(`/api/v1/conversations/${id}/read`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.conversation(res.data) - }) - }) - } - - // ====================================== - // timelines/lists - // ====================================== - /** - * GET /api/v1/lists - * - * @return Array of lists. - */ - public async getLists(): Promise>> { - return this.client.get>('/api/v1/lists').then(res => { - return Object.assign(res, { - data: res.data.map(l => PleromaAPI.Converter.list(l)) - }) - }) - } - - /** - * GET /api/v1/lists/:id - * - * @param id Target list ID. - * @return List. - */ - public async getList(id: string): Promise> { - return this.client.get(`/api/v1/lists/${id}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.list(res.data) - }) - }) - } - - /** - * POST /api/v1/lists - * - * @param title List name. - * @return List. - */ - public async createList(title: string): Promise> { - return this.client - .post('/api/v1/lists', { - title: title - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.list(res.data) - }) - }) - } - - /** - * PUT /api/v1/lists/:id - * - * @param id Target list ID. - * @param title New list name. - * @return List. - */ - public async updateList(id: string, title: string): Promise> { - return this.client - .put(`/api/v1/lists/${id}`, { - title: title - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.list(res.data) - }) - }) - } - - /** - * DELETE /api/v1/lists/:id - * - * @param id Target list ID. - */ - public deleteList(id: string): Promise> { - return this.client.del<{}>(`/api/v1/lists/${id}`) - } - - /** - * GET /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param options.limit Max number of results to return. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @return Array of accounts. - */ - public async getAccountsInList( - id: string, - options?: { - limit?: number - max_id?: string - since_id?: string - } - ): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - } - return this.client.get>(`/api/v1/lists/${id}/accounts`, params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - /** - * POST /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public addAccountsToList(id: string, account_ids: Array): Promise> { - return this.client.post<{}>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - /** - * DELETE /api/v1/lists/:id/accounts - * - * @param id Target list ID. - * @param account_ids Array of account IDs to add to the list. - */ - public deleteAccountsFromList(id: string, account_ids: Array): Promise> { - return this.client.del<{}>(`/api/v1/lists/${id}/accounts`, { - account_ids: account_ids - }) - } - - // ====================================== - // timelines/markers - // ====================================== - /** - * GET /api/v1/markers - * - * @param timelines Array of timeline names, String enum anyOf home, notifications. - * @return Marker or empty object. - */ - public async getMarkers(timeline: Array): Promise>> { - return this.client - .get>('/api/v1/markers', { - timeline: timeline - }) - .then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.marker(res.data) - }) - }) - } - - /** - * POST /api/v1/markers - * - * @param options.home Marker position of the last read status ID in home timeline. - * @param options.notifications Marker position of the last read notification ID in notifications. - * @return Marker. - */ - public async saveMarkers(options?: { - home?: { last_read_id: string } - notifications?: { last_read_id: string } - }): Promise> { - let params = {} - if (options) { - if (options.home) { - params = Object.assign(params, { - home: options.home - }) - } - if (options.notifications) { - params = Object.assign(params, { - notifications: options.notifications - }) - } - } - return this.client.post('/api/v1/markers', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.marker(res.data) - }) - }) - } - - // ====================================== - // notifications - // ====================================== - /** - * GET /api/v1/notifications - * - * @param options.limit Max number of results to return. Defaults to 20. - * @param options.max_id Return results older than ID. - * @param options.since_id Return results newer than ID. - * @param options.min_id Return results immediately newer than ID. - * @param options.exclude_types Array of types to exclude. - * @param options.account_id Return only notifications received from this account. - * @return Array of notifications. - */ - public async getNotifications(options?: { - limit?: number - max_id?: string - since_id?: string - min_id?: string - exclude_types?: Array - account_id?: string - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.since_id) { - params = Object.assign(params, { - since_id: options.since_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.exclude_types) { - params = Object.assign(params, { - exclude_types: options.exclude_types.map(e => PleromaAPI.Converter.encodeNotificationType(e)) - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - } - return this.client.get>('/api/v1/notifications', params).then(res => { - return Object.assign(res, { - data: res.data.flatMap(n => { - const notify = PleromaAPI.Converter.notification(n) - if (notify instanceof UnknownNotificationTypeError) return [] - return notify - }) - }) - }) - } - - /** - * GET /api/v1/notifications/:id - * - * @param id Target notification ID. - * @return Notification. - */ - public async getNotification(id: string): Promise> { - const res = await this.client.get(`/api/v1/notifications/${id}`) - const notify = PleromaAPI.Converter.notification(res.data) - if (notify instanceof UnknownNotificationTypeError) { - throw new UnknownNotificationTypeError() - } - return { ...res, data: notify } - } - - /** - * POST /api/v1/notifications/clear - */ - public dismissNotifications(): Promise> { - return this.client.post<{}>('/api/v1/notifications/clear') - } - - /** - * POST /api/v1/notifications/:id/dismiss - * - * @param id Target notification ID. - */ - public dismissNotification(id: string): Promise> { - return this.client.post<{}>(`/api/v1/notifications/${id}/dismiss`) - } - - /** - * POST /api/v1/pleroma/notifcations/read - * - * @param id A single notification ID to read - * @param max_id Read all notifications up to this ID - * @return Array of notifications - */ - public async readNotifications(options: { - id?: string - max_id?: string - }): Promise>> { - if (options.id) { - const res = await this.client.post('/api/v1/pleroma/notifications/read', { - id: options.id - }) - const notify = PleromaAPI.Converter.notification(res.data) - if (notify instanceof UnknownNotificationTypeError) return { ...res, data: [] } - return { ...res, data: notify } - } else if (options.max_id) { - const res = await this.client.post>('/api/v1/pleroma/notifications/read', { - max_id: options.max_id - }) - return { - ...res, - data: res.data.flatMap(n => { - const notify = PleromaAPI.Converter.notification(n) - if (notify instanceof UnknownNotificationTypeError) return [] - return notify - }) - } - } else { - return new Promise((_, reject) => { - const err = new ArgumentError('id or max_id is required') - reject(err) - }) - } - } - - // ====================================== - // notifications/push - // ====================================== - /** - * POST /api/v1/push/subscription - * - * @param subscription[endpoint] Endpoint URL that is called when a notification event occurs. - * @param subscription[keys][p256dh] User agent public key. Base64 encoded string of public key of ECDH key using prime256v1 curve. - * @param subscription[keys] Auth secret. Base64 encoded string of 16 bytes of random data. - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async subscribePushNotification( - subscription: { endpoint: string; keys: { p256dh: string; auth: string } }, - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = { - subscription - } - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.post('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * GET /api/v1/push/subscription - * - * @return PushSubscription. - */ - public async getPushSubscription(): Promise> { - return this.client.get('/api/v1/push/subscription').then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * PUT /api/v1/push/subscription - * - * @param data[alerts][follow] Receive follow notifications? - * @param data[alerts][favourite] Receive favourite notifications? - * @param data[alerts][reblog] Receive reblog notifictaions? - * @param data[alerts][mention] Receive mention notifications? - * @param data[alerts][poll] Receive poll notifications? - * @return PushSubscription. - */ - public async updatePushSubscription( - data?: { alerts: { follow?: boolean; favourite?: boolean; reblog?: boolean; mention?: boolean; poll?: boolean } } | null - ): Promise> { - let params = {} - if (data) { - params = Object.assign(params, { - data - }) - } - return this.client.put('/api/v1/push/subscription', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.push_subscription(res.data) - }) - }) - } - - /** - * DELETE /api/v1/push/subscription - */ - public deletePushSubscription(): Promise> { - return this.client.del<{}>('/api/v1/push/subscription') - } - - // ====================================== - // search - // ====================================== - /** - * GET /api/v2/search - * - * @param q The search query. - * @param options.type Enum of search target. - * @param options.limit Maximum number of results to load, per type. Defaults to 20. Max 40. - * @param options.max_id Return results older than this id. - * @param options.min_id Return results immediately newer than this id. - * @param options.resolve Attempt WebFinger lookup. Defaults to false. - * @param options.following Only include accounts that the user is following. Defaults to false. - * @param options.account_id If provided, statuses returned will be authored only by this account. - * @param options.exclude_unreviewed Filter out unreviewed tags? Defaults to false. - * @return Results. - */ - public async search( - q: string, - options?: { - type?: 'accounts' | 'hashtags' | 'statuses' - limit?: number - max_id?: string - min_id?: string - resolve?: boolean - offset?: number - following?: boolean - account_id?: string - exclude_unreviewed?: boolean - } - ): Promise> { - let params = { - q - } - if (options) { - if (options.type) { - params = Object.assign(params, { - type: options.type - }) - } - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.max_id) { - params = Object.assign(params, { - max_id: options.max_id - }) - } - if (options.min_id) { - params = Object.assign(params, { - min_id: options.min_id - }) - } - if (options.resolve !== undefined) { - params = Object.assign(params, { - resolve: options.resolve - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.following !== undefined) { - params = Object.assign(params, { - following: options.following - }) - } - if (options.account_id) { - params = Object.assign(params, { - account_id: options.account_id - }) - } - if (options.exclude_unreviewed) { - params = Object.assign(params, { - exclude_unreviewed: options.exclude_unreviewed - }) - } - } - return this.client.get('/api/v2/search', params).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.results(res.data) - }) - }) - } - - // ====================================== - // instance - // ====================================== - /** - * GET /api/v1/instance - */ - public async getInstance(): Promise> { - return this.client.get('/api/v1/instance').then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.instance(res.data) - }) - }) - } - - /** - * GET /api/v1/instance/peers - */ - public getInstancePeers(): Promise>> { - return this.client.get>('/api/v1/instance/peers') - } - - /** - * GET /api/v1/instance/activity - */ - public async getInstanceActivity(): Promise>> { - return this.client.get>('/api/v1/instance/activity').then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.activity(a)) - }) - }) - } - - // ====================================== - // instance/trends - // ====================================== - /** - * GET /api/v1/trends - * - * @param limit Maximum number of results to return. Defaults to 10. - */ - public async getInstanceTrends(limit?: number | null): Promise>> { - let params = {} - if (limit) { - params = Object.assign(params, { - limit - }) - } - return this.client.get>('/api/v1/trends', params).then(res => { - return Object.assign(res, { - data: res.data.map(t => PleromaAPI.Converter.tag(t)) - }) - }) - } - - // ====================================== - // instance/directory - // ====================================== - /** - * GET /api/v1/directory - * - * @param options.limit How many accounts to load. Default 40. - * @param options.offset How many accounts to skip before returning results. Default 0. - * @param options.order Order of results. - * @param options.local Only return local accounts. - * @return Array of accounts. - */ - public async getInstanceDirectory(options?: { - limit?: number - offset?: number - order?: 'active' | 'new' - local?: boolean - }): Promise>> { - let params = {} - if (options) { - if (options.limit) { - params = Object.assign(params, { - limit: options.limit - }) - } - if (options.offset) { - params = Object.assign(params, { - offset: options.offset - }) - } - if (options.order) { - params = Object.assign(params, { - order: options.order - }) - } - if (options.local !== undefined) { - params = Object.assign(params, { - local: options.local - }) - } - } - return this.client.get>('/api/v1/directory', params).then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.account(a)) - }) - }) - } - - // ====================================== - // instance/custom_emojis - // ====================================== - /** - * GET /api/v1/custom_emojis - * - * @return Array of emojis. - */ - public async getInstanceCustomEmojis(): Promise>> { - return this.client.get>('/api/v1/custom_emojis').then(res => { - return Object.assign(res, { - data: res.data.map(e => PleromaAPI.Converter.emoji(e)) - }) - }) - } - - // ====================================== - // instance/announcements - // ====================================== - /** - * GET /api/v1/announcements - * - * @return Array of announcements. - */ - public async getInstanceAnnouncements(): Promise>> { - return this.client.get>('/api/v1/announcements').then(res => { - return Object.assign(res, { - data: res.data.map(a => PleromaAPI.Converter.announcement(a)) - }) - }) - } - - /** - * POST /api/v1/announcements/:id/dismiss - * - * @param id The ID of the Announcement in the database. - */ - public async dismissInstanceAnnouncement(id: string): Promise>> { - return this.client.post>(`/api/v1/announcements/${id}/dismiss`) - } - - /** - * PUT /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async addReactionToAnnouncement(_id: string, _name: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - /** - * DELETE /api/v1/announcements/:id/reactions/:name - * - * @param id The ID of the Announcement in the database. - * @param name Unicode emoji, or the shortcode of a custom emoji. - */ - public async removeReactionFromAnnouncement(_id: string, _name: string): Promise>> { - return new Promise((_, reject) => { - const err = new NoImplementedError('pleroma does not support') - reject(err) - }) - } - - // ====================================== - // Emoji reactions - // ====================================== - /** - * PUT /api/v1/pleroma/statuses/:status_id/reactions/:emoji - * - * @param {string} id Target status ID. - * @param {string} emoji Reaction emoji string. This string is raw unicode emoji. - */ - public async createEmojiReaction(id: string, emoji: string): Promise> { - return this.client.put(`/api/v1/pleroma/statuses/${id}/reactions/${encodeURI(emoji)}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * DELETE /api/v1/pleroma/statuses/:status_id/reactions/:emoji - * - * @param {string} id Target status ID. - * @param {string} emoji Reaction emoji string. This string is raw unicode emoji. - */ - public async deleteEmojiReaction(id: string, emoji: string): Promise> { - return this.client.del(`/api/v1/pleroma/statuses/${id}/reactions/${encodeURI(emoji)}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.status(res.data) - }) - }) - } - - /** - * GET /api/v1/pleroma/statuses/:status_id/reactions - * - * @param {string} id Target status ID. - */ - public async getEmojiReactions(id: string): Promise>> { - return this.client.get>(`/api/v1/pleroma/statuses/${id}/reactions`).then(res => { - return Object.assign(res, { - data: res.data.map(r => PleromaAPI.Converter.reaction(r)) - }) - }) - } - - /** - * GET /api/v1/pleroma/statuses/:status_id/reactions/:emoji - * - * @param {string} id Target status ID. - * @param {string} emoji Reaction emoji string. This string is url encoded unicode emoji. - */ - public async getEmojiReaction(id: string, emoji: string): Promise> { - return this.client.get(`/api/v1/pleroma/statuses/${id}/reactions/${encodeURI(emoji)}`).then(res => { - return Object.assign(res, { - data: PleromaAPI.Converter.reaction(res.data) - }) - }) - } - - // ====================================== - // WebSocket - // ====================================== - public userSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'user') - } - - public publicSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public') - } - - public localSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'public:local') - } - - public tagSocket(tag: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'hashtag', `tag=${tag}`) - } - - public listSocket(list_id: string): WebSocket { - return this.client.socket('/api/v1/streaming', 'list', `list=${list_id}`) - } - - public directSocket(): WebSocket { - return this.client.socket('/api/v1/streaming', 'direct') - } -} diff --git a/packages/megalodon/src/pleroma/api_client.ts b/packages/megalodon/src/pleroma/api_client.ts deleted file mode 100644 index c20350b67c..0000000000 --- a/packages/megalodon/src/pleroma/api_client.ts +++ /dev/null @@ -1,824 +0,0 @@ -import axios, { AxiosResponse, AxiosRequestConfig } from 'axios' -import objectAssignDeep from 'object-assign-deep' - -import MegalodonEntity from '../entity' -import PleromaEntity from './entity' -import Response from '../response' -import { RequestCanceledError } from '../cancel' -import proxyAgent, { ProxyConfig } from '../proxy_config' -import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from '../default' -import WebSocket from './web_socket' -import NotificationType, { UnknownNotificationTypeError } from '../notification' -import PleromaNotificationType from './notification' - -namespace PleromaAPI { - export namespace Entity { - export type Account = PleromaEntity.Account - export type Activity = PleromaEntity.Activity - export type Announcement = PleromaEntity.Announcement - export type Application = PleromaEntity.Application - export type AsyncAttachment = PleromaEntity.AsyncAttachment - export type Attachment = PleromaEntity.Attachment - export type Card = PleromaEntity.Card - export type Context = PleromaEntity.Context - export type Conversation = PleromaEntity.Conversation - export type Emoji = PleromaEntity.Emoji - export type FeaturedTag = PleromaEntity.FeaturedTag - export type Field = PleromaEntity.Field - export type Filter = PleromaEntity.Filter - export type History = PleromaEntity.History - export type IdentityProof = PleromaEntity.IdentityProof - export type Instance = PleromaEntity.Instance - export type List = PleromaEntity.List - export type Marker = PleromaEntity.Marker - export type Mention = PleromaEntity.Mention - export type Notification = PleromaEntity.Notification - export type Poll = PleromaEntity.Poll - export type PollOption = PleromaEntity.PollOption - export type Preferences = PleromaEntity.Preferences - export type PushSubscription = PleromaEntity.PushSubscription - export type Reaction = PleromaEntity.Reaction - export type Relationship = PleromaEntity.Relationship - export type Report = PleromaEntity.Report - export type Results = PleromaEntity.Results - export type ScheduledStatus = PleromaEntity.ScheduledStatus - export type Source = PleromaEntity.Source - export type Stats = PleromaEntity.Stats - export type Status = PleromaEntity.Status - export type StatusParams = PleromaEntity.StatusParams - export type StatusSource = PleromaEntity.StatusSource - export type Tag = PleromaEntity.Tag - export type Token = PleromaEntity.Token - export type URLs = PleromaEntity.URLs - } - - export namespace Converter { - export const decodeNotificationType = ( - t: PleromaEntity.NotificationType - ): MegalodonEntity.NotificationType | UnknownNotificationTypeError => { - switch (t) { - case PleromaNotificationType.Mention: - return NotificationType.Mention - case PleromaNotificationType.Reblog: - return NotificationType.Reblog - case PleromaNotificationType.Favourite: - return NotificationType.Favourite - case PleromaNotificationType.Follow: - return NotificationType.Follow - case PleromaNotificationType.Poll: - return NotificationType.PollExpired - case PleromaNotificationType.PleromaEmojiReaction: - return NotificationType.EmojiReaction - case PleromaNotificationType.FollowRequest: - return NotificationType.FollowRequest - case PleromaNotificationType.Update: - return NotificationType.Update - case PleromaNotificationType.Move: - return NotificationType.Move - default: - return new UnknownNotificationTypeError() - } - } - export const encodeNotificationType = ( - t: MegalodonEntity.NotificationType - ): PleromaEntity.NotificationType | UnknownNotificationTypeError => { - switch (t) { - case NotificationType.Follow: - return PleromaNotificationType.Follow - case NotificationType.Favourite: - return PleromaNotificationType.Favourite - case NotificationType.Reblog: - return PleromaNotificationType.Reblog - case NotificationType.Mention: - return PleromaNotificationType.Mention - case NotificationType.PollExpired: - return PleromaNotificationType.Poll - case NotificationType.EmojiReaction: - return PleromaNotificationType.PleromaEmojiReaction - case NotificationType.FollowRequest: - return PleromaNotificationType.FollowRequest - case NotificationType.Update: - return PleromaNotificationType.Update - case NotificationType.Move: - return PleromaNotificationType.Move - default: - return new UnknownNotificationTypeError() - } - } - - export const account = (a: Entity.Account): MegalodonEntity.Account => { - return { - id: a.id, - username: a.username, - acct: a.acct, - display_name: a.display_name, - locked: a.locked, - discoverable: a.discoverable, - group: null, - noindex: a.noindex, - suspended: a.suspended, - limited: a.limited, - 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, - bot: a.bot, - source: a.source - } - } - export const activity = (a: Entity.Activity): MegalodonEntity.Activity => a - export const announcement = (a: Entity.Announcement): MegalodonEntity.Announcement => ({ - id: a.id, - content: a.content, - starts_at: a.starts_at, - ends_at: a.ends_at, - published: a.published, - all_day: a.all_day, - published_at: a.published_at, - updated_at: a.updated_at, - read: null, - mentions: a.mentions, - statuses: a.statuses, - tags: a.tags, - emojis: a.emojis, - reactions: a.reactions - }) - 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: null, - author_url: null, - provider_name: c.provider_name, - provider_url: c.provider_url, - html: null, - width: null, - height: null, - embed_url: null, - blurhash: null - }) - 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 = (f: Entity.FeaturedTag): MegalodonEntity.FeaturedTag => f - export const field = (f: Entity.Field): MegalodonEntity.Field => f - export const filter = (f: Entity.Filter): MegalodonEntity.Filter => 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 => ({ - uri: i.uri, - title: i.title, - description: i.description, - email: i.email, - version: i.version, - thumbnail: i.thumbnail, - urls: urls(i.urls), - stats: stats(i.stats), - languages: i.languages, - registrations: i.registrations, - approval_required: i.approval_required, - configuration: { - statuses: { - max_characters: i.max_toot_chars, - max_media_attachments: i.max_media_attachments - }, - polls: { - max_options: i.poll_limits.max_options, - max_characters_per_option: i.poll_limits.max_option_chars, - min_expiration: i.poll_limits.min_expiration, - max_expiration: i.poll_limits.max_expiration - } - } - }) - export const list = (l: Entity.List): MegalodonEntity.List => ({ - id: l.id, - title: l.title, - replies_policy: null - }) - export const marker = (m: Entity.Marker | Record): MegalodonEntity.Marker | Record => { - if ((m as any).notifications) { - const mm = m as Entity.Marker - return { - notifications: { - last_read_id: mm.notifications.last_read_id, - version: mm.notifications.version, - updated_at: mm.notifications.updated_at, - unread_count: mm.notifications.pleroma.unread_count - } - } - } else { - return {} - } - } - 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 && n.emoji) { - return { - id: n.id, - account: account(n.account), - created_at: n.created_at, - status: status(n.status), - emoji: n.emoji, - type: notificationType - } - } else if (n.status) { - return { - id: n.id, - account: account(n.account), - created_at: n.created_at, - status: status(n.status), - type: notificationType - } - } else if (n.target) { - return { - id: n.id, - account: account(n.account), - created_at: n.created_at, - target: account(n.target), - type: notificationType - } - } else { - return { - id: n.id, - account: account(n.account), - created_at: n.created_at, - type: notificationType - } - } - } - export const poll = (p: Entity.Poll): MegalodonEntity.Poll => p - export const pollOption = (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 reaction = (r: Entity.Reaction): MegalodonEntity.Reaction => { - const p = { - count: r.count, - me: r.me, - name: r.name - } - if (r.accounts) { - return Object.assign({}, p, { - accounts: r.accounts.map(a => account(a)) - }) - } - return p - } - export const relationship = (r: Entity.Relationship): MegalodonEntity.Relationship => ({ - id: r.id, - following: r.following, - followed_by: r.followed_by, - blocking: r.blocking, - blocked_by: r.blocked_by, - muting: r.muting, - muting_notifications: r.muting_notifications, - requested: r.requested, - domain_blocking: r.domain_blocking, - showing_reblogs: r.showing_reblogs, - endorsed: r.endorsed, - notifying: r.notifying, - note: r.note - }) - export const report = (r: Entity.Report): MegalodonEntity.Report => ({ - id: r.id, - action_taken: r.action_taken, - action_taken_at: null, - category: null, - comment: null, - forwarded: null, - status_ids: null, - rule_ids: null - }) - 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 => ({ - id: s.id, - scheduled_at: s.scheduled_at, - params: status_params(s.params), - media_attachments: Array.isArray(s.media_attachments) ? s.media_attachments.map(m => attachment(m)) : 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) : null, - content: s.content, - plain_content: s.pleroma.content?.['text/plain'] ? s.pleroma.content['text/plain'] : null, - created_at: s.created_at, - edited_at: s.edited_at || null, - 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: Array.isArray(s.pleroma.emoji_reactions) ? s.pleroma.emoji_reactions.map(r => reaction(r)) : [], - bookmarked: s.bookmarked ? s.bookmarked : false, - quote: s.reblog !== null && s.reblog.content !== s.content - }) - export const status_params = (s: Entity.StatusParams): MegalodonEntity.StatusParams => { - return { - text: s.text, - in_reply_to_id: s.in_reply_to_id, - media_ids: Array.isArray(s.media_ids) ? s.media_ids : null, - sensitive: s.sensitive, - spoiler_text: s.spoiler_text, - visibility: s.visibility, - scheduled_at: s.scheduled_at, - application_id: null - } - } - 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 - } - - /** - * Interface - */ - export interface Interface { - get(path: string, params?: any, headers?: { [key: string]: string }): Promise> - put(path: string, params?: any, headers?: { [key: string]: string }): Promise> - putForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patch(path: string, params?: any, headers?: { [key: string]: string }): Promise> - patchForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - post(path: string, params?: any, headers?: { [key: string]: string }): Promise> - postForm(path: string, params?: any, headers?: { [key: string]: string }): Promise> - del(path: string, params?: any, headers?: { [key: string]: string }): Promise> - cancel(): void - socket(path: string, stream: string, params?: string): WebSocket - } - - /** - * Mastodon API client. - * - * Using axios for request, you will handle promises. - */ - export class Client implements Interface { - static DEFAULT_SCOPE = DEFAULT_SCOPE - static DEFAULT_URL = 'https://pleroma.io' - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - let options: AxiosRequestConfig = { - params: params, - headers: headers - } - 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(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 = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options) - .catch((err: Error) => { - if (axios.isCancel(err)) { - throw new RequestCanceledError(err.message) - } else { - throw err - } - }) - .then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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(this.baseUrl + path, params, options).then((resp: AxiosResponse) => { - const res: Response = { - 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(path: string, params = {}, headers: { [key: string]: string } = {}): Promise> { - 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 = { - 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 default PleromaAPI diff --git a/packages/megalodon/src/pleroma/entities/account.ts b/packages/megalodon/src/pleroma/entities/account.ts deleted file mode 100644 index 29d42643fc..0000000000 --- a/packages/megalodon/src/pleroma/entities/account.ts +++ /dev/null @@ -1,31 +0,0 @@ -/// -/// -/// -namespace PleromaEntity { - export type Account = { - id: string - username: string - acct: string - display_name: string - locked: boolean - discoverable?: boolean - noindex: boolean | null - suspended: boolean | null - limited: boolean | null - created_at: string - followers_count: number - following_count: number - statuses_count: number - note: string - url: string - avatar: string - avatar_static: string - header: string - header_static: string - emojis: Array - moved: Account | null - fields: Array - bot: boolean - source?: Source - } -} diff --git a/packages/megalodon/src/pleroma/entities/activity.ts b/packages/megalodon/src/pleroma/entities/activity.ts deleted file mode 100644 index f70ad168eb..0000000000 --- a/packages/megalodon/src/pleroma/entities/activity.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace PleromaEntity { - export type Activity = { - week: string - statuses: string - logins: string - registrations: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/announcement.ts b/packages/megalodon/src/pleroma/entities/announcement.ts deleted file mode 100644 index 247ad90c5b..0000000000 --- a/packages/megalodon/src/pleroma/entities/announcement.ts +++ /dev/null @@ -1,39 +0,0 @@ -/// - -namespace PleromaEntity { - 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 - mentions: Array - statuses: Array - tags: Array - emojis: Array - reactions: Array - } - - export type AnnouncementAccount = { - id: string - username: string - url: string - acct: string - } - - export type AnnouncementStatus = { - id: string - url: string - } - - export type AnnouncementReaction = { - name: string - count: number - me: boolean | null - url: string | null - static_url: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/application.ts b/packages/megalodon/src/pleroma/entities/application.ts deleted file mode 100644 index 055592d6ce..0000000000 --- a/packages/megalodon/src/pleroma/entities/application.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace PleromaEntity { - export type Application = { - name: string - website?: string | null - vapid_key?: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/async_attachment.ts b/packages/megalodon/src/pleroma/entities/async_attachment.ts deleted file mode 100644 index 8784979cbb..0000000000 --- a/packages/megalodon/src/pleroma/entities/async_attachment.ts +++ /dev/null @@ -1,14 +0,0 @@ -/// -namespace PleromaEntity { - export type AsyncAttachment = { - id: string - type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio' - url: string | null - remote_url: string | null - preview_url: string - text_url: string | null - meta: Meta | null - description: string | null - blurhash: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/attachment.ts b/packages/megalodon/src/pleroma/entities/attachment.ts deleted file mode 100644 index 18d4371daf..0000000000 --- a/packages/megalodon/src/pleroma/entities/attachment.ts +++ /dev/null @@ -1,49 +0,0 @@ -namespace PleromaEntity { - export type Sub = { - // For Image, Gifv, and Video - width?: number - height?: number - size?: string - aspect?: number - - // For Gifv and Video - frame_rate?: string - - // For Audio, Gifv, and Video - duration?: number - bitrate?: number - } - - export type Focus = { - x: number - y: number - } - - export type Meta = { - original?: Sub - small?: Sub - focus?: Focus - length?: string - duration?: number - fps?: number - size?: string - width?: number - height?: number - aspect?: number - audio_encode?: string - audio_bitrate?: string - audio_channel?: string - } - - export type Attachment = { - id: string - type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio' - url: string - remote_url: string | null - preview_url: string | null - text_url: string | null - meta: Meta | null - description: string | null - blurhash: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/card.ts b/packages/megalodon/src/pleroma/entities/card.ts deleted file mode 100644 index 9aca99a8c8..0000000000 --- a/packages/megalodon/src/pleroma/entities/card.ts +++ /dev/null @@ -1,11 +0,0 @@ -namespace PleromaEntity { - export type Card = { - url: string - title: string - description: string - type: 'link' | 'photo' | 'video' | 'rich' - image: string | null - provider_name: string - provider_url: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/context.ts b/packages/megalodon/src/pleroma/entities/context.ts deleted file mode 100644 index f297bd2c17..0000000000 --- a/packages/megalodon/src/pleroma/entities/context.ts +++ /dev/null @@ -1,8 +0,0 @@ -/// - -namespace PleromaEntity { - export type Context = { - ancestors: Array - descendants: Array - } -} diff --git a/packages/megalodon/src/pleroma/entities/conversation.ts b/packages/megalodon/src/pleroma/entities/conversation.ts deleted file mode 100644 index 624e6da389..0000000000 --- a/packages/megalodon/src/pleroma/entities/conversation.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// -/// - -namespace PleromaEntity { - export type Conversation = { - id: string - accounts: Array - last_status: Status | null - unread: boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/emoji.ts b/packages/megalodon/src/pleroma/entities/emoji.ts deleted file mode 100644 index 43ea22d770..0000000000 --- a/packages/megalodon/src/pleroma/entities/emoji.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace PleromaEntity { - export type Emoji = { - shortcode: string - static_url: string - url: string - visible_in_picker: boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/featured_tag.ts b/packages/megalodon/src/pleroma/entities/featured_tag.ts deleted file mode 100644 index a42e27f9d0..0000000000 --- a/packages/megalodon/src/pleroma/entities/featured_tag.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace PleromaEntity { - export type FeaturedTag = { - id: string - name: string - statuses_count: number - last_status_at: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/field.ts b/packages/megalodon/src/pleroma/entities/field.ts deleted file mode 100644 index 01803078a9..0000000000 --- a/packages/megalodon/src/pleroma/entities/field.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace PleromaEntity { - export type Field = { - name: string - value: string - verified_at: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/filter.ts b/packages/megalodon/src/pleroma/entities/filter.ts deleted file mode 100644 index 08a18089c2..0000000000 --- a/packages/megalodon/src/pleroma/entities/filter.ts +++ /dev/null @@ -1,12 +0,0 @@ -namespace PleromaEntity { - export type Filter = { - id: string - phrase: string - context: Array - expires_at: string | null - irreversible: boolean - whole_word: boolean - } - - export type FilterContext = string -} diff --git a/packages/megalodon/src/pleroma/entities/history.ts b/packages/megalodon/src/pleroma/entities/history.ts deleted file mode 100644 index 9aaaeb8def..0000000000 --- a/packages/megalodon/src/pleroma/entities/history.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace PleromaEntity { - export type History = { - day: string - uses: number - accounts: number - } -} diff --git a/packages/megalodon/src/pleroma/entities/identity_proof.ts b/packages/megalodon/src/pleroma/entities/identity_proof.ts deleted file mode 100644 index 463fdc6817..0000000000 --- a/packages/megalodon/src/pleroma/entities/identity_proof.ts +++ /dev/null @@ -1,9 +0,0 @@ -namespace PleromaEntity { - export type IdentityProof = { - provider: string - provider_username: string - updated_at: string - proof_url: string - profile_url: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/instance.ts b/packages/megalodon/src/pleroma/entities/instance.ts deleted file mode 100644 index 0b57e805e9..0000000000 --- a/packages/megalodon/src/pleroma/entities/instance.ts +++ /dev/null @@ -1,46 +0,0 @@ -/// -/// -/// - -namespace PleromaEntity { - export type Instance = { - uri: string - title: string - description: string - email: string - version: string - thumbnail: string | null - urls: URLs - stats: Stats - languages: Array - registrations: boolean - approval_required: boolean - max_toot_chars: number - max_media_attachments?: number - pleroma: { - metadata: { - account_activation_required: boolean - birthday_min_age: number - birthday_required: boolean - features: Array - federation: { - enabled: boolean - exclusions: boolean - } - fields_limits: { - max_fields: number - max_remote_fields: number - name_length: number - value_length: number - } - post_formats: Array - } - } - poll_limits: { - max_expiration: number - min_expiration: number - max_option_chars: number - max_options: number - } - } -} diff --git a/packages/megalodon/src/pleroma/entities/list.ts b/packages/megalodon/src/pleroma/entities/list.ts deleted file mode 100644 index a3d4362d9e..0000000000 --- a/packages/megalodon/src/pleroma/entities/list.ts +++ /dev/null @@ -1,6 +0,0 @@ -namespace PleromaEntity { - export type List = { - id: string - title: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/marker.ts b/packages/megalodon/src/pleroma/entities/marker.ts deleted file mode 100644 index 720d4a9055..0000000000 --- a/packages/megalodon/src/pleroma/entities/marker.ts +++ /dev/null @@ -1,12 +0,0 @@ -namespace PleromaEntity { - export type Marker = { - notifications: { - last_read_id: string - version: number - updated_at: string - pleroma: { - unread_count: number - } - } - } -} diff --git a/packages/megalodon/src/pleroma/entities/mention.ts b/packages/megalodon/src/pleroma/entities/mention.ts deleted file mode 100644 index 0d68b4ec21..0000000000 --- a/packages/megalodon/src/pleroma/entities/mention.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace PleromaEntity { - export type Mention = { - id: string - username: string - url: string - acct: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/notification.ts b/packages/megalodon/src/pleroma/entities/notification.ts deleted file mode 100644 index edfa456deb..0000000000 --- a/packages/megalodon/src/pleroma/entities/notification.ts +++ /dev/null @@ -1,16 +0,0 @@ -/// -/// - -namespace PleromaEntity { - export type Notification = { - account: Account - created_at: string - id: string - status?: Status - emoji?: string - type: NotificationType - target?: Account - } - - export type NotificationType = string -} diff --git a/packages/megalodon/src/pleroma/entities/poll.ts b/packages/megalodon/src/pleroma/entities/poll.ts deleted file mode 100644 index 82e0182adc..0000000000 --- a/packages/megalodon/src/pleroma/entities/poll.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -namespace PleromaEntity { - export type Poll = { - id: string - expires_at: string | null - expired: boolean - multiple: boolean - votes_count: number - options: Array - voted: boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/poll_option.ts b/packages/megalodon/src/pleroma/entities/poll_option.ts deleted file mode 100644 index 69717ca0f3..0000000000 --- a/packages/megalodon/src/pleroma/entities/poll_option.ts +++ /dev/null @@ -1,6 +0,0 @@ -namespace PleromaEntity { - export type PollOption = { - title: string - votes_count: number | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/preferences.ts b/packages/megalodon/src/pleroma/entities/preferences.ts deleted file mode 100644 index 99f8d6bca1..0000000000 --- a/packages/megalodon/src/pleroma/entities/preferences.ts +++ /dev/null @@ -1,9 +0,0 @@ -namespace PleromaEntity { - export type Preferences = { - 'posting:default:visibility': 'public' | 'unlisted' | 'private' | 'direct' - 'posting:default:sensitive': boolean - 'posting:default:language': string | null - 'reading:expand:media': 'default' | 'show_all' | 'hide_all' - 'reading:expand:spoilers': boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/push_subscription.ts b/packages/megalodon/src/pleroma/entities/push_subscription.ts deleted file mode 100644 index b3e14e68a3..0000000000 --- a/packages/megalodon/src/pleroma/entities/push_subscription.ts +++ /dev/null @@ -1,16 +0,0 @@ -namespace PleromaEntity { - export type Alerts = { - follow: boolean - favourite: boolean - mention: boolean - reblog: boolean - poll: boolean - } - - export type PushSubscription = { - id: string - endpoint: string - server_key: string - alerts: Alerts - } -} diff --git a/packages/megalodon/src/pleroma/entities/reaction.ts b/packages/megalodon/src/pleroma/entities/reaction.ts deleted file mode 100644 index 662600f252..0000000000 --- a/packages/megalodon/src/pleroma/entities/reaction.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -namespace PleromaEntity { - export type Reaction = { - count: number - me: boolean - name: string - accounts?: Array - } -} diff --git a/packages/megalodon/src/pleroma/entities/relationship.ts b/packages/megalodon/src/pleroma/entities/relationship.ts deleted file mode 100644 index 039f8ec74b..0000000000 --- a/packages/megalodon/src/pleroma/entities/relationship.ts +++ /dev/null @@ -1,18 +0,0 @@ -namespace PleromaEntity { - 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 - subscribing: boolean - notifying: boolean - note: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/report.ts b/packages/megalodon/src/pleroma/entities/report.ts deleted file mode 100644 index 5b9c650a16..0000000000 --- a/packages/megalodon/src/pleroma/entities/report.ts +++ /dev/null @@ -1,6 +0,0 @@ -namespace PleromaEntity { - export type Report = { - id: string - action_taken: boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/results.ts b/packages/megalodon/src/pleroma/entities/results.ts deleted file mode 100644 index cd42e3b090..0000000000 --- a/packages/megalodon/src/pleroma/entities/results.ts +++ /dev/null @@ -1,11 +0,0 @@ -/// -/// -/// - -namespace PleromaEntity { - export type Results = { - accounts: Array - statuses: Array - hashtags: Array - } -} diff --git a/packages/megalodon/src/pleroma/entities/scheduled_status.ts b/packages/megalodon/src/pleroma/entities/scheduled_status.ts deleted file mode 100644 index 547d35fd8f..0000000000 --- a/packages/megalodon/src/pleroma/entities/scheduled_status.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// -/// -namespace PleromaEntity { - export type ScheduledStatus = { - id: string - scheduled_at: string - params: StatusParams - media_attachments: Array | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/source.ts b/packages/megalodon/src/pleroma/entities/source.ts deleted file mode 100644 index f2fa74ab70..0000000000 --- a/packages/megalodon/src/pleroma/entities/source.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// -namespace PleromaEntity { - export type Source = { - privacy: string | null - sensitive: boolean | null - language: string | null - note: string - fields: Array - } -} diff --git a/packages/megalodon/src/pleroma/entities/stats.ts b/packages/megalodon/src/pleroma/entities/stats.ts deleted file mode 100644 index ab3e778454..0000000000 --- a/packages/megalodon/src/pleroma/entities/stats.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace PleromaEntity { - export type Stats = { - user_count: number - status_count: number - domain_count: number - } -} diff --git a/packages/megalodon/src/pleroma/entities/status.ts b/packages/megalodon/src/pleroma/entities/status.ts deleted file mode 100644 index 7c2b887e53..0000000000 --- a/packages/megalodon/src/pleroma/entities/status.ts +++ /dev/null @@ -1,65 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// -/// - -namespace PleromaEntity { - 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 - created_at: string - edited_at?: string | null - emojis: Emoji[] - replies_count: number - reblogs_count: number - favourites_count: number - reblogged: boolean | null - favourited: boolean | null - muted: boolean | null - sensitive: boolean - spoiler_text: string - visibility: 'public' | 'unlisted' | 'private' | 'direct' - media_attachments: Array - mentions: Array - tags: Array - card: Card | null - poll: Poll | null - application: Application | null - language: string | null - pinned: boolean | null - bookmarked?: boolean - // Reblogged status contains only local parameter. - pleroma: { - content?: { - 'text/plain': string - } - spoiler_text?: { - 'text/plain': string - } - conversation_id?: number - direct_conversation_id?: number | null - emoji_reactions?: Array - expires_at?: string - in_reply_to_account_acct?: string - local: boolean - parent_visible?: boolean - pinned_at?: string - thread_muted?: boolean - } - } - - export type StatusTag = { - name: string - url: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/status_params.ts b/packages/megalodon/src/pleroma/entities/status_params.ts deleted file mode 100644 index eda13a0b9b..0000000000 --- a/packages/megalodon/src/pleroma/entities/status_params.ts +++ /dev/null @@ -1,11 +0,0 @@ -namespace PleromaEntity { - export type StatusParams = { - text: string - in_reply_to_id: string | null - media_ids?: Array | null - sensitive: boolean | null - spoiler_text: string | null - visibility: 'public' | 'unlisted' | 'private' | 'direct' | null - scheduled_at: string | null - } -} diff --git a/packages/megalodon/src/pleroma/entities/status_source.ts b/packages/megalodon/src/pleroma/entities/status_source.ts deleted file mode 100644 index 57d2bea781..0000000000 --- a/packages/megalodon/src/pleroma/entities/status_source.ts +++ /dev/null @@ -1,7 +0,0 @@ -namespace PleromaEntity { - export type StatusSource = { - id: string - text: string - spoiler_text: string - } -} diff --git a/packages/megalodon/src/pleroma/entities/tag.ts b/packages/megalodon/src/pleroma/entities/tag.ts deleted file mode 100644 index e323ec72c3..0000000000 --- a/packages/megalodon/src/pleroma/entities/tag.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -namespace PleromaEntity { - export type Tag = { - name: string - url: string - history: Array - following?: boolean - } -} diff --git a/packages/megalodon/src/pleroma/entities/token.ts b/packages/megalodon/src/pleroma/entities/token.ts deleted file mode 100644 index 0ac565b517..0000000000 --- a/packages/megalodon/src/pleroma/entities/token.ts +++ /dev/null @@ -1,8 +0,0 @@ -namespace PleromaEntity { - export type Token = { - access_token: string - token_type: string - scope: string - created_at: number - } -} diff --git a/packages/megalodon/src/pleroma/entities/urls.ts b/packages/megalodon/src/pleroma/entities/urls.ts deleted file mode 100644 index 7ad6faf2b0..0000000000 --- a/packages/megalodon/src/pleroma/entities/urls.ts +++ /dev/null @@ -1,5 +0,0 @@ -namespace PleromaEntity { - export type URLs = { - streaming_api: string - } -} diff --git a/packages/megalodon/src/pleroma/entity.ts b/packages/megalodon/src/pleroma/entity.ts deleted file mode 100644 index bd486f62bd..0000000000 --- a/packages/megalodon/src/pleroma/entity.ts +++ /dev/null @@ -1,39 +0,0 @@ -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// > -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// - -export default PleromaEntity diff --git a/packages/megalodon/src/pleroma/notification.ts b/packages/megalodon/src/pleroma/notification.ts deleted file mode 100644 index 2dad51a6e3..0000000000 --- a/packages/megalodon/src/pleroma/notification.ts +++ /dev/null @@ -1,15 +0,0 @@ -import PleromaEntity from './entity' - -namespace PleromaNotificationType { - export const Mention: PleromaEntity.NotificationType = 'mention' - export const Reblog: PleromaEntity.NotificationType = 'reblog' - export const Favourite: PleromaEntity.NotificationType = 'favourite' - export const Follow: PleromaEntity.NotificationType = 'follow' - export const Poll: PleromaEntity.NotificationType = 'poll' - export const PleromaEmojiReaction: PleromaEntity.NotificationType = 'pleroma:emoji_reaction' - export const FollowRequest: PleromaEntity.NotificationType = 'follow_request' - export const Update: PleromaEntity.NotificationType = 'update' - export const Move: PleromaEntity.NotificationType = 'move' -} - -export default PleromaNotificationType diff --git a/packages/megalodon/src/pleroma/web_socket.ts b/packages/megalodon/src/pleroma/web_socket.ts deleted file mode 100644 index f96ea5dc56..0000000000 --- a/packages/megalodon/src/pleroma/web_socket.ts +++ /dev/null @@ -1,349 +0,0 @@ -import WS from 'ws' -import dayjs, { Dayjs } from 'dayjs' -import { EventEmitter } from 'events' - -import proxyAgent, { ProxyConfig } from '../proxy_config' -import { WebSocketInterface } from '../megalodon' -import PleromaAPI from './api_client' -import { UnknownNotificationTypeError } from '../notification' - -/** - * WebSocket - * Pleroma is not support streaming. It is support websocket instead of streaming. - * So this class connect to Phoenix websocket for Pleroma. - */ -export default class WebSocket extends EventEmitter implements WebSocketInterface { - public url: string - public stream: string - public params: string | null - public parser: Parser - public headers: { [key: string]: string } - public proxyConfig: ProxyConfig | false = false - private _accessToken: string - private _reconnectInterval: number - private _reconnectMaxAttempts: number - private _reconnectCurrentAttempts: number - private _connectionClosed: boolean - private _client: WS | null - private _pongReceivedTimestamp: Dayjs - private _heartbeatInterval: number = 60000 - private _pongWaiting: boolean = false - - /** - * @param url Full url of websocket: e.g. https://pleroma.io/api/v1/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 - * @param accessToken The access token. - * @param userAgent The specified User Agent. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - */ - constructor( - url: string, - stream: string, - params: string | undefined, - accessToken: string, - userAgent: string, - proxyConfig: ProxyConfig | false = false - ) { - super() - this.url = url - this.stream = stream - if (params === undefined) { - this.params = null - } else { - this.params = params - } - this.parser = new Parser() - this.headers = { - 'User-Agent': userAgent - } - this.proxyConfig = proxyConfig - this._accessToken = accessToken - this._reconnectInterval = 10000 - this._reconnectMaxAttempts = Infinity - this._reconnectCurrentAttempts = 0 - this._connectionClosed = false - this._client = null - this._pongReceivedTimestamp = dayjs() - } - - /** - * Start websocket connection. - */ - public start() { - this._connectionClosed = false - this._resetRetryParams() - this._startWebSocketConnection() - } - - /** - * Reset connection and start new websocket connection. - */ - private _startWebSocketConnection() { - this._resetConnection() - this._setupParser() - this._client = this._connect(this.url, this.stream, this.params, this._accessToken, this.headers, this.proxyConfig) - this._bindSocket(this._client) - } - - /** - * Stop current connection. - */ - public stop() { - this._connectionClosed = true - this._resetConnection() - this._resetRetryParams() - } - - /** - * Clean up current connection, and listeners. - */ - private _resetConnection() { - if (this._client) { - this._client.close(1000) - this._client.removeAllListeners() - this._client = null - } - - if (this.parser) { - this.parser.removeAllListeners() - } - } - - /** - * Resets the parameters used in reconnect. - */ - private _resetRetryParams() { - this._reconnectCurrentAttempts = 0 - } - - /** - * Reconnects to the same endpoint. - */ - private _reconnect() { - setTimeout(() => { - // Skip reconnect when client is connecting. - // https://github.com/websockets/ws/blob/7.2.1/lib/websocket.js#L365 - if (this._client && this._client.readyState === WS.CONNECTING) { - return - } - - if (this._reconnectCurrentAttempts < this._reconnectMaxAttempts) { - this._reconnectCurrentAttempts++ - this._clearBinding() - if (this._client) { - // In reconnect, we want to close the connection immediately, - // because recoonect is necessary when some problems occur. - this._client.terminate() - } - // Call connect methods - console.log('Reconnecting') - this._client = this._connect(this.url, this.stream, this.params, this._accessToken, this.headers, this.proxyConfig) - this._bindSocket(this._client) - } - }, this._reconnectInterval) - } - - /** - * @param url Base url of streaming endpoint. - * @param stream The specified stream name. - * @param accessToken Access token. - * @param headers The specified headers. - * @param proxyConfig Proxy setting, or set false if don't use proxy. - * @return A WebSocket instance. - */ - private _connect( - url: string, - stream: string, - params: string | null, - accessToken: string, - headers: { [key: string]: string }, - proxyConfig: ProxyConfig | false - ): WS { - const parameter: Array = [`stream=${stream}`] - - if (params) { - parameter.push(params) - } - - if (accessToken !== null) { - parameter.push(`access_token=${accessToken}`) - } - const requestURL: string = `${url}/?${parameter.join('&')}` - let options: WS.ClientOptions = { - headers: headers - } - if (proxyConfig) { - options = Object.assign(options, { - agent: proxyAgent(proxyConfig) - }) - } - - const cli: WS = new WS(requestURL, options) - return cli - } - - /** - * Clear binding event for web socket client. - */ - private _clearBinding() { - if (this._client) { - this._client.removeAllListeners('close') - this._client.removeAllListeners('pong') - this._client.removeAllListeners('open') - this._client.removeAllListeners('message') - this._client.removeAllListeners('error') - } - } - - /** - * Bind event for web socket client. - * @param client A WebSocket instance. - */ - private _bindSocket(client: WS) { - client.on('close', (code: number, _reason: Buffer) => { - // Refer the code: https://tools.ietf.org/html/rfc6455#section-7.4 - if (code === 1000) { - this.emit('close', {}) - } else { - console.log(`Closed connection with ${code}`) - // If already called close method, it does not retry. - if (!this._connectionClosed) { - this._reconnect() - } - } - }) - client.on('pong', () => { - this._pongWaiting = false - this.emit('pong', {}) - this._pongReceivedTimestamp = dayjs() - // It is required to anonymous function since get this scope in checkAlive. - setTimeout(() => this._checkAlive(this._pongReceivedTimestamp), this._heartbeatInterval) - }) - client.on('open', () => { - this.emit('connect', {}) - // Call first ping event. - setTimeout(() => { - client.ping('') - }, 10000) - }) - client.on('message', (data: WS.Data, isBinary: boolean) => { - this.parser.parse(data, isBinary) - }) - client.on('error', (err: Error) => { - this.emit('error', err) - }) - } - - /** - * Set up parser when receive message. - */ - private _setupParser() { - this.parser.on('update', (status: PleromaAPI.Entity.Status) => { - this.emit('update', PleromaAPI.Converter.status(status)) - }) - this.parser.on('notification', (notification: PleromaAPI.Entity.Notification) => { - const n = PleromaAPI.Converter.notification(notification) - if (n instanceof UnknownNotificationTypeError) { - console.warn(`Unknown notification event has received: ${notification}`) - } else { - this.emit('notification', n) - } - }) - this.parser.on('delete', (id: string) => { - this.emit('delete', id) - }) - this.parser.on('conversation', (conversation: PleromaAPI.Entity.Conversation) => { - this.emit('conversation', PleromaAPI.Converter.conversation(conversation)) - }) - this.parser.on('status_update', (status: PleromaAPI.Entity.Status) => { - this.emit('status_update', PleromaAPI.Converter.status(status)) - }) - this.parser.on('error', (err: Error) => { - this.emit('parser-error', err) - }) - this.parser.on('heartbeat', _ => { - this.emit('heartbeat', 'heartbeat') - }) - } - - /** - * Call ping and wait to pong. - */ - private _checkAlive(timestamp: Dayjs) { - const now: Dayjs = dayjs() - // Block multiple calling, if multiple pong event occur. - // It the duration is less than interval, through ping. - if (now.diff(timestamp) > this._heartbeatInterval - 1000 && !this._connectionClosed) { - // Skip ping when client is connecting. - // https://github.com/websockets/ws/blob/7.2.1/lib/websocket.js#L289 - if (this._client && this._client.readyState !== WS.CONNECTING) { - this._pongWaiting = true - this._client.ping('') - setTimeout(() => { - if (this._pongWaiting) { - this._pongWaiting = false - this._reconnect() - } - }, 10000) - } - } - } -} - -/** - * Parser - * This class provides parser for websocket message. - */ -export class Parser extends EventEmitter { - /** - * @param message Message body of websocket. - */ - public parse(data: WS.Data, isBinary: boolean) { - const message = isBinary ? data : data.toString() - if (typeof message !== 'string') { - this.emit('heartbeat', {}) - return - } - - if (message === '') { - this.emit('heartbeat', {}) - return - } - - let event = '' - let payload = '' - let mes = {} - try { - const obj = JSON.parse(message) - event = obj.event - payload = obj.payload - mes = JSON.parse(payload) - } catch (err) { - // delete event does not have json object - if (event !== 'delete') { - this.emit('error', new Error(`Error parsing websocket reply: ${message}, error message: ${err}`)) - return - } - } - - switch (event) { - case 'update': - this.emit('update', mes as PleromaAPI.Entity.Status) - break - case 'notification': - this.emit('notification', mes as PleromaAPI.Entity.Notification) - break - case 'conversation': - this.emit('conversation', mes as PleromaAPI.Entity.Conversation) - break - case 'delete': - this.emit('delete', payload) - break - case 'status.update': - this.emit('status_update', mes as PleromaAPI.Entity.Status) - break - default: - this.emit('error', new Error(`Unknown event has received: ${message}`)) - } - } -} diff --git a/packages/megalodon/test/integration/cancel.spec.ts b/packages/megalodon/test/integration/cancel.spec.ts deleted file mode 100644 index efc9d49770..0000000000 --- a/packages/megalodon/test/integration/cancel.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import MastodonAPI from '@/mastodon/api_client' -import { Worker } from 'jest-worker' - -jest.mock('axios', () => { - const mockAxios = jest.requireActual('axios') - mockAxios.get = (_path: string) => { - return new Promise(resolve => { - setTimeout(() => { - console.log('hoge') - resolve({ - data: 'hoge', - status: 200, - statusText: '200OK', - headers: [], - config: {} - }) - }, 5000) - }) - } - return mockAxios -}) - -const worker = async (client: MastodonAPI.Client) => { - const w: any = new Worker(require.resolve('./cancelWorker.ts')) - await w.cancel(client) -} - -// Could not use jest-worker under typescript. -// I'm waiting for resolve this issue. -// https://github.com/facebook/jest/issues/8872 -describe.skip('cancel', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - it('should be raised', async () => { - const getPromise = client.get<{}>('/timelines/home') - worker(client) - await expect(getPromise).rejects.toThrow() - }) -}) diff --git a/packages/megalodon/test/integration/cancelWorker.ts b/packages/megalodon/test/integration/cancelWorker.ts deleted file mode 100644 index 17a0722780..0000000000 --- a/packages/megalodon/test/integration/cancelWorker.ts +++ /dev/null @@ -1,5 +0,0 @@ -import MastodonAPI from '@/mastodon/api_client' - -export function cancel(client: MastodonAPI.Client) { - return client.cancel() -} diff --git a/packages/megalodon/test/integration/mastodon.spec.ts b/packages/megalodon/test/integration/mastodon.spec.ts deleted file mode 100644 index 172d11a863..0000000000 --- a/packages/megalodon/test/integration/mastodon.spec.ts +++ /dev/null @@ -1,218 +0,0 @@ -import MastodonEntity from '@/mastodon/entity' -import MastodonNotificationType from '@/mastodon/notification' -import Mastodon from '@/mastodon' -import MegalodonNotificationType from '@/notification' -import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' - -jest.mock('axios') - -const account: MastodonEntity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - group: false, - noindex: false, - suspended: false, - limited: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: [], - bot: false, - source: { - privacy: null, - sensitive: false, - language: null, - note: 'test', - fields: [] - } -} - -const status: MastodonEntity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as MastodonEntity.Application, - language: null, - pinned: null, - bookmarked: false -} - -const follow: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '1', - type: MastodonNotificationType.Follow -} - -const favourite: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '2', - status: status, - type: MastodonNotificationType.Favourite -} - -const mention: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '3', - status: status, - type: MastodonNotificationType.Mention -} - -const reblog: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '4', - status: status, - type: MastodonNotificationType.Reblog -} - -const poll: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '5', - type: MastodonNotificationType.Poll -} - -const followRequest: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '6', - type: MastodonNotificationType.FollowRequest -} - -const toot: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '7', - status: status, - type: MastodonNotificationType.Status -} - -const unknownEvent: MastodonEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '8', - type: 'unknown' -} - -;(axios.CancelToken.source as any).mockImplementation(() => { - return { - token: { - throwIfRequested: () => {}, - promise: { - then: () => {}, - catch: () => {} - } - } - } -}) - -describe('getNotifications', () => { - const client = new Mastodon('http://localhost', 'sample token') - const cases: Array<{ event: MastodonEntity.Notification; expected: Entity.NotificationType; title: string }> = [ - { - event: follow, - expected: MegalodonNotificationType.Follow, - title: 'follow' - }, - { - event: favourite, - expected: MegalodonNotificationType.Favourite, - title: 'favourite' - }, - { - event: mention, - expected: MegalodonNotificationType.Mention, - title: 'mention' - }, - { - event: reblog, - expected: MegalodonNotificationType.Reblog, - title: 'reblog' - }, - { - event: poll, - expected: MegalodonNotificationType.PollExpired, - title: 'poll' - }, - { - event: followRequest, - expected: MegalodonNotificationType.FollowRequest, - title: 'followRequest' - }, - { - event: toot, - expected: MegalodonNotificationType.Status, - title: 'status' - } - ] - cases.forEach(c => { - it(`should be ${c.title} event`, async () => { - const config: InternalAxiosRequestConfig = { - headers: new AxiosHeaders() - } - const mockResponse: AxiosResponse> = { - data: [c.event], - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - ;(axios.get as any).mockResolvedValue(mockResponse) - const res = await client.getNotifications() - expect(res.data[0].type).toEqual(c.expected) - }) - }) - it('UnknownEvent should be ignored', async () => { - const config: InternalAxiosRequestConfig = { - headers: new AxiosHeaders() - } - const mockResponse: AxiosResponse> = { - data: [unknownEvent], - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - ;(axios.get as any).mockResolvedValue(mockResponse) - const res = await client.getNotifications() - expect(res.data).toEqual([]) - }) -}) diff --git a/packages/megalodon/test/integration/mastodon/api_client.spec.ts b/packages/megalodon/test/integration/mastodon/api_client.spec.ts deleted file mode 100644 index 51caf4e227..0000000000 --- a/packages/megalodon/test/integration/mastodon/api_client.spec.ts +++ /dev/null @@ -1,177 +0,0 @@ -import MastodonAPI from '@/mastodon/api_client' -import Entity from '@/entity' -import Response from '@/response' -import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' - -jest.mock('axios') - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - group: false, - noindex: false, - suspended: false, - limited: false, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: [], - bot: false, - source: { - privacy: null, - sensitive: false, - language: null, - note: 'test', - fields: [] - } -} - -const status: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: null, - created_at: '2019-03-26T21:40:32', - edited_at: null, - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} -;(axios.CancelToken.source as any).mockImplementation(() => { - return { - token: { - throwIfRequested: () => {}, - promise: { - then: () => {}, - catch: () => {} - } - } - } -}) - -const config: InternalAxiosRequestConfig = { - headers: new AxiosHeaders() -} - -describe('get', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - const mockResponse: AxiosResponse> = { - data: [status], - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - it('should be responsed', async () => { - ;(axios.get as any).mockResolvedValue(mockResponse) - const response: Response> = await client.get>('/timelines/home') - expect(response.data).toEqual([status]) - }) -}) - -describe('put', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - const mockResponse: AxiosResponse = { - data: account, - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - it('should be responsed', async () => { - ;(axios.put as any).mockResolvedValue(mockResponse) - const response: Response = await client.put('/accounts/update_credentials', { - display_name: 'hoge' - }) - expect(response.data).toEqual(account) - }) -}) - -describe('patch', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - const mockResponse: AxiosResponse = { - data: account, - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - it('should be responsed', async () => { - ;(axios.patch as any).mockResolvedValue(mockResponse) - const response: Response = await client.patch('/accounts/update_credentials', { - display_name: 'hoge' - }) - expect(response.data).toEqual(account) - }) -}) - -describe('post', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - const mockResponse: AxiosResponse = { - data: status, - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - it('should be responsed', async () => { - ;(axios.post as any).mockResolvedValue(mockResponse) - const response: Response = await client.post('/statuses', { - status: 'hoge' - }) - expect(response.data).toEqual(status) - }) -}) - -describe('del', () => { - const client = new MastodonAPI.Client('testToken', 'https://pleroma.io/api/v1') - const mockResponse: AxiosResponse<{}> = { - data: {}, - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - it('should be responsed', async () => { - ;(axios.delete as any).mockResolvedValue(mockResponse) - const response: Response<{}> = await client.del<{}>('/statuses/12asdf34') - expect(response.data).toEqual({}) - }) -}) diff --git a/packages/megalodon/test/integration/pleroma.spec.ts b/packages/megalodon/test/integration/pleroma.spec.ts deleted file mode 100644 index 1e1f449e17..0000000000 --- a/packages/megalodon/test/integration/pleroma.spec.ts +++ /dev/null @@ -1,222 +0,0 @@ -import PleromaEntity from '@/pleroma/entity' -import Pleroma from '@/pleroma' -import MegalodonNotificationType from '@/notification' -import PleromaNotificationType from '@/pleroma/notification' -import axios, { AxiosResponse, InternalAxiosRequestConfig, AxiosHeaders } from 'axios' - -jest.mock('axios') - -const account: PleromaEntity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - noindex: null, - suspended: null, - limited: null, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: [], - bot: false, - source: { - privacy: null, - sensitive: false, - language: null, - note: 'test', - fields: [] - } -} - -const status: PleromaEntity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as MastodonEntity.Application, - language: null, - pinned: null, - bookmarked: false, - pleroma: { - local: false - } -} - -const follow: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '1', - type: PleromaNotificationType.Follow -} - -const favourite: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '2', - type: PleromaNotificationType.Favourite, - status: status -} - -const mention: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '3', - type: PleromaNotificationType.Mention, - status: status -} - -const reblog: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '4', - type: PleromaNotificationType.Reblog, - status: status -} - -const poll: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '5', - type: PleromaNotificationType.Poll, - status: status -} - -const emojiReaction: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '6', - type: PleromaNotificationType.PleromaEmojiReaction, - status: status, - emoji: '♥' -} - -const unknownEvent: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '8', - type: 'unknown' -} - -const followRequest: PleromaEntity.Notification = { - account: account, - created_at: '2021-01-31T23:33:26', - id: '7', - type: PleromaNotificationType.FollowRequest -} - -;(axios.CancelToken.source as any).mockImplementation(() => { - return { - token: { - throwIfRequested: () => {}, - promise: { - then: () => {}, - catch: () => {} - } - } - } -}) - -describe('getNotifications', () => { - const client = new Pleroma('http://localhost', 'sample token') - const cases: Array<{ event: PleromaEntity.Notification; expected: Entity.NotificationType; title: string }> = [ - { - event: follow, - expected: MegalodonNotificationType.Follow, - title: 'follow' - }, - { - event: favourite, - expected: MegalodonNotificationType.Favourite, - title: 'favourite' - }, - { - event: mention, - expected: MegalodonNotificationType.Mention, - title: 'mention' - }, - { - event: reblog, - expected: MegalodonNotificationType.Reblog, - title: 'reblog' - }, - { - event: poll, - expected: MegalodonNotificationType.PollExpired, - title: 'poll' - }, - { - event: emojiReaction, - expected: MegalodonNotificationType.EmojiReaction, - title: 'emojiReaction' - }, - { - event: followRequest, - expected: MegalodonNotificationType.FollowRequest, - title: 'followRequest' - } - ] - cases.forEach(c => { - it(`should be ${c.title} event`, async () => { - const config: InternalAxiosRequestConfig = { - headers: new AxiosHeaders() - } - const mockResponse: AxiosResponse> = { - data: [c.event], - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - ;(axios.get as any).mockResolvedValue(mockResponse) - const res = await client.getNotifications() - expect(res.data[0].type).toEqual(c.expected) - }) - }) - it('UnknownEvent should be ignored', async () => { - const config: InternalAxiosRequestConfig = { - headers: new AxiosHeaders() - } - const mockResponse: AxiosResponse> = { - data: [unknownEvent], - status: 200, - statusText: '200OK', - headers: {}, - config: config - } - ;(axios.get as any).mockResolvedValue(mockResponse) - const res = await client.getNotifications() - expect(res.data).toEqual([]) - }) -}) diff --git a/packages/megalodon/test/unit/mastodon.spec.ts b/packages/megalodon/test/unit/mastodon.spec.ts deleted file mode 100644 index 311f60d128..0000000000 --- a/packages/megalodon/test/unit/mastodon.spec.ts +++ /dev/null @@ -1,6 +0,0 @@ -describe('test', () => { - it('should be true', () => { - const res = true - expect(res).toEqual(true) - }) -}) diff --git a/packages/megalodon/test/unit/mastodon/api_client.spec.ts b/packages/megalodon/test/unit/mastodon/api_client.spec.ts deleted file mode 100644 index 1e3c6b5237..0000000000 --- a/packages/megalodon/test/unit/mastodon/api_client.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import MastodonAPI from '@/mastodon/api_client' -import MegalodonEntity from '@/entity' -import MastodonEntity from '@/mastodon/entity' -import MegalodonNotificationType from '@/notification' -import MastodonNotificationType from '@/mastodon/notification' - -describe('api_client', () => { - describe('notification', () => { - describe('encode', () => { - it('megalodon notification type should be encoded to mastodon notification type', () => { - const cases: Array<{ src: MegalodonEntity.NotificationType; dist: MastodonEntity.NotificationType }> = [ - { - src: MegalodonNotificationType.Follow, - dist: MastodonNotificationType.Follow - }, - { - src: MegalodonNotificationType.Favourite, - dist: MastodonNotificationType.Favourite - }, - { - src: MegalodonNotificationType.Reblog, - dist: MastodonNotificationType.Reblog - }, - { - src: MegalodonNotificationType.Mention, - dist: MastodonNotificationType.Mention - }, - { - src: MegalodonNotificationType.PollExpired, - dist: MastodonNotificationType.Poll - }, - { - src: MegalodonNotificationType.FollowRequest, - dist: MastodonNotificationType.FollowRequest - }, - { - src: MegalodonNotificationType.Status, - dist: MastodonNotificationType.Status - } - ] - cases.forEach(c => { - expect(MastodonAPI.Converter.encodeNotificationType(c.src)).toEqual(c.dist) - }) - }) - }) - describe('decode', () => { - it('mastodon notification type should be decoded to megalodon notification type', () => { - const cases: Array<{ src: MastodonEntity.NotificationType; dist: MegalodonEntity.NotificationType }> = [ - { - src: MastodonNotificationType.Follow, - dist: MegalodonNotificationType.Follow - }, - { - src: MastodonNotificationType.Favourite, - dist: MegalodonNotificationType.Favourite - }, - { - src: MastodonNotificationType.Mention, - dist: MegalodonNotificationType.Mention - }, - { - src: MastodonNotificationType.Reblog, - dist: MegalodonNotificationType.Reblog - }, - { - src: MastodonNotificationType.Poll, - dist: MegalodonNotificationType.PollExpired - }, - { - src: MastodonNotificationType.FollowRequest, - dist: MegalodonNotificationType.FollowRequest - } - ] - cases.forEach(c => { - expect(MastodonAPI.Converter.decodeNotificationType(c.src)).toEqual(c.dist) - }) - }) - }) - }) -}) diff --git a/packages/megalodon/test/unit/pleroma/api_client.spec.ts b/packages/megalodon/test/unit/pleroma/api_client.spec.ts deleted file mode 100644 index 98c9ec8e4c..0000000000 --- a/packages/megalodon/test/unit/pleroma/api_client.spec.ts +++ /dev/null @@ -1,226 +0,0 @@ -import PleromaAPI from '@/pleroma/api_client' -import MegalodonEntity from '@/entity' -import PleromaEntity from '@/pleroma/entity' -import MegalodonNotificationType from '@/notification' -import PleromaNotificationType from '@/pleroma/notification' - -const account: PleromaEntity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - noindex: null, - suspended: null, - limited: null, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: [], - bot: false, - source: { - privacy: null, - sensitive: false, - language: null, - note: 'test', - fields: [] - } -} - -describe('api_client', () => { - describe('notification', () => { - describe('encode', () => { - it('megalodon notification type should be encoded to pleroma notification type', () => { - const cases: Array<{ src: MegalodonEntity.NotificationType; dist: PleromaEntity.NotificationType }> = [ - { - src: MegalodonNotificationType.Follow, - dist: PleromaNotificationType.Follow - }, - { - src: MegalodonNotificationType.Favourite, - dist: PleromaNotificationType.Favourite - }, - { - src: MegalodonNotificationType.Reblog, - dist: PleromaNotificationType.Reblog - }, - { - src: MegalodonNotificationType.Mention, - dist: PleromaNotificationType.Mention - }, - { - src: MegalodonNotificationType.PollExpired, - dist: PleromaNotificationType.Poll - }, - { - src: MegalodonNotificationType.EmojiReaction, - dist: PleromaNotificationType.PleromaEmojiReaction - }, - { - src: MegalodonNotificationType.FollowRequest, - dist: PleromaNotificationType.FollowRequest - }, - { - src: MegalodonNotificationType.Update, - dist: PleromaNotificationType.Update - }, - { - src: MegalodonNotificationType.Move, - dist: PleromaNotificationType.Move - } - ] - cases.forEach(c => { - expect(PleromaAPI.Converter.encodeNotificationType(c.src)).toEqual(c.dist) - }) - }) - }) - describe('decode', () => { - it('pleroma notification type should be decoded to megalodon notification type', () => { - const cases: Array<{ src: PleromaEntity.NotificationType; dist: MegalodonEntity.NotificationType }> = [ - { - src: PleromaNotificationType.Follow, - dist: MegalodonNotificationType.Follow - }, - { - src: PleromaNotificationType.Favourite, - dist: MegalodonNotificationType.Favourite - }, - { - src: PleromaNotificationType.Mention, - dist: MegalodonNotificationType.Mention - }, - { - src: PleromaNotificationType.Reblog, - dist: MegalodonNotificationType.Reblog - }, - { - src: PleromaNotificationType.Poll, - dist: MegalodonNotificationType.PollExpired - }, - { - src: PleromaNotificationType.PleromaEmojiReaction, - dist: MegalodonNotificationType.EmojiReaction - }, - { - src: PleromaNotificationType.FollowRequest, - dist: MegalodonNotificationType.FollowRequest - }, - { - src: PleromaNotificationType.Update, - dist: MegalodonNotificationType.Update - }, - { - src: PleromaNotificationType.Move, - dist: MegalodonNotificationType.Move - } - ] - cases.forEach(c => { - expect(PleromaAPI.Converter.decodeNotificationType(c.src)).toEqual(c.dist) - }) - }) - }) - }) - - describe('status', () => { - describe('plain content is included', () => { - it('plain content in pleroma entity should be exported in plain_content column', () => { - const plainContent = 'hoge\nfuga\nfuga' - const content = '

hoge
fuga
fuga

' - const pleromaStatus: PleromaEntity.Status = { - id: '1', - uri: 'https://pleroma.io/notice/1', - url: 'https://pleroma.io/notice/1', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: content, - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as MastodonEntity.Application, - language: null, - pinned: null, - bookmarked: false, - pleroma: { - content: { - 'text/plain': plainContent - }, - local: false - } - } - const megalodonStatus = PleromaAPI.Converter.status(pleromaStatus) - expect(megalodonStatus.plain_content).toEqual(plainContent) - expect(megalodonStatus.content).toEqual(content) - }) - }) - - describe('plain content is not included', () => { - it('plain_content should be null', () => { - const content = '

hoge
fuga
fuga

' - const pleromaStatus: PleromaEntity.Status = { - id: '1', - uri: 'https://pleroma.io/notice/1', - url: 'https://pleroma.io/notice/1', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: content, - created_at: '2019-03-26T21:40:32', - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as MastodonEntity.Application, - language: null, - pinned: null, - bookmarked: false, - pleroma: { - local: false - } - } - const megalodonStatus = PleromaAPI.Converter.status(pleromaStatus) - expect(megalodonStatus.plain_content).toBeNull() - expect(megalodonStatus.content).toEqual(content) - }) - }) - }) -}) diff --git a/packages/megalodon/test/unit/webo_socket.spec.ts b/packages/megalodon/test/unit/webo_socket.spec.ts deleted file mode 100644 index b3b684efb4..0000000000 --- a/packages/megalodon/test/unit/webo_socket.spec.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Parser } from '@/mastodon/web_socket' -import Entity from '@/entity' - -const account: Entity.Account = { - id: '1', - username: 'h3poteto', - acct: 'h3poteto@pleroma.io', - display_name: 'h3poteto', - locked: false, - group: false, - noindex: null, - suspended: null, - limited: null, - created_at: '2019-03-26T21:30:32', - followers_count: 10, - following_count: 10, - statuses_count: 100, - note: 'engineer', - url: 'https://pleroma.io', - avatar: '', - avatar_static: '', - header: '', - header_static: '', - emojis: [], - moved: null, - fields: [], - bot: false -} -const status: Entity.Status = { - id: '1', - uri: 'http://example.com', - url: 'http://example.com', - account: account, - in_reply_to_id: null, - in_reply_to_account_id: null, - reblog: null, - content: 'hoge', - plain_content: 'hoge', - created_at: '2019-03-26T21:40:32', - edited_at: null, - emojis: [], - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - reblogged: null, - favourited: null, - muted: null, - sensitive: false, - spoiler_text: '', - visibility: 'public', - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: { - name: 'Web' - } as Entity.Application, - language: null, - pinned: null, - emoji_reactions: [], - bookmarked: false, - quote: false -} - -const notification: Entity.Notification = { - id: '1', - account: account, - status: status, - type: 'favourite', - created_at: '2019-04-01T17:01:32' -} - -const conversation: Entity.Conversation = { - id: '1', - accounts: [account], - last_status: status, - unread: true -} - -describe('Parser', () => { - let parser: Parser - - beforeEach(() => { - parser = new Parser() - }) - - describe('parse', () => { - describe('message is heartbeat', () => { - describe('message is an object', () => { - const message = Buffer.alloc(0) - - it('should be called', () => { - const spy = jest.fn() - parser.once('heartbeat', spy) - parser.parse(message, true) - expect(spy).toHaveBeenCalledWith({}) - }) - }) - describe('message is empty string', () => { - const message: string = '' - - it('should be called', () => { - const spy = jest.fn() - parser.once('heartbeat', spy) - parser.parse(Buffer.from(message), false) - expect(spy).toHaveBeenCalledWith({}) - }) - }) - }) - - describe('message is not json', () => { - describe('event is delete', () => { - const message = JSON.stringify({ - event: 'delete', - payload: '12asdf34' - }) - - it('should be called', () => { - const spy = jest.fn() - parser.once('delete', spy) - parser.parse(Buffer.from(message), false) - expect(spy).toHaveBeenCalledWith('12asdf34') - }) - }) - describe('event is not delete', () => { - const message = JSON.stringify({ - event: 'event', - payload: '12asdf34' - }) - - it('should be called', () => { - const error = jest.fn() - const deleted = jest.fn() - parser.once('error', error) - parser.once('delete', deleted) - parser.parse(Buffer.from(message), false) - expect(error).toHaveBeenCalled() - expect(deleted).not.toHaveBeenCalled() - }) - }) - }) - - describe('message is json', () => { - describe('event is update', () => { - const message = JSON.stringify({ - event: 'update', - payload: JSON.stringify(status) - }) - it('should be called', () => { - const spy = jest.fn() - parser.once('update', spy) - parser.parse(Buffer.from(message), false) - expect(spy).toHaveBeenCalledWith(status) - }) - }) - - describe('event is notification', () => { - const message = JSON.stringify({ - event: 'notification', - payload: JSON.stringify(notification) - }) - it('should be called', () => { - const spy = jest.fn() - parser.once('notification', spy) - parser.parse(Buffer.from(message), false) - expect(spy).toHaveBeenCalledWith(notification) - }) - }) - - describe('event is conversation', () => { - const message = JSON.stringify({ - event: 'conversation', - payload: JSON.stringify(conversation) - }) - it('should be called', () => { - const spy = jest.fn() - parser.once('conversation', spy) - parser.parse(Buffer.from(message), false) - expect(spy).toHaveBeenCalledWith(conversation) - }) - }) - }) - }) -})