From 14e8f61f02f2aed8f0826939dfd5d18ba0febfe9 Mon Sep 17 00:00:00 2001 From: Nicolas Haquet Date: Fri, 30 Sep 2022 17:41:33 +0200 Subject: [PATCH 1/2] Add legacyMode to use rtm.connect rtm.connect used instead of app connection following deprecation of rtm.start legacyMode is the default mode Change-Id: I5b38877986ec952cf7abdc1a2cd1befe3cdafe1e --- README.md | 7 + .../impl/AbstractSlackSessionImpl.java | 6 +- .../impl/SlackJSONMessageParser.java | 42 +++-- .../impl/SlackJSONSessionStatusParser.java | 146 ------------------ .../impl/SlackSessionFactory.java | 27 +++- .../impl/SlackWebSocketSessionImpl.java | 133 ++++++++++------ .../TestSlackJSONSessionStatusParser.java | 47 ------ .../impl/TestSlackWebSocketSessionImpl.java | 2 +- 8 files changed, 138 insertions(+), 272 deletions(-) delete mode 100644 sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONSessionStatusParser.java delete mode 100644 sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackJSONSessionStatusParser.java diff --git a/README.md b/README.md index 9737b97a..326ee79b 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,10 @@ All these events can be listen provided your bot has the rights to (IE : the bot * reaction removed from message event (since v0.5.0) * team joined event +# Attention + +From version 1.4.0 upward, channel members are not loaded in memory anymore in order for the api to start up faster. This should have no impact +on the current behavior. # Thanks @@ -97,6 +101,9 @@ Many thanks to everyone who has contributed to this library : * Rhys Kenwell * Aman Gupta * Miklos Sagi +* Nathalie Mahe +* Sebastien Lelouvier +* Nicolas Haquet (Let me know if I forgot someone, I'll fix that ASAP ;) ) diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java index 02efe588..497f6624 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java @@ -99,11 +99,7 @@ public SlackChannel findChannelById(String channelId) SlackChannel toReturn = channels.get(channelId); if (toReturn == null) { - // direct channel case - if (channelId != null && channelId.startsWith("D")) - { - toReturn = SlackChannel.builder().id(channelId).name("").topic("").name("").build(); - } + toReturn = SlackChannel.builder().id(channelId).name("").topic("").name("").build(); } return toReturn; } diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONMessageParser.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONMessageParser.java index 034be440..03dc90ae 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONMessageParser.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONMessageParser.java @@ -1,5 +1,10 @@ package com.ullink.slack.simpleslackapi.impl; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -8,13 +13,6 @@ import com.ullink.slack.simpleslackapi.events.userchange.SlackTeamJoin; import com.ullink.slack.simpleslackapi.events.userchange.SlackUserChange; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - class SlackJSONMessageParser { private static final Logger LOGGER = LoggerFactory.getLogger(SlackJSONMessageParser.class); @@ -214,29 +212,39 @@ private static SlackMessageDeleted parseMessageDeleted(JsonObject obj, SlackChan return new SlackMessageDeleted(channel, deletedTs, ts); } - private static SlackMessagePosted parseMessagePublished(JsonObject obj, SlackChannel channel, String ts, SlackSession slackSession) { + private static SlackMessagePosted parseMessagePublished(JsonObject obj, SlackChannel channel, String ts, SlackSession slackSession) + { String text = GsonHelper.getStringOrNull(obj.get("text")); String subtype = GsonHelper.getStringOrNull(obj.get("subtype")); String userId; //sloppy fix for finding userId inside File_comment subtype. - if (subtype !=null && subtype.equals("file_comment")) { + if (subtype != null && subtype.equals("file_comment")) + { userId = GsonHelper.getStringOrNull(obj.get("comment").getAsJsonObject().get("user")); - } else { + } + else + { userId = GsonHelper.getStringOrNull(obj.get("user")); - } - if (userId == null) { + } + if (userId == null) + { userId = GsonHelper.getStringOrNull(obj.get("bot_id")); } SlackUser user = slackSession.findUserById(userId); String threadTimestamp = GsonHelper.getStringOrNull(obj.get("thread_ts")); - if (user == null) { + if (user == null) + { SlackIntegration integration = slackSession.findIntegrationById(userId); - if (integration == null) { - throw new IllegalStateException("unknown user id: " + userId); + if (integration == null) + { + LOGGER.info("unknown user id: " + userId + ". Fallbacking..."); + user = SlackPersonaImpl.builder().id(userId).build(); + } + else + { + user = new SlackIntegrationUser(integration); } - user = new SlackIntegrationUser(integration); - } Map reacs = extractReactionsFromMessageJSON(obj); ArrayList attachments = extractAttachmentsFromMessageJSON(obj); diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONSessionStatusParser.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONSessionStatusParser.java deleted file mode 100644 index 5d24e3ea..00000000 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackJSONSessionStatusParser.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.ullink.slack.simpleslackapi.impl; - -import java.util.HashMap; -import java.util.Map; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.ullink.slack.simpleslackapi.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -class SlackJSONSessionStatusParser { - private static final Logger LOGGER = LoggerFactory.getLogger(SlackJSONSessionStatusParser.class); - - private final Map channels = new HashMap<>(); - private final Map users = new HashMap<>(); - private final Map integrations = new HashMap<>(); - - private SlackPersona sessionPersona; - - private SlackTeam team; - - private String webSocketURL; - - private final String toParse; - - private String error; - - SlackJSONSessionStatusParser(String toParse) - { - this.toParse = toParse; - } - - Map getChannels() - { - return channels; - } - - Map getUsers() - { - return users; - } - - Map getIntegrations() { - return integrations; - } - - public String getWebSocketURL() - { - return webSocketURL; - } - - public String getError() - { - return error; - } - - void parse() - { - LOGGER.debug("parsing session status : " + toParse); - JsonParser parser = new JsonParser(); - JsonObject jsonResponse = parser.parse(toParse).getAsJsonObject(); - Boolean ok = jsonResponse.get("ok").getAsBoolean(); - if (Boolean.FALSE.equals(ok)) { - error = jsonResponse.get("error").getAsString(); - return; - } - JsonArray usersJson = jsonResponse.get("users").getAsJsonArray(); - - for (JsonElement jsonObject : usersJson) - { - JsonObject jsonUser = jsonObject.getAsJsonObject(); - SlackUser slackUser = SlackJSONParsingUtils.buildSlackUser(jsonUser); - LOGGER.debug("slack user found : " + slackUser.getId()); - users.put(slackUser.getId(), slackUser); - } - - if (jsonResponse.get("bots") != null) { - JsonArray integrationsJson = jsonResponse.get("bots").getAsJsonArray(); - for (JsonElement jsonElement : integrationsJson) - { - JsonObject jsonIntegration = jsonElement.getAsJsonObject(); - SlackIntegration slackIntegration = SlackJSONParsingUtils.buildSlackIntegration(jsonIntegration); - LOGGER.debug("slack integration found : " + slackIntegration.getId()); - integrations.put(slackIntegration.getId(), slackIntegration); - } - } - - JsonArray channelsJson = jsonResponse.get("channels").getAsJsonArray(); - for (JsonElement jsonObject : channelsJson) - { - JsonObject jsonChannel = jsonObject.getAsJsonObject(); - SlackChannel channel = SlackJSONParsingUtils.buildSlackChannel(jsonChannel); - LOGGER.debug("slack public channel found : " + channel.getId()); - channels.put(channel.getId(), channel); - } - - if (jsonResponse.get("groups") != null) - { - JsonArray groupsJson = jsonResponse.get("groups").getAsJsonArray(); - for (JsonElement jsonObject : groupsJson) - { - JsonObject jsonChannel = jsonObject.getAsJsonObject(); - SlackChannel channel = SlackJSONParsingUtils.buildSlackChannel(jsonChannel); - LOGGER.debug("slack private group found : " + channel.getId()); - channels.put(channel.getId(), channel); - } - } - - if (jsonResponse.get("ims") != null) - { - JsonArray imsJson = jsonResponse.get("ims").getAsJsonArray(); - - for (JsonElement jsonObject : imsJson) - { - JsonObject jsonChannel = jsonObject.getAsJsonObject(); - SlackChannel channel = SlackJSONParsingUtils.buildSlackImChannel(jsonChannel, users); - LOGGER.debug("slack im channel found : " + channel.getId()); - channels.put(channel.getId(), channel); - } - } - - - JsonObject selfJson = jsonResponse.get("self").getAsJsonObject(); - sessionPersona = SlackJSONParsingUtils.buildSlackUser(selfJson); - - JsonObject teamJson = jsonResponse.get("team").getAsJsonObject(); - team = SlackJSONParsingUtils.buildSlackTeam(teamJson); - - webSocketURL = jsonResponse.get("url").getAsString(); - - } - - public SlackPersona getSessionPersona() - { - return sessionPersona; - } - - public SlackTeam getTeam() - { - return team; - } -} diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java index 6282aafd..4d5c40ad 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java @@ -15,16 +15,28 @@ public class SlackSessionFactory { */ public static SlackSession createWebSocketSlackSession(String authToken, String appLevelToken) { - return new SlackWebSocketSessionImpl(null, authToken, appLevelToken, null, true, true, 0, null); + return new SlackWebSocketSessionImpl(null, authToken, appLevelToken, null, true, true, 0, null, true); } /** - * @deprecated use the authToken and appLevelToken method + * Add a new variable because Slack Api change the way to be called. Now it requires auth token and app level token + * @param authToken + * @param appLevelToken + * @param legacyMode set to false to use the appLevelToken connection + * @return + */ + public static SlackSession createWebSocketSlackSession(String authToken, String appLevelToken, boolean legacyMode) + { + return new SlackWebSocketSessionImpl(null, authToken, appLevelToken, null, true, true, 0, null, legacyMode); + } + + + /** * @param authToken */ public static SlackSession createWebSocketSlackSession(String authToken) { - return new SlackWebSocketSessionImpl(null, authToken, null, null, true, true, 0, null); + return new SlackWebSocketSessionImpl(null, authToken, null, null, true, true, 0, null, true); } /** @@ -64,6 +76,7 @@ public static class SlackSessionFactoryBuilder { private WebSocketContainerProvider provider; private boolean autoreconnection; private boolean rateLimitSupport = true; + private boolean legacyMode; /** * Add new variable -appLevelToken @@ -82,7 +95,6 @@ private SlackSessionFactoryBuilder(String authToken, String appLevelToken) { */ private SlackSessionFactoryBuilder(String authToken) { this.authToken = authToken; - this.appLevelToken = appLevelToken; } public SlackSessionFactoryBuilder withBaseApiUrl(String slackBaseApi) { @@ -127,8 +139,13 @@ public SlackSessionFactoryBuilder withRateLimitSupport(boolean rateLimitSupport) return this; } + public SlackSessionFactoryBuilder withLegacyMode(boolean legacyMode) { + this.legacyMode = legacyMode; + return this; + } + public SlackSession build() { - return new SlackWebSocketSessionImpl(provider, authToken, appLevelToken, slackBaseApi, proxyType, proxyAddress, proxyPort, proxyUser, proxyPassword, autoreconnection, rateLimitSupport, heartbeat, unit); + return new SlackWebSocketSessionImpl(provider, authToken, appLevelToken, slackBaseApi, proxyType, proxyAddress, proxyPort, proxyUser, proxyPassword, autoreconnection, rateLimitSupport, heartbeat, unit, legacyMode); } } } diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackWebSocketSessionImpl.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackWebSocketSessionImpl.java index ab9df077..7fda9142 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackWebSocketSessionImpl.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackWebSocketSessionImpl.java @@ -5,6 +5,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.net.ConnectException; import java.net.Proxy; import java.net.URI; import java.nio.charset.Charset; @@ -188,6 +189,7 @@ private interface REACTIONS private final boolean reconnectOnDisconnection; private final boolean isRateLimitSupported; + private final boolean legacyMode; private volatile boolean wantDisconnect; private Thread connectionMonitoringThread; //TODO: replace this with a scheduled executor @@ -218,24 +220,7 @@ public SlackMessageHandle sendMessageToUser(String userName, return sendMessageToUser(findUserByUserName(userName), message, attachment); } - private List getAllIMChannels() { - Collection allChannels = getChannels(); - List iMChannels = new ArrayList<>(); - for (SlackChannel channel : allChannels) { - if (channel.isDirect()) { - iMChannels.add(channel); - } - } - return iMChannels; - } - private SlackChannel getIMChannelForUser(SlackUser user) { - List imcs = getAllIMChannels(); - for (SlackChannel channel : imcs) { - if (channel.getMembers().contains(user)) { - return channel; - } - } SlackMessageHandle reply = openDirectMessageChannel(user); String channelAnswer = reply.getReply().getPlainAnswer(); JsonParser parser = new JsonParser(); @@ -335,11 +320,11 @@ private > void dispatchImp /** * newly added variable */ - SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) { - this(webSocketContainerProvider, authToken, appLevelToken, slackApiBase, null, null, -1, null, null, reconnectOnDisconnection, isRateLimitSupported, heartbeat, unit); + SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit, boolean legacyMode) { + this(webSocketContainerProvider, authToken, appLevelToken, slackApiBase, null, null, -1, null, null, reconnectOnDisconnection, isRateLimitSupported, heartbeat, unit, legacyMode); } - SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, Proxy.Type proxyType, String proxyAddress, int proxyPort, String proxyUser, String proxyPassword, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit) { + SlackWebSocketSessionImpl(WebSocketContainerProvider webSocketContainerProvider, String authToken, String appLevelToken, String slackApiBase, Proxy.Type proxyType, String proxyAddress, int proxyPort, String proxyUser, String proxyPassword, boolean reconnectOnDisconnection, boolean isRateLimitSupported, long heartbeat, TimeUnit unit, boolean legacyMode) { this.authToken = authToken; /** * newly added variable @@ -357,6 +342,7 @@ private > void dispatchImp } this.reconnectOnDisconnection = reconnectOnDisconnection; this.isRateLimitSupported = isRateLimitSupported; + this.legacyMode = legacyMode; this.heartbeat = heartbeat != 0 ? unit.toMillis(heartbeat) : DEFAULT_HEARTBEAT_IN_MILLIS; this.webSocketContainerProvider = webSocketContainerProvider != null ? webSocketContainerProvider : new DefaultWebSocketContainerProvider(this.proxyAddress, this.proxyPort, this.proxyUser, this.proxyPassword); addInternalListeners(); @@ -402,7 +388,6 @@ public void reconnect() throws IOException{ } } - @Override public boolean isConnected() { @@ -418,6 +403,8 @@ public boolean isConnected() */ private void connectImpl() throws IOException { + JsonParser parser = new JsonParser(); + LOGGER.info("connecting to slack"); /** * Populate users @@ -426,31 +413,7 @@ private void connectImpl() throws IOException /** * Populate channels */ - String channelsAnswer = listChannels().getReply().getPlainAnswer(); - JsonParser parser = new JsonParser(); - - JsonObject answerJson = parser.parse(channelsAnswer).getAsJsonObject(); - JsonArray channelsJson = answerJson.get("channels").getAsJsonArray(); - - for (JsonElement jsonObject : channelsJson) { - JsonObject jsonChannel = jsonObject.getAsJsonObject(); - SlackChannel channel = SlackJSONParsingUtils.buildSlackChannel(jsonChannel); - LOGGER.debug("slack public channel found : " + channel.getId()); - - String channelMembersAnswer = listChannelMembers(channel.getId()).getReply().getPlainAnswer(); - JsonObject channelMembersJsonObject = parser.parse(channelMembersAnswer).getAsJsonObject(); - - JsonArray channelMembersJsonArray = channelMembersJsonObject.getAsJsonArray("members"); - - if(channelMembersJsonArray != null){ - for (JsonElement memberJson : channelMembersJsonArray) { - SlackUser user = users.get(memberJson.getAsString()); - channel.addUser(user); - } - } - - channels.put(channel.getId(), channel); - } + fecthChannels(parser); /** * Get auth test metadata @@ -467,15 +430,36 @@ private void connectImpl() throws IOException * Get user info about self and build sessionPersona */ String userInfoAnswer = getUserInfo(user_id).getReply().getPlainAnswer(); - JsonObject userInfoJson = parser.parse(userInfoAnswer).getAsJsonObject(); + JsonObject userInfoJson = parser.parse(userInfoAnswer).getAsJsonObject().get("user").getAsJsonObject(); sessionPersona = SlackJSONParsingUtils.buildSlackUser(userInfoJson); /** * Get web socket URL */ - String appsConnectionsOpenAnswer = appsConnectionsOpen().getReply().getPlainAnswer(); - JsonObject appsConnectionsOpenJson = parser.parse(appsConnectionsOpenAnswer).getAsJsonObject(); - webSocketConnectionURL = appsConnectionsOpenJson.get("url").getAsString(); + if (legacyMode) + { + HttpClient httpClient = getHttpClient(); + HttpPost request = new HttpPost(slackApiBase + "rtm.connect"); + request.setHeader(HttpHeaders.CONTENT_TYPE,"application/x-www-form-urlencoded"); + request.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + authToken); + HttpResponse response = httpClient.execute(request); + LOGGER.debug(response.getStatusLine().toString()); + JsonObject jsonResponse = parser.parse(consumeToString(response.getEntity().getContent())).getAsJsonObject(); + Boolean ok = jsonResponse.get("ok").getAsBoolean(); + if (Boolean.FALSE.equals(ok)) + { + String error = jsonResponse.get("error").getAsString(); + LOGGER.error("Error during authentication : " + error); + throw new ConnectException(error); + } + webSocketConnectionURL = jsonResponse.get("url").getAsString(); + } + else + { + String appsConnectionsOpenAnswer = appsConnectionsOpen().getReply().getPlainAnswer(); + JsonObject appsConnectionsOpenJson = parser.parse(appsConnectionsOpenAnswer).getAsJsonObject(); + webSocketConnectionURL = appsConnectionsOpenJson.get("url").getAsString(); + } LOGGER.info("Team " + team.getId() + " : " + team.getName()); LOGGER.info("Self " + sessionPersona.getId() + " : " + sessionPersona.getUserName()); @@ -1129,8 +1113,26 @@ public SlackMessageHandle listEmoji() { */ public SlackMessageHandle listChannels() { - return postGenericSlackCommand(CONVERSATION.LIST_COMMAND); + return listChannels(null); } + + /** + * Get the channel's conversation details + * CS427 Issue Link: https://github.com/Itiviti/simple-slack-api/issues/279 + * @return channel conversation details + */ + public SlackMessageHandle listChannels(String nextCursor) + { + Map params = new HashMap<>(); + params.put("limit", "1000"); + params.put("types", "public_channel,private_channel"); + if (nextCursor != null) + { + params.put("cursor", nextCursor); + } + return postGenericSlackCommand(params, CONVERSATION.LIST_COMMAND); + } + /** * Get the channel members * CS427 Issue Link: https://github.com/Itiviti/simple-slack-api/issues/279 @@ -1175,6 +1177,7 @@ public SlackMessageHandle getUserInfo(String user_id) { @Override public void refetchUsers() { + LOGGER.info("Fetching users"); Map params = new HashMap<>(); params.put("presence", "1"); SlackMessageHandle handle = postGenericSlackCommand(params, USERS.LIST_COMMAND); @@ -1195,6 +1198,34 @@ public void refetchUsers() { users = members; } + private void fecthChannels(JsonParser parser) + { + String nextCursor = null; + int nbChannels = 1; + LOGGER.info("Fetching channels"); + do + { + String channelsAnswer = listChannels(nextCursor).getReply().getPlainAnswer(); + JsonObject answerJson = parser.parse(channelsAnswer).getAsJsonObject(); + nextCursor = answerJson.get("response_metadata").getAsJsonObject().get("next_cursor").getAsString(); + JsonArray channelsJson = answerJson.get("channels").getAsJsonArray(); + + for (JsonElement jsonObject : channelsJson) + { + JsonObject jsonChannel = jsonObject.getAsJsonObject(); + SlackChannel channel = SlackJSONParsingUtils.buildSlackChannel(jsonChannel); + LOGGER.debug("slack public channel found : " + channel.getId()); + channels.put(channel.getId(), channel); + if (nbChannels % 1000 == 0) + { + LOGGER.info(nbChannels + " channels loaded"); + } + nbChannels++; + } + } + while (nextCursor != null && !nextCursor.isEmpty()); + } + private void postSlackCommand(Map params, String command, SlackMessageHandle handle, Class replyType) { HttpClient client = getHttpClient(); HttpPost request = new HttpPost(slackApiBase + command); diff --git a/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackJSONSessionStatusParser.java b/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackJSONSessionStatusParser.java deleted file mode 100644 index 8bfa4bda..00000000 --- a/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackJSONSessionStatusParser.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.ullink.slack.simpleslackapi.impl; - -import org.junit.Test; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; - -import static org.assertj.core.api.Assertions.assertThat; - -public class TestSlackJSONSessionStatusParser -{ - @Test - public void testParsingSessionDescription() throws Exception - { - InputStream stream = getClass().getResourceAsStream("/test_json.json"); - InputStreamReader isReader = new InputStreamReader(stream); - BufferedReader reader = new BufferedReader(isReader); - StringBuilder strBuilder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - strBuilder.append(line); - } - - SlackJSONSessionStatusParser parser = new SlackJSONSessionStatusParser(strBuilder.toString()); - parser.parse(); - - assertThat(parser.getChannels()).containsOnlyKeys("CHANNELID1", "CHANNELID2", "CHANNELID3", "GROUPID1", "DIM01"); - assertThat(parser.getUsers()).containsOnlyKeys("USERID1","USERID2","USERID3","USERID4","BOTID1","BOTID2"); - assertThat(parser.getWebSocketURL()).isEqualTo("wss://mywebsocketurl"); - assertThat(parser.getUsers().get("USERID1").getTimeZone()).isEqualTo("Europe/Amsterdam"); - assertThat(parser.getUsers().get("USERID1").getTimeZoneLabel()).isEqualTo("Central European Summer Time"); - assertThat(parser.getUsers().get("USERID1").getTimeZoneOffset()).isEqualTo(7200); - - assertThat(parser.getSessionPersona().getId()).isEqualTo("SELF"); - assertThat(parser.getSessionPersona().getUserName()).isEqualTo("myself"); - - assertThat(parser.getTeam().getId()).isEqualTo("TEAM"); - assertThat(parser.getTeam().getName()).isEqualTo("Example Team"); - assertThat(parser.getTeam().getDomain()).isEqualTo("example"); - - assertThat(parser.getIntegrations().get("INTEGRATION1").getName()).isEqualTo("bot1"); - assertThat(parser.getIntegrations().get("INTEGRATION1").isDeleted()).isEqualTo(false); - assertThat(parser.getIntegrations().get("INTEGRATION2").getName()).isEqualTo("bot2"); - assertThat(parser.getIntegrations().get("INTEGRATION2").isDeleted()).isEqualTo(true); - } -} diff --git a/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackWebSocketSessionImpl.java b/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackWebSocketSessionImpl.java index 51b76cc7..1e7c4fae 100644 --- a/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackWebSocketSessionImpl.java +++ b/sources/src/test/java/com/ullink/slack/simpleslackapi/impl/TestSlackWebSocketSessionImpl.java @@ -14,7 +14,7 @@ public class TestSlackWebSocketSessionImpl { @Test(expected = IllegalArgumentException.class) public void testSendMessageWithNullChannel(@Mocked WebSocketContainerProvider provider) { - SlackWebSocketSessionImpl webSocketSession = new SlackWebSocketSessionImpl(provider, "", "", null, false, false, 42L, TimeUnit.MILLISECONDS); + SlackWebSocketSessionImpl webSocketSession = new SlackWebSocketSessionImpl(provider, "", "", null, false, false, 42L, TimeUnit.MILLISECONDS, true); try { webSocketSession.sendMessage((SlackChannel) null, ""); } catch (NullPointerException e) { From 55c79bb14337f49335b4255e9c09f2ce1bbfc5df Mon Sep 17 00:00:00 2001 From: Nicolas Haquet Date: Mon, 3 Oct 2022 12:04:06 +0200 Subject: [PATCH 2/2] Different fixes : Fix SlackSessionFactoryBuilder with legacyMode Fix nullpointer when sending messages with no attachments Change-Id: I83f86790b2794753878bb3603555c9f7cee506b8 --- .../impl/AbstractSlackSessionImpl.java | 123 +++++++++++------- .../impl/SlackSessionFactory.java | 26 +++- 2 files changed, 101 insertions(+), 48 deletions(-) diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java index 497f6624..808fb3bb 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/AbstractSlackSessionImpl.java @@ -1,46 +1,48 @@ package com.ullink.slack.simpleslackapi.impl; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import com.ullink.slack.simpleslackapi.*; import com.ullink.slack.simpleslackapi.listeners.*; import com.ullink.slack.simpleslackapi.replies.SlackMessageReply; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; - abstract class AbstractSlackSessionImpl implements SlackSession { - protected Map channels = new ConcurrentHashMap<>(); - protected Map users = new ConcurrentHashMap<>(); - protected Map integrations = new ConcurrentHashMap<>(); - protected SlackPersona sessionPersona; + protected Map channels = new ConcurrentHashMap<>(); + protected Map users = new ConcurrentHashMap<>(); + protected Map integrations = new ConcurrentHashMap<>(); + protected SlackPersona sessionPersona; protected SlackTeam team; - protected final List channelArchiveListener = new CopyOnWriteArrayList<>(); - protected final List channelCreateListener = new CopyOnWriteArrayList<>(); - protected final List channelDeleteListener = new CopyOnWriteArrayList<>(); - protected final List channelRenamedListener = new CopyOnWriteArrayList<>(); - protected final List channelUnarchiveListener = new CopyOnWriteArrayList<>(); - protected final List channelJoinedListener = new CopyOnWriteArrayList<>(); - protected final List channelLeftListener = new CopyOnWriteArrayList<>(); - protected final List groupJoinedListener = new CopyOnWriteArrayList<>(); - protected final List messageDeletedListener = new CopyOnWriteArrayList<>(); - protected final List messagePostedListener = new CopyOnWriteArrayList<>(); - protected final List messageUpdatedListener = new CopyOnWriteArrayList<>(); - protected final List slackConnectedListener = new CopyOnWriteArrayList<>(); - protected final List reactionAddedListener = new CopyOnWriteArrayList<>(); - protected final List reactionRemovedListener = new CopyOnWriteArrayList<>(); - protected final List slackUserChangeListener = new CopyOnWriteArrayList<>(); - protected final List slackTeamJoinListener = new CopyOnWriteArrayList<>(); - protected final List pinAddedListener = new CopyOnWriteArrayList<>(); - protected final List pinRemovedListener = new CopyOnWriteArrayList<>(); - protected final List presenceChangeListener = new CopyOnWriteArrayList<>(); - protected final List slackDisconnectedListener = new CopyOnWriteArrayList<>(); - protected final List userTypingListener = new CopyOnWriteArrayList<>(); - - static final SlackChatConfiguration DEFAULT_CONFIGURATION = SlackChatConfiguration.getConfiguration().asUser(); - static final boolean DEFAULT_UNFURL = true; + protected final List channelArchiveListener = new CopyOnWriteArrayList<>(); + protected final List channelCreateListener = new CopyOnWriteArrayList<>(); + protected final List channelDeleteListener = new CopyOnWriteArrayList<>(); + protected final List channelRenamedListener = new CopyOnWriteArrayList<>(); + protected final List channelUnarchiveListener = new CopyOnWriteArrayList<>(); + protected final List channelJoinedListener = new CopyOnWriteArrayList<>(); + protected final List channelLeftListener = new CopyOnWriteArrayList<>(); + protected final List groupJoinedListener = new CopyOnWriteArrayList<>(); + protected final List messageDeletedListener = new CopyOnWriteArrayList<>(); + protected final List messagePostedListener = new CopyOnWriteArrayList<>(); + protected final List messageUpdatedListener = new CopyOnWriteArrayList<>(); + protected final List slackConnectedListener = new CopyOnWriteArrayList<>(); + protected final List reactionAddedListener = new CopyOnWriteArrayList<>(); + protected final List reactionRemovedListener = new CopyOnWriteArrayList<>(); + protected final List slackUserChangeListener = new CopyOnWriteArrayList<>(); + protected final List slackTeamJoinListener = new CopyOnWriteArrayList<>(); + protected final List pinAddedListener = new CopyOnWriteArrayList<>(); + protected final List pinRemovedListener = new CopyOnWriteArrayList<>(); + protected final List presenceChangeListener = new CopyOnWriteArrayList<>(); + protected final List slackDisconnectedListener = new CopyOnWriteArrayList<>(); + protected final List userTypingListener = new CopyOnWriteArrayList<>(); + + static final SlackChatConfiguration DEFAULT_CONFIGURATION = SlackChatConfiguration.getConfiguration().asUser(); + static final boolean DEFAULT_UNFURL = true; @Override public SlackTeam getTeam() @@ -61,7 +63,8 @@ public Collection getUsers() } @Override - public Collection getIntegrations() { + public Collection getIntegrations() + { return new ArrayList<>(integrations.values()); } @@ -190,19 +193,30 @@ public SlackMessageHandle sendMessage(SlackChannel channel, S } @Override - public SlackMessageHandle sendMessage(SlackChannel channel, SlackPreparedMessage preparedMessage) { + public SlackMessageHandle sendMessage(SlackChannel channel, SlackPreparedMessage preparedMessage) + { return sendMessage(channel, preparedMessage, DEFAULT_CONFIGURATION); } @Override public SlackMessageHandle sendMessage(SlackChannel channel, String message, SlackAttachment attachment, SlackChatConfiguration chatConfiguration, boolean unfurl) { - SlackPreparedMessage preparedMessage = SlackPreparedMessage.builder() + SlackPreparedMessage preparedMessage; + if (attachment != null) + { + preparedMessage = SlackPreparedMessage.builder() .message(message) .unfurl(unfurl) .attachment(attachment) .build(); - + } + else + { + preparedMessage = SlackPreparedMessage.builder() + .message(message) + .unfurl(unfurl) + .build(); + } return sendMessage(channel, preparedMessage, chatConfiguration); } @@ -215,11 +229,22 @@ public SlackMessageHandle sendEphemeralMessage(SlackChannel c @Override public SlackMessageHandle sendEphemeralMessage(SlackChannel channel, SlackUser user, String message, SlackAttachment attachment, SlackChatConfiguration chatConfiguration, boolean unfurl) { - SlackPreparedMessage preparedMessage = SlackPreparedMessage.builder() + SlackPreparedMessage preparedMessage; + if (attachment != null) + { + preparedMessage = SlackPreparedMessage.builder() .message(message) .unfurl(unfurl) .attachment(attachment) .build(); + } + else + { + preparedMessage = SlackPreparedMessage.builder() + .message(message) + .unfurl(unfurl) + .build(); + } return sendEphemeralMessage(channel, user, preparedMessage, chatConfiguration); } @@ -246,9 +271,9 @@ public SlackMessageHandle sendEphemeralMessage(SlackChannel c public SlackMessageHandle sendEphemeralMessage(SlackChannel channel, SlackUser user, String message, boolean unfurl) { SlackPreparedMessage preparedMessage = SlackPreparedMessage.builder() - .message(message) - .unfurl(unfurl) - .build(); + .message(message) + .unfurl(unfurl) + .build(); return sendEphemeralMessage(channel, user, preparedMessage, DEFAULT_CONFIGURATION); } @@ -403,12 +428,14 @@ public void removeSlackConnectedListener(SlackConnectedListener listener) } @Override - public void addSlackDisconnectedListener(SlackDisconnectedListener listener) { + public void addSlackDisconnectedListener(SlackDisconnectedListener listener) + { slackDisconnectedListener.add(listener); } @Override - public void removeSlackDisconnectedListener(SlackDisconnectedListener listener) { + public void removeSlackDisconnectedListener(SlackDisconnectedListener listener) + { slackDisconnectedListener.remove(listener); } @@ -485,22 +512,26 @@ public void removePinRemovedListener(PinRemovedListener listener) } @Override - public void addPresenceChangeListener(PresenceChangeListener listener) { + public void addPresenceChangeListener(PresenceChangeListener listener) + { presenceChangeListener.add(listener); } @Override - public void removePresenceChangeListener(PresenceChangeListener listener) { + public void removePresenceChangeListener(PresenceChangeListener listener) + { presenceChangeListener.remove(listener); } @Override - public void addUserTypingListener(UserTypingListener listener) { + public void addUserTypingListener(UserTypingListener listener) + { userTypingListener.add(listener); } @Override - public void removeUserTypingListener(UserTypingListener listener) { + public void removeUserTypingListener(UserTypingListener listener) + { userTypingListener.remove(listener); } } diff --git a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java index 4d5c40ad..be29bf8b 100644 --- a/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java +++ b/sources/src/main/java/com/ullink/slack/simpleslackapi/impl/SlackSessionFactory.java @@ -76,7 +76,7 @@ public static class SlackSessionFactoryBuilder { private WebSocketContainerProvider provider; private boolean autoreconnection; private boolean rateLimitSupport = true; - private boolean legacyMode; + private boolean legacyMode = true; /** * Add new variable -appLevelToken @@ -87,16 +87,38 @@ public static class SlackSessionFactoryBuilder { private SlackSessionFactoryBuilder(String authToken, String appLevelToken) { this.authToken = authToken; this.appLevelToken = appLevelToken; + this.legacyMode = false; } /** - * @deprecated use the authToken and appLevelToken constructor + * Add new variable -appLevelToken + * CS427 Issue Link: https://github.com/Itiviti/simple-slack-api/issues/283 + * @param authToken + * @param appLevelToken + */ + private SlackSessionFactoryBuilder(String authToken, String appLevelToken, boolean legacyMode) { + this.authToken = authToken; + this.appLevelToken = appLevelToken; + this.legacyMode = legacyMode; + } + + /** + * use the authToken and legacy mode * @param authToken */ private SlackSessionFactoryBuilder(String authToken) { this.authToken = authToken; } + /** + * use the authToken and legacy mode + * @param authToken + */ + private SlackSessionFactoryBuilder(String authToken, boolean legacyMode) { + this.authToken = authToken; + this.legacyMode = legacyMode; + } + public SlackSessionFactoryBuilder withBaseApiUrl(String slackBaseApi) { this.slackBaseApi = slackBaseApi; return this;