From 30949a9dc5a99d5f28d0e275ed20d912376cf9d0 Mon Sep 17 00:00:00 2001
From: AkiraFukushima
Date: Sun, 5 Jan 2025 15:14:18 +0900
Subject: [PATCH] Add Pixelfed client
---
.../typescript/src/pixelfed/authorization.ts | 44 +
example/typescript/src/pixelfed/instance.ts | 9 +
example/typescript/src/pixelfed/timeline.ts | 11 +
megalodon/src/detector.ts | 8 +-
megalodon/src/index.ts | 2 +
megalodon/src/mastodon/api_client.ts | 2 +-
megalodon/src/mastodon/web_socket.ts | 4 +-
megalodon/src/megalodon.ts | 9 +-
megalodon/src/pixelfed.ts | 3112 +++++++++++++++++
megalodon/src/pixelfed/api_client.ts | 607 ++++
megalodon/src/pixelfed/entities/account.ts | 27 +
.../src/pixelfed/entities/announcement.ts | 39 +
.../src/pixelfed/entities/application.ts | 5 +
.../src/pixelfed/entities/async_attachment.ts | 13 +
megalodon/src/pixelfed/entities/attachment.ts | 47 +
megalodon/src/pixelfed/entities/context.ts | 6 +
.../src/pixelfed/entities/conversation.ts | 9 +
megalodon/src/pixelfed/entities/emoji.ts | 7 +
megalodon/src/pixelfed/entities/field.ts | 5 +
megalodon/src/pixelfed/entities/filter.ts | 10 +
megalodon/src/pixelfed/entities/history.ts | 5 +
megalodon/src/pixelfed/entities/instance.ts | 44 +
megalodon/src/pixelfed/entities/marker.ts | 12 +
megalodon/src/pixelfed/entities/mention.ts | 6 +
.../src/pixelfed/entities/notification.ts | 12 +
megalodon/src/pixelfed/entities/poll.ts | 14 +
.../src/pixelfed/entities/preferences.ts | 9 +
.../src/pixelfed/entities/relationship.ts | 16 +
megalodon/src/pixelfed/entities/report.ts | 15 +
megalodon/src/pixelfed/entities/results.ts | 9 +
.../src/pixelfed/entities/scheduled_status.ts | 9 +
megalodon/src/pixelfed/entities/source.ts | 9 +
megalodon/src/pixelfed/entities/stats.ts | 5 +
megalodon/src/pixelfed/entities/status.ts | 43 +
.../src/pixelfed/entities/status_params.ts | 12 +
megalodon/src/pixelfed/entities/tag.ts | 8 +
megalodon/src/pixelfed/entities/token.ts | 6 +
megalodon/src/pixelfed/entity.ts | 62 +
megalodon/src/pixelfed/notification.ts | 11 +
megalodon/src/pixelfed/oauth.ts | 42 +
megalodon/src/pixelfed/web_socket.ts | 341 ++
megalodon/test/integration/detector.spec.ts | 8 +
42 files changed, 4668 insertions(+), 6 deletions(-)
create mode 100644 example/typescript/src/pixelfed/authorization.ts
create mode 100644 example/typescript/src/pixelfed/instance.ts
create mode 100644 example/typescript/src/pixelfed/timeline.ts
create mode 100644 megalodon/src/pixelfed.ts
create mode 100644 megalodon/src/pixelfed/api_client.ts
create mode 100644 megalodon/src/pixelfed/entities/account.ts
create mode 100644 megalodon/src/pixelfed/entities/announcement.ts
create mode 100644 megalodon/src/pixelfed/entities/application.ts
create mode 100644 megalodon/src/pixelfed/entities/async_attachment.ts
create mode 100644 megalodon/src/pixelfed/entities/attachment.ts
create mode 100644 megalodon/src/pixelfed/entities/context.ts
create mode 100644 megalodon/src/pixelfed/entities/conversation.ts
create mode 100644 megalodon/src/pixelfed/entities/emoji.ts
create mode 100644 megalodon/src/pixelfed/entities/field.ts
create mode 100644 megalodon/src/pixelfed/entities/filter.ts
create mode 100644 megalodon/src/pixelfed/entities/history.ts
create mode 100644 megalodon/src/pixelfed/entities/instance.ts
create mode 100644 megalodon/src/pixelfed/entities/marker.ts
create mode 100644 megalodon/src/pixelfed/entities/mention.ts
create mode 100644 megalodon/src/pixelfed/entities/notification.ts
create mode 100644 megalodon/src/pixelfed/entities/poll.ts
create mode 100644 megalodon/src/pixelfed/entities/preferences.ts
create mode 100644 megalodon/src/pixelfed/entities/relationship.ts
create mode 100644 megalodon/src/pixelfed/entities/report.ts
create mode 100644 megalodon/src/pixelfed/entities/results.ts
create mode 100644 megalodon/src/pixelfed/entities/scheduled_status.ts
create mode 100644 megalodon/src/pixelfed/entities/source.ts
create mode 100644 megalodon/src/pixelfed/entities/stats.ts
create mode 100644 megalodon/src/pixelfed/entities/status.ts
create mode 100644 megalodon/src/pixelfed/entities/status_params.ts
create mode 100644 megalodon/src/pixelfed/entities/tag.ts
create mode 100644 megalodon/src/pixelfed/entities/token.ts
create mode 100644 megalodon/src/pixelfed/entity.ts
create mode 100644 megalodon/src/pixelfed/notification.ts
create mode 100644 megalodon/src/pixelfed/oauth.ts
create mode 100644 megalodon/src/pixelfed/web_socket.ts
diff --git a/example/typescript/src/pixelfed/authorization.ts b/example/typescript/src/pixelfed/authorization.ts
new file mode 100644
index 000000000..c1e0e2dc1
--- /dev/null
+++ b/example/typescript/src/pixelfed/authorization.ts
@@ -0,0 +1,44 @@
+import * as readline from 'readline'
+import generator, { OAuth } from 'megalodon'
+
+const rl: readline.ReadLine = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout
+})
+
+const SCOPES: Array = ['read', 'write', 'follow']
+const BASE_URL: string = process.env.PIXELFED_URL!
+
+let clientId: string
+let clientSecret: string
+
+const client = generator('pixelfed', BASE_URL)
+
+client
+ .registerApp('Test App', {
+ scopes: SCOPES
+ })
+ .then(appData => {
+ clientId = appData.client_id
+ clientSecret = appData.client_secret
+ console.log('Authorization URL is generated.')
+ console.log(appData.url)
+ console.log()
+ return new Promise(resolve => {
+ rl.question('Enter the authorization code from website: ', code => {
+ resolve(code)
+ rl.close()
+ })
+ })
+ })
+ .then((code: string) => {
+ return client.fetchAccessToken(clientId, clientSecret, code)
+ })
+ .then((tokenData: OAuth.TokenData) => {
+ console.log('\naccess_token:')
+ console.log(tokenData.access_token)
+ console.log('\nrefresh_token:')
+ console.log(tokenData.refresh_token)
+ console.log()
+ })
+ .catch((err: Error) => console.error(err))
diff --git a/example/typescript/src/pixelfed/instance.ts b/example/typescript/src/pixelfed/instance.ts
new file mode 100644
index 000000000..272f44a3d
--- /dev/null
+++ b/example/typescript/src/pixelfed/instance.ts
@@ -0,0 +1,9 @@
+import generator, { Entity, Response } from 'megalodon'
+
+const BASE_URL: string = process.env.PIXELFED_URL!
+
+const client = generator('pixelfed', BASE_URL)
+
+client.getInstance().then((res: Response) => {
+ console.log(res.data)
+})
diff --git a/example/typescript/src/pixelfed/timeline.ts b/example/typescript/src/pixelfed/timeline.ts
new file mode 100644
index 000000000..88339f577
--- /dev/null
+++ b/example/typescript/src/pixelfed/timeline.ts
@@ -0,0 +1,11 @@
+import generator, { MegalodonInterface, Entity, Response } from 'megalodon'
+
+const BASE_URL: string = process.env.PIXELFED_URL!
+
+const access_token: string = process.env.PIXELFED_ACCESS_TOKEN!
+
+const client: MegalodonInterface = generator('pixelfed', BASE_URL, access_token)
+
+client.getPublicTimeline().then((resp: Response>) => {
+ console.log(resp.data)
+})
diff --git a/megalodon/src/detector.ts b/megalodon/src/detector.ts
index 7cc792626..cca66a9e2 100644
--- a/megalodon/src/detector.ts
+++ b/megalodon/src/detector.ts
@@ -47,7 +47,7 @@ type Metadata = {
* @param proxyConfig Proxy setting, or set false if don't use proxy.
* @return SNS name.
*/
-export const detector = async (url: string): Promise<'mastodon' | 'pleroma' | 'friendica' | 'firefish' | 'gotosocial'> => {
+export const detector = async (url: string): Promise<'mastodon' | 'pleroma' | 'friendica' | 'firefish' | 'gotosocial' | 'pixelfed'> => {
const options: AxiosRequestConfig = {
timeout: 20000
}
@@ -73,6 +73,8 @@ export const detector = async (url: string): Promise<'mastodon' | 'pleroma' | 'f
return 'firefish'
case 'mastodon':
return 'mastodon'
+ case 'pixelfed':
+ return 'pixelfed'
case 'pleroma':
return 'pleroma'
case 'sharkey':
@@ -101,6 +103,8 @@ export const detector = async (url: string): Promise<'mastodon' | 'pleroma' | 'f
return 'firefish'
case 'mastodon':
return 'mastodon'
+ case 'pixelfed':
+ return 'pixelfed'
case 'pleroma':
return 'pleroma'
case 'sharkey':
@@ -129,6 +133,8 @@ export const detector = async (url: string): Promise<'mastodon' | 'pleroma' | 'f
return 'firefish'
case 'mastodon':
return 'mastodon'
+ case 'pixelfed':
+ return 'pixelfed'
case 'pleroma':
return 'pleroma'
case 'sharkey':
diff --git a/megalodon/src/index.ts b/megalodon/src/index.ts
index b3de72ce1..a5a32155a 100644
--- a/megalodon/src/index.ts
+++ b/megalodon/src/index.ts
@@ -5,6 +5,7 @@ import generator, { MegalodonInterface, WebSocketInterface } from './megalodon'
import { detector } from './detector'
import Mastodon from './mastodon'
import Pleroma from './pleroma'
+import Pixelfed from './pixelfed'
import Firefish from './firefish'
import Gotosocial from './gotosocial'
import Entity from './entity'
@@ -23,6 +24,7 @@ export {
FilterContext,
Mastodon,
Pleroma,
+ Pixelfed,
Firefish,
Gotosocial,
Entity
diff --git a/megalodon/src/mastodon/api_client.ts b/megalodon/src/mastodon/api_client.ts
index fa7291aee..290f42f52 100644
--- a/megalodon/src/mastodon/api_client.ts
+++ b/megalodon/src/mastodon/api_client.ts
@@ -368,7 +368,7 @@ namespace MastodonAPI {
* Get connection and receive websocket connection for Pleroma API.
*
* @param url Streaming url.
- * @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 stream Stream name
* @returns WebSocket, which inherits from EventEmitter
*/
public socket(url: string, stream: string, params?: string): Streaming {
diff --git a/megalodon/src/mastodon/web_socket.ts b/megalodon/src/mastodon/web_socket.ts
index 4940a6dbf..a5bd4b1bd 100644
--- a/megalodon/src/mastodon/web_socket.ts
+++ b/megalodon/src/mastodon/web_socket.ts
@@ -27,8 +27,8 @@ export default class Streaming extends EventEmitter implements WebSocketInterfac
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 url Full url of websocket: e.g. https://mastodon.social/api/v1/streaming
+ * @param stream Stream name
* @param accessToken The access token.
* @param userAgent The specified User Agent.
*/
diff --git a/megalodon/src/megalodon.ts b/megalodon/src/megalodon.ts
index 97adf87e3..d84a11349 100644
--- a/megalodon/src/megalodon.ts
+++ b/megalodon/src/megalodon.ts
@@ -6,6 +6,7 @@ import Entity from './entity'
import Friendica from './friendica'
import Firefish from './firefish'
import Gotosocial from './gotosocial'
+import Pixelfed from './pixelfed'
export interface WebSocketInterface {
start(): void
@@ -1436,14 +1437,14 @@ export class NodeinfoError extends Error {
/**
* Get client for each SNS according to megalodon interface.
*
- * @param sns Name of your SNS, `mastodon`, `pleroma`, `firefish`, or `gotosocial`.
+ * @param sns Name of your SNS, `mastodon`, `pleroma`, `firefish`, `gotosocial`, or `pixelfed`.
* @param baseUrl hostname or base URL.
* @param accessToken access token from OAuth2 authorization
* @param userAgent UserAgent is specified in header on request.
* @return Client instance for each SNS you specified.
*/
const generator = (
- sns: 'mastodon' | 'pleroma' | 'friendica' | 'firefish' | 'gotosocial',
+ sns: 'mastodon' | 'pleroma' | 'friendica' | 'firefish' | 'gotosocial' | 'pixelfed',
baseUrl: string,
accessToken: string | null = null,
userAgent: string | null = null
@@ -1469,6 +1470,10 @@ const generator = (
const gotosocial = new Gotosocial(baseUrl, accessToken, userAgent)
return gotosocial
}
+ case 'pixelfed': {
+ const pixelfed = new Pixelfed(baseUrl, accessToken, userAgent)
+ return pixelfed
+ }
}
}
diff --git a/megalodon/src/pixelfed.ts b/megalodon/src/pixelfed.ts
new file mode 100644
index 000000000..eff90d636
--- /dev/null
+++ b/megalodon/src/pixelfed.ts
@@ -0,0 +1,3112 @@
+import { OAuth2Client } from '@badgateway/oauth2-client'
+import FormData from 'form-data'
+import dayjs from 'dayjs'
+
+import { parseLinkHeader } from './parse_link_header'
+import OAuth from './oauth'
+import PixelfedAPI from './pixelfed/api_client'
+import { MegalodonInterface, NotImplementedError, WebSocketInterface } from './megalodon'
+import * as PixelfedOAuth from './pixelfed/oauth'
+import Entity from './entity'
+import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default'
+import Response from './response'
+import { UnknownNotificationTypeError } from './notification'
+
+export default class Pixelfed implements MegalodonInterface {
+ public client: PixelfedAPI.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.
+ */
+ constructor(baseUrl: string, accessToken: string | null = null, userAgent: string | null = DEFAULT_UA) {
+ let token: string = ''
+ if (accessToken) {
+ token = accessToken
+ }
+ let agent: string = DEFAULT_UA
+ if (userAgent) {
+ agent = userAgent
+ }
+ this.client = new PixelfedAPI.Client(baseUrl, token, agent)
+ 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) => PixelfedOAuth.toAppData(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 | null }>
+ ): Promise {
+ const scope = options.scope || DEFAULT_SCOPE
+ const redirect_uri = options.redirect_uri || NO_REDIRECT
+ return new Promise(resolve => {
+ const oauthClient = new OAuth2Client({
+ server: this.baseUrl,
+ clientId: clientId,
+ clientSecret: clientSecret,
+ tokenEndpoint: '/oauth/token',
+ authorizationEndpoint: '/oauth/authorize'
+ })
+ const url = oauthClient.authorizationCode.getAuthorizeUri({
+ redirectUri: redirect_uri,
+ scope: scope
+ })
+ 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) => PixelfedOAuth.toTokenData(res.data))
+ }
+
+ /**
+ * POST /oauth/token
+ *
+ * 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) => PixelfedOAuth.toTokenData(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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.Converter.status(s))
+ })
+ })
+ }
+
+ public getAccountFavourites(
+ _id: string,
+ _options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ }
+ ): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ 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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.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'], undefined, undefined, true)
+ converted = Object.assign({}, converted, {
+ data: [...converted.data, ...nextRes.data.map(a => PixelfedAPI.Converter.account(a))]
+ })
+ if (nextRes.headers.link === undefined) {
+ break
+ }
+ 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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/identity_proofs
+ *
+ * @param id The account ID.
+ * @return Array of IdentityProof
+ */
+ public async getIdentityProof(_id: string): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 NotImplementedError('Pixelfed does not support this method')
+ 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 NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/note
+ *
+ * @param id
+ * @param note
+ * @return Relationship
+ */
+ public async setAccountNote(_id: string, _note?: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ 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: PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.Converter.account(a))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/lookup
+ *
+ * @param acct The username or Webfinger address to lookup.
+ * @return Account.
+ */
+ public async lookupAccount(_acct: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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 => PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.Converter.account(a))
+ })
+ })
+ } else {
+ return this.client.get>('/api/v1/follow_requests').then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.Converter.account(a))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/featured_tags
+ // ======================================
+ /**
+ * GET /api/v1/featured_tags
+ *
+ * @return Array of featured tag.
+ */
+ public async getFeaturedTags(): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/featured_tags
+ *
+ * @param name Target hashtag name.
+ * @return FeaturedTag.
+ */
+ public async createFeaturedTag(_name: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * DELETE /api/v1/featured_tags/:id
+ *
+ * @param id Target featured tag id.
+ * @return Empty
+ */
+ public deleteFeaturedTag(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/featured_tags/suggestions
+ *
+ * @return Array of tag.
+ */
+ public async getSuggestedTags(): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ 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: PixelfedAPI.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 => PixelfedAPI.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>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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?: Entity.StatusVisibility
+ scheduled_at?: string
+ language?: string
+ quote_id?: string
+ }
+ ): Promise> {
+ let params = {
+ status: status
+ }
+ let scheduled = false
+ 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 && dayjs(options.scheduled_at).diff(dayjs(), 'seconds') > 300) {
+ scheduled = true
+ 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 (scheduled) {
+ return this.client.post('/api/v1/statuses', params).then(res => {
+ return Object.assign(res, {
+ data: PixelfedAPI.Converter.scheduled_status(res.data)
+ })
+ })
+ }
+ return this.client.post('/api/v1/statuses', params).then(res => {
+ return Object.assign(res, {
+ data: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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 => PixelfedAPI.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 => PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unmute
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async unmuteStatus(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/pin
+ * @param id The target status id.
+ * @return Status
+ */
+ public async pinStatus(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unpin
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async unpinStatus(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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 => PixelfedAPI.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>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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 => PixelfedAPI.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: PixelfedAPI.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: []
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/lists/:id
+ *
+ * @param id Target list ID.
+ * @return List.
+ */
+ public async getList(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/lists
+ *
+ * @param title List name.
+ * @return List.
+ */
+ public async createList(_title: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * DELETE /api/v1/lists/:id
+ *
+ * @param id Target list ID.
+ */
+ public deleteList(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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: PixelfedAPI.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: PixelfedAPI.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 => PixelfedAPI.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 = PixelfedAPI.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> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/notifications/clear
+ */
+ public dismissNotifications(): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/notifications/:id/dismiss
+ *
+ * @param id Target notification ID.
+ */
+ public dismissNotification(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public readNotifications(_options: { id?: string; max_id?: string }): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ 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> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/push/subscription
+ *
+ * @return PushSubscription.
+ */
+ public async getPushSubscription(): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * 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> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ /**
+ * DELETE /api/v1/push/subscription
+ */
+ public deletePushSubscription(): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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: PixelfedAPI.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: PixelfedAPI.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 new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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 => PixelfedAPI.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>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // 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 => PixelfedAPI.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 => PixelfedAPI.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 NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async deleteEmojiReaction(_id: string, _emoji: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async getEmojiReactions(_id: string): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async getEmojiReaction(_id: string, _emoji: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // WebSocket
+ // ======================================
+ public async streamingURL(): Promise {
+ const instance = await this.getInstance()
+ if (instance.data.urls) {
+ return instance.data.urls.streaming_api
+ }
+ return this.baseUrl
+ }
+
+ public async userStreaming(): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async publicStreaming(): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async localStreaming(): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async tagStreaming(_tag: string): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async listStreaming(_list_id: string): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+
+ public async directStreaming(): Promise {
+ return new Promise((_, reject) => {
+ const err = new NotImplementedError('Pixelfed does not support this method')
+ reject(err)
+ })
+ }
+}
diff --git a/megalodon/src/pixelfed/api_client.ts b/megalodon/src/pixelfed/api_client.ts
new file mode 100644
index 000000000..5236fddcd
--- /dev/null
+++ b/megalodon/src/pixelfed/api_client.ts
@@ -0,0 +1,607 @@
+import axios, { AxiosResponse, AxiosRequestConfig } from 'axios'
+import objectAssignDeep from 'object-assign-deep'
+
+import Response from '../response'
+import { RequestCanceledError } from '../cancel'
+import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from '../default'
+import PixelfedEntity from './entity'
+import MegalodonEntity from '../entity'
+import NotificationType, { UnknownNotificationTypeError } from '../notification'
+import PixelfedNotificationType from './notification'
+
+namespace PixelfedAPI {
+ /**
+ * 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