diff --git a/.github/workflows/development-branch.yml b/.github/workflows/development-branch.yml index be0a4d1..c78dca0 100644 --- a/.github/workflows/development-branch.yml +++ b/.github/workflows/development-branch.yml @@ -1,4 +1,4 @@ -name: pull-request +name: development on: push: branches: diff --git a/.github/workflows/feature-branch.yml b/.github/workflows/pull-request.yml similarity index 100% rename from .github/workflows/feature-branch.yml rename to .github/workflows/pull-request.yml diff --git a/README.md b/README.md index 5e366df..99b4490 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -[![Build status](https://ci.appveyor.com/api/projects/status/twce208g6yb18vgl/branch/development?svg=true)](https://ci.appveyor.com/project/shayaantx/botdarr/branch/development) +![Build status](https://github.com/shayaantx/botdarr/actions/workflows/development-branch.yml/badge.svg) +![Docker Pulls](https://img.shields.io/docker/pulls/shayaantx/botdarr) +![Latest Version](https://img.shields.io/docker/v/shayaantx/botdarr) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) # Summary -Made this simple slack/discord/telegram/matrix bot so I could access radarr, sonarr, and lidarr all from a multiple slack/discord/telegram/matrix channels without a UI/server. +Made this simple multi chat-client bot to access radarr, sonarr, and lidarr without a UI/server.
@@ -36,6 +38,7 @@ Made this simple slack/discord/telegram/matrix bot so I could access radarr, son - [x] (discord/slack only) Thumbs up reaction will add search results - [x] User requests audited to local database\ - [x] Blacklist content by paths from showing up in searches +- [x] Get status of radarr, lidarr, sonarr, and any additional configured endpoints - [ ] Lookup torrents for movies and force download - [ ] Cancel/blacklist existing downloads - [ ] Episode/season search @@ -170,6 +173,7 @@ botdarr: | MAX_DOWNLOADS_TO_SHOW | The max number of downloads to show. If you set this to any value less than or equal to 0, no downloads will show | yes | 20 | | MAX_RESULTS_TO_SHOW | The max number of results to show per search command. If you set this to any value less than 0, the bot won't startup | yes | 20 | | COMMAND_PREFIX | The command prefix (default is !). Any prefix is allowed (but I haven't tested every single prefix in every client) | yes | ! | +| STATUS_ENDPOINTS | Endpoints that can be used to return statuses via !status command. The endpoints are separated by a comma and each endpoint is in the following format - name:hostname:port | no | |
diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 6ae6c8b..aab39a2 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -52,6 +52,7 @@ if [ ! -e "$propertiesFile" ]; then [[ ! -z "${MAX_SHOW_REQUESTS_PER_USER}" ]] && addConfiguration "max-show-requests-per-user" "${MAX_SHOW_REQUESTS_PER_USER}" "${propertiesFile}" [[ ! -z "${MAX_MOVIE_REQUESTS_PER_USER}" ]] && addConfiguration "max-movie-requests-per-user" "${MAX_MOVIE_REQUESTS_PER_USER}" "${propertiesFile}" [[ ! -z "${EXISTING_ITEM_PATHS_BLACKLIST}" ]] && addConfiguration "existing-item-paths-blacklist" "${EXISTING_ITEM_PATHS_BLACKLIST}" "${propertiesFile}" + [[ ! -z "${STATUS_ENDPOINTS}" ]] && addConfiguration "status-endpoints" "${STATUS_ENDPOINTS}" "${propertiesFile}" addConfiguration "max-downloads-to-show" "${MAX_DOWNLOADS_TO_SHOW:-20}" "${propertiesFile}" addConfiguration "max-results-to-show" "${MAX_RESULTS_TO_SHOW:-20}" "${propertiesFile}" diff --git a/sample.properties b/sample.properties index 232ccff..27b7f76 100644 --- a/sample.properties +++ b/sample.properties @@ -82,4 +82,8 @@ command-prefix=! # If you want content to NOT appear in searches against your library, you can list blacklisted paths here # in comma delimited form, and they will be ignored when building up responses -existing-item-paths-blacklist= \ No newline at end of file +existing-item-paths-blacklist= + +# Comma delimited Status endpoints (i.e., endpoints you want checked when the !status command is called) +# i.e., some-name:hostname:port,some-other-name:hostname2:port +#status-endpoints= \ No newline at end of file diff --git a/src/main/java/com/botdarr/Config.java b/src/main/java/com/botdarr/Config.java index 2a7c72e..97af705 100644 --- a/src/main/java/com/botdarr/Config.java +++ b/src/main/java/com/botdarr/Config.java @@ -1,15 +1,20 @@ package com.botdarr; import com.botdarr.clients.ChatClientType; +import com.botdarr.commands.StatusCommand; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Strings; import java.io.FileInputStream; import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; import java.util.stream.Collectors; +import static com.botdarr.commands.StatusCommand.*; + public class Config { private static volatile Config instance; @@ -79,14 +84,16 @@ private Config() { } String configuredPrefix = properties.getProperty(Config.Constants.COMMAND_PREFIX); - if (!Strings.isEmpty(configuredPrefix) && configuredPrefix.length() > 1) { - throw new RuntimeException("Command prefix must be a single character"); - } - if (chatClientType == ChatClientType.SLACK && configuredPrefix.equals("/")) { - throw new RuntimeException("Cannot use / command prefix in slack since /help command was deprecated by slack"); - } - if (chatClientType == ChatClientType.MATRIX && configuredPrefix.equals("/")) { - throw new RuntimeException("Cannot use / command prefix in matrix since /help command is used by element by default"); + if (!Strings.isEmpty(configuredPrefix)) { + if (configuredPrefix.length() > 1) { + throw new RuntimeException("Command prefix must be a single character"); + } + if (chatClientType == ChatClientType.SLACK && configuredPrefix.equals("/")) { + throw new RuntimeException("Cannot use / command prefix in slack since /help command was deprecated by slack"); + } + if (chatClientType == ChatClientType.MATRIX && configuredPrefix.equals("/")) { + throw new RuntimeException("Cannot use / command prefix in matrix since /help command is used by element by default"); + } } } catch (Exception ex) { LOGGER.error("Error loading properties file", ex); @@ -115,12 +122,64 @@ public static ChatClientType getChatClientType() { } public static List getExistingItemBlacklistPaths() { - String paths = getProperty(Constants.EXISTING_ITEMS_PATHS_BLACKLIST); - if (paths != null) { - if (paths.contains(",")) { - return Arrays.asList(paths.split(",")); + return getCommaDelimitedList(getProperty(Constants.EXISTING_ITEMS_PATHS_BLACKLIST)); + } + + public static List getStatusEndpoints() { + List endpoints = getCommaDelimitedList(getProperty(Constants.STATUS_ENDPOINTS)); + List statusEndPoints = new ArrayList<>(); + if (!endpoints.isEmpty()) { + for (String endpoint : endpoints) { + String[] splitEndpoint = endpoint.split(":"); + if (splitEndpoint.length != 3) { + LOGGER.warn("Status endpoint not formatted correctly, usage - name:hostname:port, endpoint=" + endpoint); + } + statusEndPoints.add(new StatusEndPoint(splitEndpoint[0], splitEndpoint[1], Integer.valueOf(splitEndpoint[2]))); + } + } + StatusEndPoint endpoint; + if (isLidarrEnabled()) { + endpoint = getDomain(Constants.LIDARR_URL, "lidarr"); + if (endpoint != null) { + statusEndPoints.add(endpoint); + } + } + if (isRadarrEnabled()) { + endpoint = getDomain(Constants.RADARR_URL, "radarr"); + if (endpoint != null) { + statusEndPoints.add(endpoint); + } + } + if (isSonarrEnabled()) { + endpoint = getDomain(Constants.SONARR_URL, "sonarr"); + if (endpoint != null) { + statusEndPoints.add(endpoint); + } + } + return statusEndPoints; + } + + private static StatusEndPoint getDomain(String constant, String name) { + try { + URI uri = new URI(getProperty(constant)); + int port = uri.getPort(); + if (port == -1) { + port = uri.getHost().startsWith("https") ? 443 : 80; + } + return new StatusEndPoint(name, uri.getHost(), port); + } catch (URISyntaxException e) { + LOGGER.error("Error trying to get uri for " + constant, e); + } + return null; + } + + private static List getCommaDelimitedList(String list) { + if (!Strings.isEmpty(list) && list.contains(",")) { + if (list.contains(",")) { + return Arrays.asList(list.split(",")); + } else { + return new ArrayList() {{add(list);}}; } - return new ArrayList() {{add(paths);}}; } return new ArrayList<>(); } @@ -307,6 +366,11 @@ public static final class Constants { * The paths of items to blacklist from searches */ public static final String EXISTING_ITEMS_PATHS_BLACKLIST = "existing-item-paths-blacklist"; + + /** + * The additional status endpoints to check + */ + public static final String STATUS_ENDPOINTS = "status-endpoints"; } private static String propertiesPath = "config/properties"; diff --git a/src/main/java/com/botdarr/api/radarr/RadarrApi.java b/src/main/java/com/botdarr/api/radarr/RadarrApi.java index ef439c6..640052e 100644 --- a/src/main/java/com/botdarr/api/radarr/RadarrApi.java +++ b/src/main/java/com/botdarr/api/radarr/RadarrApi.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; import java.net.URLEncoder; +import java.nio.charset.Charset; import java.util.*; public class RadarrApi implements Api { @@ -282,8 +283,8 @@ private ChatClientResponse addMovie(RadarrMovie radarrMovie) { } try (CloseableHttpResponse response = client.execute(post)) { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Respone=" + response.toString()); - LOGGER.debug("Response content=" + IOUtils.toString(response.getEntity().getContent())); + LOGGER.debug("Response=" + response.toString()); + LOGGER.debug("Response content=" + IOUtils.toString(response.getEntity().getContent(), Charset.defaultCharset())); LOGGER.debug("Reason=" + response.getStatusLine().toString()); } int statusCode = response.getStatusLine().getStatusCode(); diff --git a/src/main/java/com/botdarr/clients/ChatClientResponseBuilder.java b/src/main/java/com/botdarr/clients/ChatClientResponseBuilder.java index 46400ea..6a19f3a 100644 --- a/src/main/java/com/botdarr/clients/ChatClientResponseBuilder.java +++ b/src/main/java/com/botdarr/clients/ChatClientResponseBuilder.java @@ -16,6 +16,7 @@ import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; public interface ChatClientResponseBuilder { @@ -38,6 +39,7 @@ public interface ChatClientResponseBuilder { T getNewOrExistingMovie(RadarrMovie lookupMovie, RadarrMovie existingMovie, boolean findNew); T getNewOrExistingArtist(LidarrArtist lookupArtist, LidarrArtist existingArtist, boolean findNew); T getDiscoverableMovies(RadarrMovie radarrMovie); + T getStatusCommandResponse(Map endpointStatuses); static String getVersion() throws IOException { ClassLoader classloader = Thread.currentThread().getContextClassLoader(); diff --git a/src/main/java/com/botdarr/clients/ChatClientType.java b/src/main/java/com/botdarr/clients/ChatClientType.java index 4943dd0..cdbebb3 100644 --- a/src/main/java/com/botdarr/clients/ChatClientType.java +++ b/src/main/java/com/botdarr/clients/ChatClientType.java @@ -21,6 +21,7 @@ import com.botdarr.clients.telegram.TelegramResponse; import com.botdarr.clients.telegram.TelegramResponseBuilder; import com.github.seratch.jslack.Slack; +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; @@ -244,7 +245,12 @@ public void handle(String message) { SlackMessage slackMessage = new Gson().fromJson(json, SlackMessage.class); if (slackMessage.getType() != null) { if (slackMessage.getType().equalsIgnoreCase("message")) { - handleCommand(slackMessage.getText(), slackChatClient.getUser(slackMessage.getUserId()).getName(), slackMessage.getChannel()); + User user = slackChatClient.getUser(slackMessage.getUserId()); + if (user == null) { + LOGGER.debug("Could not find user for slack message " + slackMessage); + return; + } + handleCommand(slackMessage.getText(), user.getName(), slackMessage.getChannel()); } else if (slackMessage.getType().equalsIgnoreCase("reaction_added") && slackMessage.getReaction().equalsIgnoreCase("+1")) { //thumbsup = +1 in slack for some reason try { @@ -353,6 +359,9 @@ private static ApisAndCommandConfig buildConfig(C commands.addAll(lidarrCommands); apis.add(lidarrApi); } + if (!Config.getStatusEndpoints().isEmpty()) { + commands.add(new StatusCommand<>(responseChatClientResponseBuilder)); + } commands.addAll(HelpCommands.getCommands(responseChatClientResponseBuilder, radarrCommands, sonarrCommands, lidarrCommands)); return new ApisAndCommandConfig(apis, commands); } diff --git a/src/main/java/com/botdarr/clients/discord/DiscordResponseBuilder.java b/src/main/java/com/botdarr/clients/discord/DiscordResponseBuilder.java index f0c5f5a..1c427a4 100644 --- a/src/main/java/com/botdarr/clients/discord/DiscordResponseBuilder.java +++ b/src/main/java/com/botdarr/clients/discord/DiscordResponseBuilder.java @@ -11,17 +11,18 @@ import com.botdarr.utilities.ListUtils; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Strings; import java.awt.*; import java.io.IOException; import java.util.List; +import java.util.Map; import static com.botdarr.api.lidarr.LidarrApi.ADD_ARTIST_COMMAND_FIELD_PREFIX; import static com.botdarr.api.radarr.RadarrApi.ADD_MOVIE_COMMAND_FIELD_PREFIX; import static com.botdarr.api.sonarr.SonarrApi.ADD_SHOW_COMMAND_FIELD_PREFIX; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND_DESCRIPTION; import static net.dv8tion.jda.api.entities.MessageEmbed.VALUE_MAX_LENGTH; public class DiscordResponseBuilder implements ChatClientResponseBuilder { @@ -52,6 +53,9 @@ public DiscordResponse getHelpResponse() { if (!radarrEnabled && !sonarrEnabled && !lidarrEnabled) { embedBuilder.appendDescription("No radarr or sonarr or lidarr commands configured, check your properties file and logs"); } + if (!Config.getStatusEndpoints().isEmpty()) { + embedBuilder.addField(new CommandProcessor().getPrefix() + STATUS_COMMAND, STATUS_COMMAND_DESCRIPTION, false); + } return new DiscordResponse(embedBuilder.build()); } @@ -250,6 +254,16 @@ public DiscordResponse getDiscoverableMovies(RadarrMovie radarrMovie) { return getMovieResponse(radarrMovie); } + @Override + public DiscordResponse getStatusCommandResponse(Map endpointStatuses) { + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Endpoint Statuses"); + for (Map.Entry endpointStatusEntry : endpointStatuses.entrySet()) { + embedBuilder.addField(endpointStatusEntry.getKey(), endpointStatusEntry.getValue() ? "\uD83D\uDFE2" : "\uD83D\uDD34", false); + } + return new DiscordResponse(embedBuilder.build()); + } + @Override public DiscordResponse createErrorMessage(String message) { return new DiscordResponse(createErrorMessageEmbed(message)); @@ -293,6 +307,4 @@ private DiscordResponse getListOfCommands(List commands) { } return new DiscordResponse(embedBuilder.build()); } - - private static final Logger LOGGER = LogManager.getLogger("DiscordLog"); } diff --git a/src/main/java/com/botdarr/clients/matrix/MatrixResponse.java b/src/main/java/com/botdarr/clients/matrix/MatrixResponse.java index 42d1d78..c124eea 100644 --- a/src/main/java/com/botdarr/clients/matrix/MatrixResponse.java +++ b/src/main/java/com/botdarr/clients/matrix/MatrixResponse.java @@ -10,6 +10,10 @@ public void addContent(String content) { this.content.append(content + "
"); } + public void addRawContent(String content) { + this.content.append(content); + } + public void addImage(String imageUrl) { if (imageUrl == null || imageUrl.isEmpty()) { return; diff --git a/src/main/java/com/botdarr/clients/matrix/MatrixResponseBuilder.java b/src/main/java/com/botdarr/clients/matrix/MatrixResponseBuilder.java index 53a46d7..c4d5081 100644 --- a/src/main/java/com/botdarr/clients/matrix/MatrixResponseBuilder.java +++ b/src/main/java/com/botdarr/clients/matrix/MatrixResponseBuilder.java @@ -12,10 +12,13 @@ import org.apache.logging.log4j.util.Strings; import java.util.List; +import java.util.Map; import static com.botdarr.api.lidarr.LidarrApi.ADD_ARTIST_COMMAND_FIELD_PREFIX; import static com.botdarr.api.radarr.RadarrApi.ADD_MOVIE_COMMAND_FIELD_PREFIX; import static com.botdarr.api.sonarr.SonarrApi.ADD_SHOW_COMMAND_FIELD_PREFIX; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND_DESCRIPTION; public class MatrixResponseBuilder implements ChatClientResponseBuilder { @Override @@ -41,6 +44,10 @@ public MatrixResponse getHelpResponse() { if (!radarrEnabled && !sonarrEnabled && !lidarrEnabled) { matrixResponse.addContent("No radarr or sonarr or lidarr commands configured, check your properties file and logs"); } + + if (!Config.getStatusEndpoints().isEmpty()) { + matrixResponse.addContent("" + new CommandProcessor().getPrefix() + STATUS_COMMAND + " - " + STATUS_COMMAND_DESCRIPTION); + } return matrixResponse; } catch (Exception e) { throw new RuntimeException("Error getting botdarr version", e); @@ -261,6 +268,25 @@ public MatrixResponse getDiscoverableMovies(RadarrMovie radarrMovie) { return matrixResponse; } + @Override + public MatrixResponse getStatusCommandResponse(Map endpointStatuses) { + MatrixResponse matrixResponse = new MatrixResponse(); + matrixResponse.addRawContent(""); + matrixResponse.addRawContent(""); + for (Map.Entry endpointStatusEntry : endpointStatuses.entrySet()) { + matrixResponse.addRawContent(""); + matrixResponse.addRawContent(""); + matrixResponse.addRawContent(""); + matrixResponse.addRawContent(""); + } + matrixResponse.addRawContent("
EndpointStatuses
"); + matrixResponse.addRawContent(endpointStatusEntry.getKey()); + matrixResponse.addRawContent(""); + matrixResponse.addRawContent(endpointStatusEntry.getValue() ? "\uD83D\uDFE2" : "\uD83D\uDD34"); + matrixResponse.addRawContent("
"); + return matrixResponse; + } + private MatrixResponse getListOfCommands(List commands) { MatrixResponse matrixResponse = new MatrixResponse(); matrixResponse.addContent("Commands"); diff --git a/src/main/java/com/botdarr/clients/slack/SlackMessage.java b/src/main/java/com/botdarr/clients/slack/SlackMessage.java index 71ec1df..2fdd7ab 100644 --- a/src/main/java/com/botdarr/clients/slack/SlackMessage.java +++ b/src/main/java/com/botdarr/clients/slack/SlackMessage.java @@ -25,6 +25,18 @@ public SlackMessageItem getItem() { return item; } + @Override + public String toString() { + return "SlackMessage{" + + "type='" + type + '\'' + + ", text='" + text + '\'' + + ", user='" + user + '\'' + + ", channel='" + channel + '\'' + + ", reaction='" + reaction + '\'' + + ", item=" + item + + '}'; + } + private String type; private String text; private String user; //slack user id diff --git a/src/main/java/com/botdarr/clients/slack/SlackMessageItem.java b/src/main/java/com/botdarr/clients/slack/SlackMessageItem.java index 808440a..10bd5ed 100644 --- a/src/main/java/com/botdarr/clients/slack/SlackMessageItem.java +++ b/src/main/java/com/botdarr/clients/slack/SlackMessageItem.java @@ -17,6 +17,16 @@ public String getChannel() { return channel; } + @Override + public String toString() { + return "SlackMessageItem{" + + "type='" + type + '\'' + + ", event_ts='" + event_ts + '\'' + + ", ts='" + ts + '\'' + + ", channel='" + channel + '\'' + + '}'; + } + private String type; private String event_ts; private String ts; diff --git a/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java b/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java index bc78049..ece2e6b 100644 --- a/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java +++ b/src/main/java/com/botdarr/clients/slack/SlackResponseBuilder.java @@ -21,10 +21,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import static com.botdarr.api.lidarr.LidarrApi.ADD_ARTIST_COMMAND_FIELD_PREFIX; import static com.botdarr.api.radarr.RadarrApi.ADD_MOVIE_COMMAND_FIELD_PREFIX; import static com.botdarr.api.sonarr.SonarrApi.ADD_SHOW_COMMAND_FIELD_PREFIX; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND_DESCRIPTION; import static net.dv8tion.jda.api.entities.MessageEmbed.VALUE_MAX_LENGTH; public class SlackResponseBuilder implements ChatClientResponseBuilder { @@ -63,6 +66,11 @@ public SlackResponse getHelpResponse() { .text(MarkdownTextObject.builder().text("*No radarr or sonarr or lidarr commands configured, check your properties file and logs*").build()) .build()); } + if (!Config.getStatusEndpoints().isEmpty()) { + slackResponse.addBlock(SectionBlock.builder() + .text(MarkdownTextObject.builder().text(new CommandProcessor().getPrefix() + STATUS_COMMAND + " - " + STATUS_COMMAND_DESCRIPTION).build()) + .build()); + } } catch (IOException e) { throw new RuntimeException("Error getting botdarr version", e); } @@ -434,6 +442,20 @@ public SlackResponse getDiscoverableMovies(RadarrMovie radarrMovie) { return getMovieResponse(radarrMovie); } + @Override + public SlackResponse getStatusCommandResponse(Map endpointStatuses) { + SlackResponse slackResponse = new SlackResponse(); + slackResponse.addBlock(SectionBlock.builder() + .text(MarkdownTextObject.builder().text("*Endpoint Statuses*").build()) + .build()); + for (Map.Entry endpointStatusEntry : endpointStatuses.entrySet()) { + slackResponse.addBlock(SectionBlock.builder() + .text(MarkdownTextObject.builder().text(endpointStatusEntry.getKey() + " - " + (endpointStatusEntry.getValue() ? "\uD83D\uDFE2" : "\uD83D\uDD34")).build()) + .build()); + } + return slackResponse; + } + private SlackResponse getListOfCommands(List commands) { SlackResponse slackResponse = new SlackResponse(); slackResponse.addBlock(SectionBlock.builder() diff --git a/src/main/java/com/botdarr/clients/telegram/TelegramResponseBuilder.java b/src/main/java/com/botdarr/clients/telegram/TelegramResponseBuilder.java index 472d0e1..1e02055 100644 --- a/src/main/java/com/botdarr/clients/telegram/TelegramResponseBuilder.java +++ b/src/main/java/com/botdarr/clients/telegram/TelegramResponseBuilder.java @@ -15,12 +15,15 @@ import static com.botdarr.api.lidarr.LidarrApi.ADD_ARTIST_COMMAND_FIELD_PREFIX; import static com.botdarr.api.radarr.RadarrApi.ADD_MOVIE_COMMAND_FIELD_PREFIX; import static com.botdarr.api.sonarr.SonarrApi.ADD_SHOW_COMMAND_FIELD_PREFIX; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND; +import static com.botdarr.commands.StatusCommand.STATUS_COMMAND_DESCRIPTION; import static j2html.TagCreator.*; import static net.dv8tion.jda.api.entities.MessageEmbed.VALUE_MAX_LENGTH; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class TelegramResponseBuilder implements ChatClientResponseBuilder { @Override @@ -44,6 +47,9 @@ public TelegramResponse getHelpResponse() { if (!radarrEnabled && !sonarrEnabled) { domContents.add(b("*No radarr or sonarr or lidarr commands configured, check your properties file and logs*")); } + if (!Config.getStatusEndpoints().isEmpty()) { + domContents.add(text(new CommandProcessor().getPrefix() + STATUS_COMMAND + " - " + STATUS_COMMAND_DESCRIPTION)); + } return new TelegramResponse(domContents); } catch (IOException e) { throw new RuntimeException("Error getting help response", e); @@ -276,6 +282,16 @@ public TelegramResponse getDiscoverableMovies(RadarrMovie radarrMovie) { return getMovieResponse(radarrMovie); } + @Override + public TelegramResponse getStatusCommandResponse(Map endpointStatuses) { + List domContents = new ArrayList<>(); + domContents.add(u(b("*Endpoint Statuses*"))); + for (Map.Entry endpointStatusEntry : endpointStatuses.entrySet()) { + domContents.add(text(endpointStatusEntry.getKey() + " - " + (endpointStatusEntry.getValue() ? "\uD83D\uDFE2" : "\uD83D\uDD34"))); + } + return new TelegramResponse(domContents); + } + private List getListOfCommands(List commands) { List domContents = new ArrayList<>(); domContents.add(u(b("*Commands*"))); diff --git a/src/main/java/com/botdarr/commands/StatusCommand.java b/src/main/java/com/botdarr/commands/StatusCommand.java new file mode 100644 index 0000000..d342318 --- /dev/null +++ b/src/main/java/com/botdarr/commands/StatusCommand.java @@ -0,0 +1,56 @@ +package com.botdarr.commands; + +import com.botdarr.Config; +import com.botdarr.clients.ChatClientResponse; +import com.botdarr.clients.ChatClientResponseBuilder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class StatusCommand extends BaseCommand { + public StatusCommand(ChatClientResponseBuilder responseChatClientResponseBuilder) { + super(STATUS_COMMAND, STATUS_COMMAND_DESCRIPTION); + this.responseChatClientResponseBuilder = responseChatClientResponseBuilder; + } + + @Override + public CommandResponse execute(String command) { + List endpoints = Config.getStatusEndpoints(); + if (!endpoints.isEmpty()) { + Map endpointStatuses = new HashMap<>(); + for (StatusEndPoint endPoint : endpoints) { + try (Socket socket = new Socket()) { + socket.connect(new InetSocketAddress(endPoint.hostname, endPoint.port), 2000); + endpointStatuses.put(endPoint.name, true); + } catch (IOException e) { + LOGGER.error("Error fetching status for " + endPoint.name, e); + endpointStatuses.put(endPoint.name, false); + } + } + return new CommandResponse(responseChatClientResponseBuilder.getStatusCommandResponse(endpointStatuses)); + } + return new CommandResponse( + responseChatClientResponseBuilder.createErrorMessage("No status endpoints configured")); + } + private final ChatClientResponseBuilder responseChatClientResponseBuilder; + + public static class StatusEndPoint { + public StatusEndPoint(String name, String hostname, int port) { + this.name = name; + this.hostname = hostname; + this.port = port; + } + private final String name; + private final String hostname; + private final int port; + } + public static final String STATUS_COMMAND = "status"; + public static final String STATUS_COMMAND_DESCRIPTION = "Gets the statuses of configured endpoints"; + private static Logger LOGGER = LogManager.getLogger(); +} diff --git a/src/main/java/com/botdarr/connections/ConnectionHelper.java b/src/main/java/com/botdarr/connections/ConnectionHelper.java index 25d70fc..8560c3b 100644 --- a/src/main/java/com/botdarr/connections/ConnectionHelper.java +++ b/src/main/java/com/botdarr/connections/ConnectionHelper.java @@ -4,11 +4,8 @@ import com.botdarr.Config; import com.botdarr.clients.ChatClientResponse; import com.botdarr.clients.ChatClientResponseBuilder; -import com.google.gson.Gson; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.*; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; diff --git a/src/main/resources/version.txt b/src/main/resources/version.txt index 8710cfd..693ad74 100644 --- a/src/main/resources/version.txt +++ b/src/main/resources/version.txt @@ -1 +1 @@ -5.1.6 +5.1.7 \ No newline at end of file diff --git a/src/test/java/com/botdarr/TestResponseBuilder.java b/src/test/java/com/botdarr/TestResponseBuilder.java index 95dab48..41154cd 100644 --- a/src/test/java/com/botdarr/TestResponseBuilder.java +++ b/src/test/java/com/botdarr/TestResponseBuilder.java @@ -12,6 +12,7 @@ import com.botdarr.commands.Command; import java.util.List; +import java.util.Map; public class TestResponseBuilder implements ChatClientResponseBuilder { @@ -109,4 +110,9 @@ public TestResponse getMovieResponse(RadarrMovie radarrMovie) { public TestResponse getDiscoverableMovies(RadarrMovie radarrMovie) { return new TestResponse(radarrMovie); } + + @Override + public TestResponse getStatusCommandResponse(Map endpointStatuses) { + return new TestResponse(); + } } \ No newline at end of file