DiscordBotLinuxMonitor 1.6.3__tar.gz → 1.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DiscordBotLinuxMonitor
3
- Version: 1.6.3
3
+ Version: 1.6.4
4
4
  Summary: From discord channels: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
5
5
  Home-page: https://github.com/QuentinCG/Discord-Bot-Linux-Monitor-Python-Library
6
6
  Author: Quentin Comte-Gaz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DiscordBotLinuxMonitor
3
- Version: 1.6.3
3
+ Version: 1.6.4
4
4
  Summary: From discord channels: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)
5
5
  Home-page: https://github.com/QuentinCG/Discord-Bot-Linux-Monitor-Python-Library
6
6
  Author: Quentin Comte-Gaz
@@ -33,7 +33,7 @@ __email__ = "quentin@comte-gaz.com"
33
33
  __license__ = "MIT License"
34
34
  __copyright__ = "Copyright Quentin Comte-Gaz (2026)"
35
35
  __python_version__ = "3.+"
36
- __version__ = "1.6.3 (2026/08/24)"
36
+ __version__ = "1.6.4 (2026/08/24)"
37
37
  __status__ = "Usable for any Linux project"
38
38
 
39
39
  # pyright: reportMissingTypeStubs=false
@@ -309,6 +309,25 @@ class DiscordBotLinuxMonitor:
309
309
 
310
310
  return fallback_seconds
311
311
 
312
+ def _format_duration(self, total_seconds: float) -> str:
313
+ seconds: int = max(0, int(total_seconds))
314
+
315
+ days, remainder = divmod(seconds, 86400)
316
+ hours, remainder = divmod(remainder, 3600)
317
+ minutes, secs = divmod(remainder, 60)
318
+
319
+ parts: List[str] = []
320
+ if days > 0:
321
+ parts.append(f"{days}d")
322
+ if hours > 0:
323
+ parts.append(f"{hours}h")
324
+ if minutes > 0:
325
+ parts.append(f"{minutes}m")
326
+ if secs > 0 or len(parts) == 0:
327
+ parts.append(f"{secs}s")
328
+
329
+ return " ".join(parts)
330
+
312
331
  async def _delete_message_with_rate_limit_retry(self, message: discord.Message, reason: str, max_retries: int = 10, on_rate_limit: Optional[Callable[[], None]] = None) -> None:
313
332
  for attempt in range(max_retries + 1):
314
333
  try:
@@ -903,6 +922,10 @@ class DiscordBotLinuxMonitor:
903
922
  started_monotonic: float = asyncio.get_running_loop().time()
904
923
  last_progress_monotonic: float = started_monotonic
905
924
  last_reported_deleted_count: int = 0
925
+ heartbeat_frames: List[str] = ["|", "/", "-", "\\"]
926
+ heartbeat_index: int = 0
927
+ heartbeat_stop_event = asyncio.Event()
928
+ heartbeat_task: Optional[asyncio.Task] = None
906
929
 
907
930
  def _on_rate_limit_hit() -> None:
908
931
  nonlocal rate_limit_retries
@@ -911,6 +934,7 @@ class DiscordBotLinuxMonitor:
911
934
  async def _update_progress(force: bool = False) -> None:
912
935
  nonlocal last_progress_monotonic
913
936
  nonlocal last_reported_deleted_count
937
+ nonlocal heartbeat_index
914
938
 
915
939
  now = asyncio.get_running_loop().time()
916
940
  should_update = force
@@ -922,12 +946,17 @@ class DiscordBotLinuxMonitor:
922
946
  if not should_update:
923
947
  return
924
948
 
