UNPKG

megalodon

Version:

Fediverse API client for node.js and browser

1,502 lines (1,501 loc) 54.9 kB
import FormData from 'form-data'; import FirefishAPI from './firefish/api_client.js'; import { DEFAULT_UA } from './default.js'; import * as FirefishOAuth from './firefish/oauth.js'; import { NotImplementedError, ArgumentError, UnexpectedError } from './megalodon.js'; import { UnknownNotificationTypeError } from './notification.js'; export default class Firefish { 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 FirefishAPI.Client(baseUrl, token, agent); this.baseUrl = baseUrl; } cancel() { return this.client.cancel(); } async registerApp(client_name, options = { scopes: FirefishAPI.DEFAULT_SCOPE, redirect_uris: this.baseUrl }) { return this.createApp(client_name, options).then(async (appData) => { return this.generateAuthUrlAndToken(appData.client_secret).then(session => { appData.url = session.url; appData.session_token = session.token; return appData; }); }); } async createApp(client_name, options = { scopes: FirefishAPI.DEFAULT_SCOPE, redirect_uris: this.baseUrl }) { const redirect_uris = options.redirect_uris || this.baseUrl; const scopes = options.scopes || FirefishAPI.DEFAULT_SCOPE; const params = { name: client_name, description: '', permission: scopes, callbackUrl: redirect_uris }; return this.client.post('/api/app/create', params).then((res) => { return FirefishOAuth.toAppData(res.data); }); } async generateAuthUrlAndToken(clientSecret) { return this.client .post('/api/auth/session/generate', { appSecret: clientSecret }) .then((res) => res.data); } async verifyAppCredentials() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async fetchAccessToken(_client_id, client_secret, session_token, _redirect_uri) { return this.client .post('/api/auth/session/userkey', { appSecret: client_secret, token: session_token }) .then(res => { return FirefishOAuth.toTokenData(res.data); }); } async refreshToken(_client_id, _client_secret, _refresh_token) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async revokeToken(_client_id, _client_secret, _token) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async registerAccount(_username, _email, _password, _agreement, _locale, _reason) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async verifyAccountCredentials() { return this.client.post('/api/i').then(res => { return Object.assign(res, { data: FirefishAPI.Converter.userDetail(res.data) }); }); } async updateCredentials(options) { let params = {}; if (options) { if (options.bot !== undefined) { params = Object.assign(params, { isBot: options.bot }); } if (options.display_name) { params = Object.assign(params, { name: options.display_name }); } if (options.note) { params = Object.assign(params, { description: options.note }); } if (options.locked !== undefined) { params = Object.assign(params, { isLocked: options.locked }); } if (options.source) { if (options.source.language) { params = Object.assign(params, { lang: options.source.language }); } if (options.source.sensitive) { params = Object.assign(params, { alwaysMarkNsfw: options.source.sensitive }); } } } return this.client.post('/api/i', params).then(res => { return Object.assign(res, { data: FirefishAPI.Converter.userDetail(res.data) }); }); } async getAccount(id) { return this.client .post('/api/users/show', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.userDetail(res.data) }); }); } async getAccountStatuses(id, options) { if (options && options.pinned) { return this.client .post('/api/users/show', { userId: id }) .then(res => { if (res.data.pinnedNotes) { return { ...res, data: res.data.pinnedNotes.map(n => FirefishAPI.Converter.note(n)) }; } return { ...res, data: [] }; }); } let params = { userId: id }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.exclude_replies) { params = Object.assign(params, { includeReplies: false }); } if (options.exclude_reblogs) { params = Object.assign(params, { includeMyRenotes: false }); } if (options.only_media) { params = Object.assign(params, { withFiles: options.only_media }); } } return this.client.post('/api/users/notes', params).then(res => { const statuses = res.data.map(note => FirefishAPI.Converter.note(note)); return Object.assign(res, { data: statuses }); }); } async getAccountFavourites(_id, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async subscribeAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unsubscribeAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getAccountFollowers(id, options) { let params = { userId: id }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } } return this.client.post('/api/users/followers', params).then(res => { return Object.assign(res, { data: res.data.map(f => FirefishAPI.Converter.follower(f)) }); }); } async getAccountFollowing(id, options) { let params = { userId: id }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } } return this.client.post('/api/users/following', params).then(res => { return Object.assign(res, { data: res.data.map(f => FirefishAPI.Converter.following(f)) }); }); } async getAccountLists(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getIdentityProof(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async followAccount(id, _options) { await this.client.post('/api/following/create', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async unfollowAccount(id) { await this.client.post('/api/following/delete', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async blockAccount(id) { await this.client.post('/api/blocking/create', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async unblockAccount(id) { await this.client.post('/api/blocking/delete', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async muteAccount(id, _notifications) { await this.client.post('/api/mute/create', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async unmuteAccount(id) { await this.client.post('/api/mute/delete', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async pinAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unpinAccount(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async setAccountNote(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getRelationship(id) { return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async getRelationships(ids) { return Promise.all(ids.map(id => this.getRelationship(id))).then(results => ({ ...results[0], data: results.map(r => r.data) })); } async searchAccount(q, options) { let params = { query: q, detail: true }; if (options) { if (options.resolve !== undefined) { params = Object.assign(params, { localOnly: options.resolve }); } if (options.limit) { params = Object.assign(params, { limit: options.limit }); } } return this.client.post('/api/users/search', params).then(res => { return Object.assign(res, { data: res.data.map(u => FirefishAPI.Converter.userDetail(u)) }); }); } async lookupAccount(_acct) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getBookmarks(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getFavourites(options) { let params = {}; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client.post('/api/i/favorites', params).then(res => { return Object.assign(res, { data: res.data.map(fav => FirefishAPI.Converter.note(fav.note)) }); }); } async getMutes(options) { let params = {}; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client.post('/api/mute/list', params).then(res => { return Object.assign(res, { data: res.data.map(mute => FirefishAPI.Converter.userDetail(mute.mutee)) }); }); } async getBlocks(options) { let params = {}; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client.post('/api/blocking/list', params).then(res => { return Object.assign(res, { data: res.data.map(blocking => FirefishAPI.Converter.userDetail(blocking.blockee)) }); }); } async getDomainBlocks(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async blockDomain(_domain) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unblockDomain(_domain) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getFilters() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getFilter(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async createFilter(_phrase, _context, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async updateFilter(_id, _phrase, _context, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async deleteFilter(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async report(account_id, options) { const category = 'other'; return this.client .post('/api/users/report-abuse', { userId: account_id, comment: options.comment }) .then(res => { return Object.assign(res, { data: { id: '', action_taken: false, action_taken_at: null, comment: options.comment, category: category, forwarded: false, status_ids: null, rule_ids: null } }); }); } async getFollowRequests(_limit) { return this.client.post('/api/following/requests/list').then(res => { return Object.assign(res, { data: res.data.map(r => FirefishAPI.Converter.user(r.follower)) }); }); } async acceptFollowRequest(id) { await this.client.post('/api/following/requests/accept', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async rejectFollowRequest(id) { await this.client.post('/api/following/requests/reject', { userId: id }); return this.client .post('/api/users/relation', { userId: id }) .then(res => { return Object.assign(res, { data: FirefishAPI.Converter.relation(res.data) }); }); } async getEndorsements(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getFeaturedTags() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async createFeaturedTag(_name) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async deleteFeaturedTag(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getSuggestedTags() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getPreferences() { return this.client.post('/api/i').then(async (res) => { return Object.assign(res, { data: FirefishAPI.Converter.userPreferences(res.data, await this.getDefaultPostPrivacy()) }); }); } async getDefaultPostPrivacy() { return this.client .post('/api/i/registry/get-unsecure', { key: 'defaultNoteVisibility', scope: ['client', 'base'] }) .then(res => { if (!res.data || (res.data != 'public' && res.data != 'home' && res.data != 'followers' && res.data != 'specified')) return 'public'; return FirefishAPI.Converter.visibility(res.data); }) .catch(_ => 'public'); } async getFollowedTags() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getSuggestions(limit) { let params = {}; if (limit) { params = Object.assign(params, { limit: limit }); } return this.client .post('/api/users/recommendation', params) .then(res => ({ ...res, data: res.data.map(u => FirefishAPI.Converter.userDetail(u)) })); } async getTag(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async followTag(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unfollowTag(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async postStatus(status, options) { let params = { text: status }; if (options) { if (options.media_ids) { params = Object.assign(params, { fileIds: options.media_ids }); } if (options.poll) { let pollParam = { choices: options.poll.options, expiresAt: null, expiredAfter: options.poll.expires_in }; if (options.poll.multiple !== undefined) { pollParam = Object.assign(pollParam, { multiple: options.poll.multiple }); } params = Object.assign(params, { poll: pollParam }); } if (options.in_reply_to_id) { params = Object.assign(params, { replyId: options.in_reply_to_id }); } if (options.spoiler_text) { params = Object.assign(params, { cw: options.spoiler_text }); } if (options.visibility) { params = Object.assign(params, { visibility: FirefishAPI.Converter.encodeVisibility(options.visibility) }); } if (options.quote_id) { params = Object.assign(params, { renoteId: options.quote_id }); } } return this.client .post('/api/notes/create', params) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data.createdNote) })); } async getStatus(id) { return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } async editStatus(_id, _options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async deleteStatus(id) { return this.client.post('/api/notes/delete', { noteId: id }); } async getStatusContext(id, options) { let paramsDescendants = { noteId: id }; if (options) { if (options.limit) { paramsDescendants = Object.assign(paramsDescendants, { limit: options.limit }); } if (options.max_id) { paramsDescendants = Object.assign(paramsDescendants, { untilId: options.max_id }); } if (options.since_id) { paramsDescendants = Object.assign(paramsDescendants, { sinceId: options.since_id }); } } let paramsAncestors = { noteId: id }; if (options) { if (options.limit) { paramsAncestors = Object.assign(paramsDescendants, { limit: options.limit }); } } const ancestorsPromise = this.client.post('/api/notes/children', paramsAncestors); const descendantsRes = await this.client.post('/api/notes/conversation', paramsDescendants); const context = { ancestors: (await ancestorsPromise).data.map(n => FirefishAPI.Converter.note(n)).reverse(), descendants: descendantsRes.data.map(n => FirefishAPI.Converter.note(n)) }; return { ...descendantsRes, data: context }; } async getStatusSource(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getStatusRebloggedBy(id) { return this.client .post('/api/notes/renotes', { noteId: id }) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.user(n.user)) })); } async getStatusFavouritedBy(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async favouriteStatus(id) { await this.client.post('/api/notes/favorites/create', { noteId: id }); return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } async unfavouriteStatus(id) { await this.client.post('/api/notes/favorites/delete', { noteId: id }); return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } async reblogStatus(id) { return this.client .post('/api/notes/create', { renoteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data.createdNote) })); } async unreblogStatus(id) { await this.client.post('/api/notes/unrenote', { noteId: id }); return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } async bookmarkStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unbookmarkStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async muteStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async unmuteStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async pinStatus(id) { await this.client.post('/api/i/pin', { noteId: id }); return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } async unpinStatus(id) { await this.client.post('/api/i/unpin', { noteId: id }); return this.client .post('/api/notes/show', { noteId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.note(res.data) })); } reactionName(name) { const isUnicodeEmoji = /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu.test(name); if (isUnicodeEmoji) { return name; } return `:${name}:`; } async uploadMedia(file, _options) { const formData = new FormData(); formData.append('file', file); let headers = {}; if (typeof formData.getHeaders === 'function') { headers = formData.getHeaders(); } return this.client .post('/api/drive/files/create', formData, headers) .then(res => ({ ...res, data: FirefishAPI.Converter.file(res.data) })); } async getMedia(id) { const res = await this.client.post('/api/drive/files/show', { fileId: id }); return { ...res, data: FirefishAPI.Converter.file(res.data) }; } async updateMedia(id, options) { let params = { fileId: id }; if (options) { if (options.is_sensitive !== undefined) { params = Object.assign(params, { isSensitive: options.is_sensitive }); } } return this.client .post('/api/drive/files/update', params) .then(res => ({ ...res, data: FirefishAPI.Converter.file(res.data) })); } async getPoll(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async votePoll(_id, choices, status_id) { if (!status_id) { return new Promise((_, reject) => { const err = new ArgumentError('status_id is required'); reject(err); }); } const params = { noteId: status_id, choice: choices[0] }; await this.client.post('/api/notes/polls/vote', params); const res = await this.client .post('/api/notes/show', { noteId: status_id }) .then(res => { const note = FirefishAPI.Converter.note(res.data); return { ...res, data: note.poll }; }); if (!res.data) { return new Promise((_, reject) => { const err = new UnexpectedError('poll does not exist'); reject(err); }); } return { ...res, data: res.data }; } async getScheduledStatuses(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getScheduledStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async scheduleStatus(_id, _scheduled_at) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async cancelScheduledStatus(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getPublicTimeline(options) { let params = {}; if (options) { if (options.only_media !== undefined) { params = Object.assign(params, { withFiles: options.only_media }); } if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/global-timeline', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.note(n)) })); } async getLocalTimeline(options) { let params = {}; if (options) { if (options.only_media !== undefined) { params = Object.assign(params, { withFiles: options.only_media }); } if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/local-timeline', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.note(n)) })); } async getTagTimeline(hashtag, options) { let params = { tag: hashtag }; if (options) { if (options.only_media !== undefined) { params = Object.assign(params, { withFiles: options.only_media }); } if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/search-by-tag', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.note(n)) })); } async getHomeTimeline(options) { let params = { withFiles: false }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/timeline', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.note(n)) })); } async getListTimeline(list_id, options) { let params = { listId: list_id, withFiles: false }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/user-list-timeline', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.note(n)) })); } async getConversationTimeline(options) { let params = { visibility: 'specified' }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } } return this.client .post('/api/notes/mentions', params) .then(res => ({ ...res, data: res.data.map(n => FirefishAPI.Converter.noteToConversation(n)) })); } async deleteConversation(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async readConversation(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getLists() { return this.client .post('/api/users/lists/list') .then(res => ({ ...res, data: res.data.map(l => FirefishAPI.Converter.list(l)) })); } async getList(id) { return this.client .post('/api/users/lists/show', { listId: id }) .then(res => ({ ...res, data: FirefishAPI.Converter.list(res.data) })); } async createList(title) { return this.client .post('/api/users/lists/create', { name: title }) .then(res => ({ ...res, data: FirefishAPI.Converter.list(res.data) })); } async updateList(id, title) { return this.client .post('/api/users/lists/update', { listId: id, name: title }) .then(res => ({ ...res, data: FirefishAPI.Converter.list(res.data) })); } async deleteList(id) { return this.client.post('/api/users/lists/delete', { listId: id }); } async getAccountsInList(id, _options) { const res = await this.client.post('/api/users/lists/show', { listId: id }); const promise = res.data.userIds?.map(userId => this.getAccount(userId)); if (promise) { const accounts = await Promise.all(promise); return { ...res, data: accounts.map(r => r.data) }; } else { return { ...res, data: [] }; } } async addAccountsToList(id, account_ids) { return this.client.post('/api/users/lists/push', { listId: id, userId: account_ids[0] }); } async deleteAccountsFromList(id, account_ids) { return this.client.post('/api/users/lists/pull', { listId: id, userId: account_ids[0] }); } async getMarkers(_timeline) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async saveMarkers(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } 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, { untilId: options.max_id }); } if (options.since_id) { params = Object.assign(params, { sinceId: options.since_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } if (options.exclude_type) { params = Object.assign(params, { excludeType: options.exclude_type.map(e => FirefishAPI.Converter.encodeNotificationType(e)) }); } } const res = await this.client.post('/api/i/notifications', params); const notifications = res.data.flatMap(n => { const notify = FirefishAPI.Converter.notification(n); if (notify instanceof UnknownNotificationTypeError) { return []; } return notify; }); return { ...res, data: notifications }; } async getNotification(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async dismissNotifications() { return this.client.post('/api/notifications/mark-all-as-read'); } async dismissNotification(_id) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async readNotifications(_options) { return new Promise((_, reject) => { const err = new NotImplementedError('mastodon does not support'); reject(err); }); } async subscribePushNotification(_subscription, _data) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async getPushSubscription() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async updatePushSubscription(_data) { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async deletePushSubscription() { return new Promise((_, reject) => { const err = new NotImplementedError('Firefish does not support this method'); reject(err); }); } async searchAccounts(q, options) { let params = { query: q }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.offset) { params = Object.assign(params, { offset: options.offset }); } if (options.resolve) { params = Object.assign(params, { localOnly: options.resolve }); } } const res = await this.client.post('/api/users/search', params); return res.data; } async searchStatuses(q, options) { let params = { query: q }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.offset) { params = Object.assign(params, { offset: options.offset }); } if (options.max_id) { params = Object.assign(params, { untilId: options.max_id }); } if (options.min_id) { params = Object.assign(params, { sinceId: options.min_id }); } if (options.account_id) { params = Object.assign(params, { userId: options.account_id }); } } const res = await this.client.post('/api/notes/search', params); return res.data; } async searchHashtags(q, options) { let params = { query: q }; if (options) { if (options.limit) { params = Object.assign(params, { limit: options.limit }); } if (options.offset) { params = Object.assign(params, { offset: options.offset }); } } const res = await this.client.post('/api/hashtags/search', params); return res.data; } async searchAll(q, options) { let accounts = []; try { accounts = await this.searchAccounts(q, options); } catch (e) { console.warn(e); } let statuses = []; try { statuses = await this.searchStatuses(q, options); } catch (e) { console.warn(e); } let hashtags = []; try { hashtags = await this.searchHashtags(q, options); } catch (e) { console.warn(e); } return { data: { accounts: accounts.map(a => FirefishAPI.Converter.userDetail(a)), statuses: statuses.map(n => FirefishAPI.Converter.note(n)), hashtags: hashtags.map(h => ({ name: h, url: h, history: [], following: false })) }, status: 200, statusText: '200', headers: null }; } async search(q, options) { if (options) { switch (options.type) { case 'accounts': { const accounts = await this.searchAccounts(q, options); return { data: { accounts: accounts.map(a => FirefishAPI.Converter.userDetail(a)), statuses: [], hashtags: [] }, status: 200, statusText: '200', headers: null }; } case 'statuses': { const statuses = await this.searchStatuses(q, options); return { data: { accounts: [], statuses: statuses.map(n => FirefishAPI.Converter.note(n)), hashtags: [] }, status: 200, statusText: '200', headers: null }; } case 'hashtags': { const hashtags = await this.searchHashtags(q, options); return { data: { accounts: [], statuses: [],