diff --git a/README.md b/README.md
index 8d573c4..e40ccbd 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ To make Non-Slash commands work you need to go to Discord Developer section -> A
## Slack Bot Installation
-See https://github.com/shayaantx/botdarr/wiki/Install-Slack-Bot
+See https://github.com/shayaantx/botdarr/wiki/Install-Slack-Bot-via-Slack-modern-app
(WARNING) If you are using slack "/help" has been deprecated by slack, so pick a different prefix for your commands (i.e., command-prefix=$). Otherwise help commands won't work
@@ -140,20 +140,20 @@ botdarr:
#### Chat Client Variables
-| Environment Variable | Description | Required | Default |
-| :---: | :---: | :---: | :---: |
-| DISCORD_TOKEN | The discord bot token (don't share) | yes - if you use discord | |
-| DISCORD_CHANNELS | The actual discord channel(s) names the bot has access to. If multiple channel names specified, separate via a comma | yes - if you use discord |
-| TELEGRAM_TOKEN | The telegram bot token (don't share) | yes - if you use telegram |
-| TELEGRAM_PRIVATE_CHANNELS | Your actual telegram channels your bot can respond in. This should be a list containing the name and id of the channel, i.e., CHANNEL_NAME:CHANNEL_ID to get the channel id, right click any post in private channel and copy post link you should see something like this, https://t.me/c/1408146664/63 the id is between c// example: plex-channel1:id1,plex-channel2:id2 | yes - if you use telegram |
-| TELEGRAM_PRIVATE_GROUPS | Your actual telegram groups your bot can respond in. This should be a list containing the name and id of the group, i.e., GROUP_NAME:GROUP_ID to get the channel id, right click any post in private group and copy post link (you need to enable message history for this) you should see something like this, https://t.me/c/1408146664/63 the id is between c// example: plex-group1:id1,plex-group2:id2 | yes - if you use telegram |
-| SLACK_BOT_TOKEN | The slack bot oauth authentication token (don't share) | yes - if you use slack |
-| SLACK_USER_TOKEN | The slack user oauth authentication token | yes - if you use slack |
-| SLACK_CHANNELS | The actual slack channel(s) you want to post slack messages to | yes - if you use slack |
-| MATRIX_USERNAME | The matrix bot username | yes - if you use matrix |
-| MATRIX_PASSWORD | The matrix bot password | yes - if you use matrix |
-| MATRIX_ROOM | The comma delimited list of matrix rooms you want to send/receive messages from | yes - if you use matrix |
-| MATRIX_HOME_SERVER_URL | The url of your homeserver | yes - if you use matrix |
+| Environment Variable | Description | Required | Default |
+|:-------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| :---: | :---: |
+| DISCORD_TOKEN | The discord bot token (don't share) | yes - if you use discord | |
+| DISCORD_CHANNELS | The actual discord channel(s) names the bot has access to. If multiple channel names specified, separate via a comma | yes - if you use discord |
+| TELEGRAM_TOKEN | The telegram bot token (don't share) | yes - if you use telegram |
+| TELEGRAM_PRIVATE_CHANNELS | Your actual telegram channels your bot can respond in. This should be a list containing the name and id of the channel, i.e., CHANNEL_NAME:CHANNEL_ID to get the channel id, right click any post in private channel and copy post link you should see something like this, https://t.me/c/1408146664/63 the id is between c// example: plex-channel1:id1,plex-channel2:id2 | yes - if you use telegram |
+| TELEGRAM_PRIVATE_GROUPS | Your actual telegram groups your bot can respond in. This should be a list containing the name and id of the group, i.e., GROUP_NAME:GROUP_ID to get the channel id, right click any post in private group and copy post link (you need to enable message history for this) you should see something like this, https://t.me/c/1408146664/63 the id is between c// example: plex-group1:id1,plex-group2:id2 | yes - if you use telegram |
+| SLACK_BOT_TOKEN | The slack bot oauth authentication token (don't share) | yes - if you use slack |
+| SLACK_APP_TOKEN | The slack app authentication token | yes - if you use slack |
+| SLACK_CHANNELS | The actual slack channel(s) you want to post slack messages to | yes - if you use slack |
+| MATRIX_USERNAME | The matrix bot username | yes - if you use matrix |
+| MATRIX_PASSWORD | The matrix bot password | yes - if you use matrix |
+| MATRIX_ROOM | The comma delimited list of matrix rooms you want to send/receive messages from | yes - if you use matrix |
+| MATRIX_HOME_SERVER_URL | The url of your homeserver | yes - if you use matrix |
#### Radarr
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh
index 54f0cc2..9a672a0 100644
--- a/docker/entrypoint.sh
+++ b/docker/entrypoint.sh
@@ -22,7 +22,7 @@ if [ ! -e "$propertiesFile" ]; then
[[ ! -z "${TELEGRAM_PRIVATE_GROUPS}" ]] && addConfiguration "telegram-private-groups" "${TELEGRAM_PRIVATE_GROUPS}" "${propertiesFile}"
[[ ! -z "${SLACK_BOT_TOKEN}" ]] && addConfiguration "slack-bot-token" "${SLACK_BOT_TOKEN}" "${propertiesFile}"
- [[ ! -z "${SLACK_USER_TOKEN}" ]] && addConfiguration "slack-user-token" "${SLACK_USER_TOKEN}" "${propertiesFile}"
+ [[ ! -z "${SLACK_APP_TOKEN}" ]] && addConfiguration "slack-app-token" "${SLACK_APP_TOKEN}" "${propertiesFile}"
[[ ! -z "${SLACK_CHANNELS}" ]] && addConfiguration "slack-channels" "${SLACK_CHANNELS}" "${propertiesFile}"
[[ ! -z "${DISCORD_TOKEN}" ]] && addConfiguration "discord-token" "${DISCORD_TOKEN}" "${propertiesFile}"
diff --git a/images/slack-modern-app-level-token.png b/images/slack-modern-app-level-token.png
new file mode 100644
index 0000000..386c5b7
Binary files /dev/null and b/images/slack-modern-app-level-token.png differ
diff --git a/images/slack-modern-create-app-from-scratch.png b/images/slack-modern-create-app-from-scratch.png
new file mode 100644
index 0000000..2375c19
Binary files /dev/null and b/images/slack-modern-create-app-from-scratch.png differ
diff --git a/images/slack-modern-create-app.png b/images/slack-modern-create-app.png
new file mode 100644
index 0000000..698b665
Binary files /dev/null and b/images/slack-modern-create-app.png differ
diff --git a/images/slack-modern-enable-socket-mode.png b/images/slack-modern-enable-socket-mode.png
new file mode 100644
index 0000000..fdcb863
Binary files /dev/null and b/images/slack-modern-enable-socket-mode.png differ
diff --git a/images/slack-modern-event-subscriptions.png b/images/slack-modern-event-subscriptions.png
new file mode 100644
index 0000000..9618c8f
Binary files /dev/null and b/images/slack-modern-event-subscriptions.png differ
diff --git a/images/slack-modern-scopes.png b/images/slack-modern-scopes.png
new file mode 100644
index 0000000..ce05546
Binary files /dev/null and b/images/slack-modern-scopes.png differ
diff --git a/pom.xml b/pom.xml
index 1ad4d8d..2bb2b0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,9 +74,9 @@
29.0-jre
- com.github.seratch
- jslack-lightning
- 3.4.2
+ com.slack.api
+ slack-api-client
+ 1.45.0
javax.websocket
diff --git a/src/main/java/com/botdarr/Config.java b/src/main/java/com/botdarr/Config.java
index 9b01d3e..4b99f89 100644
--- a/src/main/java/com/botdarr/Config.java
+++ b/src/main/java/com/botdarr/Config.java
@@ -276,9 +276,9 @@ public static final class Constants {
public static final String SLACK_BOT_TOKEN = "slack-bot-token";
/**
- * The slack user oauth token
+ * The slack app token for socket mode
*/
- public static final String SLACK_USER_TOKEN = "slack-user-token";
+ public static final String SLACK_APP_TOKEN = "slack-app-token";
/**
* The slack channel(s) to send notifications to
diff --git a/src/main/java/com/botdarr/clients/matrix/MatrixBootstrap.java b/src/main/java/com/botdarr/clients/matrix/MatrixBootstrap.java
index 4438c2b..9b305ec 100644
--- a/src/main/java/com/botdarr/clients/matrix/MatrixBootstrap.java
+++ b/src/main/java/com/botdarr/clients/matrix/MatrixBootstrap.java
@@ -7,6 +7,8 @@
import com.botdarr.scheduling.Scheduler;
import org.apache.logging.log4j.util.Strings;
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.Properties;
public class MatrixBootstrap extends ChatClientBootstrap {
@@ -28,13 +30,28 @@ public void init() throws Exception {
chatClient.listen();
}
+ private boolean isValidURL(String urlString) {
+ try {
+ new URL(urlString);
+ return true;
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+
@Override
public boolean isConfigured(Properties properties) {
- return
+ final boolean isConfigured =
!Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_USERNAME)) &&
- !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_PASSWORD)) &&
- !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_ROOM)) &&
- !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_HOME_SERVER));
+ !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_PASSWORD)) &&
+ !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_ROOM)) &&
+ !Strings.isBlank(properties.getProperty(Config.Constants.MATRIX_HOME_SERVER));
+
+ if (isConfigured && !isValidURL(properties.getProperty(Config.Constants.MATRIX_HOME_SERVER))) {
+ throw new RuntimeException("Matrix home server must be a url");
+ }
+
+ return isConfigured;
}
@Override
diff --git a/src/main/java/com/botdarr/clients/slack/SlackBootstrap.java b/src/main/java/com/botdarr/clients/slack/SlackBootstrap.java
index bb0bc93..10b9433 100644
--- a/src/main/java/com/botdarr/clients/slack/SlackBootstrap.java
+++ b/src/main/java/com/botdarr/clients/slack/SlackBootstrap.java
@@ -5,18 +5,20 @@
import com.botdarr.clients.ChatClientResponseBuilder;
import com.botdarr.commands.CommandContext;
import com.botdarr.scheduling.Scheduler;
-import com.github.seratch.jslack.Slack;
-import com.github.seratch.jslack.api.model.Message;
-import com.github.seratch.jslack.api.model.User;
-import com.github.seratch.jslack.api.model.block.LayoutBlock;
-import com.github.seratch.jslack.api.model.block.SectionBlock;
-import com.github.seratch.jslack.api.model.block.composition.MarkdownTextObject;
-import com.github.seratch.jslack.api.rtm.RTMMessageHandler;
+import com.slack.api.Slack;
+import com.slack.api.model.Message;
+import com.slack.api.model.User;
+import com.slack.api.model.block.LayoutBlock;
+import com.slack.api.model.block.SectionBlock;
+import com.slack.api.model.block.composition.MarkdownTextObject;
+import com.slack.api.rtm.RTMMessageHandler;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import com.slack.api.socket_mode.listener.EnvelopeListener;
+import com.slack.api.socket_mode.request.EventsApiEnvelope;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
@@ -33,16 +35,17 @@ public class SlackBootstrap extends ChatClientBootstrap {
@Override
public void init() throws Exception {
JsonParser jsonParser = new JsonParser();
- SlackChatClient slackChatClient = new SlackChatClient(Slack.getInstance().rtm(Config.getProperty(Config.Constants.SLACK_BOT_TOKEN)));
+ SlackChatClient slackChatClient = new SlackChatClient(Slack.getInstance().socketMode(Config.getProperty(Config.Constants.SLACK_APP_TOKEN)));
ChatClientResponseBuilder responseChatClientResponseBuilder = new SlackResponseBuilder();
ChatClientBootstrap.ApisAndCommandConfig config = buildConfig();
- slackChatClient.addMessageHandler(new RTMMessageHandler() {
+ slackChatClient.addMessageHandler(new EnvelopeListener() {
@Override
- public void handle(String message) {
- JsonObject json = jsonParser.parse(message).getAsJsonObject();
- SlackMessage slackMessage = new Gson().fromJson(json, SlackMessage.class);
+ public void handle(EventsApiEnvelope eventsApiEnvelope) {
+ JsonObject json = jsonParser.parse(eventsApiEnvelope.getPayload().toString()).getAsJsonObject();
+ SlackEventContainer slackEventContainer = new Gson().fromJson(json, SlackEventContainer.class);
+ SlackMessage slackMessage = slackEventContainer.getEvent();
if (slackMessage.getType() != null) {
if (slackMessage.getType().equalsIgnoreCase("message")) {
User user = slackChatClient.getUser(slackMessage.getUserId());
@@ -62,7 +65,7 @@ public void handle(String message) {
}
if (conversationMessages != null) {
conversationMessageLoop:
- for (com.github.seratch.jslack.api.model.Message conversationMessage : conversationMessages) {
+ for (com.slack.api.model.Message conversationMessage : conversationMessages) {
for (LayoutBlock layoutBlock : conversationMessage.getBlocks()) {
if (layoutBlock.getType().equals("section")) {
SectionBlock sectionBlock = (SectionBlock) layoutBlock;
@@ -98,7 +101,6 @@ private void handleCommand(String text, String userId, String channel) {
});
}
});
-
//start the scheduler threads that send notifications and cache data periodically
initScheduling(slackChatClient, responseChatClientResponseBuilder, config.getApis());
diff --git a/src/main/java/com/botdarr/clients/slack/SlackChatClient.java b/src/main/java/com/botdarr/clients/slack/SlackChatClient.java
index 0cda6f5..ab5e0c7 100644
--- a/src/main/java/com/botdarr/clients/slack/SlackChatClient.java
+++ b/src/main/java/com/botdarr/clients/slack/SlackChatClient.java
@@ -2,24 +2,28 @@
import com.botdarr.Config;
import com.botdarr.clients.ChatClient;
-import com.github.seratch.jslack.Slack;
-import com.github.seratch.jslack.api.methods.SlackApiException;
-import com.github.seratch.jslack.api.methods.request.chat.ChatPostMessageRequest;
-import com.github.seratch.jslack.api.methods.request.conversations.ConversationsHistoryRequest;
-import com.github.seratch.jslack.api.methods.request.conversations.ConversationsListRequest;
-import com.github.seratch.jslack.api.methods.request.groups.GroupsHistoryRequest;
-import com.github.seratch.jslack.api.methods.request.users.UsersInfoRequest;
-import com.github.seratch.jslack.api.methods.response.conversations.ConversationsListResponse;
-import com.github.seratch.jslack.api.model.Conversation;
-import com.github.seratch.jslack.api.model.ConversationType;
-import com.github.seratch.jslack.api.model.Message;
-import com.github.seratch.jslack.api.model.User;
-import com.github.seratch.jslack.api.model.block.DividerBlock;
-import com.github.seratch.jslack.api.model.block.LayoutBlock;
-import com.github.seratch.jslack.api.rtm.RTMClient;
-import com.github.seratch.jslack.api.rtm.RTMMessageHandler;
+import com.slack.api.Slack;
+import com.slack.api.methods.SlackApiException;
+import com.slack.api.methods.request.chat.ChatPostMessageRequest;
+import com.slack.api.methods.request.conversations.ConversationsHistoryRequest;
+import com.slack.api.methods.request.conversations.ConversationsListRequest;
+import com.slack.api.methods.request.groups.GroupsHistoryRequest;
+import com.slack.api.methods.request.users.UsersInfoRequest;
+import com.slack.api.methods.response.conversations.ConversationsListResponse;
+import com.slack.api.model.Conversation;
+import com.slack.api.model.ConversationType;
+import com.slack.api.model.Message;
+import com.slack.api.model.User;
+import com.slack.api.model.block.DividerBlock;
+import com.slack.api.model.block.LayoutBlock;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
+import com.slack.api.socket_mode.SocketModeClient;
+import com.slack.api.socket_mode.listener.EnvelopeListener;
+import com.slack.api.socket_mode.listener.WebSocketMessageListener;
+import com.slack.api.socket_mode.request.EventsApiEnvelope;
+import com.slack.api.socket_mode.request.InteractiveEnvelope;
+import com.slack.api.socket_mode.request.SlashCommandsEnvelope;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.Strings;
@@ -29,32 +33,44 @@
import java.util.concurrent.atomic.AtomicBoolean;
public class SlackChatClient implements ChatClient {
- public SlackChatClient(RTMClient rtmClient) {
- this.rtm = rtmClient;
- rtm.addCloseHandler(reason -> {
- connected.set(false);
- LOGGER.error("Error caught during slack close handler, reason=" + reason.toString());
+ public SlackChatClient(SocketModeClient socketClient) {
+ this.socketClient = socketClient;
+ this.socketClient.addWebSocketCloseListener((code, s) -> {
+ connected.set(false);
+ LOGGER.error("Error caught during slack close handler, reason=" + s + ",code=" + code);
});
- rtm.addErrorHandler(reason -> {
- LOGGER.error("Error caught from slack error handler", reason);
+ this.socketClient.addWebSocketErrorListener(throwable -> {
+ LOGGER.error("Error caught from slack error handler", throwable);
});
}
- public void addMessageHandler(RTMMessageHandler messageHandler) {
- rtm.addMessageHandler(messageHandler);
+ public void addMessageHandler(EnvelopeListener messageHandler) {
+ socketClient.addEventsApiEnvelopeListener(messageHandler);
+ socketClient.addWebSocketMessageListener(new WebSocketMessageListener() {
+ @Override
+ public void handle(String ee) {
+ int i = 0;
+ }
+ });
+ socketClient.addInteractiveEnvelopeListener(new EnvelopeListener() {
+ @Override
+ public void handle(InteractiveEnvelope ff) {
+ int i = 0;
+ }
+ });
+ socketClient.addSlashCommandsEnvelopeListener(new EnvelopeListener() {
+ @Override
+ public void handle(SlashCommandsEnvelope fff) {
+ int i = 0;
+ }
+ });
}
public void connect() throws Exception {
- // must connect within 30 seconds after establishing wss endpoint
- this.rtm.connect();
+ this.socketClient.setAutoReconnectEnabled(true);
+ this.socketClient.connect();
while(true) {
- //set state of whether we are connected or not (jslack doesn't expose session in rtm client so we need our own state)
- connected.set(true);
- while (connected.get()) {
- Thread.sleep(1000);
- }
- //if we for some reason stop being connected, reconnect and retry
- this.rtm.reconnect();
+ Thread.sleep(1000);
}
}
@@ -91,21 +107,21 @@ public void sendMessage(List chatClientResponses, String channel)
public List getPublicMessages(SlackMessage slackMessage) throws IOException, SlackApiException {
return Slack.getInstance().methods().conversationsHistory(ConversationsHistoryRequest.builder()
- .token(Config.getProperty(Config.Constants.SLACK_USER_TOKEN))
+ .token(Config.getProperty(Config.Constants.SLACK_BOT_TOKEN))
.channel(slackMessage.getItem().getChannel())
.oldest(slackMessage.getItem().getTs())
.inclusive(true)
- .limit(1)
+ .limit(Integer.valueOf(1))
.build()).getMessages();
}
public List getPrivateMessages(SlackMessage slackMessage) throws IOException, SlackApiException {
return Slack.getInstance().methods().groupsHistory(GroupsHistoryRequest.builder()
- .token(Config.getProperty(Config.Constants.SLACK_USER_TOKEN))
+ .token(Config.getProperty(Config.Constants.SLACK_BOT_TOKEN))
.channel(slackMessage.getItem().getChannel())
.oldest(slackMessage.getItem().getTs())
.inclusive(true)
- .count(1)
+ .count(Integer.valueOf(1))
.build()).getMessages();
}
@@ -163,6 +179,6 @@ private interface MessageSender {
private AtomicBoolean connected = new AtomicBoolean(false);
- private final RTMClient rtm;
+ private final SocketModeClient socketClient;
private static final Logger LOGGER = LogManager.getLogger("SlackLog");
}
diff --git a/src/main/java/com/botdarr/clients/slack/SlackEventContainer.java b/src/main/java/com/botdarr/clients/slack/SlackEventContainer.java
new file mode 100644
index 0000000..f4cbf59
--- /dev/null
+++ b/src/main/java/com/botdarr/clients/slack/SlackEventContainer.java
@@ -0,0 +1,22 @@
+package com.botdarr.clients.slack;
+
+public class SlackEventContainer {
+ public SlackMessage getEvent() {
+ return event;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "SlackEventContainer{" +
+ "event=" + event +
+ ", type='" + type + '\'' +
+ '}';
+ }
+
+ private SlackMessage event;
+ private String type;
+}
diff --git a/src/main/java/com/botdarr/clients/slack/SlackResponse.java b/src/main/java/com/botdarr/clients/slack/SlackResponse.java
index d4fe9be..b601a62 100644
--- a/src/main/java/com/botdarr/clients/slack/SlackResponse.java
+++ b/src/main/java/com/botdarr/clients/slack/SlackResponse.java
@@ -1,7 +1,7 @@
package com.botdarr.clients.slack;
import com.botdarr.clients.ChatClientResponse;
-import com.github.seratch.jslack.api.model.block.LayoutBlock;
+import com.slack.api.model.block.LayoutBlock;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java b/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java
index 438ad3d..0dddf47 100644
--- a/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java
+++ b/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java
@@ -11,10 +11,10 @@
import com.botdarr.commands.*;
import com.botdarr.commands.responses.*;
import com.botdarr.utilities.ListUtils;
-import com.github.seratch.jslack.api.model.block.*;
-import com.github.seratch.jslack.api.model.block.composition.MarkdownTextObject;
-import com.github.seratch.jslack.api.model.block.composition.PlainTextObject;
-import com.github.seratch.jslack.api.model.block.element.ButtonElement;
+import com.slack.api.model.block.*;
+import com.slack.api.model.block.composition.MarkdownTextObject;
+import com.slack.api.model.block.composition.PlainTextObject;
+import com.slack.api.model.block.element.ButtonElement;
import org.apache.logging.log4j.util.Strings;
import java.io.IOException;