Skip to content

Commit

Permalink
Use Discord timestamps for ETA, embed updates, deprecate timezone/24-…
Browse files Browse the repository at this point in the history
…hour config options (#238)
  • Loading branch information
nwithan8 authored Feb 16, 2025
1 parent e9e7d06 commit a2bb709
Show file tree
Hide file tree
Showing 17 changed files with 42 additions and 157 deletions.
13 changes: 0 additions & 13 deletions .schema/config_v2.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -534,21 +534,8 @@
"description": "Settings for how the bot handles time",
"type": "object",
"properties": {
"ServerTimeZone": {
"title": "Server time zone",
"description": "The time zone of the Plex server",
"type": "string",
"format": "date-time"
},
"Use24HourTime": {
"title": "Use 24-hour (military) time",
"description": "Whether to use 24-hour (military) time",
"type": "boolean"
}
},
"required": [
"ServerTimeZone",
"Use24HourTime"
]
},
"UseFriendlyNames": {
Expand Down
2 changes: 2 additions & 0 deletions documentation/upgrade_guides/v4_to_v5_migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,13 @@ in `tauticord.yaml`:
- Tautulli -> Customization -> ServerTimeZone
+ Display -> Time -> ServerTimeZone
```
(**DEPRECATED in v5.12.0**)

```diff
- Tautulli -> Customization -> Use24HourTime
+ Display -> Time -> Use24HourTime
```
(**DEPRECATED in v5.12.0**)

```diff
- Tautulli -> Customization -> VoiceChannels -> Stats -> CategoryName
Expand Down
7 changes: 2 additions & 5 deletions legacy/config_parser_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import modules.logs as logging
from legacy import statics, utils
from legacy.text_manager import TextManager
from legacy.time_manager import TimeManager
from modules.time_manager import TimeManager


def _extract_bool(value):
Expand Down Expand Up @@ -100,10 +100,7 @@ def terminate_message(self) -> str:

@property
def time_manager(self) -> TimeManager:
timezone = self._customization._get_value(key='ServerTimeZone', default=None, env_name_override="TZ")
mil_time = self._customization._get_value(key='Use24HourTime', default=False,
env_name_override="TC_USE_24_HOUR_TIME")
return TimeManager(timezone=timezone, military_time=mil_time)
return TimeManager()

@property
def _voice_channels(self) -> ConfigSection:
Expand Down
10 changes: 3 additions & 7 deletions legacy/text_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, rules: Dict[str, Any]) -> None:
self._anon_hide_progress: bool = rules.get(statics.KEY_HIDE_PROGRESS, False)
self._anon_hide_eta: bool = rules.get(statics.KEY_HIDE_ETA, False)
self._use_friendly_names: bool = rules.get(statics.KEY_USE_FRIENDLY_NAMES, False)
self._time_manager: TimeManager = rules.get(statics.KEY_TIME_MANAGER, TimeManager(timezone="UTC", military_time=False)) # fallback should not be needed
self._time_manager: TimeManager = rules.get(statics.KEY_TIME_MANAGER, TimeManager()) # fallback should not be needed

def _session_user_message(self, session, emoji_manager: EmojiManager) -> Union[str, None]:
if self._anon_hide_usernames:
Expand Down Expand Up @@ -102,13 +102,11 @@ def session_body(self, session, emoji_manager: EmojiManager) -> str:
return "\n".join(stubs)

def overview_footer(self, no_connection: bool, activity, emoji_manager: EmojiManager, add_termination_tip: bool) -> str:
timestamp = f"\n\nUpdated {self._time_manager.now_string()}"

if no_connection or activity is None:
return f"{utils.bold('Connection lost.')}\n\n{timestamp}"
return f"{utils.bold('Connection lost.')}"

if activity.stream_count == 0:
return timestamp
return ""

stream_count = activity.stream_count
stream_count_word = utils.make_plural(word='stream', count=stream_count)
Expand All @@ -128,8 +126,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."

Expand Down
29 changes: 0 additions & 29 deletions legacy/time_manager.py

This file was deleted.

16 changes: 0 additions & 16 deletions legacy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
from typing import List, Optional, Any
from urllib.parse import quote_plus

from pytz import timezone

import modules.logs as logging
from legacy.statics import ENCODING_SEPARATOR_1, ENCODING_SEPARATOR_2

Expand Down Expand Up @@ -122,20 +120,6 @@ def milliseconds_to_minutes_seconds(milliseconds: int) -> str:
return f"{minutes}:{seconds}"


def now(timezone_code: str = None) -> datetime:
if timezone_code:
return datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
return datetime.now()


def now_plus_milliseconds(milliseconds: int, timezone_code: str = None) -> datetime:
if timezone_code:
now = datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
else:
now = datetime.now()
return now + timedelta(milliseconds=milliseconds)


