mirror of
https://github.com/Sevichecc/raycast-akkoma-extension.git
synced 2025-04-30 14:49:29 +08:00
refactor: seperate api and oauth
This commit is contained in:
parent
2e0377f7f4
commit
f9f8bbe3b6
6 changed files with 297 additions and 140 deletions
|
@ -1,3 +1,3 @@
|
||||||
# Post to Akkoma
|
# Post to Akkoma
|
||||||
|
|
||||||
Write new post and send it to Akkoma
|
Write a new post and send it to Akkoma
|
14
package.json
14
package.json
|
@ -28,7 +28,7 @@
|
||||||
"placeholder": "such as: example.dev"
|
"placeholder": "such as: example.dev"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "defaultVisbility",
|
"name": "defaultVisibility",
|
||||||
"type": "dropdown",
|
"type": "dropdown",
|
||||||
"required": false,
|
"required": false,
|
||||||
"data": [
|
"data": [
|
||||||
|
@ -36,17 +36,21 @@
|
||||||
"title": "🌎 Public",
|
"title": "🌎 Public",
|
||||||
"value": "public"
|
"value": "public"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "👥 Private",
|
|
||||||
"value": "private"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "🙈 Unlist",
|
"title": "🙈 Unlist",
|
||||||
"value": "unlist"
|
"value": "unlist"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "👥 Private",
|
||||||
|
"value": "private"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "✉️ Direct",
|
"title": "✉️ Direct",
|
||||||
"value": "direct"
|
"value": "direct"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "📍 Local",
|
||||||
|
"value": "local"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Akkoma instance's URL",
|
"title": "Akkoma instance's URL",
|
||||||
|
|
75
src/api.ts
Normal file
75
src/api.ts
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import fetch from "node-fetch";
|
||||||
|
import { OAuth, getPreferenceValues } from "@raycast/api";
|
||||||
|
import { AppResponse, Preference, Status } from "./types";
|
||||||
|
import { authorize } from "./oauth";
|
||||||
|
|
||||||
|
export const fetchToken = async (params: URLSearchParams, errorMessage: string): Promise<OAuth.TokenResponse> => {
|
||||||
|
const { instance } = getPreferenceValues<Preference>();
|
||||||
|
|
||||||
|
const response = await fetch(`https://${instance}/oauth/token`, {
|
||||||
|
method: "POST",
|
||||||
|
body: params,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error(errorMessage, await response.text());
|
||||||
|
throw new Error(response.statusText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await response.json()) as OAuth.TokenResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createApp = async (): Promise<AppResponse> => {
|
||||||
|
const { instance } = getPreferenceValues<Preference>();
|
||||||
|
|
||||||
|
const response = await fetch(`https://${instance}/api/v1/apps`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
client_name: "raycast-akkoma-extension",
|
||||||
|
redirect_uris: "https://raycast.com/redirect?packageName=Extension",
|
||||||
|
scopes: "read write push",
|
||||||
|
website: "https://raycast.com",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to create Akkoma app");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await response.json()) as AppResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const postNewStatus = async <T>({
|
||||||
|
status,
|
||||||
|
visibility,
|
||||||
|
spoiler_text,
|
||||||
|
sensitive,
|
||||||
|
scheduled_at,
|
||||||
|
}: Status): Promise<T> => {
|
||||||
|
const { instance } = getPreferenceValues<Preference>();
|
||||||
|
const token = await authorize();
|
||||||
|
|
||||||
|
const response = await fetch(`https://${instance}/api/v1/statuses`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: "Bearer" + token,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
status,
|
||||||
|
visibility,
|
||||||
|
spoiler_text,
|
||||||
|
sensitive,
|
||||||
|
scheduled_at,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to pulish new status");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await response.json()) as T;
|
||||||
|
};
|
105
src/index.tsx
105
src/index.tsx
|
@ -1,78 +1,51 @@
|
||||||
import { useCallback, useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Form, ActionPanel, Action, showToast } from "@raycast/api";
|
import { Form, ActionPanel, Action, showToast } from "@raycast/api";
|
||||||
import { authorize, client } from "./oauth";
|
import { authorize } from "./oauth";
|
||||||
import fetch from "node-fetch";
|
import { postNewStatus } from "./api";
|
||||||
import { getPreferenceValues } from "@raycast/api";
|
import { Status, VisibilityOption } from "./types";
|
||||||
import { Preference,ApiResponse } from "./types";
|
|
||||||
|
|
||||||
type Values = {
|
|
||||||
textfield: string;
|
|
||||||
textarea: string;
|
|
||||||
datepicker: Date;
|
|
||||||
sensitive: boolean;
|
|
||||||
dropdown: string;
|
|
||||||
files: { name: string; url: string }[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Command() {
|
export default function Command() {
|
||||||
const { instance } = getPreferenceValues<Preference>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
authorize();
|
authorize();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSubmit = useCallback(
|
const visibilityOptions: VisibilityOption[] = [
|
||||||
async (values: Values) => {
|
{ value: "direct", title: "Direct" },
|
||||||
try {
|
{ value: "private", title: "Private" },
|
||||||
const token = await client.getTokens();
|
{ value: "unlisted", title: "Unlisted" },
|
||||||
|
{ value: "public", title: "Public" },
|
||||||
|
{ value: "local", title: "Local" },
|
||||||
|
];
|
||||||
|
|
||||||
const response = await fetch(`https://${instance}/api/v1/statuses`, {
|
const handleSubmit = async (values: Status) => {
|
||||||
method: "POST",
|
try {
|
||||||
headers: {
|
await postNewStatus({ ...values });
|
||||||
"Content-Type": "application/json",
|
showToast({ title: "Submitted form", message: "Status has been posted!" });
|
||||||
"Authorization": `Bearer ${token?.accessToken}`,
|
} catch (error) {
|
||||||
},
|
console.error(error);
|
||||||
body: JSON.stringify({
|
showToast({ title: "Error", message: "Something went wrong!" });
|
||||||
status: values.textarea,
|
}
|
||||||
visibility: values.dropdown,
|
};
|
||||||
spoiler_text: values.textfield,
|
|
||||||
sensitive: values.sensitive,
|
|
||||||
scheduled_at: values.datepicker.toISOString(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = (await response.json()) as ApiResponse;
|
|
||||||
console.log(data);
|
|
||||||
showToast({ title: "Submitted form", message: "Status has been posted!" });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast({ title: "Error", message: "Something went wrong!" });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[instance]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Form
|
||||||
<Form
|
actions={
|
||||||
actions={
|
<ActionPanel>
|
||||||
<ActionPanel>
|
<Action.SubmitForm onSubmit={handleSubmit} />
|
||||||
<Action.SubmitForm onSubmit={handleSubmit} />
|
</ActionPanel>
|
||||||
</ActionPanel>
|
}
|
||||||
}
|
>
|
||||||
>
|
<Form.TextField id="textfield" title="Content Warning" placeholder="" />
|
||||||
<Form.TextField id="textfield" title="Content Warning" placeholder="" />
|
<Form.TextArea id="staus" title="Post detail" placeholder="" enableMarkdown={true} />
|
||||||
<Form.TextArea id="textarea" title="Post detail" placeholder="" enableMarkdown={true} />
|
<Form.Separator />
|
||||||
<Form.Separator />
|
<Form.DatePicker id="datepicker" title="Scheduled Time" />
|
||||||
<Form.DatePicker id="datepicker" title="Scheduled Time" />
|
<Form.Dropdown id="visibility" title="Visibility" storeValue={true} defaultValue="">
|
||||||
<Form.Dropdown id="dropdown" title="Visibility" storeValue={true} defaultValue="">
|
{visibilityOptions.map(({value, title}) => (
|
||||||
<Form.Dropdown.Item value="direct" title="Direct" />
|
<Form.Dropdown.Item key={value} value={value} title={title} />
|
||||||
<Form.Dropdown.Item value="private" title="Private" />
|
))}
|
||||||
<Form.Dropdown.Item value="unlisted" title="Unlisted" />
|
</Form.Dropdown>
|
||||||
<Form.Dropdown.Item value="public" title="Public" />
|
<Form.FilePicker id="files" />
|
||||||
</Form.Dropdown>
|
<Form.Checkbox id="sensitive" title="Sensitive" label="Sensitive" />
|
||||||
<Form.FilePicker id="files" />
|
</Form>
|
||||||
<Form.Checkbox id="sensitive" title="Sensitive" label="Sensitive" />
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
65
src/oauth.ts
65
src/oauth.ts
|
@ -1,10 +1,8 @@
|
||||||
import { OAuth, getPreferenceValues } from "@raycast/api";
|
import { OAuth, getPreferenceValues } from "@raycast/api";
|
||||||
import fetch from "node-fetch";
|
import { Preference } from "./types";
|
||||||
import { Preference, AppResponse } from "./types";
|
import { fetchToken,createApp} from "./api";
|
||||||
|
|
||||||
const redirectUri = "https://raycast.com/redirect?packageName=Extension";
|
const client = new OAuth.PKCEClient({
|
||||||
|
|
||||||
export const client = new OAuth.PKCEClient({
|
|
||||||
redirectMethod: OAuth.RedirectMethod.Web,
|
redirectMethod: OAuth.RedirectMethod.Web,
|
||||||
providerName: "Akkoma",
|
providerName: "Akkoma",
|
||||||
providerIcon: "akkoma-icon.png",
|
providerIcon: "akkoma-icon.png",
|
||||||
|
@ -12,37 +10,12 @@ export const client = new OAuth.PKCEClient({
|
||||||
description: "Connect to your Akkoma | Pleroma | Mastodon account",
|
description: "Connect to your Akkoma | Pleroma | Mastodon account",
|
||||||
});
|
});
|
||||||
|
|
||||||
const createAkkomaApp = async (): Promise<AppResponse> => {
|
const requestAccessToken = async (
|
||||||
const { instance } = getPreferenceValues<Preference>();
|
|
||||||
|
|
||||||
const response = await fetch(`https://${instance}/api/v1/apps`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
client_name: "raycast-akkoma-extension",
|
|
||||||
redirect_uris: redirectUri,
|
|
||||||
scopes: "read write push",
|
|
||||||
website: "https://raycast.com",
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error("Failed to create Akkoma app");
|
|
||||||
}
|
|
||||||
|
|
||||||
const appResponse = await response.json();
|
|
||||||
return appResponse as AppResponse;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const requestAccessToken = async (
|
|
||||||
clientId: string,
|
clientId: string,
|
||||||
clientSecret: string,
|
clientSecret: string,
|
||||||
authRequest: OAuth.AuthorizationRequest,
|
authRequest: OAuth.AuthorizationRequest,
|
||||||
authCode: string
|
authCode: string
|
||||||
): Promise<OAuth.TokenResponse> => {
|
): Promise<OAuth.TokenResponse> => {
|
||||||
const { instance } = getPreferenceValues<Preference>();
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append("client_id", clientId);
|
params.append("client_id", clientId);
|
||||||
|
@ -52,23 +25,14 @@ export const requestAccessToken = async (
|
||||||
params.append("grant_type", "authorization_code");
|
params.append("grant_type", "authorization_code");
|
||||||
params.append("redirect_uri", authRequest.redirectURI);
|
params.append("redirect_uri", authRequest.redirectURI);
|
||||||
|
|
||||||
const response = await fetch(`https://${instance}/oauth/token`, {
|
return await fetchToken(params, "fetch tokens error:");
|
||||||
method: "POST",
|
|
||||||
body: params,
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("fetch tokens error:", await response.text());
|
|
||||||
throw new Error(response.statusText);
|
|
||||||
}
|
|
||||||
return (await response.json()) as OAuth.TokenResponse;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const refreshToken = async (
|
const refreshToken = async (
|
||||||
clientId: string,
|
clientId: string,
|
||||||
clientSecret: string,
|
clientSecret: string,
|
||||||
refreshToken: string
|
refreshToken: string
|
||||||
): Promise<OAuth.TokenResponse> => {
|
): Promise<OAuth.TokenResponse> => {
|
||||||
const { instance } = getPreferenceValues<Preference>();
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append("client_id", clientId);
|
params.append("client_id", clientId);
|
||||||
|
@ -76,40 +40,33 @@ export const refreshToken = async (
|
||||||
params.append("refresh_token", refreshToken);
|
params.append("refresh_token", refreshToken);
|
||||||
params.append("grant_type", "refresh_token");
|
params.append("grant_type", "refresh_token");
|
||||||
|
|
||||||
const response = await fetch(`https://${instance}/oauth/token`, {
|
const tokenResponse = await fetchToken(params, "refresh tokens error:");
|
||||||
method: "POST",
|
|
||||||
body: params,
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error("refresh tokens error:", await response.text());
|
|
||||||
throw new Error(response.statusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenResponse = (await response.json()) as OAuth.TokenResponse;
|
|
||||||
tokenResponse.refresh_token = tokenResponse.refresh_token ?? refreshToken;
|
tokenResponse.refresh_token = tokenResponse.refresh_token ?? refreshToken;
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 授权过程
|
|
||||||
export const authorize = async (): Promise<void> => {
|
export const authorize = async (): Promise<void> => {
|
||||||
const { instance } = getPreferenceValues<Preference>();
|
const { instance } = getPreferenceValues<Preference>();
|
||||||
const tokenSet = await client.getTokens();
|
const tokenSet = await client.getTokens();
|
||||||
|
|
||||||
if (tokenSet?.accessToken) {
|
if (tokenSet?.accessToken) {
|
||||||
if (tokenSet.refreshToken && tokenSet.isExpired()) {
|
if (tokenSet.refreshToken && tokenSet.isExpired()) {
|
||||||
const { client_id, client_secret } = await createAkkomaApp();
|
const { client_id, client_secret } = await createApp();
|
||||||
await client.setTokens(await refreshToken(client_id, client_secret, tokenSet.refreshToken));
|
await client.setTokens(await refreshToken(client_id, client_secret, tokenSet.refreshToken));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { client_id, client_secret } = await createAkkomaApp();
|
const { client_id, client_secret } = await createApp();
|
||||||
|
|
||||||
const authRequest = await client.authorizationRequest({
|
const authRequest = await client.authorizationRequest({
|
||||||
endpoint: `https://${instance}/oauth/authorize`,
|
endpoint: `https://${instance}/oauth/authorize`,
|
||||||
clientId: client_id,
|
clientId: client_id,
|
||||||
scope: "read write push",
|
scope: "read write push",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { authorizationCode } = await client.authorize(authRequest);
|
const { authorizationCode } = await client.authorize(authRequest);
|
||||||
|
|
||||||
await client.setTokens(await requestAccessToken(client_id, client_secret, authRequest, authorizationCode));
|
await client.setTokens(await requestAccessToken(client_id, client_secret, authRequest, authorizationCode));
|
||||||
|
|
174
src/types.ts
174
src/types.ts
|
@ -1,5 +1,13 @@
|
||||||
|
export type VisibilityScope = "public" | "unlisted" | "direct" | "private" | "local"
|
||||||
|
|
||||||
|
export interface VisibilityOption {
|
||||||
|
title: string;
|
||||||
|
value: VisibilityScope
|
||||||
|
}
|
||||||
|
|
||||||
export interface Preference {
|
export interface Preference {
|
||||||
instance: string
|
instance: string;
|
||||||
|
defaultVisibility: VisibilityScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Credentials {
|
export interface Credentials {
|
||||||
|
@ -28,18 +36,158 @@ export interface AppResponse {
|
||||||
vapid_key: string;
|
vapid_key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Status {
|
interface Poll {
|
||||||
content_type: string;
|
expired_in: number;
|
||||||
expires_in: number;
|
hide_totals?: boolean | string;
|
||||||
language: string;
|
multiple?: boolean | string | number;
|
||||||
media_ids: string[];
|
options: string[]
|
||||||
preview: boolean | string | number;
|
|
||||||
scheduled_at: string;
|
|
||||||
sensitive: string | boolean | number;
|
|
||||||
spoiler_text: string;
|
|
||||||
status: string;
|
|
||||||
to: string[];
|
|
||||||
visibility: "direct" | "private" | "unlisted" | "public";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Status {
|
||||||
|
content_type?: string;
|
||||||
|
expires_in?: number;
|
||||||
|
in_reply_to_conversation_id?: string;
|
||||||
|
in_reply_to_id?: string;
|
||||||
|
language?: string;
|
||||||
|
media_ids?: string[];
|
||||||
|
poll?: Poll;
|
||||||
|
preview?: boolean | string | number;
|
||||||
|
scheduled_at?: string;
|
||||||
|
sensitive?: string | boolean | number;
|
||||||
|
spoiler_text?: string;
|
||||||
|
status?: string;
|
||||||
|
to?: string[];
|
||||||
|
visibility?: VisibilityScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface Account {
|
||||||
|
// acct: string;
|
||||||
|
// avatar: string;
|
||||||
|
// avatar_static: string;
|
||||||
|
// bot: boolean;
|
||||||
|
// created_at: string;
|
||||||
|
// display_name: string;
|
||||||
|
// emojis: Emoji[];
|
||||||
|
// fields: Field[];
|
||||||
|
// followers_count: number;
|
||||||
|
// following_count: number;
|
||||||
|
// header: string;
|
||||||
|
// header_static: string;
|
||||||
|
// id: string;
|
||||||
|
// is_confirmed: boolean;
|
||||||
|
// note: string;
|
||||||
|
// pleroma: Pleroma;
|
||||||
|
// source: Source;
|
||||||
|
// statuses_count: number;
|
||||||
|
// url: string;
|
||||||
|
// username: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface Emoji {
|
||||||
|
// shortcode: string;
|
||||||
|
// static_url: string;
|
||||||
|
// url: string;
|
||||||
|
// visible_in_picker: boolean;
|
||||||
|
// }
|
||||||
|
// interface Field {
|
||||||
|
// name: string;
|
||||||
|
// value: string;
|
||||||
|
// verified_at: string | null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface Pleroma {
|
||||||
|
// background_image: null;
|
||||||
|
// hide_favorites: boolean;
|
||||||
|
// hide_followers: boolean;
|
||||||
|
// hide_followers_count: boolean;
|
||||||
|
// hide_follows: boolean;
|
||||||
|
// hide_follows_count: boolean;
|
||||||
|
// is_admin: boolean;
|
||||||
|
// is_confirmed: boolean;
|
||||||
|
// is_moderator: boolean;
|
||||||
|
// relationship: Relationship;
|
||||||
|
// skip_thread_containment: boolean;
|
||||||
|
// tags: any[];
|
||||||
|
// }
|
||||||
|
// type Actor = "Application" | "Group" | "Organization" | "Person" | "Service"
|
||||||
|
|
||||||
|
// interface Relationship {
|
||||||
|
// blocked_by: boolean;
|
||||||
|
// blocking: boolean;
|
||||||
|
// domain_blocking: boolean;
|
||||||
|
// endorsed: boolean;
|
||||||
|
// followed_by: boolean;
|
||||||
|
// following: boolean;
|
||||||
|
// id: string;
|
||||||
|
// muting: boolean;
|
||||||
|
// muting_notifications: boolean;
|
||||||
|
// note: string;
|
||||||
|
// notifying: boolean;
|
||||||
|
// requested: boolean;
|
||||||
|
// showing_reblogs: boolean;
|
||||||
|
// subscribing: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface Source {
|
||||||
|
// fields: Field[];
|
||||||
|
// note: string;
|
||||||
|
// pleroma: SourcePleroma;
|
||||||
|
// privacy: VisibilityScope;
|
||||||
|
// sensitive: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface SourcePleroma {
|
||||||
|
// actor_type: Actor;
|
||||||
|
// discoverable: boolean;
|
||||||
|
// no_rich_text: boolean;
|
||||||
|
// show_role: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface StatusResponse {
|
||||||
|
// account: Account;
|
||||||
|
// application: null;
|
||||||
|
// bookmarked: boolean;
|
||||||
|
// card: null;
|
||||||
|
// content: string;
|
||||||
|
// created_at: string;
|
||||||
|
// emojis: any[];
|
||||||
|
// favourited: boolean;
|
||||||
|
// favourites_count: number;
|
||||||
|
// id: string;
|
||||||
|
// in_reply_to_account_id: null;
|
||||||
|
// in_reply_to_id: null;
|
||||||
|
// language: null;
|
||||||
|
// media_attachments: any[];
|
||||||
|
// mentions: any[];
|
||||||
|
// muted: boolean;
|
||||||
|
// pinned: boolean;
|
||||||
|
// pleroma: StatusPleroma;
|
||||||
|
// poll: null;
|
||||||
|
// reblog: null;
|
||||||
|
// reblogged: boolean;
|
||||||
|
// reblogs_count: number;
|
||||||
|
// replies_count: number;
|
||||||
|
// sensitive: boolean;
|
||||||
|
// spoiler_text: string;
|
||||||
|
// tags: any[];
|
||||||
|
// uri: string;
|
||||||
|
// url: string;
|
||||||
|
// visibility: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface StatusPleroma {
|
||||||
|
// content: PleromaContent;
|
||||||
|
// context: string;
|
||||||
|
// conversation_id: number;
|
||||||
|
// direct_conversation_id: null;
|
||||||
|
// emoji_reactions: any[];
|
||||||
|
// expires_at: null;
|
||||||
|
// in_reply_to_account_acct: null;
|
||||||
|
// local: boolean;
|
||||||
|
// spoiler_text: PleromaContent;
|
||||||
|
// thread_muted: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface PleromaContent {
|
||||||
|
// "text/plain": string;
|
||||||
|
// }
|
Loading…
Reference in a new issue