DiscordBotLinuxMonitor 1.6.4__tar.gz → 1.6.5__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.4 → discordbotlinuxmonitor-1.6.5}/DiscordBotLinuxMonitor.egg-info/PKG-INFO +1 -1
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/PKG-INFO +1 -1
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/discordbotlinuxmonitor/discordbotlinuxmonitor.py +113 -28
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/setup.py +1 -1
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/DiscordBotLinuxMonitor.egg-info/SOURCES.txt +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/DiscordBotLinuxMonitor.egg-info/dependency_links.txt +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/DiscordBotLinuxMonitor.egg-info/requires.txt +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/DiscordBotLinuxMonitor.egg-info/top_level.txt +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/LICENSE.md +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/README.md +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/discordbotlinuxmonitor/__init__.py +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/discordbotlinuxmonitor/__main__.py +0 -0
- {discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/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.5
|
|
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
|
+
Version: 1.6.5
|
|
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.
|
|
36
|
+
__version__ = "1.6.5 (2026/08/24)"
|
|
37
37
|
__status__ = "Usable for any Linux project"
|
|
38
38
|
|
|
39
39
|
# pyright: reportMissingTypeStubs=false
|
|
@@ -328,6 +328,68 @@ class DiscordBotLinuxMonitor:
|
|
|
328
328
|
|
|
329
329
|
return " ".join(parts)
|
|
330
330
|
|
|
331
|
+
def _get_message_preview(self, message: discord.Message, max_length: int = 80) -> str:
|
|
332
|
+
content: str = str(getattr(message, "content", "") or "").strip()
|
|
333
|
+
|
|
334
|
+
if content == "":
|
|
335
|
+
attachments = getattr(message, "attachments", [])
|
|
336
|
+
embeds = getattr(message, "embeds", [])
|
|
337
|
+
if len(attachments) > 0:
|
|
338
|
+
content = f"[{len(attachments)} attachment(s)]"
|
|
339
|
+
elif len(embeds) > 0:
|
|
340
|
+
content = f"[{len(embeds)} embed(s)]"
|
|
341
|
+
else:
|
|
342
|
+
content = "[no text]"
|
|
343
|
+
|
|
344
|
+
content = content.replace("\n", " ").replace("\r", " ")
|
|
345
|
+
created_at = getattr(message, "created_at", None)
|
|
346
|
+
if created_at is not None:
|
|
347
|
+
try:
|
|
348
|
+
timestamp = created_at.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
349
|
+
except Exception:
|
|
350
|
+
timestamp = str(created_at)
|
|
351
|
+
else:
|
|
352
|
+
timestamp = "unknown-time"
|
|
353
|
+
|
|
354
|
+
if len(content) > max_length:
|
|
355
|
+
content = content[:max_length - 3] + "..."
|
|
356
|
+
|
|
357
|
+
return f"[{timestamp}] {content}"
|
|
358
|
+
|
|
359
|
+
def _build_cleanup_embed(self, channel_name: str, state: str, deleted_count: int, rate_limit_retries: int, elapsed_seconds: float, last_deleted_preview: str, spinner_frame: str = "", scanned_count: Optional[int] = None, error_text: str = "") -> "discord.Embed":
|
|
360
|
+
# state is one of: "running", "done", "error".
|
|
361
|
+
if state == "done":
|
|
362
|
+
color = discord.Color.green()
|
|
363
|
+
title = "🧹 Channel Cleanup — Completed"
|
|
364
|
+
status_value = "✅ Completed"
|
|
365
|
+
elif state == "error":
|
|
366
|
+
color = discord.Color.red()
|
|
367
|
+
title = "🧹 Channel Cleanup — Failed"
|
|
368
|
+
status_value = "❌ Failed"
|
|
369
|
+
else:
|
|
370
|
+
color = discord.Color.blurple()
|
|
371
|
+
title = "🧹 Channel Cleanup — In progress"
|
|
372
|
+
status_value = f"{spinner_frame} Working…".strip()
|
|
373
|
+
|
|
374
|
+
embed = discord.Embed(title=title, color=color)
|
|
375
|
+
embed.description = f"Target channel: **#{channel_name}**"
|
|
376
|
+
|
|
377
|
+
embed.add_field(name="Status", value=status_value, inline=True)
|
|
378
|
+
embed.add_field(name="🗑️ Deleted", value=f"**{deleted_count}** message(s)", inline=True)
|
|
379
|
+
embed.add_field(name="⏳ Elapsed", value=self._format_duration(elapsed_seconds), inline=True)
|
|
380
|
+
|
|
381
|
+
if scanned_count is not None:
|
|
382
|
+
embed.add_field(name="🔎 Scanned", value=f"{scanned_count} message(s)", inline=True)
|
|
383
|
+
embed.add_field(name="🚦 Rate-limit waits", value=str(rate_limit_retries), inline=True)
|
|
384
|
+
|
|
385
|
+
if error_text != "":
|
|
386
|
+
embed.add_field(name="⚠️ Error", value=f"```sh\n{error_text[:1000]}\n```", inline=False)
|
|
387
|
+
|
|
388
|
+
embed.add_field(name="🕗 Last deleted message", value=(last_deleted_preview if last_deleted_preview != "" else "N/A")[:1024], inline=False)
|
|
389
|
+
|
|
390
|
+
embed.set_footer(text=f"Bot v{__version__} • Last update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
391
|
+
return embed
|
|
392
|
+
|
|
331
393
|
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:
|
|
332
394
|
for attempt in range(max_retries + 1):
|
|
333
395
|
try:
|
|
@@ -922,10 +984,12 @@ class DiscordBotLinuxMonitor:
|
|
|
922
984
|
started_monotonic: float = asyncio.get_running_loop().time()
|
|
923
985
|
last_progress_monotonic: float = started_monotonic
|
|
924
986
|
last_reported_deleted_count: int = 0
|
|
925
|
-
heartbeat_frames: List[str] = ["
|
|
987
|
+
heartbeat_frames: List[str] = ["◐", "◓", "◑", "◒"]
|
|
926
988
|
heartbeat_index: int = 0
|
|
927
989
|
heartbeat_stop_event = asyncio.Event()
|
|
928
990
|
heartbeat_task: Optional[asyncio.Task] = None
|
|
991
|
+
last_deleted_message_preview: str = "N/A"
|
|
992
|
+
scanned_count: int = 0
|
|
929
993
|
|
|
930
994
|
def _on_rate_limit_hit() -> None:
|
|
931
995
|
nonlocal rate_limit_retries
|
|
@@ -946,21 +1010,21 @@ class DiscordBotLinuxMonitor:
|
|
|
946
1010
|
if not should_update:
|
|
947
1011
|
return
|
|
948
1012
|
|
|
949
|
-
elapsed_duration = self._format_duration(now - started_monotonic)
|
|
950
|
-
last_update = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
951
1013
|
heartbeat_frame = heartbeat_frames[heartbeat_index % len(heartbeat_frames)]
|
|
952
1014
|
heartbeat_index += 1
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1015
|
+
embed = self._build_cleanup_embed(
|
|
1016
|
+
channel_name=channel.name,
|
|
1017
|
+
state="running",
|
|
1018
|
+
deleted_count=deleted_count,
|
|
1019
|
+
rate_limit_retries=rate_limit_retries,
|
|
1020
|
+
elapsed_seconds=now - started_monotonic,
|
|
1021
|
+
last_deleted_preview=last_deleted_message_preview,
|
|
1022
|
+
spinner_frame=heartbeat_frame,
|
|
1023
|
+
scanned_count=scanned_count,
|
|
960
1024
|
)
|
|
961
1025
|
|
|
962
1026
|
try:
|
|
963
|
-
await interaction.edit_original_response(content=
|
|
1027
|
+
await interaction.edit_original_response(content=None, embed=embed)
|
|
964
1028
|
last_progress_monotonic = now
|
|
965
1029
|
last_reported_deleted_count = deleted_count
|
|
966
1030
|
except discord.HTTPException as e:
|
|
@@ -984,46 +1048,67 @@ class DiscordBotLinuxMonitor:
|
|
|
984
1048
|
recent_batch: List[discord.Message] = []
|
|
985
1049
|
|
|
986
1050
|
async for message in channel.history(limit=None, oldest_first=False):
|
|
1051
|
+
scanned_count += 1
|
|
987
1052
|
if message.created_at >= two_weeks_ago:
|
|
988
1053
|
recent_batch.append(message)
|
|
989
1054
|
|
|
990
1055
|
# Bulk delete by chunks of 100 to reduce API calls and rate-limit pressure.
|
|
991
1056
|
if len(recent_batch) == 100:
|
|
1057
|
+
last_deleted_message_preview = self._get_message_preview(recent_batch[-1])
|
|
992
1058
|
await self._bulk_delete_messages_with_rate_limit_retry(channel=channel, messages=recent_batch, reason=delete_reason, on_rate_limit=_on_rate_limit_hit)
|
|
993
1059
|
deleted_count += len(recent_batch)
|
|
994
1060
|
recent_batch = []
|
|
995
1061
|
await _update_progress()
|
|
996
1062
|
else:
|
|
1063
|
+
last_deleted_message_preview = self._get_message_preview(message)
|
|
997
1064
|
await self._delete_message_with_rate_limit_retry(message=message, reason=delete_reason, on_rate_limit=_on_rate_limit_hit)
|
|
998
1065
|
deleted_count += 1
|
|
999
1066
|
await _update_progress()
|
|
1000
1067
|
|
|
1001
1068
|
if len(recent_batch) > 0:
|
|
1069
|
+
last_deleted_message_preview = self._get_message_preview(recent_batch[-1])
|
|
1002
1070
|
await self._bulk_delete_messages_with_rate_limit_retry(channel=channel, messages=recent_batch, reason=delete_reason, on_rate_limit=_on_rate_limit_hit)
|
|
1003
1071
|
deleted_count += len(recent_batch)
|
|
1004
1072
|
await _update_progress()
|
|
1005
1073
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
f" - Last update: {last_update}"
|
|
1074
|
+
embed = self._build_cleanup_embed(
|
|
1075
|
+
channel_name=channel.name,
|
|
1076
|
+
state="done",
|
|
1077
|
+
deleted_count=deleted_count,
|
|
1078
|
+
rate_limit_retries=rate_limit_retries,
|
|
1079
|
+
elapsed_seconds=asyncio.get_running_loop().time() - started_monotonic,
|
|
1080
|
+
last_deleted_preview=last_deleted_message_preview,
|
|
1081
|
+
scanned_count=scanned_count,
|
|
1015
1082
|
)
|
|
1016
|
-
await interaction.edit_original_response(content=
|
|
1083
|
+
await interaction.edit_original_response(content=None, embed=embed)
|
|
1017
1084
|
except discord.HTTPException as e:
|
|
1018
1085
|
if e.status == 429:
|
|
1019
1086
|
_on_rate_limit_hit()
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1087
|
+
embed = self._build_cleanup_embed(
|
|
1088
|
+
channel_name=channel.name,
|
|
1089
|
+
state="error",
|
|
1090
|
+
deleted_count=deleted_count,
|
|
1091
|
+
rate_limit_retries=rate_limit_retries,
|
|
1092
|
+
elapsed_seconds=asyncio.get_running_loop().time() - started_monotonic,
|
|
1093
|
+
last_deleted_preview=last_deleted_message_preview,
|
|
1094
|
+
scanned_count=scanned_count,
|
|
1095
|
+
error_text=f"Discord API error (status {e.status}): {e}",
|
|
1096
|
+
)
|
|
1097
|
+
logging.exception(msg=f"Discord API error while clearing messages in channel '{channel.name}': {e}")
|
|
1098
|
+
await interaction.edit_original_response(content=None, embed=embed)
|
|
1023
1099
|
except Exception as e:
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1100
|
+
embed = self._build_cleanup_embed(
|
|
1101
|
+
channel_name=channel.name,
|
|
1102
|
+
state="error",
|
|
1103
|
+
deleted_count=deleted_count,
|
|
1104
|
+
rate_limit_retries=rate_limit_retries,
|
|
1105
|
+
elapsed_seconds=asyncio.get_running_loop().time() - started_monotonic,
|
|
1106
|
+
last_deleted_preview=last_deleted_message_preview,
|
|
1107
|
+
scanned_count=scanned_count,
|
|
1108
|
+
error_text=str(e),
|
|
1109
|
+
)
|
|
1110
|
+
logging.exception(msg=f"Internal error while clearing messages in channel '{channel.name}': {e}")
|
|
1111
|
+
await interaction.edit_original_response(content=None, embed=embed)
|
|
1027
1112
|
finally:
|
|
1028
1113
|
heartbeat_stop_event.set()
|
|
1029
1114
|
if heartbeat_task is not None and not heartbeat_task.done():
|
|
@@ -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.5",
|
|
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
|
|
File without changes
|
{discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/discordbotlinuxmonitor/__init__.py
RENAMED
|
File without changes
|
{discordbotlinuxmonitor-1.6.4 → discordbotlinuxmonitor-1.6.5}/discordbotlinuxmonitor/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|