diff --git a/.schema/config_v2.schema.json b/.schema/config_v2.schema.json index cb53e6c..06b7b37 100644 --- a/.schema/config_v2.schema.json +++ b/.schema/config_v2.schema.json @@ -358,6 +358,11 @@ "description": "The name of the channel where the bot will post messages", "$ref": "#/definitions/discordTextChannelName" }, + "EnableTermination": { + "title": "Enable stream termination", + "description": "Whether to enable stream termination", + "type": "boolean" + }, "EnableSlashCommands": { "title": "Enable slash commands", "description": "Whether to enable slash commands for the bot", diff --git a/documentation/ANNOUNCEMENTS.md b/documentation/ANNOUNCEMENTS.md index b92fdee..2b4c399 100644 --- a/documentation/ANNOUNCEMENTS.md +++ b/documentation/ANNOUNCEMENTS.md @@ -4,11 +4,11 @@ **Latest Release**: *v5.3.4* -**Affected Release**: *v5.4.0+* +**Affected Release**: *v5.x.0+* **Affected Users**: Those using Tauticord with Tautulli versions v2.12.x and v2.13.x -The next minor release of Tauticord will drop support for Tautulli versions v2.12.x and v2.13.x. This is due to the +An upcoming minor release of Tauticord will drop support for Tautulli versions v2.12.x and v2.13.x. This is due to the release of Tautulli v2.14.0, which introduced breaking changes to the API. Because Tauticord enforces strict compatibility checking with Tautulli, the underlying `tautulli` API library will need diff --git a/migrations/m002_old_config_to_new_config.py b/migrations/m002_old_config_to_new_config.py index 1e3fd76..3e48ae6 100644 --- a/migrations/m002_old_config_to_new_config.py +++ b/migrations/m002_old_config_to_new_config.py @@ -136,6 +136,7 @@ def forward(self): new_config.migrate_value(value=old_config.discord.use_summary_text_message, to_path=["Discord", "PostSummaryMessage"], default=True) new_config.migrate_value(value=old_config.discord.channel_name, to_path=["Discord", "ChannelName"]) + new_config.add(value=True, key_path=["Discord", "EnableTermination"]) new_config.migrate_value(value=old_config.discord.enable_slash_commands, to_path=["Discord", "EnableSlashCommands"], default=False) status_message_settings = { diff --git a/modules/discord/commands/summary.py b/modules/discord/commands/summary.py index 1108a2f..f63678a 100644 --- a/modules/discord/commands/summary.py +++ b/modules/discord/commands/summary.py @@ -35,6 +35,6 @@ async def summary(self, interaction: discord.Interaction, share: Optional[bool] if not await self.check_admin(interaction): return - # Does NOT include new version reminder. - summary = self._tautulli.refresh_data(emoji_manager=self._emoji_manager) + # Does NOT include new version reminder or stream termination. + summary = self._tautulli.refresh_data(enable_stream_termination_if_possible=False, emoji_manager=self._emoji_manager) await interaction.response.send_message(embed=summary.embed, ephemeral=not share) diff --git a/modules/discord/models/tautulli_activity_summary.py b/modules/discord/models/tautulli_activity_summary.py index eeddbd5..a7b3415 100644 --- a/modules/discord/models/tautulli_activity_summary.py +++ b/modules/discord/models/tautulli_activity_summary.py @@ -16,13 +16,11 @@ def __init__(self, emoji_manager: EmojiManager, text_manager: TextManager, streams: List[TautulliStreamInfo] = None, - has_plex_pass: bool = False, error_occurred: bool = False, additional_embed_fields: List[dict] = None, additional_embed_footers: List[str] = None): self.activity = activity self.plex_online = plex_online - self.has_plex_pass = has_plex_pass self.error_occurred = error_occurred self.additional_embed_fields = additional_embed_fields or [] self.additional_embed_footers = additional_embed_footers or [] @@ -48,8 +46,7 @@ def embed(self) -> discord.Embed: footer_text = self._text_manager.overview_footer(no_connection=self.error_occurred, activity=self.activity, - emoji_manager=self._emoji_manager, - add_termination_tip=self.has_plex_pass) + emoji_manager=self._emoji_manager) if self.additional_embed_footers: footer_text += "\n" for additional_footer in self.additional_embed_footers: diff --git a/modules/discord/services/live_activity.py b/modules/discord/services/live_activity.py index 1c57af7..f781469 100644 --- a/modules/discord/services/live_activity.py +++ b/modules/discord/services/live_activity.py @@ -34,6 +34,7 @@ def __init__(self, self.admin_ids: list[int] = discord_settings.admin_ids self.refresh_time: int = tautulli_settings.refresh_interval_seconds self.use_summary_message: bool = discord_settings.use_summary_message + self.enable_stream_termination_if_possible: bool = discord_settings.enable_termination self.stats_settings: settings_models.Stats = stats_settings self.emoji_manager: EmojiManager = emoji_manager self.version_checker: versioning.VersionChecker = version_checker @@ -117,6 +118,7 @@ async def on_ready(self): tautulli_connector=self.tautulli, guild_id=self.guild_id, message=summary_message, + enable_stream_termination_if_possible=self.enable_stream_termination_if_possible, emoji_manager=self.emoji_manager, version_checker=self.version_checker, voice_category=activity_stats_voice_category) diff --git a/modules/settings/config_parser.py b/modules/settings/config_parser.py index 2d9b8bb..3eb359a 100644 --- a/modules/settings/config_parser.py +++ b/modules/settings/config_parser.py @@ -91,6 +91,7 @@ def to_model(self) -> settings_models.Discord: channel_name = self.get_value(key="ChannelName", default="tauticord") channel_name = utils.discord_text_channel_name_format(string=channel_name) use_summary_message = utils.extract_boolean(self.get_value(key="PostSummaryMessage", default=True)) + enable_termination = utils.extract_boolean(self.get_value(key="EnableTermination", default=True)) enable_slash_commands = utils.extract_boolean(self.get_value(key="EnableSlashCommands", default=False)) status_message_settings_data = self.get_subsection_data(key="StatusMessage", optional=True) @@ -102,6 +103,7 @@ def to_model(self) -> settings_models.Discord: admin_ids=admin_ids, channel_name=channel_name, use_summary_message=use_summary_message, + enable_termination=enable_termination, enable_slash_commands=enable_slash_commands, status_message_settings=status_message_settings ) diff --git a/modules/settings/models/discord.py b/modules/settings/models/discord.py index 394cc69..fd2e005 100644 --- a/modules/settings/models/discord.py +++ b/modules/settings/models/discord.py @@ -45,6 +45,7 @@ class Discord(BaseConfig): channel_name: str enable_slash_commands: bool use_summary_message: bool + enable_termination: bool server_id: int status_message_settings: StatusMessage @@ -55,6 +56,7 @@ def as_dict(self) -> dict: "channel_name": self.channel_name, "enable_slash_commands": self.enable_slash_commands, "use_summary_message": self.use_summary_message, + "enable_termination": self.enable_termination, "server_id": self.server_id, "status_message_settings": self.status_message_settings.as_dict() } diff --git a/modules/tasks/activity.py b/modules/tasks/activity.py index 76cf73b..f81fcec 100644 --- a/modules/tasks/activity.py +++ b/modules/tasks/activity.py @@ -23,6 +23,7 @@ def __init__(self, tautulli_connector: TautulliConnector, guild_id: int, message: discord.Message, + enable_stream_termination_if_possible: bool, discord_status_settings: modules.settings.models.DiscordStatusMessage, emoji_manager: EmojiManager, version_checker: VersionChecker, @@ -32,6 +33,7 @@ def __init__(self, service_entrypoint=self.update_activity_details, voice_category=voice_category) self.message = message + self.enable_stream_termination_if_possible = enable_stream_termination_if_possible self.stats_settings = settings self.discord_status_settings = discord_status_settings self.tautulli = tautulli_connector @@ -158,7 +160,7 @@ async def update_activity_summary_message(self, # update the message regardless of whether the content has changed self.message = await discord_utils.send_embed_message(embed=summary.embed, message=self.message) - if summary.has_plex_pass: + if self.tautulli.plex_pass_feature_is_allowed(feature=self.enable_stream_termination_if_possible): await self.add_stream_number_emoji_reactions(count=len(summary.streams), emoji_manager=self.emoji_manager) # on_raw_reaction_add will handle the rest @@ -174,7 +176,9 @@ async def update_activity_details(self) -> None: {"name": "🔔 New Version Available", "value": f"A new version of Tauticord is available! [Click here]({consts.GITHUB_REPO_FULL_LINK}) to download it."}) - summary = self.tautulli.refresh_data(emoji_manager=self.emoji_manager, additional_embed_fields=embed_fields) + summary = self.tautulli.refresh_data( + enable_stream_termination_if_possible=self.enable_stream_termination_if_possible, + emoji_manager=self.emoji_manager, additional_embed_fields=embed_fields) if self.stats_settings.enable: await self.update_activity_stats(summary=summary) diff --git a/modules/tautulli/tautulli_connector.py b/modules/tautulli/tautulli_connector.py index 171bab8..2db21df 100644 --- a/modules/tautulli/tautulli_connector.py +++ b/modules/tautulli/tautulli_connector.py @@ -57,7 +57,14 @@ def _error_and_analytics(self, error_message, function_name) -> None: logging.error(error_message) self.analytics.event(event_category="Error", event_action=function_name, random_uuid_if_needed=True) + def plex_pass_feature_is_allowed(self, feature: bool) -> bool: + if not self.has_plex_pass: + return False + + return feature + def refresh_data(self, + enable_stream_termination_if_possible: bool, emoji_manager: EmojiManager, additional_embed_fields: List[dict] = None, additional_embed_footers: List[str] = None) -> TautulliActivitySummary: @@ -68,6 +75,12 @@ def refresh_data(self, # Erase session ID mappings from last refresh self.session_id_mappings = {} + # Add termination tip if enabled + if self.plex_pass_feature_is_allowed(feature=enable_stream_termination_if_possible): + additional_embed_footers = additional_embed_footers or [] + additional_embed_footers.append( + f"To terminate a stream, react with the stream number.") + data = self.api.activity() # Tautulli returned data (is online) @@ -88,7 +101,6 @@ def refresh_data(self, emoji_manager=emoji_manager, text_manager=self.text_manager, streams=session_details, - has_plex_pass=self.has_plex_pass, server_name=self.server_name, additional_embed_fields=additional_embed_fields, additional_embed_footers=additional_embed_footers) @@ -100,7 +112,6 @@ def refresh_data(self, emoji_manager=emoji_manager, text_manager=self.text_manager, error_occurred=True, - has_plex_pass=False, # Tautulli is unreachable, so we can't know check server_name=self.server_name, additional_embed_fields=additional_embed_fields, additional_embed_footers=additional_embed_footers) diff --git a/modules/text_manager.py b/modules/text_manager.py index bde0732..5f69052 100644 --- a/modules/text_manager.py +++ b/modules/text_manager.py @@ -101,8 +101,7 @@ def session_body(self, session, emoji_manager: EmojiManager) -> str: stubs = [stub for stub in stubs if stub is not None] return "\n".join(stubs) - def overview_footer(self, no_connection: bool, activity, emoji_manager: EmojiManager, - add_termination_tip: bool) -> str: + def overview_footer(self, no_connection: bool, activity, emoji_manager: EmojiManager) -> str: timestamp = f"\n\nUpdated {self.time_manager.now_string()}" if no_connection or activity is None: @@ -129,9 +128,6 @@ def overview_footer(self, no_connection: bool, activity, emoji_manager: EmojiMan lan_bandwidth = activity.lan_bandwidth overview_message += f""" {lan_bandwidth_emoji} {lan_bandwidth}""" - overview_message += f"\n\n{timestamp}" - - if add_termination_tip: - overview_message += f"\n\nTo terminate a stream, react with the stream number." + overview_message += f"{timestamp}\n" return overview_message diff --git a/tauticord.yaml.example b/tauticord.yaml.example index b7fbabf..688f3c9 100644 --- a/tauticord.yaml.example +++ b/tauticord.yaml.example @@ -26,6 +26,8 @@ Discord: PostSummaryMessage: true # The name of the channel where the live stats summary message will be posted ChannelName: "tautulli" + # Whether to enable termination capabilities (requires Plex Pass) + EnableTermination: true # Whether to enable slash commands EnableSlashCommands: true # Settings for the activity/status message