diff --git a/json-logs/samples/api/chat.appendStream.json b/json-logs/samples/api/chat.appendStream.json new file mode 100644 index 000000000..cf7c54735 --- /dev/null +++ b/json-logs/samples/api/chat.appendStream.json @@ -0,0 +1,8 @@ +{ + "ok": false, + "error": "", + "needed": "", + "provided": "", + "channel": "C00000000", + "ts": "0000000000.000000" +} \ No newline at end of file diff --git a/json-logs/samples/api/chat.startStream.json b/json-logs/samples/api/chat.startStream.json new file mode 100644 index 000000000..75498bde6 --- /dev/null +++ b/json-logs/samples/api/chat.startStream.json @@ -0,0 +1,8 @@ +{ + "ok": false, + "ts": "0000000000.000000", + "error": "", + "needed": "", + "provided": "", + "channel": "C00000000" +} \ No newline at end of file diff --git a/json-logs/samples/api/chat.stopStream.json b/json-logs/samples/api/chat.stopStream.json new file mode 100644 index 000000000..7925fa8ac --- /dev/null +++ b/json-logs/samples/api/chat.stopStream.json @@ -0,0 +1,7 @@ +{ + "ok": false, + "error": "", + "needed": "", + "provided": "" +} + diff --git a/metadata/web-api/rate_limit_tiers.json b/metadata/web-api/rate_limit_tiers.json index 6c53581a8..cdeeba994 100644 --- a/metadata/web-api/rate_limit_tiers.json +++ b/metadata/web-api/rate_limit_tiers.json @@ -150,6 +150,7 @@ "channels.setPurpose": "Tier2", "channels.setTopic": "Tier2", "channels.unarchive": "Tier2", + "chat.appendStream": "Tier4", "chat.delete": "Tier3", "chat.deleteScheduledMessage": "Tier3", "chat.getPermalink": "SpecialTier_chat_getPermalink", @@ -158,6 +159,8 @@ "chat.postMessage": "SpecialTier_chat_postMessage", "chat.scheduleMessage": "Tier3", "chat.scheduledMessages.list": "Tier3", + "chat.startStream": "Tier2", + "chat.stopStream": "Tier2", "chat.unfurl": "Tier3", "chat.update": "Tier3", "conversations.acceptSharedInvite": "Tier1", diff --git a/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java b/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java index 888c9c88c..8fe5aecd7 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/AsyncMethodsClient.java @@ -939,6 +939,10 @@ CompletableFuture // chat // ------------------------------ + CompletableFuture chatAppendStream(ChatAppendStreamRequest req); + + CompletableFuture chatAppendStream(RequestConfigurator req); + CompletableFuture chatGetPermalink(ChatGetPermalinkRequest req); CompletableFuture chatGetPermalink(RequestConfigurator req); @@ -967,6 +971,14 @@ CompletableFuture CompletableFuture chatScheduleMessage(RequestConfigurator req); + CompletableFuture chatStartStream(ChatStartStreamRequest req); + + CompletableFuture chatStartStream(RequestConfigurator req); + + CompletableFuture chatStopStream(ChatStopStreamRequest req); + + CompletableFuture chatStopStream(RequestConfigurator req); + CompletableFuture chatUpdate(ChatUpdateRequest req); CompletableFuture chatUpdate(RequestConfigurator req); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/Methods.java b/slack-api-client/src/main/java/com/slack/api/methods/Methods.java index 4d66b803b..7a8248dec 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/Methods.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/Methods.java @@ -378,6 +378,7 @@ private Methods() { // chat // ------------------------------ + public static final String CHAT_APPEND_STREAM = "chat.appendStream"; public static final String CHAT_DELETE = "chat.delete"; public static final String CHAT_DELETE_SCHEDULED_MESSAGE = "chat.deleteScheduledMessage"; public static final String CHAT_GET_PERMALINK = "chat.getPermalink"; @@ -385,6 +386,8 @@ private Methods() { public static final String CHAT_POST_EPHEMERAL = "chat.postEphemeral"; public static final String CHAT_POST_MESSAGE = "chat.postMessage"; public static final String CHAT_SCHEDULE_MESSAGE = "chat.scheduleMessage"; + public static final String CHAT_START_STREAM = "chat.startStream"; + public static final String CHAT_STOP_STREAM = "chat.stopStream"; public static final String CHAT_UNFURL = "chat.unfurl"; public static final String CHAT_UPDATE = "chat.update"; diff --git a/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java b/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java index b8288f2cc..5a2017654 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/MethodsClient.java @@ -1225,6 +1225,10 @@ AdminUsergroupsRemoveChannelsResponse adminUsergroupsRemoveChannels( // chat // ------------------------------ + ChatAppendStreamResponse chatAppendStream(ChatAppendStreamRequest req) throws IOException, SlackApiException; + + ChatAppendStreamResponse chatAppendStream(RequestConfigurator req) throws IOException, SlackApiException; + ChatGetPermalinkResponse chatGetPermalink(ChatGetPermalinkRequest req) throws IOException, SlackApiException; ChatGetPermalinkResponse chatGetPermalink(RequestConfigurator req) throws IOException, SlackApiException; @@ -1253,6 +1257,14 @@ AdminUsergroupsRemoveChannelsResponse adminUsergroupsRemoveChannels( ChatScheduleMessageResponse chatScheduleMessage(RequestConfigurator req) throws IOException, SlackApiException; + ChatStartStreamResponse chatStartStream(ChatStartStreamRequest req) throws IOException, SlackApiException; + + ChatStartStreamResponse chatStartStream(RequestConfigurator req) throws IOException, SlackApiException; + + ChatStopStreamResponse chatStopStream(ChatStopStreamRequest req) throws IOException, SlackApiException; + + ChatStopStreamResponse chatStopStream(RequestConfigurator req) throws IOException, SlackApiException; + ChatUpdateResponse chatUpdate(ChatUpdateRequest req) throws IOException, SlackApiException; ChatUpdateResponse chatUpdate(RequestConfigurator req) throws IOException, SlackApiException; diff --git a/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java b/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java index 5ee384958..5f44af813 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/MethodsRateLimits.java @@ -15,7 +15,7 @@ /** * The comprehensive list of Slack Web API rate limits. * - * @see api.slack.com document + * @see API reference */ @Slf4j public class MethodsRateLimits { @@ -257,6 +257,7 @@ public static void setRateLimitTier(String methodName, MethodsRateLimitTier tier setRateLimitTier(CANVASES_SECTIONS_LOOKUP, Tier3); setRateLimitTier(CONVERSATIONS_CANVASES_CREATE, Tier2); + setRateLimitTier(CHAT_APPEND_STREAM, Tier4); setRateLimitTier(CHAT_DELETE, Tier3); setRateLimitTier(CHAT_DELETE_SCHEDULED_MESSAGE, Tier3); setRateLimitTier(CHAT_GET_PERMALINK, SpecialTier_chat_getPermalink); @@ -264,6 +265,8 @@ public static void setRateLimitTier(String methodName, MethodsRateLimitTier tier setRateLimitTier(CHAT_POST_EPHEMERAL, Tier4); setRateLimitTier(CHAT_POST_MESSAGE, SpecialTier_chat_postMessage); setRateLimitTier(CHAT_SCHEDULE_MESSAGE, Tier3); + setRateLimitTier(CHAT_START_STREAM, Tier2); + setRateLimitTier(CHAT_STOP_STREAM, Tier2); setRateLimitTier(CHAT_UNFURL, Tier3); setRateLimitTier(CHAT_UPDATE, Tier3); setRateLimitTier(CHAT_SCHEDULED_MESSAGES_LIST, Tier3); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java b/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java index ded83836b..b0a6a162f 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/RequestFormBuilder.java @@ -1447,10 +1447,11 @@ public static FormBody.Builder toForm(ChannelsUnarchiveRequest req) { return form; } - public static FormBody.Builder toForm(ChatGetPermalinkRequest req) { + public static FormBody.Builder toForm(ChatAppendStreamRequest req) { FormBody.Builder form = new FormBody.Builder(); setIfNotNull("channel", req.getChannel(), form); - setIfNotNull("message_ts", req.getMessageTs(), form); + setIfNotNull("ts", req.getTs(), form); + setIfNotNull("markdown_text", req.getMarkdownText(), form); return form; } @@ -1470,6 +1471,13 @@ public static FormBody.Builder toForm(ChatDeleteScheduledMessageRequest req) { return form; } + public static FormBody.Builder toForm(ChatGetPermalinkRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("channel", req.getChannel(), form); + setIfNotNull("message_ts", req.getMessageTs(), form); + return form; + } + public static FormBody.Builder toForm(ChatMeMessageRequest req) { FormBody.Builder form = new FormBody.Builder(); setIfNotNull("channel", req.getChannel(), form); @@ -1619,6 +1627,40 @@ public static FormBody.Builder toForm(ChatPostMessageRequest req) { return form; } + public static FormBody.Builder toForm(ChatStartStreamRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("channel", req.getChannel(), form); + setIfNotNull("thread_ts", req.getThreadTs(), form); + setIfNotNull("markdown_text", req.getMarkdownText(), form); + setIfNotNull("recipient_user_id", req.getRecipientUserId(), form); + setIfNotNull("recipient_team_id", req.getRecipientTeamId(), form); + return form; + } + + public static FormBody.Builder toForm(ChatStopStreamRequest req) { + FormBody.Builder form = new FormBody.Builder(); + setIfNotNull("channel", req.getChannel(), form); + setIfNotNull("ts", req.getTs(), form); + setIfNotNull("markdown_text", req.getMarkdownText(), form); + + if (req.getMetadataAsString() != null) { + form.add("metadata", req.getMetadataAsString()); + } else if (req.getMetadata() != null) { + String json = GSON.toJson(req.getMetadata()); + form.add("metadata", json); + } + if (req.getBlocksAsString() != null) { + form.add("blocks", req.getBlocksAsString()); + } else if (req.getBlocks() != null) { + String json = getJsonWithGsonAnonymInnerClassHandling(req.getBlocks()); + form.add("blocks", json); + } + if (req.getBlocksAsString() != null && req.getBlocks() != null) { + log.warn("Although you set both blocksAsString and blocks, only blocksAsString was used."); + } + return form; + } + public static FormBody.Builder toForm(ChatUpdateRequest req) { warnIfEitherTextOrAttachmentFallbackIsMissing( "chat.update", diff --git a/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java b/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java index b15432db5..5dda8da89 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/impl/AsyncMethodsClientImpl.java @@ -1595,13 +1595,13 @@ public CompletableFuture canvasesSectionsLookup( } @Override - public CompletableFuture chatGetPermalink(ChatGetPermalinkRequest req) { - return executor.execute(CHAT_GET_PERMALINK, toMap(req), () -> methods.chatGetPermalink(req)); + public CompletableFuture chatAppendStream(ChatAppendStreamRequest req) { + return executor.execute(CHAT_APPEND_STREAM, toMap(req), () -> methods.chatAppendStream(req)); } @Override - public CompletableFuture chatGetPermalink(RequestConfigurator req) { - return chatGetPermalink(req.configure(ChatGetPermalinkRequest.builder()).build()); + public CompletableFuture chatAppendStream(RequestConfigurator req) { + return chatAppendStream(req.configure(ChatAppendStreamRequest.builder()).build()); } @Override @@ -1624,6 +1624,16 @@ public CompletableFuture chatDeleteScheduled return chatDeleteScheduledMessage(req.configure(ChatDeleteScheduledMessageRequest.builder()).build()); } + @Override + public CompletableFuture chatGetPermalink(ChatGetPermalinkRequest req) { + return executor.execute(CHAT_GET_PERMALINK, toMap(req), () -> methods.chatGetPermalink(req)); + } + + @Override + public CompletableFuture chatGetPermalink(RequestConfigurator req) { + return chatGetPermalink(req.configure(ChatGetPermalinkRequest.builder()).build()); + } + @Override public CompletableFuture chatMeMessage(ChatMeMessageRequest req) { return executor.execute(CHAT_ME_MESSAGE, toMap(req), () -> methods.chatMeMessage(req)); @@ -1667,6 +1677,26 @@ public CompletableFuture chatScheduleMessage(Reques return chatScheduleMessage(req.configure(ChatScheduleMessageRequest.builder()).build()); } + @Override + public CompletableFuture chatStartStream(ChatStartStreamRequest req) { + return executor.execute(CHAT_START_STREAM, toMap(req), () -> methods.chatStartStream(req)); + } + + @Override + public CompletableFuture chatStartStream(RequestConfigurator req) { + return chatStartStream(req.configure(ChatStartStreamRequest.builder()).build()); + } + + @Override + public CompletableFuture chatStopStream(ChatStopStreamRequest req) { + return executor.execute(CHAT_STOP_STREAM, toMap(req), () -> methods.chatStopStream(req)); + } + + @Override + public CompletableFuture chatStopStream(RequestConfigurator req) { + return chatStopStream(req.configure(ChatStopStreamRequest.builder()).build()); + } + @Override public CompletableFuture chatUpdate(ChatUpdateRequest req) { return executor.execute(CHAT_UPDATE, toMap(req), () -> methods.chatUpdate(req)); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java b/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java index b2b1944ec..1619d94ff 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/impl/MethodsClientImpl.java @@ -1832,13 +1832,13 @@ public ChannelsUnarchiveResponse channelsUnarchive(RequestConfigurator req) throws IOException, SlackApiException { - return chatGetPermalink(req.configure(ChatGetPermalinkRequest.builder()).build()); + public ChatAppendStreamResponse chatAppendStream(RequestConfigurator req) throws IOException, SlackApiException { + return chatAppendStream(req.configure(ChatAppendStreamRequest.builder()).build()); } @Override @@ -1861,6 +1861,17 @@ public ChatDeleteScheduledMessageResponse chatDeleteScheduledMessage(RequestConf return chatDeleteScheduledMessage(req.configure(ChatDeleteScheduledMessageRequest.builder()).build()); } + @Override + public ChatGetPermalinkResponse chatGetPermalink(ChatGetPermalinkRequest req) throws IOException, SlackApiException { + return postFormWithTokenAndParseResponse(toForm(req), Methods.CHAT_GET_PERMALINK, getToken(req), ChatGetPermalinkResponse.class); + } + + @Override + public ChatGetPermalinkResponse chatGetPermalink(RequestConfigurator req) throws IOException, SlackApiException { + return chatGetPermalink(req.configure(ChatGetPermalinkRequest.builder()).build()); + } + + @Override public ChatMeMessageResponse chatMeMessage(ChatMeMessageRequest req) throws IOException, SlackApiException { return postFormWithTokenAndParseResponse(toForm(req), Methods.CHAT_ME_MESSAGE, getToken(req), ChatMeMessageResponse.class); @@ -1901,6 +1912,26 @@ public ChatScheduleMessageResponse chatScheduleMessage(RequestConfigurator req) throws IOException, SlackApiException { + return chatStartStream(req.configure(ChatStartStreamRequest.builder()).build()); + } + + @Override + public ChatStopStreamResponse chatStopStream(ChatStopStreamRequest req) throws IOException, SlackApiException { + return postFormWithTokenAndParseResponse(toForm(req), Methods.CHAT_STOP_STREAM, getToken(req), ChatStopStreamResponse.class); + } + + @Override + public ChatStopStreamResponse chatStopStream(RequestConfigurator req) throws IOException, SlackApiException { + return chatStopStream(req.configure(ChatStopStreamRequest.builder()).build()); + } + @Override public ChatUpdateResponse chatUpdate(ChatUpdateRequest req) throws IOException, SlackApiException { return postFormWithTokenAndParseResponse(toForm(req), Methods.CHAT_UPDATE, getToken(req), ChatUpdateResponse.class); diff --git a/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatAppendStreamRequest.java b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatAppendStreamRequest.java new file mode 100644 index 000000000..7e6b90c65 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatAppendStreamRequest.java @@ -0,0 +1,33 @@ +package com.slack.api.methods.request.chat; + +import com.slack.api.methods.SlackApiRequest; +import lombok.Builder; +import lombok.Data; + +/** + * https://docs.slack.dev/reference/methods/chat.appendStream + */ +@Data +@Builder +public class ChatAppendStreamRequest implements SlackApiRequest { + + /** + * Authentication token. Requires scope: `chat:write` + */ + private String token; + + /** + * An encoded ID that represents a channel, private group, or DM. + */ + private String channel; + + /** + * The timestamp of the streaming message. + */ + private String ts; + + /** + * Accepts message text formatted in markdown. Limit this field to 12,000 characters. This text is what will be appended to the message received so far. + */ + private String markdownText; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatPostMessageRequest.java b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatPostMessageRequest.java index 573236caa..4899a4160 100644 --- a/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatPostMessageRequest.java +++ b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatPostMessageRequest.java @@ -21,7 +21,7 @@ public class ChatPostMessageRequest implements SlackApiRequest { private String token; /** - * aSet your bot's user name. + * Set your bot's user name. * Must be used in conjunction with `as_user` set to false, otherwise ignored. See [authorship](#authorship) below. */ private String username; diff --git a/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStartStreamRequest.java b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStartStreamRequest.java new file mode 100644 index 000000000..e3ba21182 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStartStreamRequest.java @@ -0,0 +1,43 @@ +package com.slack.api.methods.request.chat; + +import com.slack.api.methods.SlackApiRequest; +import lombok.Builder; +import lombok.Data; + +/** + * https://docs.slack.dev/reference/methods/chat.startStream + */ +@Data +@Builder +public class ChatStartStreamRequest implements SlackApiRequest { + + /** + * Authentication token. Requires scope: `chat:write` + */ + private String token; + + /** + * An encoded ID that represents a channel, private group, or DM. + */ + private String channel; + + /** + * Provide another message's `ts` value to reply to. Streamed messages should always be replies to a user request. + */ + private String threadTs; + + /** + * Accepts message text formatted in markdown. Limit this field to 12,000 characters. + */ + private String markdownText; + + /** + * The encoded ID of the user to receive the streaming text. Required when streaming to channels. + */ + private String recipientUserId; + + /** + * The encoded ID of the team the user receiving the streaming text belongs to. Required when streaming to channels. + */ + private String recipientTeamId; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStopStreamRequest.java b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStopStreamRequest.java new file mode 100644 index 000000000..ebd6fb235 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/request/chat/ChatStopStreamRequest.java @@ -0,0 +1,59 @@ +package com.slack.api.methods.request.chat; + +import com.slack.api.methods.SlackApiRequest; +import com.slack.api.model.Message; +import com.slack.api.model.block.LayoutBlock; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +/** + * https://docs.slack.dev/reference/methods/chat.stopStream + */ +@Data +@Builder +public class ChatStopStreamRequest implements SlackApiRequest { + + /** + * Authentication token. Requires scope: `chat:write` + */ + private String token; + + /** + * An encoded ID that represents a channel, private group, or DM. + */ + private String channel; + + /** + * The timestamp of the streaming message. + */ + private String ts; + + /** + * Accepts message text formatted in markdown. Limit this field to 12,000 characters. + */ + private String markdownText; + + /** + * JSON object with event_type and event_payload fields, presented as a URL-encoded string. + * Metadata you post to Slack is accessible to any app or user who is a member of that workspace. + */ + private Message.Metadata metadata; + + /** + * JSON object with event_type and event_payload fields, presented as a URL-encoded string. + * Metadata you post to Slack is accessible to any app or user who is a member of that workspace. + */ + private String metadataAsString; + + /** + * A JSON-based array of structured blocks that will be rendered at the bottom of the finalized message, presented as a URL-encoded string. + */ + private List blocks; + + /** + * A JSON-based array of structured blocks as a String that will be rendered at the bottom of the finalized message, presented as a URL-encoded string. + */ + private String blocksAsString; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatAppendStreamResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatAppendStreamResponse.java new file mode 100644 index 000000000..ac9bc5f15 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatAppendStreamResponse.java @@ -0,0 +1,22 @@ +package com.slack.api.methods.response.chat; + +import com.slack.api.methods.SlackApiTextResponse; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ChatAppendStreamResponse implements SlackApiTextResponse { + + private boolean ok; + private String warning; + private String error; + private String needed; + private String provided; + private String deprecatedArgument; + private transient Map> httpResponseHeaders; + + private String channel; + private String ts; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStartStreamResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStartStreamResponse.java new file mode 100644 index 000000000..da0cb9217 --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStartStreamResponse.java @@ -0,0 +1,22 @@ +package com.slack.api.methods.response.chat; + +import com.slack.api.methods.SlackApiTextResponse; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ChatStartStreamResponse implements SlackApiTextResponse { + + private boolean ok; + private String warning; + private String error; + private String needed; + private String provided; + private String deprecatedArgument; + private transient Map> httpResponseHeaders; + + private String channel; + private String ts; +} diff --git a/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStopStreamResponse.java b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStopStreamResponse.java new file mode 100644 index 000000000..3348899de --- /dev/null +++ b/slack-api-client/src/main/java/com/slack/api/methods/response/chat/ChatStopStreamResponse.java @@ -0,0 +1,24 @@ +package com.slack.api.methods.response.chat; + +import com.slack.api.methods.SlackApiTextResponse; +import com.slack.api.model.Message; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ChatStopStreamResponse implements SlackApiTextResponse { + + private boolean ok; + private String warning; + private String error; + private String needed; + private String provided; + private String deprecatedArgument; + private transient Map> httpResponseHeaders; + + private String channel; + private String ts; + private Message message; +} diff --git a/slack-api-client/src/test/java/test_locally/api/MethodsTest.java b/slack-api-client/src/test/java/test_locally/api/MethodsTest.java index 6ae69d6fd..6b0838fb0 100644 --- a/slack-api-client/src/test/java/test_locally/api/MethodsTest.java +++ b/slack-api-client/src/test/java/test_locally/api/MethodsTest.java @@ -19,8 +19,8 @@ public class MethodsTest { public void verifyTheCoverage() { // https://docs.slack.dev/reference/methods // var methodNames = [].slice.call(document.getElementsByClassName('apiReferenceFilterableList__listItemLink')).map(e => e.href.replace("https://docs.slack.dev/reference/methods/", ""));console.log(methodNames.toString());console.log(methodNames.length); - // 288 endpoints as of April 18, 2025 - String methods = "admin.analytics.getFile,admin.apps.activities.list,admin.apps.approve,admin.apps.clearResolution,admin.apps.restrict,admin.apps.uninstall,admin.apps.approved.list,admin.apps.config.lookup,admin.apps.config.set,admin.apps.requests.cancel,admin.apps.requests.list,admin.apps.restricted.list,admin.audit.anomaly.allow.getItem,admin.audit.anomaly.allow.updateItem,admin.auth.policy.assignEntities,admin.auth.policy.getEntities,admin.auth.policy.removeEntities,admin.barriers.create,admin.barriers.delete,admin.barriers.list,admin.barriers.update,admin.conversations.archive,admin.conversations.bulkArchive,admin.conversations.bulkDelete,admin.conversations.bulkMove,admin.conversations.convertToPrivate,admin.conversations.convertToPublic,admin.conversations.create,admin.conversations.createForObjects,admin.conversations.delete,admin.conversations.disconnectShared,admin.conversations.getConversationPrefs,admin.conversations.getCustomRetention,admin.conversations.getTeams,admin.conversations.invite,admin.conversations.linkObjects,admin.conversations.lookup,admin.conversations.removeCustomRetention,admin.conversations.rename,admin.conversations.search,admin.conversations.setConversationPrefs,admin.conversations.setCustomRetention,admin.conversations.setTeams,admin.conversations.unarchive,admin.conversations.unlinkObjects,admin.conversations.ekm.listOriginalConnectedChannelInfo,admin.conversations.restrictAccess.addGroup,admin.conversations.restrictAccess.listGroups,admin.conversations.restrictAccess.removeGroup,admin.emoji.add,admin.emoji.addAlias,admin.emoji.list,admin.emoji.remove,admin.emoji.rename,admin.functions.list,admin.functions.permissions.lookup,admin.functions.permissions.set,admin.inviteRequests.approve,admin.inviteRequests.deny,admin.inviteRequests.list,admin.inviteRequests.approved.list,admin.inviteRequests.denied.list,admin.roles.addAssignments,admin.roles.listAssignments,admin.roles.removeAssignments,admin.teams.admins.list,admin.teams.create,admin.teams.list,admin.teams.owners.list,admin.teams.settings.info,admin.teams.settings.setDefaultChannels,admin.teams.settings.setDescription,admin.teams.settings.setDiscoverability,admin.teams.settings.setIcon,admin.teams.settings.setName,admin.usergroups.addChannels,admin.usergroups.addTeams,admin.usergroups.listChannels,admin.usergroups.removeChannels,admin.users.assign,admin.users.invite,admin.users.list,admin.users.remove,admin.users.setAdmin,admin.users.setExpiration,admin.users.setOwner,admin.users.setRegular,admin.users.session.clearSettings,admin.users.session.getSettings,admin.users.session.invalidate,admin.users.session.list,admin.users.session.reset,admin.users.session.resetBulk,admin.users.session.setSettings,admin.users.unsupportedVersions.export,admin.workflows.collaborators.add,admin.workflows.collaborators.remove,admin.workflows.permissions.lookup,admin.workflows.search,admin.workflows.unpublish,admin.workflows.triggers.types.permissions.lookup,admin.workflows.triggers.types.permissions.set,api.test,apps.activities.list,apps.auth.external.delete,apps.auth.external.get,apps.connections.open,apps.datastore.bulkDelete,apps.datastore.bulkGet,apps.datastore.bulkPut,apps.datastore.count,apps.datastore.delete,apps.datastore.get,apps.datastore.put,apps.datastore.query,apps.datastore.update,apps.event.authorizations.list,apps.manifest.create,apps.manifest.delete,apps.manifest.export,apps.manifest.update,apps.manifest.validate,apps.uninstall,assistant.search.context,assistant.threads.setStatus,assistant.threads.setSuggestedPrompts,assistant.threads.setTitle,auth.revoke,auth.test,auth.teams.list,bookmarks.add,bookmarks.edit,bookmarks.list,bookmarks.remove,bots.info,calls.add,calls.end,calls.info,calls.update,calls.participants.add,calls.participants.remove,canvases.access.delete,canvases.access.set,canvases.create,canvases.delete,canvases.edit,canvases.sections.lookup,channels.mark,chat.delete,chat.deleteScheduledMessage,chat.getPermalink,chat.meMessage,chat.postEphemeral,chat.postMessage,chat.scheduleMessage,chat.unfurl,chat.update,chat.scheduledMessages.list,conversations.acceptSharedInvite,conversations.approveSharedInvite,conversations.archive,conversations.close,conversations.create,conversations.declineSharedInvite,conversations.history,conversations.info,conversations.invite,conversations.inviteShared,conversations.join,conversations.kick,conversations.leave,conversations.list,conversations.listConnectInvites,conversations.mark,conversations.members,conversations.open,conversations.rename,conversations.replies,conversations.setPurpose,conversations.setTopic,conversations.unarchive,conversations.canvases.create,conversations.externalInvitePermissions.set,conversations.requestSharedInvite.approve,conversations.requestSharedInvite.deny,conversations.requestSharedInvite.list,dialog.open,dnd.endDnd,dnd.endSnooze,dnd.info,dnd.setSnooze,dnd.teamInfo,emoji.list,files.comments.delete,files.completeUploadExternal,files.delete,files.getUploadURLExternal,files.info,files.list,files.revokePublicURL,files.sharedPublicURL,files.upload,files.remote.add,files.remote.info,files.remote.list,files.remote.remove,files.remote.share,files.remote.update,functions.completeError,functions.completeSuccess,functions.distributions.permissions.add,functions.distributions.permissions.list,functions.distributions.permissions.remove,functions.distributions.permissions.set,functions.workflows.steps.list,functions.workflows.steps.responses.export,groups.mark,migration.exchange,oauth.access,oauth.v2.access,oauth.v2.exchange,openid.connect.token,openid.connect.userInfo,pins.add,pins.list,pins.remove,reactions.add,reactions.get,reactions.list,reactions.remove,reminders.add,reminders.complete,reminders.delete,reminders.info,reminders.list,rtm.connect,rtm.start,search.all,search.files,search.messages,stars.add,stars.list,stars.remove,team.accessLogs,team.billableInfo,team.info,team.integrationLogs,team.billing.info,team.externalTeams.disconnect,team.externalTeams.list,team.preferences.list,team.profile.get,tooling.tokens.rotate,usergroups.create,usergroups.disable,usergroups.enable,usergroups.list,usergroups.update,usergroups.users.list,usergroups.users.update,users.conversations,users.deletePhoto,users.getPresence,users.identity,users.info,users.list,users.lookupByEmail,users.setActive,users.setPhoto,users.setPresence,users.discoverableContacts.lookup,users.profile.get,users.profile.set,views.open,views.publish,views.push,views.update,workflows.stepCompleted,workflows.stepFailed,workflows.updateStep,workflows.triggers.permissions.add,workflows.triggers.permissions.list,workflows.triggers.permissions.remove,workflows.triggers.permissions.set,im.list,im.mark,mpim.list,mpim.mark"; + // 291 endpoints as of October 14, 2025 + String methods = "admin.analytics.getFile,admin.apps.activities.list,admin.apps.approve,admin.apps.clearResolution,admin.apps.restrict,admin.apps.uninstall,admin.apps.approved.list,admin.apps.config.lookup,admin.apps.config.set,admin.apps.requests.cancel,admin.apps.requests.list,admin.apps.restricted.list,admin.audit.anomaly.allow.getItem,admin.audit.anomaly.allow.updateItem,admin.auth.policy.assignEntities,admin.auth.policy.getEntities,admin.auth.policy.removeEntities,admin.barriers.create,admin.barriers.delete,admin.barriers.list,admin.barriers.update,admin.conversations.archive,admin.conversations.bulkArchive,admin.conversations.bulkDelete,admin.conversations.bulkMove,admin.conversations.convertToPrivate,admin.conversations.convertToPublic,admin.conversations.create,admin.conversations.createForObjects,admin.conversations.delete,admin.conversations.disconnectShared,admin.conversations.getConversationPrefs,admin.conversations.getCustomRetention,admin.conversations.getTeams,admin.conversations.invite,admin.conversations.linkObjects,admin.conversations.lookup,admin.conversations.removeCustomRetention,admin.conversations.rename,admin.conversations.search,admin.conversations.setConversationPrefs,admin.conversations.setCustomRetention,admin.conversations.setTeams,admin.conversations.unarchive,admin.conversations.unlinkObjects,admin.conversations.ekm.listOriginalConnectedChannelInfo,admin.conversations.restrictAccess.addGroup,admin.conversations.restrictAccess.listGroups,admin.conversations.restrictAccess.removeGroup,admin.emoji.add,admin.emoji.addAlias,admin.emoji.list,admin.emoji.remove,admin.emoji.rename,admin.functions.list,admin.functions.permissions.lookup,admin.functions.permissions.set,admin.inviteRequests.approve,admin.inviteRequests.deny,admin.inviteRequests.list,admin.inviteRequests.approved.list,admin.inviteRequests.denied.list,admin.roles.addAssignments,admin.roles.listAssignments,admin.roles.removeAssignments,admin.teams.admins.list,admin.teams.create,admin.teams.list,admin.teams.owners.list,admin.teams.settings.info,admin.teams.settings.setDefaultChannels,admin.teams.settings.setDescription,admin.teams.settings.setDiscoverability,admin.teams.settings.setIcon,admin.teams.settings.setName,admin.usergroups.addChannels,admin.usergroups.addTeams,admin.usergroups.listChannels,admin.usergroups.removeChannels,admin.users.assign,admin.users.invite,admin.users.list,admin.users.remove,admin.users.setAdmin,admin.users.setExpiration,admin.users.setOwner,admin.users.setRegular,admin.users.session.clearSettings,admin.users.session.getSettings,admin.users.session.invalidate,admin.users.session.list,admin.users.session.reset,admin.users.session.resetBulk,admin.users.session.setSettings,admin.users.unsupportedVersions.export,admin.workflows.collaborators.add,admin.workflows.collaborators.remove,admin.workflows.permissions.lookup,admin.workflows.search,admin.workflows.unpublish,admin.workflows.triggers.types.permissions.lookup,admin.workflows.triggers.types.permissions.set,api.test,apps.activities.list,apps.auth.external.delete,apps.auth.external.get,apps.connections.open,apps.datastore.bulkDelete,apps.datastore.bulkGet,apps.datastore.bulkPut,apps.datastore.count,apps.datastore.delete,apps.datastore.get,apps.datastore.put,apps.datastore.query,apps.datastore.update,apps.event.authorizations.list,apps.manifest.create,apps.manifest.delete,apps.manifest.export,apps.manifest.update,apps.manifest.validate,apps.uninstall,assistant.search.context,assistant.threads.setStatus,assistant.threads.setSuggestedPrompts,assistant.threads.setTitle,auth.revoke,auth.test,auth.teams.list,bookmarks.add,bookmarks.edit,bookmarks.list,bookmarks.remove,bots.info,calls.add,calls.end,calls.info,calls.update,calls.participants.add,calls.participants.remove,canvases.access.delete,canvases.access.set,canvases.create,canvases.delete,canvases.edit,canvases.sections.lookup,channels.mark,chat.appendStream,chat.delete,chat.deleteScheduledMessage,chat.getPermalink,chat.meMessage,chat.postEphemeral,chat.postMessage,chat.scheduleMessage,chat.startStream,chat.stopStream,chat.unfurl,chat.update,chat.scheduledMessages.list,conversations.acceptSharedInvite,conversations.approveSharedInvite,conversations.archive,conversations.close,conversations.create,conversations.declineSharedInvite,conversations.history,conversations.info,conversations.invite,conversations.inviteShared,conversations.join,conversations.kick,conversations.leave,conversations.list,conversations.listConnectInvites,conversations.mark,conversations.members,conversations.open,conversations.rename,conversations.replies,conversations.setPurpose,conversations.setTopic,conversations.unarchive,conversations.canvases.create,conversations.externalInvitePermissions.set,conversations.requestSharedInvite.approve,conversations.requestSharedInvite.deny,conversations.requestSharedInvite.list,dialog.open,dnd.endDnd,dnd.endSnooze,dnd.info,dnd.setSnooze,dnd.teamInfo,emoji.list,files.comments.delete,files.completeUploadExternal,files.delete,files.getUploadURLExternal,files.info,files.list,files.revokePublicURL,files.sharedPublicURL,files.upload,files.remote.add,files.remote.info,files.remote.list,files.remote.remove,files.remote.share,files.remote.update,functions.completeError,functions.completeSuccess,functions.distributions.permissions.add,functions.distributions.permissions.list,functions.distributions.permissions.remove,functions.distributions.permissions.set,functions.workflows.steps.list,functions.workflows.steps.responses.export,groups.mark,migration.exchange,oauth.access,oauth.v2.access,oauth.v2.exchange,openid.connect.token,openid.connect.userInfo,pins.add,pins.list,pins.remove,reactions.add,reactions.get,reactions.list,reactions.remove,reminders.add,reminders.complete,reminders.delete,reminders.info,reminders.list,rtm.connect,rtm.start,search.all,search.files,search.messages,stars.add,stars.list,stars.remove,team.accessLogs,team.billableInfo,team.info,team.integrationLogs,team.billing.info,team.externalTeams.disconnect,team.externalTeams.list,team.preferences.list,team.profile.get,tooling.tokens.rotate,usergroups.create,usergroups.disable,usergroups.enable,usergroups.list,usergroups.update,usergroups.users.list,usergroups.users.update,users.conversations,users.deletePhoto,users.getPresence,users.identity,users.info,users.list,users.lookupByEmail,users.setActive,users.setPhoto,users.setPresence,users.discoverableContacts.lookup,users.profile.get,users.profile.set,views.open,views.publish,views.push,views.update,workflows.stepCompleted,workflows.stepFailed,workflows.updateStep,workflows.triggers.permissions.add,workflows.triggers.permissions.list,workflows.triggers.permissions.remove,workflows.triggers.permissions.set,im.list,im.mark,mpim.list,mpim.mark"; final List existingMethods = new ArrayList<>(); for (Field f : Methods.class.getDeclaredFields()) { int modifiers = f.getModifiers(); diff --git a/slack-api-client/src/test/java/test_locally/api/methods/ChatTest.java b/slack-api-client/src/test/java/test_locally/api/methods/ChatTest.java index 749f7f751..1e9f8595f 100644 --- a/slack-api-client/src/test/java/test_locally/api/methods/ChatTest.java +++ b/slack-api-client/src/test/java/test_locally/api/methods/ChatTest.java @@ -33,6 +33,16 @@ public void tearDown() throws Exception { server.stop(); } + @Test + public void chatAppendStream() throws Exception { + assertThat( + slack.methods(ValidToken).chatAppendStream(r -> r.channel("C123").ts("123.123").markdownText("**hello!**") + ).isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatAppendStream(r -> r.channel("C123").ts("123.123").markdownText("**hello!**") + ).get().isOk(), is(true)); + } + @Test public void postMessage() throws Exception { assertThat( @@ -156,6 +166,41 @@ public void chatScheduleMessage() throws Exception { ).get().isOk(), is(true)); } + @Test + public void chatStartStream() throws Exception { + assertThat( + slack.methods(ValidToken).chatStartStream(r -> r.channel("C123").threadTs("123.123") + ).isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStartStream(r -> r.channel("C123").threadTs("123.123") + ).get().isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStartStream(r -> r.channel("C123").threadTs("123.123").markdownText("**hello!**") + ).get().isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStartStream(r -> r.channel("C123").threadTs("123.123").recipientUserId("U123").recipientTeamId("T123") + ).get().isOk(), is(true)); + } + + @Test + public void chatStopStream() throws Exception { + assertThat( + slack.methods(ValidToken).chatStopStream(r -> r.channel("C123").ts("123.123") + ).isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStopStream(r -> r.channel("C123").ts("123.123") + ).get().isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStopStream(r -> r.channel("C123").ts("123.123").markdownText("**hello!**") + ).get().isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStopStream(r -> r.channel("C123").ts("123.123").blocksAsString("[]") + ).get().isOk(), is(true)); + assertThat( + slack.methodsAsync(ValidToken).chatStopStream(r -> r.channel("C123").ts("123.123").blocks(asBlocks()) + ).get().isOk(), is(true)); + } + @Test public void chatUnfurl() throws Exception { assertThat( diff --git a/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java b/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java index 85a8b3755..7b0dd1405 100644 --- a/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java +++ b/slack-api-client/src/test/java/test_with_remote_apis/methods/chat_Test.java @@ -5,6 +5,7 @@ import com.slack.api.methods.request.chat.ChatPostMessageRequest; import com.slack.api.methods.request.chat.ChatUnfurlRequest; import com.slack.api.methods.request.chat.ChatUpdateRequest; +import com.slack.api.methods.response.auth.AuthTestResponse; import com.slack.api.methods.response.chat.*; import com.slack.api.methods.response.chat.scheduled_messages.ChatScheduledMessagesListResponse; import com.slack.api.methods.response.conversations.ConversationsHistoryResponse; @@ -800,6 +801,36 @@ public void scheduleMessages() throws IOException, SlackApiException { assertTrue(after.getScheduledMessages().size() - before.getScheduledMessages().size() == 2); } + @Test + public void streamMessages() throws IOException, SlackApiException { + AuthTestResponse auth = slack.methods(botToken).authTest(req -> req); + assertThat(auth.getError(), is(nullValue())); + loadRandomChannelId(); + String userId = findUser(); + ChatPostMessageResponse topMessage = slack.methods(botToken).chatPostMessage(req -> req + .channel(randomChannelId) + .text("Get ready to stream a response in thread!")); + assertThat(topMessage.getError(), is(nullValue())); + ChatStartStreamResponse streamer = slack.methods(botToken).chatStartStream(r -> r + .channel(randomChannelId) + .threadTs(topMessage.getTs()) + .recipientUserId(userId) + .recipientTeamId(auth.getTeamId())); + assertThat(streamer.isOk(), is(true)); + assertThat(streamer.getError(), is(nullValue())); + ChatAppendStreamResponse appends = slack.methods(botToken).chatAppendStream(r -> r + .channel(randomChannelId) + .ts(streamer.getTs()) + .markdownText("hello")); + assertThat(appends.isOk(), is(true)); + assertThat(appends.getError(), is(nullValue())); + ChatStopStreamResponse stops = slack.methods(botToken).chatStopStream(r -> r + .channel(randomChannelId) + .ts(streamer.getTs())); + assertThat(stops.isOk(), is(true)); + assertThat(stops.getError(), is(nullValue())); + } + // https://github.com/slackapi/java-slack-sdk/issues/415 @Test public void attachmentsWithBlocks_issue_415() throws IOException, SlackApiException { diff --git a/slack-api-model/src/main/java/com/slack/api/model/Message.java b/slack-api-model/src/main/java/com/slack/api/model/Message.java index f9fbf8083..ac149714b 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/Message.java +++ b/slack-api-model/src/main/java/com/slack/api/model/Message.java @@ -253,4 +253,6 @@ public static class AssistantAppThread { } private AssistantAppThread assistantAppThread; + + private String streamingState; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/event/MessageBotEvent.java b/slack-api-model/src/main/java/com/slack/api/model/event/MessageBotEvent.java index d9710d904..3c1f2f8ca 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/event/MessageBotEvent.java +++ b/slack-api-model/src/main/java/com/slack/api/model/event/MessageBotEvent.java @@ -34,4 +34,6 @@ public class MessageBotEvent implements Event { private String eventTs; private String channelType; // app_home, channel, group, im, mpim + + private String streamingState; }