diff --git a/.gitignore b/.gitignore
index 11e69b262..3b15a676a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,6 +60,7 @@ temp
# Sharkey
/packages/megalodon/lib
+/packages/megalodon-bk
# blender backups
*.blend1
diff --git a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts
index 9d1ee6105..320fdf52d 100644
--- a/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts
+++ b/packages/backend/src/server/api/mastodon/MastodonApiServerService.ts
@@ -20,7 +20,7 @@ export function getClient(BASE_URL: string, authorization: string | undefined):
const accessTokenArr = authorization?.split(" ") ?? [null];
const accessToken = accessTokenArr[accessTokenArr.length - 1];
const generator = (megalodon as any).default;
- const client = generator(BASE_URL, accessToken) as MegalodonInterface;
+ const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface;
return client;
}
@@ -303,7 +303,7 @@ export class MastodonApiServerService {
const accessTokens = _request.headers.authorization;
const client = getClient(BASE_URL, accessTokens);
try {
- const data = await client.getAccountFeaturedTags(convertId(_request.params.id, IdType.SharkeyId));
+ const data = await client.getFeaturedTags();
reply.send(data.data.map((tag) => convertFeaturedTag(tag)));
} catch (e: any) {
console.error(e);
diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts
index a4429af67..e6244455e 100644
--- a/packages/backend/src/server/api/mastodon/converters.ts
+++ b/packages/backend/src/server/api/mastodon/converters.ts
@@ -83,8 +83,6 @@ export function convertNotification(notification: Entity.Notification) {
notification.id = convertId(notification.id, IdConvertType.MastodonId);
if (notification.status)
notification.status = convertStatus(notification.status);
- if (notification.reaction)
- notification.reaction = convertReaction(notification.reaction);
return notification;
}
@@ -120,8 +118,6 @@ export function convertStatus(status: Entity.Status) {
}));
if (status.poll) status.poll = convertPoll(status.poll);
if (status.reblog) status.reblog = convertStatus(status.reblog);
- if (status.quote) status.quote = convertStatus(status.quote);
- status.reactions = status.reactions.map(convertReaction);
return status;
}
diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts
index 33bd688e0..205543f18 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/account.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts
@@ -46,7 +46,7 @@ export class apiAccountMastodon {
acct.source = {
note: acct.note,
fields: acct.fields,
- privacy: await (this.client as any).getDefaultPostPrivacy(),
+ privacy: "",
sensitive: false,
language: "",
};
@@ -72,7 +72,7 @@ export class apiAccountMastodon {
public async lookup() {
try {
- const data = await this.client.search((this.request.query as any).acct, "accounts");
+ const data = await this.client.search((this.request.query as any).acct, { type: "accounts" });
return convertAccount(data.data.accounts[0]);
} catch (e: any) {
console.error(e);
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index da976b791..aac514728 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -46,6 +46,7 @@ export function getConfig(): UserConfig {
base: '/vite/',
server: {
+ host: '0.0.0.0',
port: 5173,
},
diff --git a/packages/megalodon/.npmignore b/packages/megalodon/.npmignore
new file mode 100644
index 000000000..fd54d1deb
--- /dev/null
+++ b/packages/megalodon/.npmignore
@@ -0,0 +1,3 @@
+node_modules
+./src
+tsconfig.json
diff --git a/packages/megalodon/package.json b/packages/megalodon/package.json
index 3403b94b4..ebd958834 100644
--- a/packages/megalodon/package.json
+++ b/packages/megalodon/package.json
@@ -1,16 +1,35 @@
{
"name": "megalodon",
- "private": true,
+ "version": "7.0.1",
+ "description": "Mastodon API client for node.js and browser",
"main": "./lib/src/index.js",
"typings": "./lib/src/index.d.ts",
"scripts": {
"build": "tsc -p ./",
- "build:debug": "pnpm run build",
- "lint": "pnpm biome check **/*.ts --apply",
- "format": "pnpm biome format --write src/**/*.ts",
+ "lint": "eslint --ext .js,.ts src",
"doc": "typedoc --out ../docs ./src",
"test": "NODE_ENV=test jest -u --maxWorkers=3"
},
+ "engines": {
+ "node": ">=15.0.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/h3poteto/megalodon.git"
+ },
+ "keywords": [
+ "mastodon",
+ "client",
+ "api",
+ "streaming",
+ "rest",
+ "proxy"
+ ],
+ "author": "h3poteto",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/h3poteto/megalodon/issues"
+ },
"jest": {
"moduleFileExtensions": [
"ts",
@@ -25,59 +44,44 @@
],
"preset": "ts-jest/presets/default",
"transform": {
- "^.+\\.(ts|tsx)$": "ts-jest"
- },
- "globals": {
- "ts-jest": {
+ "^.+\\.(ts|tsx)$": ["ts-jest", {
"tsconfig": "tsconfig.json"
- }
+ }]
},
"testEnvironment": "node"
},
+ "homepage": "https://github.com/h3poteto/megalodon#readme",
"dependencies": {
- "@types/oauth": "^0.9.0",
- "@types/ws": "^8.5.4",
- "axios": "1.2.2",
- "dayjs": "^1.11.7",
+ "@types/oauth": "^0.9.2",
+ "@types/ws": "^8.5.5",
+ "axios": "1.5.0",
+ "dayjs": "^1.11.9",
"form-data": "^4.0.0",
- "https-proxy-agent": "^5.0.1",
+ "https-proxy-agent": "^7.0.2",
"oauth": "^0.10.0",
"object-assign-deep": "^0.4.0",
"parse-link-header": "^2.0.0",
- "socks-proxy-agent": "^7.0.0",
- "typescript": "4.9.4",
- "uuid": "^9.0.0",
- "ws": "8.12.0",
- "async-lock": "1.4.0"
+ "socks-proxy-agent": "^8.0.2",
+ "typescript": "5.1.6",
+ "uuid": "^9.0.1",
+ "ws": "8.14.2"
},
"devDependencies": {
- "@types/core-js": "^2.5.0",
+ "@types/core-js": "^2.5.6",
"@types/form-data": "^2.5.0",
- "@types/jest": "^29.4.0",
- "@types/object-assign-deep": "^0.4.0",
- "@types/parse-link-header": "^2.0.0",
- "@types/uuid": "^9.0.0",
- "@types/node": "18.11.18",
- "@typescript-eslint/eslint-plugin": "^5.49.0",
- "@typescript-eslint/parser": "^5.49.0",
- "@types/async-lock": "1.4.0",
- "eslint": "^8.32.0",
- "eslint-config-prettier": "^8.6.0",
- "eslint-config-standard": "^16.0.3",
- "eslint-plugin-import": "^2.27.5",
- "eslint-plugin-node": "^11.0.0",
- "eslint-plugin-prettier": "^4.2.1",
- "eslint-plugin-promise": "^6.1.1",
- "eslint-plugin-standard": "^5.0.0",
- "jest": "^29.4.0",
- "jest-worker": "^29.4.0",
+ "@types/jest": "^29.5.5",
+ "@types/object-assign-deep": "^0.4.1",
+ "@types/parse-link-header": "^2.0.1",
+ "@types/uuid": "^9.0.4",
+ "@typescript-eslint/eslint-plugin": "^6.7.2",
+ "@typescript-eslint/parser": "^6.7.2",
+ "eslint": "^8.49.0",
+ "eslint-config-prettier": "^9.0.0",
+ "jest": "^29.7.0",
+ "jest-worker": "^29.7.0",
"lodash": "^4.17.14",
- "prettier": "^2.8.3",
- "ts-jest": "^29.0.5",
- "typedoc": "^0.23.24"
- },
- "directories": {
- "lib": "lib",
- "test": "test"
+ "prettier": "^3.0.3",
+ "ts-jest": "^29.1.1",
+ "typedoc": "^0.25.1"
}
}
diff --git a/packages/megalodon/src/axios.d.ts b/packages/megalodon/src/axios.d.ts
index f19fe38a2..114cb06aa 100644
--- a/packages/megalodon/src/axios.d.ts
+++ b/packages/megalodon/src/axios.d.ts
@@ -1 +1 @@
-declare module "axios/lib/adapters/http";
+declare module 'axios/lib/adapters/http'
diff --git a/packages/megalodon/src/cancel.ts b/packages/megalodon/src/cancel.ts
index f8e4729b8..3b905a492 100644
--- a/packages/megalodon/src/cancel.ts
+++ b/packages/megalodon/src/cancel.ts
@@ -1,13 +1,13 @@
export class RequestCanceledError extends Error {
- public isCancel: boolean;
+ public isCancel: boolean
- constructor(msg: string) {
- super(msg);
- this.isCancel = true;
- Object.setPrototypeOf(this, RequestCanceledError);
- }
+ constructor(msg: string) {
+ super(msg)
+ this.isCancel = true
+ Object.setPrototypeOf(this, RequestCanceledError)
+ }
}
export const isCancel = (value: any): boolean => {
- return value && value.isCancel;
-};
+ return value && value.isCancel
+}
diff --git a/packages/megalodon/src/converter.ts b/packages/megalodon/src/converter.ts
deleted file mode 100644
index 93d669fa7..000000000
--- a/packages/megalodon/src/converter.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import MisskeyAPI from "./misskey/api_client";
-
-export default MisskeyAPI.Converter;
diff --git a/packages/megalodon/src/default.ts b/packages/megalodon/src/default.ts
index 45bce13e2..0194b3dcc 100644
--- a/packages/megalodon/src/default.ts
+++ b/packages/megalodon/src/default.ts
@@ -1,3 +1,3 @@
-export const NO_REDIRECT = "urn:ietf:wg:oauth:2.0:oob";
-export const DEFAULT_SCOPE = ["read", "write", "follow"];
-export const DEFAULT_UA = "megalodon";
+export const NO_REDIRECT = 'urn:ietf:wg:oauth:2.0:oob'
+export const DEFAULT_SCOPE = ['read', 'write', 'follow']
+export const DEFAULT_UA = 'megalodon'
diff --git a/packages/megalodon/src/detector.ts b/packages/megalodon/src/detector.ts
new file mode 100644
index 000000000..31f34d72f
--- /dev/null
+++ b/packages/megalodon/src/detector.ts
@@ -0,0 +1,137 @@
+import axios, { AxiosRequestConfig } from 'axios'
+import proxyAgent, { ProxyConfig } from './proxy_config'
+import { NodeinfoError } from './megalodon'
+
+const NODEINFO_10 = 'http://nodeinfo.diaspora.software/ns/schema/1.0'
+const NODEINFO_20 = 'http://nodeinfo.diaspora.software/ns/schema/2.0'
+const NODEINFO_21 = 'http://nodeinfo.diaspora.software/ns/schema/2.1'
+
+type Links = {
+ links: Array
+}
+
+type Link = {
+ href: string
+ rel: string
+}
+
+type Nodeinfo10 = {
+ software: Software
+ metadata: Metadata
+}
+
+type Nodeinfo20 = {
+ software: Software
+ metadata: Metadata
+}
+
+type Nodeinfo21 = {
+ software: Software
+ metadata: Metadata
+}
+
+type Software = {
+ name: string
+}
+
+type Metadata = {
+ upstream?: {
+ name: string
+ }
+}
+
+/**
+ * Detect SNS type.
+ * Now support Mastodon, Pleroma and Pixelfed. Throws an error when no known platform can be detected.
+ *
+ * @param url Base URL of SNS.
+ * @param proxyConfig Proxy setting, or set false if don't use proxy.
+ * @return SNS name.
+ */
+export const detector = async (
+ url: string,
+ proxyConfig: ProxyConfig | false = false
+): Promise<'mastodon' | 'pleroma' | 'misskey' | 'friendica'> => {
+ let options: AxiosRequestConfig = {
+ timeout: 20000
+ }
+ if (proxyConfig) {
+ options = Object.assign(options, {
+ httpsAgent: proxyAgent(proxyConfig)
+ })
+ }
+
+ const res = await axios.get(url + '/.well-known/nodeinfo', options)
+ const link = res.data.links.find(l => l.rel === NODEINFO_20 || l.rel === NODEINFO_21)
+ if (!link) throw new NodeinfoError('Could not find nodeinfo')
+ switch (link.rel) {
+ case NODEINFO_10: {
+ const res = await axios.get(link.href, options)
+ switch (res.data.software.name) {
+ case 'pleroma':
+ return 'pleroma'
+ case 'akkoma':
+ return 'pleroma'
+ case 'mastodon':
+ return 'mastodon'
+ case "wildebeest":
+ return "mastodon"
+ case 'misskey':
+ return 'misskey'
+ case 'friendica':
+ return 'friendica'
+ default:
+ if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
+ return 'mastodon'
+ }
+ throw new NodeinfoError('Unknown SNS')
+ }
+ }
+ case NODEINFO_20: {
+ const res = await axios.get(link.href, options)
+ switch (res.data.software.name) {
+ case 'pleroma':
+ return 'pleroma'
+ case 'akkoma':
+ return 'pleroma'
+ case 'mastodon':
+ return 'mastodon'
+ case "wildebeest":
+ return "mastodon"
+ case 'misskey':
+ return 'misskey'
+ case 'friendica':
+ return 'friendica'
+ default:
+ if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
+ return 'mastodon'
+ }
+ throw new NodeinfoError('Unknown SNS')
+ }
+ }
+ case NODEINFO_21: {
+ const res = await axios.get(link.href, options)
+ switch (res.data.software.name) {
+ case 'pleroma':
+ return 'pleroma'
+ case 'akkoma':
+ return 'pleroma'
+ case 'mastodon':
+ return 'mastodon'
+ case "wildebeest":
+ return "mastodon"
+ case 'misskey':
+ return 'misskey'
+ case 'friendica':
+ return 'friendica'
+ default:
+ if (res.data.metadata.upstream?.name && res.data.metadata.upstream.name === 'mastodon') {
+ return 'mastodon'
+ }
+ throw new NodeinfoError('Unknown SNS')
+ }
+ }
+ default:
+ throw new NodeinfoError('Could not find nodeinfo')
+ }
+}
diff --git a/packages/megalodon/src/entities/account.ts b/packages/megalodon/src/entities/account.ts
index 06a85eb98..89c0f17c4 100644
--- a/packages/megalodon/src/entities/account.ts
+++ b/packages/megalodon/src/entities/account.ts
@@ -1,27 +1,35 @@
///
///
///
+///
namespace Entity {
- export type Account = {
- id: string;
- username: string;
- acct: string;
- display_name: string;
- locked: boolean;
- created_at: string;
- followers_count: number;
- following_count: number;
- statuses_count: number;
- note: string;
- url: string;
- avatar: string;
- avatar_static: string;
- header: string;
- header_static: string;
- emojis: Array;
- moved: Account | null;
- fields: Array;
- bot: boolean | null;
- source?: Source;
- };
+ export type Account = {
+ id: string
+ username: string
+ acct: string
+ display_name: string
+ locked: boolean
+ discoverable?: boolean
+ group: boolean | null
+ noindex: boolean | null
+ suspended: boolean | null
+ limited: boolean | null
+ created_at: string
+ followers_count: number
+ following_count: number
+ statuses_count: number
+ note: string
+ url: string
+ avatar: string
+ avatar_static: string
+ header: string
+ header_static: string
+ emojis: Array
+ moved: Account | null
+ fields: Array
+ bot: boolean | null
+ source?: Source
+ role?: Role
+ mute_expires_at?: string
+ }
}
diff --git a/packages/megalodon/src/entities/activity.ts b/packages/megalodon/src/entities/activity.ts
index 6bc0b6d80..2494916a9 100644
--- a/packages/megalodon/src/entities/activity.ts
+++ b/packages/megalodon/src/entities/activity.ts
@@ -1,8 +1,8 @@
namespace Entity {
- export type Activity = {
- week: string;
- statuses: string;
- logins: string;
- registrations: string;
- };
+ export type Activity = {
+ week: string
+ statuses: string
+ logins: string
+ registrations: string
+ }
}
diff --git a/packages/megalodon/src/entities/announcement.ts b/packages/megalodon/src/entities/announcement.ts
index 7c7983163..0db9c23bb 100644
--- a/packages/megalodon/src/entities/announcement.ts
+++ b/packages/megalodon/src/entities/announcement.ts
@@ -1,34 +1,40 @@
-///
///
-///
namespace Entity {
- export type Announcement = {
- id: string;
- content: string;
- starts_at: string | null;
- ends_at: string | null;
- published: boolean;
- all_day: boolean;
- published_at: string;
- updated_at: string;
- read?: boolean;
- mentions: Array;
- statuses: Array;
- tags: Array;
- emojis: Array;
- reactions: Array;
- };
+ export type Announcement = {
+ id: string
+ content: string
+ starts_at: string | null
+ ends_at: string | null
+ published: boolean
+ all_day: boolean
+ published_at: string
+ updated_at: string | null
+ read: boolean | null
+ mentions: Array
+ statuses: Array
+ tags: Array
+ emojis: Array
+ reactions: Array
+ }
- export type AnnouncementAccount = {
- id: string;
- username: string;
- url: string;
- acct: string;
- };
+ export type AnnouncementAccount = {
+ id: string
+ username: string
+ url: string
+ acct: string
+ }
- export type AnnouncementStatus = {
- id: string;
- url: string;
- };
+ export type AnnouncementStatus = {
+ id: string
+ url: string
+ }
+
+ export type AnnouncementReaction = {
+ name: string
+ count: number
+ me: boolean | null
+ url: string | null
+ static_url: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/application.ts b/packages/megalodon/src/entities/application.ts
index 9b98b1277..3af64fcf9 100644
--- a/packages/megalodon/src/entities/application.ts
+++ b/packages/megalodon/src/entities/application.ts
@@ -1,7 +1,7 @@
namespace Entity {
- export type Application = {
- name: string;
- website?: string | null;
- vapid_key?: string | null;
- };
+ export type Application = {
+ name: string
+ website?: string | null
+ vapid_key?: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/async_attachment.ts b/packages/megalodon/src/entities/async_attachment.ts
index 9cc17acc5..b383f90c5 100644
--- a/packages/megalodon/src/entities/async_attachment.ts
+++ b/packages/megalodon/src/entities/async_attachment.ts
@@ -1,14 +1,14 @@
///
namespace Entity {
- export type AsyncAttachment = {
- id: string;
- type: "unknown" | "image" | "gifv" | "video" | "audio";
- url: string | null;
- remote_url: string | null;
- preview_url: string;
- text_url: string | null;
- meta: Meta | null;
- description: string | null;
- blurhash: string | null;
- };
+ export type AsyncAttachment = {
+ id: string
+ type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio'
+ url: string | null
+ remote_url: string | null
+ preview_url: string
+ text_url: string | null
+ meta: Meta | null
+ description: string | null
+ blurhash: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/attachment.ts b/packages/megalodon/src/entities/attachment.ts
index 082c79edd..aab1deade 100644
--- a/packages/megalodon/src/entities/attachment.ts
+++ b/packages/megalodon/src/entities/attachment.ts
@@ -1,49 +1,49 @@
namespace Entity {
- export type Sub = {
- // For Image, Gifv, and Video
- width?: number;
- height?: number;
- size?: string;
- aspect?: number;
+ export type Sub = {
+ // For Image, Gifv, and Video
+ width?: number
+ height?: number
+ size?: string
+ aspect?: number
- // For Gifv and Video
- frame_rate?: string;
+ // For Gifv and Video
+ frame_rate?: string
- // For Audio, Gifv, and Video
- duration?: number;
- bitrate?: number;
- };
+ // For Audio, Gifv, and Video
+ duration?: number
+ bitrate?: number
+ }
- export type Focus = {
- x: number;
- y: number;
- };
+ export type Focus = {
+ x: number
+ y: number
+ }
- export type Meta = {
- original?: Sub;
- small?: Sub;
- focus?: Focus;
- length?: string;
- duration?: number;
- fps?: number;
- size?: string;
- width?: number;
- height?: number;
- aspect?: number;
- audio_encode?: string;
- audio_bitrate?: string;
- audio_channel?: string;
- };
+ export type Meta = {
+ original?: Sub
+ small?: Sub
+ focus?: Focus
+ length?: string
+ duration?: number
+ fps?: number
+ size?: string
+ width?: number
+ height?: number
+ aspect?: number
+ audio_encode?: string
+ audio_bitrate?: string
+ audio_channel?: string
+ }
- export type Attachment = {
- id: string;
- type: "unknown" | "image" | "gifv" | "video" | "audio";
- url: string;
- remote_url: string | null;
- preview_url: string | null;
- text_url: string | null;
- meta: Meta | null;
- description: string | null;
- blurhash: string | null;
- };
+ export type Attachment = {
+ id: string
+ type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio'
+ url: string
+ remote_url: string | null
+ preview_url: string | null
+ text_url: string | null
+ meta: Meta | null
+ description: string | null
+ blurhash: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/card.ts b/packages/megalodon/src/entities/card.ts
index 356d99aee..1ef6f5e4d 100644
--- a/packages/megalodon/src/entities/card.ts
+++ b/packages/megalodon/src/entities/card.ts
@@ -1,16 +1,18 @@
namespace Entity {
- export type Card = {
- url: string;
- title: string;
- description: string;
- type: "link" | "photo" | "video" | "rich";
- image?: string;
- author_name?: string;
- author_url?: string;
- provider_name?: string;
- provider_url?: string;
- html?: string;
- width?: number;
- height?: number;
- };
+ export type Card = {
+ url: string
+ title: string
+ description: string
+ type: 'link' | 'photo' | 'video' | 'rich'
+ image: string | null
+ author_name: string | null
+ author_url: string | null
+ provider_name: string | null
+ provider_url: string | null
+ html: string | null
+ width: number | null
+ height: number | null
+ embed_url: string | null
+ blurhash: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/context.ts b/packages/megalodon/src/entities/context.ts
index a794a7c5a..3f2eda58f 100644
--- a/packages/megalodon/src/entities/context.ts
+++ b/packages/megalodon/src/entities/context.ts
@@ -1,8 +1,8 @@
///
namespace Entity {
- export type Context = {
- ancestors: Array;
- descendants: Array;
- };
+ export type Context = {
+ ancestors: Array
+ descendants: Array
+ }
}
diff --git a/packages/megalodon/src/entities/conversation.ts b/packages/megalodon/src/entities/conversation.ts
index 2bdc19666..cdadf1e0f 100644
--- a/packages/megalodon/src/entities/conversation.ts
+++ b/packages/megalodon/src/entities/conversation.ts
@@ -2,10 +2,10 @@
///
namespace Entity {
- export type Conversation = {
- id: string;
- accounts: Array;
- last_status: Status | null;
- unread: boolean;
- };
+ export type Conversation = {
+ id: string
+ accounts: Array
+ last_status: Status | null
+ unread: boolean
+ }
}
diff --git a/packages/megalodon/src/entities/emoji.ts b/packages/megalodon/src/entities/emoji.ts
index 10c32ab0b..546ef818f 100644
--- a/packages/megalodon/src/entities/emoji.ts
+++ b/packages/megalodon/src/entities/emoji.ts
@@ -1,9 +1,9 @@
namespace Entity {
- export type Emoji = {
- shortcode: string;
- static_url: string;
- url: string;
- visible_in_picker: boolean;
- category: string;
- };
+ export type Emoji = {
+ shortcode: string
+ static_url: string
+ url: string
+ visible_in_picker: boolean
+ category?: string
+ }
}
diff --git a/packages/megalodon/src/entities/featured_tag.ts b/packages/megalodon/src/entities/featured_tag.ts
index fc9f8c69c..06ae6d7a9 100644
--- a/packages/megalodon/src/entities/featured_tag.ts
+++ b/packages/megalodon/src/entities/featured_tag.ts
@@ -1,8 +1,8 @@
namespace Entity {
- export type FeaturedTag = {
- id: string;
- name: string;
- statuses_count: number;
- last_status_at: string;
- };
+ export type FeaturedTag = {
+ id: string
+ name: string
+ statuses_count: number
+ last_status_at: string
+ }
}
diff --git a/packages/megalodon/src/entities/field.ts b/packages/megalodon/src/entities/field.ts
index de4b6b2b7..03e4604b0 100644
--- a/packages/megalodon/src/entities/field.ts
+++ b/packages/megalodon/src/entities/field.ts
@@ -1,7 +1,7 @@
namespace Entity {
- export type Field = {
- name: string;
- value: string;
- verified_at: string | null;
- };
+ export type Field = {
+ name: string
+ value: string
+ verified_at: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/filter.ts b/packages/megalodon/src/entities/filter.ts
index 55b7305cc..ffbacb728 100644
--- a/packages/megalodon/src/entities/filter.ts
+++ b/packages/megalodon/src/entities/filter.ts
@@ -1,12 +1,12 @@
namespace Entity {
- export type Filter = {
- id: string;
- phrase: string;
- context: Array;
- expires_at: string | null;
- irreversible: boolean;
- whole_word: boolean;
- };
+ export type Filter = {
+ id: string
+ phrase: string
+ context: Array
+ expires_at: string | null
+ irreversible: boolean
+ whole_word: boolean
+ }
- export type FilterContext = string;
+ export type FilterContext = string
}
diff --git a/packages/megalodon/src/entities/follow_request.ts b/packages/megalodon/src/entities/follow_request.ts
new file mode 100644
index 000000000..84ea4d02c
--- /dev/null
+++ b/packages/megalodon/src/entities/follow_request.ts
@@ -0,0 +1,27 @@
+///
+///
+
+namespace Entity {
+ export type FollowRequest = {
+ id: number
+ username: string
+ acct: string
+ display_name: string
+ locked: boolean
+ bot: boolean
+ discoverable?: boolean
+ group: boolean
+ created_at: string
+ note: string
+ url: string
+ avatar: string
+ avatar_static: string
+ header: string
+ header_static: string
+ followers_count: number
+ following_count: number
+ statuses_count: number
+ emojis: Array
+ fields: Array
+ }
+}
diff --git a/packages/megalodon/src/entities/history.ts b/packages/megalodon/src/entities/history.ts
index 4676357d6..070969426 100644
--- a/packages/megalodon/src/entities/history.ts
+++ b/packages/megalodon/src/entities/history.ts
@@ -1,7 +1,7 @@
namespace Entity {
- export type History = {
- day: string;
- uses: number;
- accounts: number;
- };
+ export type History = {
+ day: string
+ uses: number
+ accounts: number
+ }
}
diff --git a/packages/megalodon/src/entities/identity_proof.ts b/packages/megalodon/src/entities/identity_proof.ts
index 3b42e6f41..ff857addb 100644
--- a/packages/megalodon/src/entities/identity_proof.ts
+++ b/packages/megalodon/src/entities/identity_proof.ts
@@ -1,9 +1,9 @@
namespace Entity {
- export type IdentityProof = {
- provider: string;
- provider_username: string;
- updated_at: string;
- proof_url: string;
- profile_url: string;
- };
+ export type IdentityProof = {
+ provider: string
+ provider_username: string
+ updated_at: string
+ proof_url: string
+ profile_url: string
+ }
}
diff --git a/packages/megalodon/src/entities/instance.ts b/packages/megalodon/src/entities/instance.ts
index 9c0f572db..8f4808be8 100644
--- a/packages/megalodon/src/entities/instance.ts
+++ b/packages/megalodon/src/entities/instance.ts
@@ -3,39 +3,38 @@
///
namespace Entity {
- export type Instance = {
- uri: string;
- title: string;
- description: string;
- email: string;
- version: string;
- thumbnail: string | null;
- urls: URLs;
- stats: Stats;
- languages: Array;
- contact_account: Account | null;
- max_toot_chars?: number;
- registrations?: boolean;
- configuration?: {
- statuses: {
- max_characters: number;
- max_media_attachments: number;
- characters_reserved_per_url: number;
- };
- media_attachments: {
- supported_mime_types: Array;
- image_size_limit: number;
- image_matrix_limit: number;
- video_size_limit: number;
- video_frame_limit: number;
- video_matrix_limit: number;
- };
- polls: {
- max_options: number;
- max_characters_per_option: number;
- min_expiration: number;
- max_expiration: number;
- };
- };
- };
+ export type Instance = {
+ uri: string
+ title: string
+ description: string
+ email: string
+ version: string
+ thumbnail: string | null
+ urls: URLs | null
+ stats: Stats
+ languages: Array
+ registrations: boolean
+ approval_required: boolean
+ invites_enabled?: boolean
+ configuration: {
+ statuses: {
+ max_characters: number
+ max_media_attachments?: number
+ characters_reserved_per_url?: number
+ }
+ polls?: {
+ max_options: number
+ max_characters_per_option: number
+ min_expiration: number
+ max_expiration: number
+ }
+ }
+ contact_account?: Account
+ rules?: Array
+ }
+
+ export type InstanceRule = {
+ id: string
+ text: string
+ }
}
diff --git a/packages/megalodon/src/entities/list.ts b/packages/megalodon/src/entities/list.ts
index 97e75286b..58c264aba 100644
--- a/packages/megalodon/src/entities/list.ts
+++ b/packages/megalodon/src/entities/list.ts
@@ -1,6 +1,9 @@
namespace Entity {
- export type List = {
- id: string;
- title: string;
- };
+ export type List = {
+ id: string
+ title: string
+ replies_policy: RepliesPolicy | null
+ }
+
+ export type RepliesPolicy = 'followed' | 'list' | 'none'
}
diff --git a/packages/megalodon/src/entities/marker.ts b/packages/megalodon/src/entities/marker.ts
index 7ee99282c..33cb98a10 100644
--- a/packages/megalodon/src/entities/marker.ts
+++ b/packages/megalodon/src/entities/marker.ts
@@ -1,15 +1,15 @@
namespace Entity {
- export type Marker = {
- home?: {
- last_read_id: string;
- version: number;
- updated_at: string;
- };
- notifications?: {
- last_read_id: string;
- version: number;
- updated_at: string;
- unread_count?: number;
- };
- };
+ export type Marker = {
+ home?: {
+ last_read_id: string
+ version: number
+ updated_at: string
+ }
+ notifications?: {
+ last_read_id: string
+ version: number
+ updated_at: string
+ unread_count?: number
+ }
+ }
}
diff --git a/packages/megalodon/src/entities/mention.ts b/packages/megalodon/src/entities/mention.ts
index 4fe36a655..046912971 100644
--- a/packages/megalodon/src/entities/mention.ts
+++ b/packages/megalodon/src/entities/mention.ts
@@ -1,8 +1,8 @@
namespace Entity {
- export type Mention = {
- id: string;
- username: string;
- url: string;
- acct: string;
- };
+ export type Mention = {
+ id: string
+ username: string
+ url: string
+ acct: string
+ }
}
diff --git a/packages/megalodon/src/entities/notification.ts b/packages/megalodon/src/entities/notification.ts
index 68eff3347..653d235d9 100644
--- a/packages/megalodon/src/entities/notification.ts
+++ b/packages/megalodon/src/entities/notification.ts
@@ -2,14 +2,15 @@
///
namespace Entity {
- export type Notification = {
- account: Account;
- created_at: string;
- id: string;
- status?: Status;
- reaction?: Reaction;
- type: NotificationType;
- };
+ export type Notification = {
+ account: Account
+ created_at: string
+ id: string
+ status?: Status
+ emoji?: string
+ type: NotificationType
+ target?: Account
+ }
- export type NotificationType = string;
+ export type NotificationType = string
}
diff --git a/packages/megalodon/src/entities/poll.ts b/packages/megalodon/src/entities/poll.ts
index 2539d68b2..69706e8ae 100644
--- a/packages/megalodon/src/entities/poll.ts
+++ b/packages/megalodon/src/entities/poll.ts
@@ -1,14 +1,13 @@
///
namespace Entity {
- export type Poll = {
- id: string;
- expires_at: string | null;
- expired: boolean;
- multiple: boolean;
- votes_count: number;
- options: Array;
- voted: boolean;
- own_votes: Array;
- };
+ export type Poll = {
+ id: string
+ expires_at: string | null
+ expired: boolean
+ multiple: boolean
+ votes_count: number
+ options: Array
+ voted: boolean
+ }
}
diff --git a/packages/megalodon/src/entities/poll_option.ts b/packages/megalodon/src/entities/poll_option.ts
index e818a8607..ae4c63849 100644
--- a/packages/megalodon/src/entities/poll_option.ts
+++ b/packages/megalodon/src/entities/poll_option.ts
@@ -1,6 +1,6 @@
namespace Entity {
- export type PollOption = {
- title: string;
- votes_count: number | null;
- };
+ export type PollOption = {
+ title: string
+ votes_count: number | null
+ }
}
diff --git a/packages/megalodon/src/entities/preferences.ts b/packages/megalodon/src/entities/preferences.ts
index 7994dc568..cb5797c4c 100644
--- a/packages/megalodon/src/entities/preferences.ts
+++ b/packages/megalodon/src/entities/preferences.ts
@@ -1,9 +1,9 @@
namespace Entity {
- export type Preferences = {
- "posting:default:visibility": "public" | "unlisted" | "private" | "direct";
- "posting:default:sensitive": boolean;
- "posting:default:language": string | null;
- "reading:expand:media": "default" | "show_all" | "hide_all";
- "reading:expand:spoilers": boolean;
- };
+ export type Preferences = {
+ 'posting:default:visibility': 'public' | 'unlisted' | 'private' | 'direct'
+ 'posting:default:sensitive': boolean
+ 'posting:default:language': string | null
+ 'reading:expand:media': 'default' | 'show_all' | 'hide_all'
+ 'reading:expand:spoilers': boolean
+ }
}
diff --git a/packages/megalodon/src/entities/push_subscription.ts b/packages/megalodon/src/entities/push_subscription.ts
index ad1146a24..fe7464e8e 100644
--- a/packages/megalodon/src/entities/push_subscription.ts
+++ b/packages/megalodon/src/entities/push_subscription.ts
@@ -1,16 +1,16 @@
namespace Entity {
- export type Alerts = {
- follow: boolean;
- favourite: boolean;
- mention: boolean;
- reblog: boolean;
- poll: boolean;
- };
+ export type Alerts = {
+ follow: boolean
+ favourite: boolean
+ mention: boolean
+ reblog: boolean
+ poll: boolean
+ }
- export type PushSubscription = {
- id: string;
- endpoint: string;
- server_key: string;
- alerts: Alerts;
- };
+ export type PushSubscription = {
+ id: string
+ endpoint: string
+ server_key: string
+ alerts: Alerts
+ }
}
diff --git a/packages/megalodon/src/entities/reaction.ts b/packages/megalodon/src/entities/reaction.ts
index 4edbec6a7..8c626f9e8 100644
--- a/packages/megalodon/src/entities/reaction.ts
+++ b/packages/megalodon/src/entities/reaction.ts
@@ -1,12 +1,10 @@
///
namespace Entity {
- export type Reaction = {
- count: number;
- me: boolean;
- name: string;
- url?: string;
- static_url?: string;
- accounts?: Array;
- };
+ export type Reaction = {
+ count: number
+ me: boolean
+ name: string
+ accounts?: Array
+ }
}
diff --git a/packages/megalodon/src/entities/relationship.ts b/packages/megalodon/src/entities/relationship.ts
index 91802d5c8..283a1158c 100644
--- a/packages/megalodon/src/entities/relationship.ts
+++ b/packages/megalodon/src/entities/relationship.ts
@@ -1,17 +1,17 @@
namespace Entity {
- export type Relationship = {
- id: string;
- following: boolean;
- followed_by: boolean;
- delivery_following?: boolean;
- blocking: boolean;
- blocked_by: boolean;
- muting: boolean;
- muting_notifications: boolean;
- requested: boolean;
- domain_blocking: boolean;
- showing_reblogs: boolean;
- endorsed: boolean;
- notifying: boolean;
- };
+ export type Relationship = {
+ id: string
+ following: boolean
+ followed_by: boolean
+ blocking: boolean
+ blocked_by: boolean
+ muting: boolean
+ muting_notifications: boolean
+ requested: boolean
+ domain_blocking: boolean
+ showing_reblogs: boolean
+ endorsed: boolean
+ notifying: boolean
+ note: string | null
+ }
}
diff --git a/packages/megalodon/src/entities/report.ts b/packages/megalodon/src/entities/report.ts
index 6862a5fab..353886a34 100644
--- a/packages/megalodon/src/entities/report.ts
+++ b/packages/megalodon/src/entities/report.ts
@@ -1,9 +1,18 @@
+///
+
namespace Entity {
- export type Report = {
- id: string;
- action_taken: string;
- comment: string;
- account_id: string;
- status_ids: Array;
- };
+ export type Report = {
+ id: string
+ action_taken: boolean
+ action_taken_at: string | null
+ status_ids: Array | null
+ rule_ids: Array | null
+ // These parameters don't exist in Pleroma
+ category: Category | null
+ comment: string | null
+ forwarded: boolean | null
+ target_account?: Account | null
+ }
+
+ export type Category = 'spam' | 'violation' | 'other'
}
diff --git a/packages/megalodon/src/entities/results.ts b/packages/megalodon/src/entities/results.ts
index 4448e5335..fe168de67 100644
--- a/packages/megalodon/src/entities/results.ts
+++ b/packages/megalodon/src/entities/results.ts
@@ -3,9 +3,9 @@
///
namespace Entity {
- export type Results = {
- accounts: Array;
- statuses: Array;
- hashtags: Array;
- };
+ export type Results = {
+ accounts: Array
+ statuses: Array
+ hashtags: Array
+ }
}
diff --git a/packages/megalodon/src/entities/role.ts b/packages/megalodon/src/entities/role.ts
new file mode 100644
index 000000000..caaae9ea1
--- /dev/null
+++ b/packages/megalodon/src/entities/role.ts
@@ -0,0 +1,5 @@
+namespace Entity {
+ export type Role = {
+ name: string
+ }
+}
diff --git a/packages/megalodon/src/entities/scheduled_status.ts b/packages/megalodon/src/entities/scheduled_status.ts
index 78dfb8ed2..561a5b9f2 100644
--- a/packages/megalodon/src/entities/scheduled_status.ts
+++ b/packages/megalodon/src/entities/scheduled_status.ts
@@ -1,10 +1,10 @@
///
///
namespace Entity {
- export type ScheduledStatus = {
- id: string;
- scheduled_at: string;
- params: StatusParams;
- media_attachments: Array;
- };
+ export type ScheduledStatus = {
+ id: string
+ scheduled_at: string
+ params: StatusParams
+ media_attachments: Array | null
+ }
}
diff --git a/packages/megalodon/src/entities/source.ts b/packages/megalodon/src/entities/source.ts
index 913b02fda..d87cf55d8 100644
--- a/packages/megalodon/src/entities/source.ts
+++ b/packages/megalodon/src/entities/source.ts
@@ -1,10 +1,10 @@
///
namespace Entity {
- export type Source = {
- privacy: string | null;
- sensitive: boolean | null;
- language: string | null;
- note: string;
- fields: Array;
- };
+ export type Source = {
+ privacy: string | null
+ sensitive: boolean | null
+ language: string | null
+ note: string
+ fields: Array
+ }
}
diff --git a/packages/megalodon/src/entities/stats.ts b/packages/megalodon/src/entities/stats.ts
index 6471df039..76f0bad34 100644
--- a/packages/megalodon/src/entities/stats.ts
+++ b/packages/megalodon/src/entities/stats.ts
@@ -1,7 +1,7 @@
namespace Entity {
- export type Stats = {
- user_count: number;
- status_count: number;
- domain_count: number;
- };
+ export type Stats = {
+ user_count: number
+ status_count: number
+ domain_count: number
+ }
}
diff --git a/packages/megalodon/src/entities/status.ts b/packages/megalodon/src/entities/status.ts
index f27f728b5..295703e57 100644
--- a/packages/megalodon/src/entities/status.ts
+++ b/packages/megalodon/src/entities/status.ts
@@ -1,7 +1,6 @@
///
///
///
-///
///
///
///
@@ -9,37 +8,42 @@
///
namespace Entity {
- export type Status = {
- id: string;
- uri: string;
- url: string;
- account: Account;
- in_reply_to_id: string | null;
- in_reply_to_account_id: string | null;
- reblog: Status | null;
- content: string;
- plain_content: string | null;
- created_at: string;
- emojis: Emoji[];
- replies_count: number;
- reblogs_count: number;
- favourites_count: number;
- reblogged: boolean | null;
- favourited: boolean | null;
- muted: boolean | null;
- sensitive: boolean;
- spoiler_text: string;
- visibility: "public" | "unlisted" | "private" | "direct";
- media_attachments: Array;
- mentions: Array;
- tags: Array;
- card: Card | null;
- poll: Poll | null;
- application: Application | null;
- language: string | null;
- pinned: boolean | null;
- reactions: Array;
- quote: Status | null;
- bookmarked: boolean;
- };
+ export type Status = {
+ id: string
+ uri: string
+ url: string
+ account: Account
+ in_reply_to_id: string | null
+ in_reply_to_account_id: string | null
+ reblog: Status | null
+ content: string
+ plain_content: string | null
+ created_at: string
+ emojis: Emoji[]
+ replies_count: number
+ reblogs_count: number
+ favourites_count: number
+ reblogged: boolean | null
+ favourited: boolean | null
+ muted: boolean | null
+ sensitive: boolean
+ spoiler_text: string
+ visibility: 'public' | 'unlisted' | 'private' | 'direct'
+ media_attachments: Array
+ mentions: Array
+ tags: Array
+ card: Card | null
+ poll: Poll | null
+ application: Application | null
+ language: string | null
+ pinned: boolean | null
+ emoji_reactions: Array
+ quote: boolean
+ bookmarked: boolean
+ }
+
+ export type StatusTag = {
+ name: string
+ url: string
+ }
}
diff --git a/packages/megalodon/src/entities/status_params.ts b/packages/megalodon/src/entities/status_params.ts
index 18908c01c..82d789086 100644
--- a/packages/megalodon/src/entities/status_params.ts
+++ b/packages/megalodon/src/entities/status_params.ts
@@ -1,12 +1,12 @@
namespace Entity {
- export type StatusParams = {
- text: string;
- in_reply_to_id: string | null;
- media_ids: Array | null;
- sensitive: boolean | null;
- spoiler_text: string | null;
- visibility: "public" | "unlisted" | "private" | "direct";
- scheduled_at: string | null;
- application_id: string;
- };
+ export type StatusParams = {
+ text: string
+ in_reply_to_id: string | null
+ media_ids: Array | null
+ sensitive: boolean | null
+ spoiler_text: string | null
+ visibility: 'public' | 'unlisted' | 'private' | 'direct' | null
+ scheduled_at: string | null
+ application_id: number | null
+ }
}
diff --git a/packages/megalodon/src/entities/status_source.ts b/packages/megalodon/src/entities/status_source.ts
new file mode 100644
index 000000000..0de7030ed
--- /dev/null
+++ b/packages/megalodon/src/entities/status_source.ts
@@ -0,0 +1,7 @@
+namespace Entity {
+ export type StatusSource = {
+ id: string
+ text: string
+ spoiler_text: string
+ }
+}
diff --git a/packages/megalodon/src/entities/tag.ts b/packages/megalodon/src/entities/tag.ts
index ccc88aece..ddc5fe92b 100644
--- a/packages/megalodon/src/entities/tag.ts
+++ b/packages/megalodon/src/entities/tag.ts
@@ -1,10 +1,10 @@
///
namespace Entity {
- export type Tag = {
- name: string;
- url: string;
- history: Array | null;
- following?: boolean;
- };
+ export type Tag = {
+ name: string
+ url: string
+ history: Array
+ following?: boolean
+ }
}
diff --git a/packages/megalodon/src/entities/token.ts b/packages/megalodon/src/entities/token.ts
index 1583edafb..6fa28e39b 100644
--- a/packages/megalodon/src/entities/token.ts
+++ b/packages/megalodon/src/entities/token.ts
@@ -1,8 +1,8 @@
namespace Entity {
- export type Token = {
- access_token: string;
- token_type: string;
- scope: string;
- created_at: number;
- };
+ export type Token = {
+ access_token: string
+ token_type: string
+ scope: string
+ created_at: number
+ }
}
diff --git a/packages/megalodon/src/entities/urls.ts b/packages/megalodon/src/entities/urls.ts
index 1ee9ed67c..4a980d589 100644
--- a/packages/megalodon/src/entities/urls.ts
+++ b/packages/megalodon/src/entities/urls.ts
@@ -1,5 +1,5 @@
namespace Entity {
- export type URLs = {
- streaming_api: string;
- };
+ export type URLs = {
+ streaming_api: string
+ }
}
diff --git a/packages/megalodon/src/entity.ts b/packages/megalodon/src/entity.ts
index b73d2b359..387981cec 100644
--- a/packages/megalodon/src/entity.ts
+++ b/packages/megalodon/src/entity.ts
@@ -11,6 +11,7 @@
///
///
///
+///
///
///
///
@@ -31,8 +32,9 @@
///
///
///
+///
///
///
///
-export default Entity;
+export default Entity
diff --git a/packages/megalodon/src/filter_context.ts b/packages/megalodon/src/filter_context.ts
index 4c83cb15f..c69be98cd 100644
--- a/packages/megalodon/src/filter_context.ts
+++ b/packages/megalodon/src/filter_context.ts
@@ -1,11 +1,11 @@
-import Entity from "./entity";
+import Entity from './entity'
namespace FilterContext {
- export const Home: Entity.FilterContext = "home";
- export const Notifications: Entity.FilterContext = "notifications";
- export const Public: Entity.FilterContext = "public";
- export const Thread: Entity.FilterContext = "thread";
- export const Account: Entity.FilterContext = "account";
+ export const Home: Entity.FilterContext = 'home'
+ export const Notifications: Entity.FilterContext = 'notifications'
+ export const Public: Entity.FilterContext = 'public'
+ export const Thread: Entity.FilterContext = 'thread'
+ export const Account: Entity.FilterContext = 'account'
}
-export default FilterContext;
+export default FilterContext
diff --git a/packages/megalodon/src/friendica.ts b/packages/megalodon/src/friendica.ts
new file mode 100644
index 000000000..c5ee9d59c
--- /dev/null
+++ b/packages/megalodon/src/friendica.ts
@@ -0,0 +1,2868 @@
+import { OAuth2 } from 'oauth'
+import FormData from 'form-data'
+import parseLinkHeader from 'parse-link-header'
+
+import FriendicaAPI from './friendica/api_client'
+import WebSocket from './friendica/web_socket'
+import { MegalodonInterface, NoImplementedError } from './megalodon'
+import Response from './response'
+import Entity from './entity'
+import { NO_REDIRECT, DEFAULT_SCOPE, DEFAULT_UA } from './default'
+import { ProxyConfig } from './proxy_config'
+import OAuth from './oauth'
+import { UnknownNotificationTypeError } from './notification'
+
+export default class Friendica implements MegalodonInterface {
+ public client: FriendicaAPI.Interface
+ public baseUrl: string
+
+ /**
+ * @param baseUrl hostname or base URL
+ * @param accessToken access token from OAuth2 authorization
+ * @param userAgent UserAgent is specified in header on request.
+ * @param proxyConfig Proxy setting, or set false if don't use proxy.
+ */
+ constructor(
+ baseUrl: string,
+ accessToken: string | null = null,
+ userAgent: string | null = DEFAULT_UA,
+ proxyConfig: ProxyConfig | false = false
+ ) {
+ let token = ''
+ if (accessToken) {
+ token = accessToken
+ }
+ let agent: string = DEFAULT_UA
+ if (userAgent) {
+ agent = userAgent
+ }
+ this.client = new FriendicaAPI.Client(baseUrl, token, agent, proxyConfig)
+ this.baseUrl = baseUrl
+ }
+
+ public cancel(): void {
+ return this.client.cancel()
+ }
+
+ /**
+ * First, call createApp to get client_id and client_secret.
+ * Next, call generateAuthUrl to get authorization url.
+ * @param client_name Form Data, which is sent to /api/v1/apps
+ * @param options Form Data, which is sent to /api/v1/apps. and properties should be **snake_case**
+ */
+ public async registerApp(
+ client_name: string,
+ options: Partial<{ scopes: Array; redirect_uris: string; website: string }>
+ ): Promise {
+ const scopes = options.scopes || DEFAULT_SCOPE
+ return this.createApp(client_name, options).then(async appData => {
+ return this.generateAuthUrl(appData.client_id, appData.client_secret, {
+ scope: scopes,
+ redirect_uri: appData.redirect_uri
+ }).then(url => {
+ appData.url = url
+ return appData
+ })
+ })
+ }
+
+ /**
+ * Call /api/v1/apps
+ *
+ * Create an application.
+ * @param client_name your application's name
+ * @param options Form Data
+ */
+ public async createApp(
+ client_name: string,
+ options: Partial<{ scopes: Array; redirect_uris: string; website: string }>
+ ): Promise {
+ const scopes = options.scopes || DEFAULT_SCOPE
+ const redirect_uris = options.redirect_uris || NO_REDIRECT
+
+ const params: {
+ client_name: string
+ redirect_uris: string
+ scopes: string
+ website?: string
+ } = {
+ client_name: client_name,
+ redirect_uris: redirect_uris,
+ scopes: scopes.join(' ')
+ }
+ if (options.website) params.website = options.website
+
+ return this.client
+ .post('/api/v1/apps', params)
+ .then((res: Response) => OAuth.AppData.from(res.data))
+ }
+
+ /**
+ * Generate authorization url using OAuth2.
+ *
+ * @param clientId your OAuth app's client ID
+ * @param clientSecret your OAuth app's client Secret
+ * @param options as property, redirect_uri and scope are available, and must be the same as when you register your app
+ */
+ public generateAuthUrl(
+ clientId: string,
+ clientSecret: string,
+ options: Partial<{ scope: Array; redirect_uri: string }>
+ ): Promise {
+ const scope = options.scope || DEFAULT_SCOPE
+ const redirect_uri = options.redirect_uri || NO_REDIRECT
+ return new Promise(resolve => {
+ const oauth = new OAuth2(clientId, clientSecret, this.baseUrl, undefined, '/oauth/token')
+ const url = oauth.getAuthorizeUrl({
+ redirect_uri: redirect_uri,
+ response_type: 'code',
+ client_id: clientId,
+ scope: scope.join(' ')
+ })
+ resolve(url)
+ })
+ }
+
+ // ======================================
+ // apps
+ // ======================================
+ /**
+ * GET /api/v1/apps/verify_credentials
+ *
+ * @return An Application
+ */
+ public verifyAppCredentials(): Promise> {
+ return this.client.get('/api/v1/apps/verify_credentials')
+ }
+
+ // ======================================
+ // apps/oauth
+ // ======================================
+ /**
+ * POST /oauth/token
+ *
+ * Fetch OAuth access token.
+ * Get an access token based client_id and client_secret and authorization code.
+ * @param client_id will be generated by #createApp or #registerApp
+ * @param client_secret will be generated by #createApp or #registerApp
+ * @param code will be generated by the link of #generateAuthUrl or #registerApp
+ * @param redirect_uri must be the same uri as the time when you register your OAuth application
+ */
+ public async fetchAccessToken(
+ client_id: string | null,
+ client_secret: string,
+ code: string,
+ redirect_uri: string = NO_REDIRECT
+ ): Promise {
+ if (!client_id) {
+ throw new Error('client_id is required')
+ }
+ return this.client
+ .post('/oauth/token', {
+ client_id,
+ client_secret,
+ code,
+ redirect_uri,
+ grant_type: 'authorization_code'
+ })
+ .then((res: Response) => OAuth.TokenData.from(res.data))
+ }
+
+ /**
+ * POST /oauth/token
+ *
+ * Refresh OAuth access token.
+ * Send refresh token and get new access token.
+ * @param client_id will be generated by #createApp or #registerApp
+ * @param client_secret will be generated by #createApp or #registerApp
+ * @param refresh_token will be get #fetchAccessToken
+ */
+ public async refreshToken(client_id: string, client_secret: string, refresh_token: string): Promise {
+ return this.client
+ .post('/oauth/token', {
+ client_id,
+ client_secret,
+ refresh_token,
+ grant_type: 'refresh_token'
+ })
+ .then((res: Response) => OAuth.TokenData.from(res.data))
+ }
+
+ /**
+ * POST /oauth/revoke
+ *
+ * Revoke an OAuth token.
+ * @param client_id will be generated by #createApp or #registerApp
+ * @param client_secret will be generated by #createApp or #registerApp
+ * @param token will be get #fetchAccessToken
+ */
+ public async revokeToken(client_id: string, client_secret: string, token: string): Promise>> {
+ return this.client.post>('/oauth/revoke', {
+ client_id,
+ client_secret,
+ token
+ })
+ }
+
+ // ======================================
+ // accounts
+ // ======================================
+ public async registerAccount(
+ _username: string,
+ _email: string,
+ _password: string,
+ _agreement: boolean,
+ _locale: string,
+ _reason?: string | null
+ ): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/verify_credentials
+ *
+ * @return Account.
+ */
+ public async verifyAccountCredentials(): Promise> {
+ return this.client.get('/api/v1/accounts/verify_credentials').then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.account(res.data)
+ })
+ })
+ }
+
+ public async updateCredentials(_options?: {
+ discoverable?: boolean
+ bot?: boolean
+ display_name?: string
+ note?: string
+ avatar?: string
+ header?: string
+ locked?: boolean
+ source?: {
+ privacy?: string
+ sensitive?: boolean
+ language?: string
+ }
+ fields_attributes?: Array<{ name: string; value: string }>
+ }): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/:id
+ *
+ * @param id The account ID.
+ * @return An account.
+ */
+ public async getAccount(id: string): Promise> {
+ return this.client.get(`/api/v1/accounts/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.account(res.data)
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/statuses
+ *
+ * @param id The account ID.
+
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID but starting with most recent.
+ * @param options.min_id Return results newer than ID.
+ * @param options.pinned Return statuses which include pinned statuses.
+ * @param options.exclude_replies Return statuses which exclude replies.
+ * @param options.exclude_reblogs Return statuses which exclude reblogs.
+ * @param options.only_media Show only statuses with media attached? Defaults to false.
+ * @return Account's statuses.
+ */
+ public async getAccountStatuses(
+ id: string,
+ options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ pinned?: boolean
+ exclude_replies?: boolean
+ exclude_reblogs?: boolean
+ only_media: boolean
+ }
+ ): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.pinned) {
+ params = Object.assign(params, {
+ pinned: options.pinned
+ })
+ }
+ if (options.exclude_replies) {
+ params = Object.assign(params, {
+ exclude_replies: options.exclude_replies
+ })
+ }
+ if (options.exclude_reblogs) {
+ params = Object.assign(params, {
+ exclude_reblogs: options.exclude_reblogs
+ })
+ }
+ if (options.only_media) {
+ params = Object.assign(params, {
+ only_media: options.only_media
+ })
+ }
+ }
+
+ return this.client.get>(`/api/v1/accounts/${id}/statuses`, params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/follow
+ *
+ * @param id Target account ID.
+ * @return Relationship.
+ */
+ public async subscribeAccount(id: string): Promise> {
+ const params = {
+ notify: true
+ }
+ return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/follow
+ *
+ * @param id Target account ID.
+ * @return Relationship.
+ */
+ public async unsubscribeAccount(id: string): Promise> {
+ const params = {
+ notify: false
+ }
+ return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ public getAccountFavourites(
+ _id: string,
+ _options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ }
+ ): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/followers
+ *
+ * @param id The account ID.
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @return The array of accounts.
+ */
+ public async getAccountFollowers(
+ id: string,
+ options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ get_all?: boolean
+ sleep_ms?: number
+ }
+ ): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.urlToAccounts(`/api/v1/accounts/${id}/followers`, params, options?.get_all || false, options?.sleep_ms || 0)
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/following
+ *
+ * @param id The account ID.
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @return The array of accounts.
+ */
+ public async getAccountFollowing(
+ id: string,
+ options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ get_all?: boolean
+ sleep_ms?: number
+ }
+ ): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.urlToAccounts(`/api/v1/accounts/${id}/following`, params, options?.get_all || false, options?.sleep_ms || 0)
+ }
+
+ /** Helper function to optionally follow Link headers as pagination */
+ private async urlToAccounts(url: string, params: Record, get_all: boolean, sleep_ms: number) {
+ const res = await this.client.get>(url, params)
+ let converted = Object.assign({}, res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ if (get_all && converted.headers.link) {
+ let parsed = parseLinkHeader(converted.headers.link)
+ while (parsed?.next) {
+ const nextRes = await this.client.get>(parsed?.next.url, undefined, undefined, true)
+ converted = Object.assign({}, converted, {
+ data: [...converted.data, ...nextRes.data.map(a => FriendicaAPI.Converter.account(a))]
+ })
+ parsed = parseLinkHeader(nextRes.headers.link)
+ if (sleep_ms) {
+ await new Promise(converted => setTimeout(converted, sleep_ms))
+ }
+ }
+ }
+ return converted
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/lists
+ *
+ * @param id The account ID.
+ * @return The array of lists.
+ */
+ public async getAccountLists(id: string): Promise>> {
+ return this.client.get>(`/api/v1/accounts/${id}/lists`).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(l => FriendicaAPI.Converter.list(l))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/:id/identity_proofs
+ *
+ * @param id The account ID.
+ * @return Array of IdentityProof
+ */
+ public async getIdentityProof(id: string): Promise>> {
+ return this.client.get>(`/api/v1/accounts/${id}/identity_proofs`).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(i => FriendicaAPI.Converter.identity_proof(i))
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/follow
+ *
+ * @param id The account ID.
+ * @param reblog Receive this account's reblogs in home timeline.
+ * @return Relationship
+ */
+ public async followAccount(id: string, options?: { reblog?: boolean }): Promise> {
+ let params = {}
+ if (options) {
+ if (options.reblog !== undefined) {
+ params = Object.assign(params, {
+ reblog: options.reblog
+ })
+ }
+ }
+ return this.client.post(`/api/v1/accounts/${id}/follow`, params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/unfollow
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async unfollowAccount(id: string): Promise> {
+ return this.client.post(`/api/v1/accounts/${id}/unfollow`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/block
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async blockAccount(id: string): Promise> {
+ return this.client.post(`/api/v1/accounts/${id}/block`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/unblock
+ *
+ * @param id The account ID.
+ * @return RElationship
+ */
+ public async unblockAccount(id: string): Promise> {
+ return this.client.post(`/api/v1/accounts/${id}/unblock`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/mute
+ *
+ * @param id The account ID.
+ * @param notifications Mute notifications in addition to statuses.
+ * @return Relationship
+ */
+ public async muteAccount(id: string, notifications = true): Promise> {
+ return this.client
+ .post(`/api/v1/accounts/${id}/mute`, {
+ notifications: notifications
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/unmute
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async unmuteAccount(id: string): Promise> {
+ return this.client.post(`/api/v1/accounts/${id}/unmute`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/pin
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async pinAccount(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * POST /api/v1/accounts/:id/unpin
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async unpinAccount(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/relationships
+ *
+ * @param id The account ID.
+ * @return Relationship
+ */
+ public async getRelationship(id: string): Promise> {
+ return this.client
+ .get>('/api/v1/accounts/relationships', {
+ id: [id]
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data[0])
+ })
+ })
+ }
+
+ /**
+ * Get multiple relationships in one method
+ *
+ * @param ids Array of account IDs.
+ * @return Array of Relationship.
+ */
+ public async getRelationships(ids: Array): Promise>> {
+ return this.client
+ .get>('/api/v1/accounts/relationships', {
+ id: ids
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: res.data.map(r => FriendicaAPI.Converter.relationship(r))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/accounts/search
+ *
+ * @param q Search query.
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @return The array of accounts.
+ */
+ public async searchAccount(
+ q: string,
+ options?: {
+ following?: boolean
+ resolve?: boolean
+ limit?: number
+ max_id?: string
+ since_id?: string
+ }
+ ): Promise>> {
+ let params = { q: q }
+ if (options) {
+ if (options.following !== undefined && options.following !== null) {
+ params = Object.assign(params, {
+ following: options.following
+ })
+ }
+ if (options.resolve !== undefined && options.resolve !== null) {
+ params = Object.assign(params, {
+ resolve: options.resolve
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/accounts/search', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/bookmarks
+ // ======================================
+ /**
+ * GET /api/v1/bookmarks
+ *
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getBookmarks(options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ }
+ return this.client.get>('/api/v1/bookmarks', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/favourites
+ // ======================================
+ /**
+ * GET /api/v1/favourites
+ *
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getFavourites(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/favourites', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/mutes
+ // ======================================
+ /**
+ * GET /api/v1/mutes
+ *
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of accounts.
+ */
+ public async getMutes(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/mutes', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/blocks
+ // ======================================
+ /**
+ * GET /api/v1/blocks
+ *
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of accounts.
+ */
+ public async getBlocks(options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/blocks', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/domain_blocks
+ // ======================================
+ public async getDomainBlocks(_options?: { limit?: number; max_id?: string; min_id?: string }): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public blockDomain(_domain: string): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public unblockDomain(_domain: string): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // accounts/filters
+ // ======================================
+ /**
+ * GET /api/v1/filters
+ *
+ * @return Array of filters.
+ */
+ public async getFilters(): Promise>> {
+ return this.client.get>('/api/v1/filters').then(res => {
+ return Object.assign(res, {
+ data: res.data.map(f => FriendicaAPI.Converter.filter(f))
+ })
+ })
+ }
+
+ public async getFilter(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public async createFilter(
+ _phrase: string,
+ _context: Array,
+ _options?: {
+ irreversible?: boolean
+ whole_word?: boolean
+ expires_in?: string
+ }
+ ): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public async updateFilter(
+ _id: string,
+ _phrase: string,
+ _context: Array,
+ _options?: {
+ irreversible?: boolean
+ whole_word?: boolean
+ expires_in?: string
+ }
+ ): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public async deleteFilter(_id: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // accounts/reports
+ // ======================================
+ public async report(
+ _account_id: string,
+ _options?: {
+ status_ids?: Array
+ comment: string
+ forward?: boolean
+ category?: Entity.Category
+ rule_ids?: Array
+ }
+ ): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // accounts/follow_requests
+ // ======================================
+ /**
+ * GET /api/v1/follow_requests
+ *
+ * @param limit Maximum number of results.
+ * @return Array of FollowRequest.
+ */
+ public async getFollowRequests(limit?: number): Promise>> {
+ if (limit) {
+ return this.client
+ .get>('/api/v1/follow_requests', {
+ limit: limit
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.follow_request(a))
+ })
+ })
+ } else {
+ return this.client.get>('/api/v1/follow_requests').then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.follow_request(a))
+ })
+ })
+ }
+ }
+
+ /**
+ * POST /api/v1/follow_requests/:id/authorize
+ *
+ * @param id The FollowRequest ID.
+ * @return Relationship.
+ */
+ public async acceptFollowRequest(id: string): Promise> {
+ return this.client.post(`/api/v1/follow_requests/${id}/authorize`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/follow_requests/:id/reject
+ *
+ * @param id The FollowRequest ID.
+ * @return Relationship.
+ */
+ public async rejectFollowRequest(id: string): Promise> {
+ return this.client.post(`/api/v1/follow_requests/${id}/reject`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.relationship(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/endorsements
+ // ======================================
+ /**
+ * GET /api/v1/endorsements
+ *
+ * @param options.limit Max number of results to return. Defaults to 40.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @return Array of accounts.
+ */
+ public async getEndorsements(options?: { limit?: number; max_id?: string; since_id?: string }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ }
+ return this.client.get>('/api/v1/endorsements', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/featured_tags
+ // ======================================
+ public async getFeaturedTags(): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public async createFeaturedTag(_name: string): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public deleteFeaturedTag(_id: string): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ public async getSuggestedTags(): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // accounts/preferences
+ // ======================================
+ /**
+ * GET /api/v1/preferences
+ *
+ * @return Preferences.
+ */
+ public async getPreferences(): Promise> {
+ return this.client.get('/api/v1/preferences').then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.preferences(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // accounts/followed_tags
+ // ======================================
+ public async getFollowedTags(): Promise>> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // accounts/suggestions
+ // ======================================
+ /**
+ * GET /api/v1/suggestions
+ *
+ * @param limit Maximum number of results.
+ * @return Array of accounts.
+ */
+ public async getSuggestions(limit?: number): Promise>> {
+ if (limit) {
+ return this.client
+ .get>('/api/v1/suggestions', {
+ limit: limit
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ } else {
+ return this.client.get>('/api/v1/suggestions').then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+ }
+
+ // ======================================
+ // accounts/tags
+ // ======================================
+ /**
+ * GET /api/v1/tags/:id
+ *
+ * @param id Target hashtag id.
+ * @return Tag
+ */
+ public async getTag(id: string): Promise> {
+ return this.client.get(`/api/v1/tags/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.tag(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/tags/:id/follow
+ *
+ * @param id Target hashtag id.
+ * @return Tag
+ */
+ public async followTag(id: string): Promise> {
+ return this.client.post(`/api/v1/tags/${id}/follow`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.tag(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/tags/:id/unfollow
+ *
+ * @param id Target hashtag id.
+ * @return Tag
+ */
+ public async unfollowTag(id: string): Promise> {
+ return this.client.post(`/api/v1/tags/${id}/unfollow`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.tag(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // statuses
+ // ======================================
+ /**
+ * POST /api/v1/statuses
+ *
+ * @param status Text content of status.
+ * @param options.media_ids Array of Attachment ids.
+ * @param options.poll Poll object.
+ * @param options.in_reply_to_id ID of the status being replied to, if status is a reply.
+ * @param options.sensitive Mark status and attached media as sensitive?
+ * @param options.spoiler_text Text to be shown as a warning or subject before the actual content.
+ * @param options.visibility Visibility of the posted status.
+ * @param options.scheduled_at ISO 8601 Datetime at which to schedule a status.
+ * @param options.language ISO 639 language code for this status.
+ * @param options.quote_id ID of the status being quoted to, if status is a quote.
+ * @return Status. When options.scheduled_at is present, ScheduledStatus is returned instead.
+ */
+ public async postStatus(
+ status: string,
+ options: {
+ media_ids?: Array
+ poll?: { options: Array; expires_in: number; multiple?: boolean; hide_totals?: boolean }
+ in_reply_to_id?: string
+ sensitive?: boolean
+ spoiler_text?: string
+ visibility?: 'public' | 'unlisted' | 'private' | 'direct'
+ scheduled_at?: string
+ language?: string
+ quote_id?: string
+ }
+ ): Promise> {
+ let params = {
+ status: status
+ }
+ if (options) {
+ if (options.media_ids) {
+ params = Object.assign(params, {
+ media_ids: options.media_ids
+ })
+ }
+ if (options.poll) {
+ let pollParam = {
+ options: options.poll.options,
+ expires_in: options.poll.expires_in
+ }
+ if (options.poll.multiple !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ multiple: options.poll.multiple
+ })
+ }
+ if (options.poll.hide_totals !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ hide_totals: options.poll.hide_totals
+ })
+ }
+ params = Object.assign(params, {
+ poll: pollParam
+ })
+ }
+ if (options.in_reply_to_id) {
+ params = Object.assign(params, {
+ in_reply_to_id: options.in_reply_to_id
+ })
+ }
+ if (options.sensitive !== undefined) {
+ params = Object.assign(params, {
+ sensitive: options.sensitive
+ })
+ }
+ if (options.spoiler_text) {
+ params = Object.assign(params, {
+ spoiler_text: options.spoiler_text
+ })
+ }
+ if (options.visibility) {
+ params = Object.assign(params, {
+ visibility: options.visibility
+ })
+ }
+ if (options.scheduled_at) {
+ params = Object.assign(params, {
+ scheduled_at: options.scheduled_at
+ })
+ }
+ if (options.language) {
+ params = Object.assign(params, {
+ language: options.language
+ })
+ }
+ if (options.quote_id) {
+ params = Object.assign(params, {
+ quote_id: options.quote_id
+ })
+ }
+ }
+ if (options.scheduled_at) {
+ return this.client.post('/api/v1/statuses', params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.scheduled_status(res.data)
+ })
+ })
+ }
+ return this.client.post('/api/v1/statuses', params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+ /**
+ * GET /api/v1/statuses/:id
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async getStatus(id: string): Promise> {
+ return this.client.get(`/api/v1/statuses/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ PUT /api/v1/statuses/:id
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async editStatus(
+ id: string,
+ options: {
+ status?: string
+ spoiler_text?: string
+ sensitive?: boolean
+ media_ids?: Array
+ poll?: { options?: Array; expires_in?: number; multiple?: boolean; hide_totals?: boolean }
+ }
+ ): Promise> {
+ let params = {}
+ if (options.status) {
+ params = Object.assign(params, {
+ status: options.status
+ })
+ }
+ if (options.spoiler_text) {
+ params = Object.assign(params, {
+ spoiler_text: options.spoiler_text
+ })
+ }
+ if (options.sensitive) {
+ params = Object.assign(params, {
+ sensitive: options.sensitive
+ })
+ }
+ if (options.media_ids) {
+ params = Object.assign(params, {
+ media_ids: options.media_ids
+ })
+ }
+ if (options.poll) {
+ let pollParam = {}
+ if (options.poll.options !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ options: options.poll.options
+ })
+ }
+ if (options.poll.expires_in !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ expires_in: options.poll.expires_in
+ })
+ }
+ if (options.poll.multiple !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ multiple: options.poll.multiple
+ })
+ }
+ if (options.poll.hide_totals !== undefined) {
+ pollParam = Object.assign(pollParam, {
+ hide_totals: options.poll.hide_totals
+ })
+ }
+ params = Object.assign(params, {
+ poll: pollParam
+ })
+ }
+ return this.client.put(`/api/v1/statuses/${id}`, params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * DELETE /api/v1/statuses/:id
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async deleteStatus(id: string): Promise> {
+ return this.client.del(`/api/v1/statuses/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/statuses/:id/context
+ *
+ * Get parent and child statuses.
+ * @param id The target status id.
+ * @return Context
+ */
+ public async getStatusContext(
+ id: string,
+ options?: { limit?: number; max_id?: string; since_id?: string }
+ ): Promise> {
+ let params = {}
+ if (options) {
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ }
+ return this.client.get(`/api/v1/statuses/${id}/context`, params).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.context(res.data)
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/statuses/:id/source
+ *
+ * Obtain the source properties for a status so that it can be edited.
+ * @param id The target status id.
+ * @return StatusSource
+ */
+ public async getStatusSource(id: string): Promise> {
+ return this.client.get(`/api/v1/statuses/${id}/source`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status_source(res.data)
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/statuses/:id/reblogged_by
+ *
+ * @param id The target status id.
+ * @return Array of accounts.
+ */
+ public async getStatusRebloggedBy(id: string): Promise>> {
+ return this.client.get>(`/api/v1/statuses/${id}/reblogged_by`).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/statuses/:id/favourited_by
+ *
+ * @param id The target status id.
+ * @return Array of accounts.
+ */
+ public async getStatusFavouritedBy(id: string): Promise>> {
+ return this.client.get>(`/api/v1/statuses/${id}/favourited_by`).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(a => FriendicaAPI.Converter.account(a))
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/favourite
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async favouriteStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/favourite`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unfavourite
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async unfavouriteStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/unfavourite`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/reblog
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async reblogStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/reblog`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unreblog
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async unreblogStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/unreblog`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/bookmark
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async bookmarkStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/bookmark`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unbookmark
+ *
+ * @param id The target status id.
+ * @return Status.
+ */
+ public async unbookmarkStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/unbookmark`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/mute
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async muteStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/mute`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unmute
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async unmuteStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/unmute`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/pin
+ * @param id The target status id.
+ * @return Status
+ */
+ public async pinStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/pin`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/statuses/:id/unpin
+ *
+ * @param id The target status id.
+ * @return Status
+ */
+ public async unpinStatus(id: string): Promise> {
+ return this.client.post(`/api/v1/statuses/${id}/unpin`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.status(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // statuses/media
+ // ======================================
+ /**
+ * POST /api/v2/media
+ *
+ * @param file The file to be attached, using multipart form data.
+ * @param options.description A plain-text description of the media.
+ * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0.
+ * @return Attachment
+ */
+ public async uploadMedia(
+ file: any,
+ options?: { description?: string; focus?: string }
+ ): Promise> {
+ const formData = new FormData()
+ formData.append('file', file)
+ if (options) {
+ if (options.description) {
+ formData.append('description', options.description)
+ }
+ if (options.focus) {
+ formData.append('focus', options.focus)
+ }
+ }
+ return this.client.postForm('/api/v2/media', formData).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.async_attachment(res.data)
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/media/:id
+ *
+ * @param id Target media ID.
+ * @return Attachment
+ */
+ public async getMedia(id: string): Promise> {
+ const res = await this.client.get(`/api/v1/media/${id}`)
+
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.attachment(res.data)
+ })
+ }
+
+ /**
+ * PUT /api/v1/media/:id
+ *
+ * @param id Target media ID.
+ * @param options.file The file to be attached, using multipart form data.
+ * @param options.description A plain-text description of the media.
+ * @param options.focus Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0.
+ * @param options.is_sensitive Whether the media is sensitive.
+ * @return Attachment
+ */
+ public async updateMedia(
+ id: string,
+ options?: {
+ file?: any
+ description?: string
+ focus?: string
+ }
+ ): Promise> {
+ const formData = new FormData()
+ if (options) {
+ if (options.file) {
+ formData.append('file', options.file)
+ }
+ if (options.description) {
+ formData.append('description', options.description)
+ }
+ if (options.focus) {
+ formData.append('focus', options.focus)
+ }
+ }
+ return this.client.putForm(`/api/v1/media/${id}`, formData).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.attachment(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // statuses/polls
+ // ======================================
+ /**
+ * GET /api/v1/polls/:id
+ *
+ * @param id Target poll ID.
+ * @return Poll
+ */
+ public async getPoll(id: string): Promise> {
+ return this.client.get(`/api/v1/polls/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.poll(res.data)
+ })
+ })
+ }
+
+ public async votePoll(_id: string, _choices: Array): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ // ======================================
+ // statuses/scheduled_statuses
+ // ======================================
+ /**
+ * GET /api/v1/scheduled_statuses
+ *
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of scheduled statuses.
+ */
+ public async getScheduledStatuses(options?: {
+ limit?: number | null
+ max_id?: string | null
+ since_id?: string | null
+ min_id?: string | null
+ }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ }
+ return this.client.get>('/api/v1/scheduled_statuses', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.scheduled_status(s))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/scheduled_statuses/:id
+ *
+ * @param id Target status ID.
+ * @return ScheduledStatus.
+ */
+ public async getScheduledStatus(id: string): Promise> {
+ return this.client.get(`/api/v1/scheduled_statuses/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.scheduled_status(res.data)
+ })
+ })
+ }
+
+ public async scheduleStatus(_id: string, _scheduled_at?: string | null): Promise> {
+ return new Promise((_, reject) => {
+ const err = new NoImplementedError('friendica does not support')
+ reject(err)
+ })
+ }
+
+ /**
+ * DELETE /api/v1/scheduled_statuses/:id
+ *
+ * @param id Target scheduled status ID.
+ */
+ public cancelScheduledStatus(id: string): Promise>> {
+ return this.client.del>(`/api/v1/scheduled_statuses/${id}`)
+ }
+
+ // ======================================
+ // timelines
+ // ======================================
+ /**
+ * GET /api/v1/timelines/public
+ *
+ * @param options.only_media Show only statuses with media attached? Defaults to false.
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getPublicTimeline(options?: {
+ only_media?: boolean
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }): Promise>> {
+ let params = {
+ local: false
+ }
+ if (options) {
+ if (options.only_media !== undefined) {
+ params = Object.assign(params, {
+ only_media: options.only_media
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/timelines/public', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/timelines/public
+ *
+ * @param options.only_media Show only statuses with media attached? Defaults to false.
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getLocalTimeline(options?: {
+ only_media?: boolean
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }): Promise>> {
+ let params = {
+ local: true
+ }
+ if (options) {
+ if (options.only_media !== undefined) {
+ params = Object.assign(params, {
+ only_media: options.only_media
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/timelines/public', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/timelines/tag/:hashtag
+ *
+ * @param hashtag Content of a #hashtag, not including # symbol.
+ * @param options.local Show only local statuses? Defaults to false.
+ * @param options.only_media Show only statuses with media attached? Defaults to false.
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getTagTimeline(
+ hashtag: string,
+ options?: {
+ local?: boolean
+ only_media?: boolean
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }
+ ): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.local !== undefined) {
+ params = Object.assign(params, {
+ local: options.local
+ })
+ }
+ if (options.only_media !== undefined) {
+ params = Object.assign(params, {
+ only_media: options.only_media
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>(`/api/v1/timelines/tag/${hashtag}`, params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/timelines/home
+ *
+ * @param options.local Show only local statuses? Defaults to false.
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getHomeTimeline(options?: {
+ local?: boolean
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.local !== undefined) {
+ params = Object.assign(params, {
+ local: options.local
+ })
+ }
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/timelines/home', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/timelines/list/:list_id
+ *
+ * @param list_id Local ID of the list in the database.
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getListTimeline(
+ list_id: string,
+ options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }
+ ): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>(`/api/v1/timelines/list/${list_id}`, params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(s => FriendicaAPI.Converter.status(s))
+ })
+ })
+ }
+
+ // ======================================
+ // timelines/conversations
+ // ======================================
+ /**
+ * GET /api/v1/conversations
+ *
+ * @param options.limit Max number of results to return. Defaults to 20.
+ * @param options.max_id Return results older than ID.
+ * @param options.since_id Return results newer than ID.
+ * @param options.min_id Return results immediately newer than ID.
+ * @return Array of statuses.
+ */
+ public async getConversationTimeline(options?: {
+ limit?: number
+ max_id?: string
+ since_id?: string
+ min_id?: string
+ }): Promise>> {
+ let params = {}
+ if (options) {
+ if (options.max_id) {
+ params = Object.assign(params, {
+ max_id: options.max_id
+ })
+ }
+ if (options.since_id) {
+ params = Object.assign(params, {
+ since_id: options.since_id
+ })
+ }
+ if (options.min_id) {
+ params = Object.assign(params, {
+ min_id: options.min_id
+ })
+ }
+ if (options.limit) {
+ params = Object.assign(params, {
+ limit: options.limit
+ })
+ }
+ }
+ return this.client.get>('/api/v1/conversations', params).then(res => {
+ return Object.assign(res, {
+ data: res.data.map(c => FriendicaAPI.Converter.conversation(c))
+ })
+ })
+ }
+
+ /**
+ * DELETE /api/v1/conversations/:id
+ *
+ * @param id Target conversation ID.
+ */
+ public deleteConversation(id: string): Promise>> {
+ return this.client.del>(`/api/v1/conversations/${id}`)
+ }
+
+ /**
+ * POST /api/v1/conversations/:id/read
+ *
+ * @param id Target conversation ID.
+ * @return Conversation.
+ */
+ public async readConversation(id: string): Promise> {
+ return this.client.post(`/api/v1/conversations/${id}/read`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.conversation(res.data)
+ })
+ })
+ }
+
+ // ======================================
+ // timelines/lists
+ // ======================================
+ /**
+ * GET /api/v1/lists
+ *
+ * @return Array of lists.
+ */
+ public async getLists(): Promise>> {
+ return this.client.get>('/api/v1/lists').then(res => {
+ return Object.assign(res, {
+ data: res.data.map(l => FriendicaAPI.Converter.list(l))
+ })
+ })
+ }
+
+ /**
+ * GET /api/v1/lists/:id
+ *
+ * @param id Target list ID.
+ * @return List.
+ */
+ public async getList(id: string): Promise> {
+ return this.client.get(`/api/v1/lists/${id}`).then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.list(res.data)
+ })
+ })
+ }
+
+ /**
+ * POST /api/v1/lists
+ *
+ * @param title List name.
+ * @return List.
+ */
+ public async createList(title: string): Promise> {
+ return this.client
+ .post('/api/v1/lists', {
+ title: title
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.list(res.data)
+ })
+ })
+ }
+
+ /**
+ * PUT /api/v1/lists/:id
+ *
+ * @param id Target list ID.
+ * @param title New list name.
+ * @return List.
+ */
+ public async updateList(id: string, title: string): Promise> {
+ return this.client
+ .put(`/api/v1/lists/${id}`, {
+ title: title
+ })
+ .then(res => {
+ return Object.assign(res, {
+ data: FriendicaAPI.Converter.list(res.data)
+ })
+ })
+ }
+
+ /**
+ * DELETE /api/v1/lists/:id
+ *
+ * @param id Target list ID.
+ */
+ public deleteList(id: string): Promise>> {
+ return this.client.del