Skip to content

Commit

Permalink
Update slack chat client to use socket mode instead of rttm + update …
Browse files Browse the repository at this point in the history
…docs
  • Loading branch information
shayaantx committed Jan 11, 2025
1 parent 40d2b6f commit 8662647
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 50 deletions.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

<b>(WARNING)</b> 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

Expand Down Expand Up @@ -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/<id>/<postId> 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/<id>/<postId> 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/<id>/<postId> 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/<id>/<postId> 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
Expand Down
2 changes: 1 addition & 1 deletion docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
Binary file added images/slack-modern-app-level-token.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/slack-modern-create-app-from-scratch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/slack-modern-create-app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/slack-modern-enable-socket-mode.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/slack-modern-event-subscriptions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/slack-modern-scopes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<dependency>
<groupId>com.slack.api</groupId>
<artifactId>slack-api-client</artifactId>
<version>1.39.3</version>
<version>1.45.0</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/botdarr/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/com/botdarr/clients/slack/SlackBootstrap.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
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;
Expand All @@ -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<SlackResponse> responseChatClientResponseBuilder = new SlackResponseBuilder();
ChatClientBootstrap.ApisAndCommandConfig config = buildConfig();

slackChatClient.addMessageHandler(new RTMMessageHandler() {
slackChatClient.addMessageHandler(new EnvelopeListener<EventsApiEnvelope>() {
@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());
Expand Down Expand Up @@ -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());

Expand Down
66 changes: 41 additions & 25 deletions src/main/java/com/botdarr/clients/slack/SlackChatClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
import com.slack.api.model.User;
import com.slack.api.model.block.DividerBlock;
import com.slack.api.model.block.LayoutBlock;
import com.slack.api.rtm.RTMClient;
import com.slack.api.rtm.RTMMessageHandler;
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;
Expand All @@ -29,32 +33,44 @@
import java.util.concurrent.atomic.AtomicBoolean;

public class SlackChatClient implements ChatClient<SlackResponse> {
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<EventsApiEnvelope> messageHandler) {
socketClient.addEventsApiEnvelopeListener(messageHandler);
socketClient.addWebSocketMessageListener(new WebSocketMessageListener() {
@Override
public void handle(String ee) {
int i = 0;
}
});
socketClient.addInteractiveEnvelopeListener(new EnvelopeListener<InteractiveEnvelope>() {
@Override
public void handle(InteractiveEnvelope ff) {
int i = 0;
}
});
socketClient.addSlashCommandsEnvelopeListener(new EnvelopeListener<SlashCommandsEnvelope>() {
@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);
}
}

Expand Down Expand Up @@ -91,21 +107,21 @@ public void sendMessage(List<SlackResponse> chatClientResponses, String channel)

public List<Message> 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<Message> 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();
}

Expand Down Expand Up @@ -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");
}
22 changes: 22 additions & 0 deletions src/main/java/com/botdarr/clients/slack/SlackEventContainer.java
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 8662647

Please sign in to comment.