925
- elapsed_seconds = int(now - started_monotonic)
949
+ elapsed_duration = self._format_duration(now - started_monotonic)
950
+ last_update = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
951
+ heartbeat_frame = heartbeat_frames[heartbeat_index % len(heartbeat_frames)]
952
+ heartbeat_index += 1
926
953
  progress_msg = (
927
954
  f"🧹 Cleaning channel '{channel.name}'...\n"
928
- f"Deleted messages: {deleted_count}\n"
929
- f"Rate-limit waits: {rate_limit_retries}\n"
930
- f"Elapsed: {elapsed_seconds}s"
955
+ f" - Status: {heartbeat_frame} Working...\n"
956
+ f" - Deleted messages: {deleted_count}\n"
957
+ f" - Rate-limit waits: {rate_limit_retries}\n"
958
+ f" - Elapsed: {elapsed_duration}\n"
959
+ f" - Last update: {last_update}"
931
960
  )
932
961
 
933
962
  try:
@@ -942,8 +971,16 @@ class DiscordBotLinuxMonitor:
942
971
  else:
943
972
  logging.warning(msg=f"Failed to update cleanup progress message: {e}")
944
973
 
974
+ async def _heartbeat_loop() -> None:
975
+ while not heartbeat_stop_event.is_set():
976
+ await asyncio.sleep(5)
977
+ if heartbeat_stop_event.is_set():
978
+ return
979
+ await _update_progress(force=True)
980
+
945
981
  try:
946
982
  await _update_progress(force=True)
983
+ heartbeat_task = asyncio.create_task(_heartbeat_loop())
947
984
  recent_batch: List[discord.Message] = []
948
985
 
949
986
  async for message in channel.history(limit=None, oldest_first=False):
@@ -966,12 +1003,15 @@ class DiscordBotLinuxMonitor:
966
1003
  deleted_count += len(recent_batch)
967
1004
  await _update_progress()
968
1005
 
969
- elapsed_seconds = int(asyncio.get_running_loop().time() - started_monotonic)
1006
+ elapsed_duration = self._format_duration(asyncio.get_running_loop().time() - started_monotonic)
1007
+ last_update = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
970
1008
  out_msg = (
971
1009
  f"✅ Cleared channel '{channel.name}'.\n"
972
- f"Deleted messages: {deleted_count}\n"
973
- f"Rate-limit waits: {rate_limit_retries}\n"
974
- f"Elapsed: {elapsed_seconds}s"
1010
+ f" - Status: Completed\n"
1011
+ f" - Deleted messages: {deleted_count}\n"
1012
+ f" - Rate-limit waits: {rate_limit_retries}\n"
1013
+ f" - Elapsed: {elapsed_duration}\n"
1014
+ f" - Last update: {last_update}"
975
1015
  )
976
1016
  await interaction.edit_original_response(content=out_msg)
977
1017
  except discord.HTTPException as e:
@@ -984,6 +1024,14 @@ class DiscordBotLinuxMonitor:
984
1024
  out_msg = f"**Internal error while clearing messages in channel '{channel.name}'**:\n```sh\n{e}\n```"
985
1025
  logging.exception(msg=out_msg)
986
1026
  await interaction.edit_original_response(content=out_msg)
1027
+ finally:
1028
+ heartbeat_stop_event.set()
1029
+ if heartbeat_task is not None and not heartbeat_task.done():
1030
+ heartbeat_task.cancel()
1031
+ try:
1032
+ await heartbeat_task
1033
+ except asyncio.CancelledError:
1034
+ pass
987
1035
 
988
1036
  async def list_commands(self, interaction: discord.Interaction) -> None:
989
1037
  if not self._check_if_valid_guild(guild=interaction.guild):
@@ -6,7 +6,7 @@ with io.open(file='README.md', mode='r', encoding='utf-8') as readme_file:
6
6
 
7
7
  setup(
8
8
  name="DiscordBotLinuxMonitor",
9
- version="1.6.3",
9
+ version="1.6.4",
10
10
  description="From discord channels: Get information and warning status of Linux server like service, port, ping, ssl certificate, disk/folder/cpu/ram/swap usage, ip connection, ... (Python and shell library, Linux ONLY)",
11
11
  long_description=readme,
12
12
  long_description_content_type="text/markdown",