UNPKG

@cutls/megalodon

Version:

Mastodon, Pleroma, Misskey API client for node.js and browser

1,780 lines (1,779 loc) 45.2 kB
import { OAuth2Client } from '@badgateway/oauth2-client' import FormData from 'form-data' import dayjs from 'dayjs' import { parseLinkHeader } from './parse_link_header.js' import PixelfedAPI from './pixelfed/api_client.js' import { NotImplementedError } from './megalodon.js' import * as PixelfedOAuth from './pixelfed/oauth.js' import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default.js' import { UnknownNotificationTypeError } from './notification.js' export default class Pixelfed { client baseUrl constructor(baseUrl, accessToken = null, userAgent = DEFAULT_UA) { let token = '' if (accessToken) { token = accessToken } let agent = DEFAULT_UA if (userAgent) { agent = userAgent } this.client = new PixelfedAPI.Client(baseUrl, token, agent) this.baseUrl = baseUrl } cancel() { return this.client.cancel() } async registerApp(client_name, options) { 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 }) }) } async createApp(client_name, options) { const scopes = options.scopes || DEFAULT_SCOPE const redirect_uris = options.redirect_uris || NO_REDIRECT const params = { 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) => PixelfedOAuth.toAppData(res.data)) } generateAuthUrl(clientId, clientSecret, options) { 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) }) } verifyAppCredentials() { return this.client.get('/api/v1/apps/verify_credentials') } async fetchAccessToken(client_id, client_secret, code, redirect_uri = NO_REDIRECT) { 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) => PixelfedOAuth.toTokenData(res.data)) } async refreshToken(client_id, client_secret, refresh_token) { return this.client .post('/oauth/token', { client_id, client_secret, refresh_token, grant_type: 'refresh_token' }) .then((res) => PixelfedOAuth.toTokenData(res.data)) } async revokeToken(client_id, client_secret, token) { return this.client.post('/oauth/revoke', { client_id, client_secret, token }) } async registerAccount(username, email, password, agreement, locale, reason) { 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) }) }) } async verifyAccountCredentials() { return this.client.get('/api/v1/accounts/verify_credentials').then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.account(res.data) }) }) } async updateCredentials(options) { 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) }) }) } async getAccount(id) { return this.client.get(`/api/v1/accounts/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.account(res.data) }) }) } async getAccountStatuses(id, options) { 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)) }) }) } getAccountFavourites(_id, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async subscribeAccount(id) { 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) }) }) } async unsubscribeAccount(id) { 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) }) }) } async getAccountFollowers(id, options) { 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) } async getAccountFollowing(id, options) { 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) } async urlToAccounts(url, params, get_all, sleep_ms) { 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 } async getAccountLists(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getIdentityProof(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async followAccount(id, options) { 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) }) }) } async unfollowAccount(id) { return this.client.post(`/api/v1/accounts/${id}/unfollow`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async blockAccount(id) { return this.client.post(`/api/v1/accounts/${id}/block`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async unblockAccount(id) { return this.client.post(`/api/v1/accounts/${id}/unblock`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async muteAccount(id, notifications = true) { return this.client .post(`/api/v1/accounts/${id}/mute`, { notifications: notifications }) .then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async unmuteAccount(id) { return this.client.post(`/api/v1/accounts/${id}/unmute`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async pinAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async unpinAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async setAccountNote(_id, _note) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getRelationship(id) { return this.client .get('/api/v1/accounts/relationships', { id: [id] }) .then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data[0]) }) }) } async getRelationships(ids) { 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)) }) }) } async searchAccount(q, options) { 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)) }) }) } async lookupAccount(_acct) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getBookmarks(options) { 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)) }) }) } async getFavourites(options) { 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)) }) }) } async getMutes(options) { 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)) }) }) } async getBlocks(options) { 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)) }) }) } async getDomainBlocks(options) { 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) } blockDomain(domain) { return this.client.post('/api/v1/domain_blocks', { domain: domain }) } unblockDomain(domain) { return this.client.del('/api/v1/domain_blocks', { domain: domain }) } async getFilters() { return this.client.get('/api/v1/filters').then((res) => { return Object.assign(res, { data: res.data.map((f) => PixelfedAPI.Converter.filter(f)) }) }) } async getFilter(id) { return this.client.get(`/api/v1/filters/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.filter(res.data) }) }) } async createFilter(phrase, context, options) { 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) }) }) } async updateFilter(id, phrase, context, options) { 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) }) }) } async deleteFilter(id) { return this.client.del(`/api/v1/filters/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.filter(res.data) }) }) } async report(account_id, options) { 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) }) }) } async getFollowRequests(limit) { 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)) }) }) } } async acceptFollowRequest(id) { return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async rejectFollowRequest(id) { return this.client.post(`/api/v1/follow_requests/${id}/reject`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.relationship(res.data) }) }) } async getEndorsements(options) { 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)) }) }) } async getFeaturedTags() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async createFeaturedTag(_name) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } deleteFeaturedTag(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getSuggestedTags() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getPreferences() { return this.client.get('/api/v1/preferences').then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.preferences(res.data) }) }) } async getFollowedTags() { return this.client.get('/api/v1/followed_tags').then((res) => { return Object.assign(res, { data: res.data.map((tag) => PixelfedAPI.Converter.tag(tag)) }) }) } async getSuggestions(_limit) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getTag(id) { return this.client.get(`/api/v1/tags/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.tag(res.data) }) }) } async followTag(id) { return this.client.post(`/api/v1/tags/${id}/follow`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.tag(res.data) }) }) } async unfollowTag(id) { return this.client.post(`/api/v1/tags/${id}/unfollow`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.tag(res.data) }) }) } async postStatus(status, options) { 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: PixelfedAPI.Converter.encodeVisibility(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) }) }) } async getStatus(id) { return this.client.get(`/api/v1/statuses/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async editStatus(id, options) { 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) }) }) } async deleteStatus(id) { return this.client.del(`/api/v1/statuses/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async getStatusContext(id, options) { 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) }) }) } async getStatusSource(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getStatusRebloggedBy(id) { 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)) }) }) } async getStatusFavouritedBy(id) { 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)) }) }) } async favouriteStatus(id) { return this.client.post(`/api/v1/statuses/${id}/favourite`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async unfavouriteStatus(id) { return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async reblogStatus(id) { return this.client.post(`/api/v1/statuses/${id}/reblog`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async unreblogStatus(id) { return this.client.post(`/api/v1/statuses/${id}/unreblog`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async bookmarkStatus(id) { return this.client.post(`/api/v1/statuses/${id}/bookmark`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async unbookmarkStatus(id) { return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.status(res.data) }) }) } async muteStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async unmuteStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async pinStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async unpinStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async uploadMedia(file, options) { 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) }) }) } async getMedia(id) { const res = await this.client.get(`/api/v1/media/${id}`) return Object.assign(res, { data: PixelfedAPI.Converter.attachment(res.data) }) } async updateMedia(id, options) { 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) }) }) } async getPoll(id) { return this.client.get(`/api/v1/polls/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.poll(res.data) }) }) } async votePoll(id, choices) { return this.client .post(`/api/v1/polls/${id}/votes`, { choices: choices }) .then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.poll(res.data) }) }) } async getScheduledStatuses(options) { 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)) }) }) } async getScheduledStatus(id) { return this.client.get(`/api/v1/scheduled_statuses/${id}`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.scheduled_status(res.data) }) }) } async scheduleStatus(id, scheduled_at) { 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) }) }) } cancelScheduledStatus(id) { return this.client.del(`/api/v1/scheduled_statuses/${id}`) } async getPublicTimeline(options) { let params = { local: false, remote: 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)) }) }) } async getLocalTimeline(options) { 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)) }) }) } async getTagTimeline(hashtag, options) { 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)) }) }) } async getHomeTimeline(options) { 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)) }) }) } async getListTimeline(_list_id, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getConversationTimeline(options) { 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)) }) }) } deleteConversation(id) { return this.client.del(`/api/v1/conversations/${id}`) } async readConversation(id) { return this.client.post(`/api/v1/conversations/${id}/read`).then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.conversation(res.data) }) }) } async getLists() { return this.client.get('/api/v1/lists').then((res) => { return Object.assign(res, { data: [] }) }) } async getList(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async createList(_title) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async updateList(_id, _title) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } deleteList(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getAccountsInList(_id, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } addAccountsToList(_id, _account_ids) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } deleteAccountsFromList(_id, _account_ids) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getMarkers(timeline) { return this.client .get('/api/v1/markers', { timeline: timeline }) .then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.marker(res.data) }) }) } async saveMarkers(options) { 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) }) }) } async getNotifications(options) { 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 }) }) }) } async getNotification(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } dismissNotifications() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } dismissNotification(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } readNotifications(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async subscribePushNotification(_subscription, _data) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getPushSubscription() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async updatePushSubscription(_data) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } deletePushSubscription() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async search(q, options) { 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) }) }) } async getInstance() { return this.client.get('/api/v1/instance').then((res) => { return Object.assign(res, { data: PixelfedAPI.Converter.instance(res.data) }) }) } getInstancePeers() { return this.client.get('/api/v1/instance/peers') } async getInstanceActivity() { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getInstanceTrends(limit) { 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)) }) }) } async getInstanceDirectory(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getInstanceCustomEmojis() { return this.client.get('/api/v1/custom_emojis').then((res) => { return Object.assign(res, { data: res.data.map((e) => PixelfedAPI.Converter.emoji(e)) }) }) } async getInstanceAnnouncements() { return this.client.get('/api/v1/announcements').then((res) => { return Object.assign(res, { data: res.data.map((a) => PixelfedAPI.Converter.announcement(a)) }) }) } async dismissInstanceAnnouncement(id) { return this.client.post(`/api/v1/announcements/${id}/dismiss`) } async addReactionToAnnouncement(id, name) { return this.client.put(`/api/v1/announcements/${id}/reactions/${name}`) } async removeReactionFromAnnouncement(id, name) { return this.client.del(`/api/v1/announcements/${id}/reactions/${name}`) } async createEmojiReaction(_id, _emoji) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async deleteEmojiReaction(_id, _emoji) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getEmojiReactions(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async getEmojiReaction(_id, _emoji) { return new Promise((_, reject) => { const err = new NotImplementedError('Pixelfed does not support this method') reject(err) }) } async streamingURL() { const instance = await this.getInstance() if (instance.data.urls) { return instance.data.urls.streaming_api } return this.baseUrl } async userStreaming() { return this.client.socket() } async publicStreaming() { return this.client.socket() } async localStreaming() { return this.client.socket() } async tagStreaming(_tag) { return this.client.socket() } async listStreaming(_list_id) { return this.client.socket() } async directStreaming() { return this.client.socket() } async userStreamingSubscription() { return this.userStreaming() } async publicStreamingSubscription(socket) { socket.subscribe('public', 'globalTimeline') return socket } async localStreamingSubscription(socket) { socket.subscribe('public:local', 'localTimeline') return socket } async tagStreamingSubscription(socket, _tag) { console.warn('misskey does not support tag streaming') return socket } async listStreamingSubscription(socket, list_id) { socket.subscribe('list', 'userList', { list: list_id }) return socket } async directStreamingSubscription(socket) { console.warn('misskey does not support direct streaming') return socket } }