601 lines
20 KiB
TypeScript
601 lines
20 KiB
TypeScript
import * as v from 'valibot';
|
||
|
||
import {
|
||
accountSchema,
|
||
bookmarkFolderSchema,
|
||
contextSchema,
|
||
emojiReactionSchema,
|
||
partialStatusSchema,
|
||
scheduledStatusSchema,
|
||
statusEditSchema,
|
||
statusSchema,
|
||
statusSourceSchema,
|
||
translationSchema,
|
||
} from '../entities';
|
||
import { filteredArray } from '../entities/utils';
|
||
import { AKKOMA, ICESHRIMP_NET, MITRA, PLEROMA } from '../features';
|
||
import { getAsyncRefreshHeader } from '../request';
|
||
|
||
import type { PlApiBaseClient } from '../client-base';
|
||
import type {
|
||
CreateStatusParams,
|
||
EditInteractionPolicyParams,
|
||
EditStatusParams,
|
||
GetFavouritedByParams,
|
||
GetRebloggedByParams,
|
||
GetStatusContextParams,
|
||
GetStatusMentionedUsersParams,
|
||
GetStatusParams,
|
||
GetStatusQuotesParams,
|
||
GetStatusReferencesParams,
|
||
GetStatusesParams,
|
||
} from '../params/statuses';
|
||
|
||
type EmptyObject = Record<string, never>;
|
||
|
||
const statuses = (client: PlApiBaseClient) => ({
|
||
/**
|
||
* Post a new status
|
||
* Publish a status with the given parameters.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#create}
|
||
*/
|
||
createStatus: async (params: CreateStatusParams) => {
|
||
type ExtendedCreateStatusParams = CreateStatusParams & {
|
||
markdown?: boolean;
|
||
circle_id?: string | null;
|
||
};
|
||
|
||
const fixedParams: ExtendedCreateStatusParams = params;
|
||
|
||
if (
|
||
params.content_type === 'text/markdown' &&
|
||
client.instanceInformation.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1
|
||
) {
|
||
fixedParams.markdown = true;
|
||
}
|
||
if (params.visibility?.startsWith('api/v1/bookmark_categories')) {
|
||
fixedParams.circle_id = params.visibility.slice(7);
|
||
fixedParams.visibility = 'circle';
|
||
}
|
||
if (params.quote_id && client.instanceInformation.api_versions.mastodon >= 7)
|
||
params.quoted_status_id = params.quote_id;
|
||
else if (params.quoted_status_id && (client.instanceInformation.api_versions.mastodon || 0) < 7)
|
||
params.quote_id = params.quoted_status_id;
|
||
|
||
const input =
|
||
params.preview && client.features.version.software === MITRA
|
||
? '/api/v1/statuses/preview'
|
||
: '/api/v1/statuses';
|
||
|
||
const response = await client.request(input, {
|
||
method: 'POST',
|
||
body: fixedParams,
|
||
});
|
||
|
||
if (response.json?.scheduled_at) return v.parse(scheduledStatusSchema, response.json);
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Requires features{@link Features.createStatusPreview}.
|
||
*/
|
||
previewStatus: async (params: CreateStatusParams) => {
|
||
const input =
|
||
client.features.version.software === PLEROMA || client.features.version.software === AKKOMA
|
||
? '/api/v1/statuses'
|
||
: '/api/v1/statuses/preview';
|
||
|
||
if (
|
||
client.features.version.software === PLEROMA ||
|
||
client.features.version.software === AKKOMA
|
||
) {
|
||
params.preview = true;
|
||
}
|
||
|
||
const response = await client.request(input, {
|
||
method: 'POST',
|
||
body: params,
|
||
});
|
||
|
||
return v.parse(v.partial(partialStatusSchema), response.json);
|
||
},
|
||
|
||
/**
|
||
* View a single status
|
||
* Obtain information about a status.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#get}
|
||
*/
|
||
getStatus: async (statusId: string, params?: GetStatusParams) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}`, { params });
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* View multiple statuses
|
||
* Obtain information about multiple statuses.
|
||
*
|
||
* Requires features{@link Features.getStatuses}.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#index}
|
||
*/
|
||
getStatuses: async (statusIds: string[], params?: GetStatusesParams) => {
|
||
const response = await client.request('/api/v1/statuses', {
|
||
params: { ...params, id: statusIds },
|
||
});
|
||
|
||
return v.parse(filteredArray(statusSchema), response.json);
|
||
},
|
||
|
||
/**
|
||
* Delete a status
|
||
* Delete one of your own statuses.
|
||
*
|
||
* `delete_media` parameter requires features{@link Features.deleteMedia}.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#delete}
|
||
*/
|
||
deleteStatus: async (statusId: string, deleteMedia?: boolean) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}`, {
|
||
method: 'DELETE',
|
||
params: { delete_media: deleteMedia },
|
||
});
|
||
|
||
return v.parse(statusSourceSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Get parent and child statuses in context
|
||
* View statuses above and below this status in the thread.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#context}
|
||
*/
|
||
getContext: async (statusId: string, params?: GetStatusContextParams) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/context`, { params });
|
||
|
||
const asyncRefreshHeader = getAsyncRefreshHeader(response);
|
||
|
||
return { asyncRefreshHeader, ...v.parse(contextSchema, response.json) };
|
||
},
|
||
|
||
/**
|
||
* Translate a status
|
||
* Translate the status content into some language.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#translate}
|
||
*/
|
||
translateStatus: async (statusId: string, lang?: string) => {
|
||
let response;
|
||
if (client.features.version.software === AKKOMA) {
|
||
response = await client.request(`/api/v1/statuses/${statusId}/translations/${lang}`);
|
||
} else {
|
||
response = await client.request(`/api/v1/statuses/${statusId}/translate`, {
|
||
method: 'POST',
|
||
body: { lang },
|
||
});
|
||
}
|
||
|
||
return v.parse(translationSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Translate multiple statuses into given language.
|
||
*
|
||
* Requires features{@link Features.lazyTranslations}.
|
||
*/
|
||
translateStatuses: async (statusIds: Array<string>, lang: string) => {
|
||
const response = await client.request('/api/v1/pl/statuses/translate', {
|
||
method: 'POST',
|
||
body: { ids: statusIds, lang },
|
||
});
|
||
|
||
return v.parse(filteredArray(translationSchema), response.json);
|
||
},
|
||
|
||
/**
|
||
* See who boosted a status
|
||
* View who boosted a given status.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#reblogged_by}
|
||
*/
|
||
getRebloggedBy: (statusId: string, params?: GetRebloggedByParams) =>
|
||
client.paginatedGet(`/api/v1/statuses/${statusId}/reblogged_by`, { params }, accountSchema),
|
||
|
||
/**
|
||
* See who favourited a status
|
||
* View who favourited a given status.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#favourited_by}
|
||
*/
|
||
getFavouritedBy: (statusId: string, params?: GetFavouritedByParams) =>
|
||
client.paginatedGet(`/api/v1/statuses/${statusId}/favourited_by`, { params }, accountSchema),
|
||
|
||
/**
|
||
* Favourite a status
|
||
* Add a status to your favourites list.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#favourite}
|
||
*/
|
||
favouriteStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/favourite`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Undo favourite of a status
|
||
* Remove a status from your favourites list.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unfavourite}
|
||
*/
|
||
unfavouriteStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/unfavourite`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Boost a status
|
||
* Reshare a status on your own profile.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#reblog}
|
||
*
|
||
* Specifying reblog visibility requires features{@link Features.reblogVisibility}.
|
||
*/
|
||
reblogStatus: async (statusId: string, visibility?: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/reblog`, {
|
||
method: 'POST',
|
||
body: { visibility },
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Undo boost of a status
|
||
* Undo a reshare of a status.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unreblog}
|
||
*/
|
||
unreblogStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/unreblog`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Bookmark a status
|
||
* Privately bookmark a status.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#bookmark}
|
||
*/
|
||
bookmarkStatus: async (statusId: string, folderId?: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/bookmark`, {
|
||
method: 'POST',
|
||
body: { folder_id: folderId },
|
||
});
|
||
|
||
if (folderId && client.features.bookmarkFoldersMultiple) {
|
||
await client.request(`/api/v1/bookmark_categories/${folderId}/statuses`, {
|
||
method: 'POST',
|
||
params: { status_ids: [statusId] },
|
||
});
|
||
}
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Undo bookmark of a status
|
||
* Remove a status from your private bookmarks.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unbookmark}
|
||
*/
|
||
unbookmarkStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/unbookmark`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Revoke a quote post
|
||
* Revoke quote authorization of status `quoting_status_id`, detaching status `id`.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#revoke_quote}
|
||
*/
|
||
revokeQuote: async (statusId: string, quotingStatusId: string) => {
|
||
const response = await client.request(
|
||
`/api/v1/statuses/${statusId}/quotes/${quotingStatusId}/revoke`,
|
||
{ method: 'POST' },
|
||
);
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Mute a conversation
|
||
* Do not receive notifications for the thread that this status is part of. Must be a thread in which you are a participant.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#mute}
|
||
*/
|
||
muteStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/mute`, { method: 'POST' });
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Unmute a conversation
|
||
* Start receiving notifications again for the thread that this status is part of.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unmute}
|
||
*/
|
||
unmuteStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/unmute`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Pin status to profile
|
||
* Feature one of your own public statuses at the top of your profile.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#pin}
|
||
*/
|
||
pinStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/pin`, { method: 'POST' });
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Unpin status from profile
|
||
* Unfeature a status from the top of your profile.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unpin}
|
||
*/
|
||
unpinStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/unpin`, { method: 'POST' });
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Edit a status
|
||
* Edit a given status to change its text, sensitivity, media attachments, or poll. Note that editing a poll’s options will reset the votes.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#unpin}
|
||
*/
|
||
editStatus: async (statusId: string, params: EditStatusParams) => {
|
||
type ExtendedEditStatusParams = EditStatusParams & {
|
||
markdown?: boolean;
|
||
};
|
||
|
||
const fixedParams: ExtendedEditStatusParams = params;
|
||
|
||
if (
|
||
params.content_type === 'text/markdown' &&
|
||
client.instanceInformation.api_versions['kmyblue_markdown.fedibird.pl-api'] >= 1
|
||
) {
|
||
fixedParams.markdown = true;
|
||
}
|
||
|
||
const response = await client.request(`/api/v1/statuses/${statusId}`, {
|
||
method: 'PUT',
|
||
body: params,
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Edit a status' interaction policies
|
||
* Edit a given status to change its interaction policies. Currently, this means changing its quote approval policy.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#edit_interaction_policy}
|
||
*/
|
||
editInteractionPolicy: async (statusId: string, params: EditInteractionPolicyParams) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}`, {
|
||
method: 'PUT',
|
||
body: params,
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* View edit history of a status
|
||
* Get all known versions of a status, including the initial and current states.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#history}
|
||
*/
|
||
getStatusHistory: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/history`);
|
||
|
||
return v.parse(filteredArray(statusEditSchema), response.json);
|
||
},
|
||
|
||
/**
|
||
* View status source
|
||
* Obtain the source properties for a status so that it can be edited.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#source}
|
||
*/
|
||
getStatusSource: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/source`);
|
||
|
||
return v.parse(statusSourceSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Get an object of emoji to account mappings with accounts that reacted to the post
|
||
*
|
||
* Requires features{@link Features.emojiReactsList}.
|
||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactions}
|
||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apiv1pleromastatusesidreactionsemoji}
|
||
*/
|
||
getStatusReactions: async (statusId: string, emoji?: string) => {
|
||
const apiVersions = client.instanceInformation.api_versions;
|
||
|
||
let response;
|
||
if (
|
||
apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 ||
|
||
client.features.version.software === ICESHRIMP_NET
|
||
) {
|
||
response = await client.request(
|
||
`/api/v1/pleroma/statuses/${statusId}/reactions${emoji ? `/${emoji}` : ''}`,
|
||
);
|
||
} else {
|
||
if (apiVersions['emoji_reaction.fedibird.pl-api'] >= 1) {
|
||
response = await client.request(`/api/v1/statuses/${statusId}/emoji_reactioned_by`);
|
||
} else {
|
||
response = await client.request(`/api/v1/statuses/${statusId}/reactions`, {
|
||
params: { emoji },
|
||
});
|
||
}
|
||
response.json = response.json?.reduce((acc: Array<any>, cur: any) => {
|
||
if (emoji && cur.name !== emoji) return acc;
|
||
|
||
const existing = acc.find((reaction) => reaction.name === cur.name);
|
||
|
||
if (existing) {
|
||
existing.accounts.push(cur.account);
|
||
existing.account_ids.push(cur.account.id);
|
||
existing.count += 1;
|
||
} else
|
||
acc.push({ count: 1, accounts: [cur.account], account_ids: [cur.account.id], ...cur });
|
||
|
||
return acc;
|
||
}, []);
|
||
}
|
||
|
||
return v.parse(filteredArray(emojiReactionSchema), response?.json || []);
|
||
},
|
||
|
||
/**
|
||
* React to a post with a unicode emoji
|
||
*
|
||
* Requires features{@link Features.emojiReacts}.
|
||
* Using custom emojis requires features{@link Features.customEmojiReacts}.
|
||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#put-apiv1pleromastatusesidreactionsemoji}
|
||
*/
|
||
createStatusReaction: async (statusId: string, emoji: string) => {
|
||
const apiVersions = client.instanceInformation.api_versions;
|
||
|
||
let response;
|
||
if (
|
||
apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 ||
|
||
client.features.version.software === MITRA
|
||
) {
|
||
response = await client.request(
|
||
`/api/v1/pleroma/statuses/${statusId}/reactions/${encodeURIComponent(emoji)}`,
|
||
{ method: 'PUT' },
|
||
);
|
||
} else {
|
||
response = await client.request(
|
||
`/api/v1/statuses/${statusId}/react/${encodeURIComponent(emoji)}`,
|
||
{ method: 'POST' },
|
||
);
|
||
}
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Remove a reaction to a post with a unicode emoji
|
||
*
|
||
* Requires features{@link Features.emojiReacts}.
|
||
* @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#delete-apiv1pleromastatusesidreactionsemoji}
|
||
*/
|
||
deleteStatusReaction: async (statusId: string, emoji: string) => {
|
||
const apiVersions = client.instanceInformation.api_versions;
|
||
|
||
let response;
|
||
if (
|
||
apiVersions['emoji_reactions.pleroma.pl-api'] >= 1 ||
|
||
client.features.version.software === MITRA
|
||
) {
|
||
response = await client.request(`/api/v1/pleroma/statuses/${statusId}/reactions/${emoji}`, {
|
||
method: 'DELETE',
|
||
});
|
||
} else {
|
||
response = await client.request(
|
||
`/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(emoji)}`,
|
||
{ method: 'POST' },
|
||
);
|
||
}
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* View quotes for a given status
|
||
*
|
||
* Requires features{@link Features.quotePosts}.
|
||
* @see {@link https://docs.joinmastodon.org/methods/statuses/#quotes}
|
||
*/
|
||
getStatusQuotes: (statusId: string, params?: GetStatusQuotesParams) =>
|
||
client.paginatedGet(
|
||
client.instanceInformation.api_versions.mastodon >= 7
|
||
? `/api/v1/statuses/${statusId}/quotes`
|
||
: `/api/v1/pleroma/statuses/${statusId}/quotes`,
|
||
{ params },
|
||
statusSchema,
|
||
),
|
||
|
||
/**
|
||
* Returns the list of accounts that have disliked the status as known by the current server
|
||
*
|
||
* Requires features{@link Features.statusDislikes}.
|
||
* @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#get-apifriendicastatusesiddisliked_by}
|
||
*/
|
||
getDislikedBy: (statusId: string) =>
|
||
client.paginatedGet(`/api/v1/statuses/${statusId}/disliked_by`, {}, accountSchema),
|
||
|
||
/**
|
||
* Marks the given status as disliked by this user
|
||
* @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#post-apifriendicastatusesiddislike}
|
||
*/
|
||
dislikeStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/friendica/statuses/${statusId}/dislike`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
/**
|
||
* Removes the dislike mark (if it exists) on this status for this user
|
||
* @see {@link https://github.com/friendica/friendica/blob/2024.06-rc/doc/API-Friendica.md#post-apifriendicastatusesidundislike}
|
||
*/
|
||
undislikeStatus: async (statusId: string) => {
|
||
const response = await client.request(`/api/friendica/statuses/${statusId}/undislike`, {
|
||
method: 'POST',
|
||
});
|
||
|
||
return v.parse(statusSchema, response.json);
|
||
},
|
||
|
||
getStatusReferences: (statusId: string, params?: GetStatusReferencesParams) =>
|
||
client.paginatedGet(`/api/v1/statuses/${statusId}/referred_by`, { params }, statusSchema),
|
||
|
||
getStatusMentionedUsers: (statusId: string, params?: GetStatusMentionedUsersParams) =>
|
||
client.paginatedGet(`/api/v1/statuses/${statusId}/mentioned_by`, { params }, accountSchema),
|
||
|
||
/**
|
||
* Load conversation from a remote server.
|
||
*
|
||
* Requires features{@link Features.loadConversation}.
|
||
*/
|
||
loadConversation: async (statusId: string) => {
|
||
const response = await client.request<EmptyObject>(
|
||
`/api/v1/statuses/${statusId}/load_conversation`,
|
||
{ method: 'POST' },
|
||
);
|
||
|
||
return response.json;
|
||
},
|
||
|
||
/**
|
||
* Requires features{@link Features.bookmarkFoldersMultiple}.
|
||
*/
|
||
getStatusBookmarkFolders: async (statusId: string) => {
|
||
const response = await client.request(`/api/v1/statuses/${statusId}/bookmark_categories`, {
|
||
method: 'GET',
|
||
});
|
||
|
||
return v.parse(filteredArray(bookmarkFolderSchema), response.json);
|
||
},
|
||
});
|
||
|
||
export { statuses };
|