def limit_text_length(text: str, limit: int, suffix: str = "...") -> str:
if len(text) <= limit:
return text
Expand Down
21 changes: 0 additions & 21 deletions migrations/m001_env_var_to_config_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,6 @@ def tautulli_customization_plex_pass(self) -> bool:
def tautulli_customization_plex_pass(self, value):
self.add(key_path=["Tautulli", "Customization", "PlexPass"], value=value)

@property
def tautulli_customization_server_time_zone(self) -> str:
return self.read(key_path=["Tautulli", "Customization", "ServerTimeZone"])

@tautulli_customization_server_time_zone.setter
def tautulli_customization_server_time_zone(self, value: str):
self.add(key_path=["Tautulli", "Customization", "ServerTimeZone"], value=value)

@property
def tautulli_customization_use_24_hour_time(self) -> bool:
return self.read(key_path=["Tautulli", "Customization", "Use24HourTime"])

@tautulli_customization_use_24_hour_time.setter
def tautulli_customization_use_24_hour_time(self, value: bool):
self.add(key_path=["Tautulli", "Customization", "Use24HourTime"], value=value)

@property
def tautulli_customization_voice_channels_stats_category_name(self) -> str:
return self.read(key_path=["Tautulli", "Customization", "VoiceChannels", "Stats", "CategoryName"])
Expand Down Expand Up @@ -519,11 +503,6 @@ def forward(self):
new_config.tautulli_customization_refresh_seconds = old_config.tautulli.refresh_interval
new_config.tautulli_customization_server_name = old_config.tautulli.server_name
new_config.tautulli_customization_terminate_message = old_config.tautulli.terminate_message
new_config.tautulli_customization_server_time_zone = old_config.tautulli._customization._get_value(
key='ServerTimeZone', default=None, env_name_override="TZ")
use_24_hour_time = old_config.tautulli._customization._get_value(key='Use24HourTime', default=False,
env_name_override="TC_USE_24_HOUR_TIME")
new_config.tautulli_customization_use_24_hour_time = config_parser._extract_bool(value=use_24_hour_time)
new_config.tautulli_customization_voice_channels_stats_category_name = old_config.tautulli.stats_voice_channel_category_name
new_config.tautulli_customization_voice_channels_stats_stream_count = old_config.tautulli.display_stream_count
new_config.tautulli_customization_voice_channels_stats_stream_count_channel_id = old_config.tautulli.stream_count_channel_id
Expand Down
7 changes: 1 addition & 6 deletions migrations/m002_old_config_to_new_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,7 @@ def forward(self):
to_path=["Display", "Anonymize", "HideProgress"], default=False)
new_config.migrate_value(value=old_config.tautulli._anonymize_hide_eta,
to_path=["Display", "Anonymize", "HideETA"], default=False)
new_config.migrate_value(
value=old_config.tautulli._customization._get_value(key='Use24HourTime', default=False),
to_path=["Display", "Time", "Use24HourTime"], default=False)
new_config.migrate_value(
value=old_config.tautulli._customization._get_value(key='ServerTimeZone', default="UTC"),
to_path=["Display", "Time", "ServerTimeZone"], default="UTC")
new_config.add(value={}, key_path=["Display", "Time"])

