megalodon
Version:
Fediverse API client for node.js and browser
1,502 lines (1,501 loc) • 54.9 kB
JavaScript
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: [],