DiscordBotLinuxMonitor 1.6.2__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.
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/DiscordBotLinuxMonitor.egg-info/PKG-INFO +3 -1
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/PKG-INFO +3 -1
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/README.md +2 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/discordbotlinuxmonitor/__main__.py +4 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/discordbotlinuxmonitor/discordbotlinuxmonitor.py +88 -17
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/setup.py +1 -1
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/DiscordBotLinuxMonitor.egg-info/SOURCES.txt +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/DiscordBotLinuxMonitor.egg-info/dependency_links.txt +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/DiscordBotLinuxMonitor.egg-info/requires.txt +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/DiscordBotLinuxMonitor.egg-info/top_level.txt +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/LICENSE.md +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/discordbotlinuxmonitor/__init__.py +0 -0
- {discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DiscordBotLinuxMonitor
|
|
3
|
-
Version: 1.6.
|
|
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
|
|
@@ -98,6 +98,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
98
98
|
|
|
99
99
|
- `/list_commands`: 📋 List all available commands 📋
|
|
100
100
|
|
|
101
|
+
- `/version`: 🤖 Show current bot version 🤖
|
|
102
|
+
|
|
101
103
|
- `/execute_command {command_name} [parameters]`: 🚀 Execute a command (optional parameters, only for commands with `accept_parameters` or a `{arg1}`/`{args}` placeholder) 🚀
|
|
102
104
|
|
|
103
105
|
- `/execute_all_commands`: 🚀 Execute all commands 🚀
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DiscordBotLinuxMonitor
|
|
3
|
-
Version: 1.6.
|
|
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
|
|
@@ -98,6 +98,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
98
98
|
|
|
99
99
|
- `/list_commands`: 📋 List all available commands 📋
|
|
100
100
|
|
|
101
|
+
- `/version`: 🤖 Show current bot version 🤖
|
|
102
|
+
|
|
101
103
|
- `/execute_command {command_name} [parameters]`: 🚀 Execute a command (optional parameters, only for commands with `accept_parameters` or a `{arg1}`/`{args}` placeholder) 🚀
|
|
102
104
|
|
|
103
105
|
- `/execute_all_commands`: 🚀 Execute all commands 🚀
|
|
@@ -56,6 +56,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
56
56
|
|
|
57
57
|
- `/list_commands`: 📋 List all available commands 📋
|
|
58
58
|
|
|
59
|
+
- `/version`: 🤖 Show current bot version 🤖
|
|
60
|
+
|
|
59
61
|
- `/execute_command {command_name} [parameters]`: 🚀 Execute a command (optional parameters, only for commands with `accept_parameters` or a `{arg1}`/`{args}` placeholder) 🚀
|
|
60
62
|
|
|
61
63
|
- `/execute_all_commands`: 🚀 Execute all commands 🚀
|
{discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/discordbotlinuxmonitor/__main__.py
RENAMED
|
@@ -54,6 +54,10 @@ def main() -> None:
|
|
|
54
54
|
async def force_sync(interaction: discord.Interaction) -> None: # type: ignore
|
|
55
55
|
await discord_bot_linux_monitor.force_sync(interaction)
|
|
56
56
|
|
|
57
|
+
@discord_bot.tree.command(name="version", description="🤖 Show bot version 🤖")
|
|
58
|
+
async def version(interaction: discord.Interaction) -> None: # type: ignore
|
|
59
|
+
await discord_bot_linux_monitor.version(interaction)
|
|
60
|
+
|
|
57
61
|
@discord_bot.tree.command(name="usage", description="📊 View disk space, CPU, RAM, ... 📊")
|
|
58
62
|
async def usage(interaction: discord.Interaction) -> None: # type: ignore
|
|
59
63
|
await discord_bot_linux_monitor.usage(interaction)
|
|
@@ -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.
|
|
36
|
+
__version__ = "1.6.4 (2026/08/24)"
|
|
37
37
|
__status__ = "Usable for any Linux project"
|
|
38
38
|
|
|
39
39
|
# pyright: reportMissingTypeStubs=false
|
|
@@ -286,6 +286,9 @@ class DiscordBotLinuxMonitor:
|
|
|
286
286
|
logging.exception(msg=out_msg)
|
|
287
287
|
return out_msg
|
|
288
288
|
|
|
289
|
+
def _welcome_message_with_version(self, welcome_message: str) -> str:
|
|
290
|
+
return f"{welcome_message}\n\n🤖 Bot version: {__version__}"
|
|
291
|
+
|
|
289
292
|
def _get_rate_limit_wait_seconds(self, error: discord.HTTPException, fallback_seconds: float = 1.5) -> float:
|
|
290
293
|
retry_after = getattr(error, "retry_after", None)
|
|
291
294
|
if isinstance(retry_after, (int, float)) and retry_after > 0:
|
|
@@ -306,10 +309,33 @@ class DiscordBotLinuxMonitor:
|
|
|
306
309
|
|
|
307
310
|
return fallback_seconds
|
|
308
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
|
+
|
|
309
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:
|
|
310
332
|
for attempt in range(max_retries + 1):
|
|
311
333
|
try:
|
|
312
|
-
|
|
334
|
+
try:
|
|
335
|
+
await message.delete(reason=reason)
|
|
336
|
+
except TypeError:
|
|
337
|
+
# Some discord.py objects (e.g. PartialMessage) do not accept `reason`.
|
|
338
|
+
await message.delete()
|
|
313
339
|
return
|
|
314
340
|
except discord.NotFound:
|
|
315
341
|
return
|
|
@@ -330,7 +356,11 @@ class DiscordBotLinuxMonitor:
|
|
|
330
356
|
for attempt in range(max_retries + 1):
|
|
331
357
|
try:
|
|
332
358
|
if len(messages) == 1:
|
|
333
|
-
|
|
359
|
+
try:
|
|
360
|
+
await messages[0].delete(reason=reason)
|
|
361
|
+
except TypeError:
|
|
362
|
+
# Some discord.py objects (e.g. PartialMessage) do not accept `reason`.
|
|
363
|
+
await messages[0].delete()
|
|
334
364
|
else:
|
|
335
365
|
await channel.delete_messages(messages, reason=reason)
|
|
336
366
|
return
|
|
@@ -370,20 +400,20 @@ class DiscordBotLinuxMonitor:
|
|
|
370
400
|
logging.info(msg="Found the public channel, it will be possible to do public commands.")
|
|
371
401
|
public_channel_cmd_ready = True
|
|
372
402
|
if self.welcome_message_for_public_commands != "":
|
|
373
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_public_commands)
|
|
403
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_public_commands))
|
|
374
404
|
|
|
375
405
|
if self.channel_name_for_private_commands != "" and channel.name == self.channel_name_for_private_commands:
|
|
376
406
|
logging.info(msg="Found the private channel, it will be possible to do private commands.")
|
|
377
407
|
private_channel_cmd_ready = True
|
|
378
408
|
if self.welcome_message_for_private_commands != "":
|
|
379
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_private_commands)
|
|
409
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_private_commands))
|
|
380
410
|
|
|
381
411
|
if self.channel_name_for_public_error_tasks != "" and channel.name == self.channel_name_for_public_error_tasks:
|
|
382
412
|
logging.info(msg="Found the public channel for error task, it will be possible to show public status issues if found periodically.")
|
|
383
413
|
public_channel_error_task_ready = True
|
|
384
414
|
|
|
385
415
|
if self.welcome_message_for_public_error_tasks != "":
|
|
386
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_public_error_tasks)
|
|
416
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_public_error_tasks))
|
|
387
417
|
|
|
388
418
|
logging.info(msg=f"Activating automatic public follow and public service restart if down with '{self.bot.user}' and guild '{guild.name}' (id: '{guild.id}') on channel '{channel.name}' (id '{channel.id}').")
|
|
389
419
|
public_channel_for_error_task: discord.TextChannel = channel
|
|
@@ -397,7 +427,7 @@ class DiscordBotLinuxMonitor:
|
|
|
397
427
|
public_channel_info_task_ready = True
|
|
398
428
|
|
|
399
429
|
if self.welcome_message_for_public_infos_tasks != "":
|
|
400
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_public_infos_tasks)
|
|
430
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_public_infos_tasks))
|
|
401
431
|
|
|
402
432
|
logging.info(msg=f"Activating automatic public follow info with '{self.bot.user}' and guild '{guild.name}' (id: '{guild.id}') on channel '{channel.name}' (id '{channel.id}').")
|
|
403
433
|
public_channel_for_info_task: discord.TextChannel = channel
|
|
@@ -411,7 +441,7 @@ class DiscordBotLinuxMonitor:
|
|
|
411
441
|
private_channel_error_task_ready = True
|
|
412
442
|
|
|
413
443
|
if self.welcome_message_for_private_error_tasks != "":
|
|
414
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_private_error_tasks)
|
|
444
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_private_error_tasks))
|
|
415
445
|
|
|
416
446
|
logging.info(msg=f"Activating automatic private follow and public service restart if down with '{self.bot.user}' and guild '{guild.name}' (id: '{guild.id}') on channel '{channel.name}' (id '{channel.id}').")
|
|
417
447
|
private_channel_for_error_task: discord.TextChannel = channel
|
|
@@ -425,7 +455,7 @@ class DiscordBotLinuxMonitor:
|
|
|
425
455
|
private_channel_info_task_ready = True
|
|
426
456
|
|
|
427
457
|
if self.welcome_message_for_private_infos_tasks != "":
|
|
428
|
-
await self._channel_send_no_limit(channel=channel, msg=self.welcome_message_for_private_infos_tasks)
|
|
458
|
+
await self._channel_send_no_limit(channel=channel, msg=self._welcome_message_with_version(self.welcome_message_for_private_infos_tasks))
|
|
429
459
|
|
|
430
460
|
logging.info(msg=f"Activating automatic private follow info with '{self.bot.user}' and guild '{guild.name}' (id: '{guild.id}') on channel '{channel.name}' (id '{channel.id}').")
|
|
431
461
|
private_channel_for_info_task: discord.TextChannel = channel
|
|
@@ -486,6 +516,18 @@ class DiscordBotLinuxMonitor:
|
|
|
486
516
|
# Respond to the user
|
|
487
517
|
await self._interaction_followup_send_no_limit(interaction=interaction, msg=out_msg)
|
|
488
518
|
|
|
519
|
+
async def version(self, interaction: discord.Interaction) -> None:
|
|
520
|
+
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
521
|
+
return
|
|
522
|
+
if not (await self._is_bot_channel_interaction(interaction=interaction, send_message_if_not_bot=True)):
|
|
523
|
+
return
|
|
524
|
+
|
|
525
|
+
out_msg: str = (
|
|
526
|
+
f"🤖 Bot version: {__version__}\n"
|
|
527
|
+
f"🐍 Python compatibility: {__python_version__}"
|
|
528
|
+
)
|
|
529
|
+
await interaction.response.send_message(content=out_msg, ephemeral=True)
|
|
530
|
+
|
|
489
531
|
async def usage(self, interaction: discord.Interaction) -> None:
|
|
490
532
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
491
533
|
return
|
|
@@ -880,6 +922,10 @@ class DiscordBotLinuxMonitor:
|
|
|
880
922
|
started_monotonic: float = asyncio.get_running_loop().time()
|
|
881
923
|
last_progress_monotonic: float = started_monotonic
|
|
882
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
|
|
883
929
|
|
|
884
930
|
def _on_rate_limit_hit() -> None:
|
|
885
931
|
nonlocal rate_limit_retries
|
|
@@ -888,6 +934,7 @@ class DiscordBotLinuxMonitor:
|
|
|
888
934
|
async def _update_progress(force: bool = False) -> None:
|
|
889
935
|
nonlocal last_progress_monotonic
|
|
890
936
|
nonlocal last_reported_deleted_count
|
|
937
|
+
nonlocal heartbeat_index
|
|
891
938
|
|
|
892
939
|
now = asyncio.get_running_loop().time()
|
|
893
940
|
should_update = force
|
|
@@ -899,12 +946,17 @@ class DiscordBotLinuxMonitor:
|
|
|
899
946
|
if not should_update:
|
|
900
947
|
return
|
|
901
948
|
|
|
902
|
-
|
|
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
|
|
903
953
|
progress_msg = (
|
|
904
954
|
f"🧹 Cleaning channel '{channel.name}'...\n"
|
|
905
|
-
f"
|
|
906
|
-
f"
|
|
907
|
-
f"
|
|
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}"
|
|
908
960
|
)
|
|
909
961
|
|
|
910
962
|
try:
|
|
@@ -919,8 +971,16 @@ class DiscordBotLinuxMonitor:
|
|
|
919
971
|
else:
|
|
920
972
|
logging.warning(msg=f"Failed to update cleanup progress message: {e}")
|
|
921
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
|
+
|
|
922
981
|
try:
|
|
923
982
|
await _update_progress(force=True)
|
|
983
|
+
heartbeat_task = asyncio.create_task(_heartbeat_loop())
|
|
924
984
|
recent_batch: List[discord.Message] = []
|
|
925
985
|
|
|
926
986
|
async for message in channel.history(limit=None, oldest_first=False):
|
|
@@ -943,12 +1003,15 @@ class DiscordBotLinuxMonitor:
|
|
|
943
1003
|
deleted_count += len(recent_batch)
|
|
944
1004
|
await _update_progress()
|
|
945
1005
|
|
|
946
|
-
|
|
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")
|
|
947
1008
|
out_msg = (
|
|
948
1009
|
f"✅ Cleared channel '{channel.name}'.\n"
|
|
949
|
-
f"
|
|
950
|
-
f"
|
|
951
|
-
f"
|
|
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}"
|
|
952
1015
|
)
|
|
953
1016
|
await interaction.edit_original_response(content=out_msg)
|
|
954
1017
|
except discord.HTTPException as e:
|
|
@@ -961,6 +1024,14 @@ class DiscordBotLinuxMonitor:
|
|
|
961
1024
|
out_msg = f"**Internal error while clearing messages in channel '{channel.name}'**:\n```sh\n{e}\n```"
|
|
962
1025
|
logging.exception(msg=out_msg)
|
|
963
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
|
|
964
1035
|
|
|
965
1036
|
async def list_commands(self, interaction: discord.Interaction) -> None:
|
|
966
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.
|
|
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",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{discordbotlinuxmonitor-1.6.2 → discordbotlinuxmonitor-1.6.4}/discordbotlinuxmonitor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|