@cutls/megalodon
Version:
Mastodon, Pleroma, Misskey API client for node.js and browser
1,627 lines (1,626 loc) • 41.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) {
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
})
}
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: [],
hashtags: hashtags.map((h) => ({ name: h, url: h, history: [], following: false }))
},
status: 200,
statusText: '200',
headers: null
}
}
default: {
return this.searchAll(q, options)
}
}
} else {
return this.searchAll(q)
}
}
async getInstance() {
return this.client.get('/api/v1/instance').then((res) => ({ ...res, data: FirefishAPI.Converter.instance(res.data) }))
}
async getInstancePeers() {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async getInstanceActivity() {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async getInstanceTrends(_limit) {
return this.client.post('/api/hashtags/trend').then((res) => ({ ...res, data: res.data.map((h) => FirefishAPI.Converter.hashtag(h)) }))
}
async getInstanceDirectory(_options) {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async getInstanceCustomEmojis() {
return this.client.post('/api/meta').then((res) => ({ ...res, data: res.data.emojis.map((e) => FirefishAPI.Converter.emoji(e)) }))
}
async getInstanceAnnouncements() {
return this.client.post('/api/announcements').then((res) => ({ ...res, data: res.data.map((a) => FirefishAPI.Converter.announcement(a)) }))
}
async dismissInstanceAnnouncement(_id) {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async addReactionToAnnouncement(_id, _name) {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async removeReactionFromAnnouncement(_id, _name) {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish does not support this method')
reject(err)
})
}
async createEmojiReaction(id, emoji) {
await this.client.post('/api/notes/reactions/create', {
noteId: id,
reaction: this.reactionName(emoji)
})
return this.client
.post('/api/notes/show', {
noteId: id
})
.then((res) => ({ ...res, data: FirefishAPI.Converter.note(res.data) }))
}
async deleteEmojiReaction(id, emoji) {
await this.client.post('/api/notes/reactions/delete', {
noteId: id,
reaction: this.reactionName(emoji)
})
return this.client
.post('/api/notes/show', {
noteId: id
})
.then((res) => ({ ...res, data: FirefishAPI.Converter.note(res.data) }))
}
async getEmojiReactions(id) {
return this.client
.post('/api/notes/reactions', {
noteId: id
})
.then((res) => ({
...res,
data: FirefishAPI.Converter.reactions(res.data)
}))
}
async getEmojiReaction(_id, _emoji) {
return new Promise((_, reject) => {
const err = new NotImplementedError('Firefish 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() {
const url = await this.streamingURL()
return this.client.socket(url, 'user')
}
async publicStreaming() {
const url = await this.streamingURL()
return this.client.socket(url, 'globalTimeline')
}
async localStreaming() {
const url = await this.streamingURL()
return this.client.socket(url, 'localTimeline')
}
async tagStreaming(_tag) {
throw new NotImplementedError('TODO: implement')
}
async listStreaming(list_id) {
const url = await this.streamingURL()
return this.client.socket(url, 'list', list_id)
}
async directStreaming() {
const url = await this.streamingURL()
return this.client.socket(url, 'conversation')
}
async userStreamingSubscription() {
return this.userStreaming()
}
async publicStreamingSubscription(_socket) {
return this.publicStreaming()
}
async localStreamingSubscription(_socket) {
return this.localStreaming()
}
async tagStreamingSubscription(_socket, tag) {
return this.tagStreaming(tag)
}
async listStreamingSubscription(_socket, list_id) {
return this.listStreaming(list_id)
}
async directStreamingSubscription(_socket) {
return this.directStreaming()
}
}