# Stats
new_config.add(value=any([
Expand Down
2 changes: 2 additions & 0 deletions modules/discord/models/tautulli_activity_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ def embed(self) -> discord.Embed:

embed.set_footer(text=footer_text)

embed.timestamp = discord.utils.utcnow()

return embed
3 changes: 1 addition & 2 deletions modules/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ def init(app_name: str,
logger.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - [%(levelname)s]: %(message)s')
timezone_abbr = os.getenv('TZ',
'UTC') # Due to chicken-egg issue, we can't parse the timezone from config, only from env
timezone_abbr = os.getenv('TZ', 'UTC')
formatter.converter = lambda *args: datetime.now(tz=timezone(timezone_abbr)).timetuple()

# Console logging
Expand Down
8 changes: 1 addition & 7 deletions modules/settings/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,7 @@ def __init__(self, data: dict):
super().__init__(data=data)

def to_model(self) -> settings_models.Time:
tautulli_server_time_zone = self.get_value(key="ServerTimeZone", default="UTC")
use_24_hour_time = utils.extract_boolean(self.get_value(key="Use24HourTime", default=False))

return settings_models.Time(
tautulli_server_time_zone=tautulli_server_time_zone,
use_24_hour_time=use_24_hour_time
)
return settings_models.Time()


class DisplayConfig(ConfigSection):
Expand Down
9 changes: 1 addition & 8 deletions modules/settings/models/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@


class Time(BaseConfig):
tautulli_server_time_zone: str
use_24_hour_time: bool

@property
def time_manager(self):
return TimeManager(
timezone=self.tautulli_server_time_zone,
use_24_hour_time=self.use_24_hour_time)
return TimeManager()

def as_dict(self) -> dict:
return {
"tautulli_server_time_zone": self.tautulli_server_time_zone,
"use_24_hour_time": self.use_24_hour_time
}
2 changes: 1 addition & 1 deletion modules/tautulli/models/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def eta(self) -> str:
if not self.duration_milliseconds or not self.location_milliseconds:
return "Unknown"
milliseconds_remaining = self.duration_milliseconds - self.location_milliseconds
return self._time_manager.now_plus_milliseconds_string(milliseconds=milliseconds_remaining)
return self._time_manager.now_plus_milliseconds_unix_timestamp(milliseconds=milliseconds_remaining)

@property
def title(self) -> str:
Expand Down
8 changes: 2 additions & 6 deletions modules/text_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,11 @@ def session_body(self, session, emoji_manager: EmojiManager) -> str:
return "\n".join(stubs)

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:
return f"{utils.bold('Connection lost.')}\n\n{timestamp}"
return f"{utils.bold('Connection lost.')}"

if activity.stream_count == 0:
return timestamp
return ""

stream_count = activity.stream_count
stream_count_word = utils.make_plural(word='stream', count=stream_count)
Expand All @@ -128,6 +126,4 @@ 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"{timestamp}\n"

return overview_message
26 changes: 9 additions & 17 deletions modules/time_manager.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
from datetime import datetime

import discord.utils
from pydantic import BaseModel

from modules import utils


class TimeManager(BaseModel):
timezone: str
use_24_hour_time: bool

@property
def time_format(self) -> str:
return "%H:%M" if self.use_24_hour_time else "%I:%M %p"

def now(self) -> datetime:
return utils.now(self.timezone)
return discord.utils.utcnow()

def now_string(self) -> str:
_datetime = self.now()
return utils.datetime_to_string(datetime_object=_datetime,
template=self.time_format)
def now_unix_timestamp(self) -> str:
_timestamp = int(self.now().timestamp())
return utils.timestamp(ts=_timestamp)

def now_plus_milliseconds(self, milliseconds: int) -> datetime:
return utils.now_plus_milliseconds(milliseconds, self.timezone)
return utils.now_plus_milliseconds(milliseconds)

def now_plus_milliseconds_string(self, milliseconds: int) -> str:
_datetime = self.now_plus_milliseconds(milliseconds)
return utils.datetime_to_string(datetime_object=_datetime,
template=self.time_format)
def now_plus_milliseconds_unix_timestamp(self, milliseconds: int) -> str:
_timestamp = int(self.now_plus_milliseconds(milliseconds).timestamp())
return utils.timestamp(ts=_timestamp)
30 changes: 16 additions & 14 deletions modules/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@
from typing import Optional, Any
from urllib.parse import quote_plus

from pytz import timezone


def pretty_print_json(json_data: dict, sort: bool = False) -> str:
"""
Return a pretty printed JSON string
:param json_data: JSON data to pretty print
Return a pretty printed JSON m json_data: JSON data to pretty print
:type json_data: dict
:param sort: (Optional) sort the keys in the JSON data
:type sort: bool, optional
Expand Down Expand Up @@ -224,18 +220,12 @@ def milliseconds_to_minutes_seconds(milliseconds: int) -> str:
return f"{minutes}:{seconds}"


def now(timezone_code: str = None) -> datetime:
if timezone_code:
return datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
def now() -> datetime:
return datetime.now()


def now_plus_milliseconds(milliseconds: int, timezone_code: str = None) -> datetime:
if timezone_code:
now = datetime.now(timezone(timezone_code)) # will raise exception if invalid timezone_code
else:
now = datetime.now()
return now + timedelta(milliseconds=milliseconds)
def now_plus_milliseconds(milliseconds: int) -> datetime:
return datetime.now() + timedelta(milliseconds=milliseconds)


def limit_text_length(text: str, limit: int, suffix: str = "...") -> str:
Expand Down Expand Up @@ -454,6 +444,18 @@ def role_mention(string: str, role_id: str) -> str:
return f"<@&{role_id}>"


def timestamp(ts: int) -> str:
"""
Return a string wrapped in timestamp markdown
:param ts: timestamp to wrap in timestamp markdown
:type ts: int
:return: string wrapped in timestamp markdown
:rtype: str
"""
return f"<t:{ts}>"


def emoji(string: str, emoji_id: str) -> str:
"""
Return a string wrapped in emoji markdown
Expand Down
6 changes: 1 addition & 5 deletions tauticord.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,7 @@ Display:
# The thousands separator
ThousandsSeparator: ''
# Time settings
Time:
# The time zone of the server
ServerTimeZone: America/Denver
# Whether to use 24 hour time
Use24HourTime: false
Time: {}
# Whether to use Plex users' names instead of usernames
UseFriendlyNames: true

Expand Down

0 comments on commit a2bb709

Please sign in to comment.