diff --git a/packages/web-api/package.json b/packages/web-api/package.json index 19e709698..2492e4870 100644 --- a/packages/web-api/package.json +++ b/packages/web-api/package.json @@ -49,7 +49,7 @@ }, "dependencies": { "@slack/logger": "^4.0.0", - "@slack/types": "^2.17.0", + "@slack/types": "^2.18.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", "axios": "^1.11.0", diff --git a/packages/web-api/src/methods.ts b/packages/web-api/src/methods.ts index 6c7452694..377924b36 100644 --- a/packages/web-api/src/methods.ts +++ b/packages/web-api/src/methods.ts @@ -177,6 +177,7 @@ import type { DndSetSnoozeArguments, DndTeamInfoArguments, EmojiListArguments, + EntityPresentDetailsArguments, FilesCommentsDeleteArguments, FilesCompleteUploadExternalArguments, FilesDeleteArguments, @@ -437,6 +438,7 @@ import type { DndSetSnoozeResponse, DndTeamInfoResponse, EmojiListResponse, + EntityPresentDetailsResponse, FilesCommentsDeleteResponse, FilesCompleteUploadExternalResponse, FilesDeleteResponse, @@ -1879,6 +1881,17 @@ export abstract class Methods extends EventEmitter { list: bindApiCallWithOptionalArgument(this, 'emoji.list'), }; + public readonly entity = { + /** + * @description Provide information about the entity to be displayed in the flexpane. + * @see {@link https://docs.slack.dev/reference/methods/entity.presentDetails} + */ + presentDetails: bindApiCall( + this, + 'entity.presentDetails', + ), + }; + public readonly files = { /** * @description Finishes an upload started with {@link https://docs.slack.dev/reference/methods/files.getUploadURLExternal `files.getUploadURLExternal`}. diff --git a/packages/web-api/src/types/request/chat.ts b/packages/web-api/src/types/request/chat.ts index 6d8f9a623..7734d7a7f 100644 --- a/packages/web-api/src/types/request/chat.ts +++ b/packages/web-api/src/types/request/chat.ts @@ -1,5 +1,6 @@ import type { Block, // TODO: these will be combined into one in a new types release + EntityMetadata, KnownBlock, LinkUnfurls, MessageAttachment, @@ -104,6 +105,19 @@ export interface BroadcastedThreadReply extends ThreadTS { // or not broadcasted. Broadcasted replies are necessarily threaded, so `thread_ts` becomes required. type ReplyInThread = WithinThreadReply | BroadcastedThreadReply; +export interface ChatPostMessageMetadata { + /** + * @description Object representing message metadata, entity and/or event data to attach to a Slack message. + * Provide 'entities' to set work object entity metadata. + * Provide 'event_type' and 'event_payload' to set event metadata. + */ + metadata?: Partial & { + /** + * @description An array of work object entities. + */ + entities?: EntityMetadata[]; + }; +} export interface Metadata { /** @description Object representing message metadata, which will be made accessible to any user or app. */ metadata?: MessageMetadata; @@ -191,7 +205,7 @@ export type ChatPostMessageArguments = TokenOverridable & Authorship & Parse & LinkNames & - Metadata & + ChatPostMessageMetadata & Unfurls & { /** @description Disable Slack markup parsing by setting to `false`. Enabled by default. */ mrkdwn?: boolean; @@ -256,13 +270,8 @@ export interface SourceAndUnfurlID { type UnfurlTarget = ChannelAndTS | SourceAndUnfurlID; // https://docs.slack.dev/reference/methods/chat.unfurl -export type ChatUnfurlArguments = { - /** - * @description URL-encoded JSON map with keys set to URLs featured in the the message, pointing to their unfurl - * blocks or message attachments. - */ - unfurls: LinkUnfurls; -} & UnfurlTarget & +export type ChatUnfurlArguments = (ChatUnfurlUnfurls | ChatUnfurlMetadata) & + UnfurlTarget & TokenOverridable & { /** * @description Provide a simply-formatted string to send as an ephemeral message to the user as invitation to @@ -286,6 +295,29 @@ export type ChatUnfurlArguments = { user_auth_blocks?: (KnownBlock | Block)[]; }; +/** + * @description The `unfurls` param of the `chat.unfurl` API. + */ +interface ChatUnfurlUnfurls { + /** + * @description Object with keys set to URLs featured in the message, pointing to their unfurl + * blocks or message attachments. + */ + unfurls: LinkUnfurls; +} + +/** + * @description The `metadata` param of the `chat.unfurl` API. + */ +interface ChatUnfurlMetadata { + /** + * @description Unfurl metadata featuring an array of entities to attach to the message based on URLs featured in the message. + */ + metadata: Partial & { + entities: EntityMetadata[]; + }; +} + // https://docs.slack.dev/reference/methods/chat.update export type ChatUpdateArguments = MessageContents & { /** @description Timestamp of the message to be updated. */ diff --git a/packages/web-api/src/types/request/entity.ts b/packages/web-api/src/types/request/entity.ts new file mode 100644 index 000000000..e3c72ac30 --- /dev/null +++ b/packages/web-api/src/types/request/entity.ts @@ -0,0 +1,47 @@ +import type { EntityActionButton, EntityMetadata } from '@slack/types'; +import type { TokenOverridable } from './common'; + +// https://docs.slack.dev/reference/methods/entity.presentDetails +export type EntityPresentDetailsArguments = TokenOverridable & { + /** + * @description Entity metadata to be presented in the flexpane. + * */ + metadata?: EntityMetadata; + /** + * @description A reference to the original user action that initated the request. + * */ + trigger_id: string; + /** + * @description Set user_auth_required to true to indicate that the user must authenticate to view the full + * flexpane data. Defaults to false. + * */ + user_auth_required?: boolean; + /** + * @description A custom URL to which users are directed for authentication if required. + * Example: "https://example.com/onboarding?user_id=xxx" + * */ + user_auth_url?: string; + /** @description Error response preventing flexpane data from being returned. */ + error?: { + /** + * @description Error status indicating why the entity could not be presented. + * */ + status: string; + /** + * @description If status is 'custom', you can use this field to provide a message to the client. + * */ + custom_message?: string; + /** + * @description String format, eg. 'markdown'. + * */ + message_format?: string; + /** + * @description If status is 'custom', you can use this field to provide a title to the client. + * */ + custom_title?: string; + /** + * @description Set of action buttons to be shown in case of a specific error. + * */ + actions?: EntityActionButton[]; + }; +}; diff --git a/packages/web-api/src/types/request/index.ts b/packages/web-api/src/types/request/index.ts index 0e99ffd47..5066143e8 100644 --- a/packages/web-api/src/types/request/index.ts +++ b/packages/web-api/src/types/request/index.ts @@ -215,6 +215,7 @@ export type { DndTeamInfoArguments, } from './dnd'; export type { EmojiListArguments } from './emoji'; +export type { EntityPresentDetailsArguments } from './entity'; export type { FilesCommentsDeleteArguments, FilesCompleteUploadExternalArguments, diff --git a/packages/web-api/src/types/request/manifest.ts b/packages/web-api/src/types/request/manifest.ts index e47308af3..05a6613d0 100644 --- a/packages/web-api/src/types/request/manifest.ts +++ b/packages/web-api/src/types/request/manifest.ts @@ -467,6 +467,7 @@ type ManifestEvent = | 'dnd_updated_user' | 'email_domain_changed' | 'emoji_changed' + | 'entity_details_requested' | 'file_change' | 'file_comment_added' | 'file_comment_deleted' diff --git a/packages/web-api/src/types/response/EntityPresentDetailsResponse.ts b/packages/web-api/src/types/response/EntityPresentDetailsResponse.ts new file mode 100644 index 000000000..3876b76cd --- /dev/null +++ b/packages/web-api/src/types/response/EntityPresentDetailsResponse.ts @@ -0,0 +1,17 @@ +///////////////////////////////////////////////////////////////////////////////////////// +// // +// !!! DO NOT EDIT THIS FILE !!! // +// // +// This file is auto-generated by scripts/generate-web-api-types.sh in the repository. // +// Please refer to the script code to learn how to update the source data. // +// // +///////////////////////////////////////////////////////////////////////////////////////// + +import type { WebAPICallResult } from '../../WebClient'; +export type EntityPresentDetailsResponse = WebAPICallResult & { + error?: string; + needed?: string; + ok?: boolean; + provided?: string; + warning?: string; +}; diff --git a/packages/web-api/src/types/response/index.ts b/packages/web-api/src/types/response/index.ts index 5ba9c0c44..210a6e374 100644 --- a/packages/web-api/src/types/response/index.ts +++ b/packages/web-api/src/types/response/index.ts @@ -202,6 +202,7 @@ export { DndInfoResponse } from './DndInfoResponse'; export { DndSetSnoozeResponse } from './DndSetSnoozeResponse'; export { DndTeamInfoResponse } from './DndTeamInfoResponse'; export { EmojiListResponse } from './EmojiListResponse'; +export { EntityPresentDetailsResponse } from './EntityPresentDetailsResponse'; export { FilesCommentsAddResponse } from './FilesCommentsAddResponse'; export { FilesCommentsDeleteResponse } from './FilesCommentsDeleteResponse'; export { FilesCommentsEditResponse } from './FilesCommentsEditResponse'; diff --git a/packages/web-api/test/types/methods/chat.test-d.ts b/packages/web-api/test/types/methods/chat.test-d.ts index be50387d6..f1ec5f025 100644 --- a/packages/web-api/test/types/methods/chat.test-d.ts +++ b/packages/web-api/test/types/methods/chat.test-d.ts @@ -1,5 +1,5 @@ +import { CustomFieldType, type EntityMetadata } from '@slack/types'; import { expectAssignable, expectError } from 'tsd'; - import { WebClient } from '../../../src/WebClient'; const web = new WebClient('TOKEN'); @@ -441,6 +441,25 @@ expectAssignable>([ reply_broadcast: false, // can send a threaded message and explicitly not broadcast it }, ]); +expectAssignable>([ + { + channel: 'C1234', + text: 'hello', + thread_ts: '1234.56', + metadata: { + entities: [ + { + entity_type: 'slack#/entities/file', + entity_payload: { + attributes: { title: { text: 'My File' } }, + }, + external_ref: { id: '' }, + url: '', + }, + ], + }, + }, +]); // adding a test for when `reply_broadcast` specific boolean value is not known ahead of time // https://github.com/slackapi/node-slack-sdk/issues/1859 function wideBooleanTest(b: boolean) { @@ -663,7 +682,7 @@ expectError( ); expectError( web.chat.unfurl({ - channel: 'C1234', // missing unfurls + channel: 'C1234', // missing both unfurls and metadata ts: '1234.56', }), ); @@ -717,6 +736,45 @@ expectAssignable>([ ts: '1234.56', }, ]); +const entityMetadata: EntityMetadata = { + entity_type: 'slack#/entities/task', + entity_payload: { + attributes: { + title: { + text: 'Important task', + }, + }, + fields: { + status: { + value: 'All clear', + }, + description: { + value: 'Details of the task.', + }, + }, + custom_fields: [ + { + label: 'My Field', + key: 'my-field', + type: CustomFieldType.Array, + item_type: 'slack#/types/channel_id', + value: [{ value: 'C0123ABC' }], + }, + ], + }, + external_ref: { id: '1234' }, + url: 'https://myappdomain.com/id/1234', + app_unfurl_url: 'https://myappdomain.com/id/1234?myquery=param', +}; +expectAssignable>([ + { + channel: 'C1234', + ts: '1234.56', + metadata: { + entities: [entityMetadata], + }, + }, +]); // chat.update // -- sad path