DiscordBotLinuxMonitor 1.6.5__tar.gz → 1.6.7__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.5 → discordbotlinuxmonitor-1.6.7}/DiscordBotLinuxMonitor.egg-info/PKG-INFO +4 -2
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/DiscordBotLinuxMonitor.egg-info/requires.txt +1 -1
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/PKG-INFO +4 -2
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/README.md +2 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/discordbotlinuxmonitor/__main__.py +6 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/discordbotlinuxmonitor/discordbotlinuxmonitor.py +184 -115
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/setup.py +2 -2
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/DiscordBotLinuxMonitor.egg-info/SOURCES.txt +0 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/DiscordBotLinuxMonitor.egg-info/dependency_links.txt +0 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/DiscordBotLinuxMonitor.egg-info/top_level.txt +0 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/LICENSE.md +0 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/discordbotlinuxmonitor/__init__.py +0 -0
- {discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/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.7
|
|
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
|
|
@@ -22,7 +22,7 @@ Classifier: Environment :: Console
|
|
|
22
22
|
Requires-Python: >=3.3
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE.md
|
|
25
|
-
Requires-Dist: linuxmonitor~=1.5.
|
|
25
|
+
Requires-Dist: linuxmonitor~=1.5.9
|
|
26
26
|
Requires-Dist: discord.py
|
|
27
27
|
Requires-Dist: typing
|
|
28
28
|
Requires-Dist: asyncio
|
|
@@ -125,6 +125,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
125
125
|
|
|
126
126
|
- `/clear_channel_messages {channel}`: 🧹 Remove all messages from a target channel (can clean any channel, command itself is private-only) 🧹
|
|
127
127
|
|
|
128
|
+
- `/list_clearable_channels`: 🧹 List text channels and whether the bot can clear them (View + Read History + Manage Messages), private-only 🧹
|
|
129
|
+
|
|
128
130
|
## How to install (for first launch)
|
|
129
131
|
|
|
130
132
|
- Install package calling `python -m pip install discordbotlinuxmonitor` (or `python setup.py install` from the root of this repository)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DiscordBotLinuxMonitor
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.7
|
|
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
|
|
@@ -22,7 +22,7 @@ Classifier: Environment :: Console
|
|
|
22
22
|
Requires-Python: >=3.3
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE.md
|
|
25
|
-
Requires-Dist: linuxmonitor~=1.5.
|
|
25
|
+
Requires-Dist: linuxmonitor~=1.5.9
|
|
26
26
|
Requires-Dist: discord.py
|
|
27
27
|
Requires-Dist: typing
|
|
28
28
|
Requires-Dist: asyncio
|
|
@@ -125,6 +125,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
125
125
|
|
|
126
126
|
- `/clear_channel_messages {channel}`: 🧹 Remove all messages from a target channel (can clean any channel, command itself is private-only) 🧹
|
|
127
127
|
|
|
128
|
+
- `/list_clearable_channels`: 🧹 List text channels and whether the bot can clear them (View + Read History + Manage Messages), private-only 🧹
|
|
129
|
+
|
|
128
130
|
## How to install (for first launch)
|
|
129
131
|
|
|
130
132
|
- Install package calling `python -m pip install discordbotlinuxmonitor` (or `python setup.py install` from the root of this repository)
|
|
@@ -83,6 +83,8 @@ Displayed infos are not the same if you do the command in private or public chan
|
|
|
83
83
|
|
|
84
84
|
- `/clear_channel_messages {channel}`: 🧹 Remove all messages from a target channel (can clean any channel, command itself is private-only) 🧹
|
|
85
85
|
|
|
86
|
+
- `/list_clearable_channels`: 🧹 List text channels and whether the bot can clear them (View + Read History + Manage Messages), private-only 🧹
|
|
87
|
+
|
|
86
88
|
## How to install (for first launch)
|
|
87
89
|
|
|
88
90
|
- Install package calling `python -m pip install discordbotlinuxmonitor` (or `python setup.py install` from the root of this repository)
|
{discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/discordbotlinuxmonitor/__main__.py
RENAMED
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
7
|
import discord
|
|
8
|
+
from discord import app_commands
|
|
8
9
|
|
|
9
10
|
def main() -> None:
|
|
10
11
|
parser = argparse.ArgumentParser(description='System Management Tool controled from Discord')
|
|
@@ -130,11 +131,16 @@ def main() -> None:
|
|
|
130
131
|
async def clear_channel_messages(interaction: discord.Interaction, channel: discord.TextChannel) -> None: # type: ignore
|
|
131
132
|
await discord_bot_linux_monitor.clear_channel_messages(interaction, channel)
|
|
132
133
|
|
|
134
|
+
@discord_bot.tree.command(name="list_clearable_channels", description="[Private] 🧹 List text channels and bot clear permissions 🧹")
|
|
135
|
+
async def list_clearable_channels(interaction: discord.Interaction) -> None: # type: ignore
|
|
136
|
+
await discord_bot_linux_monitor.list_clearable_channels(interaction)
|
|
137
|
+
|
|
133
138
|
@discord_bot.tree.command(name="list_commands", description="📋 List all available commands 📋")
|
|
134
139
|
async def list_commands(interaction: discord.Interaction) -> None: # type: ignore
|
|
135
140
|
await discord_bot_linux_monitor.list_commands(interaction)
|
|
136
141
|
|
|
137
142
|
@discord_bot.tree.command(name="execute_command", description="🚀 Execute a command (optional parameters) 🚀")
|
|
143
|
+
@app_commands.autocomplete(command_name=discord_bot_linux_monitor.autocomplete_command_name)
|
|
138
144
|
async def execute_command(interaction: discord.Interaction, command_name: str, parameters: str = "") -> None: # type: ignore
|
|
139
145
|
await discord_bot_linux_monitor.execute_command(interaction, command_name=command_name, parameters=parameters)
|
|
140
146
|
|
|
@@ -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.7 (2026/08/24)"
|
|
37
37
|
__status__ = "Usable for any Linux project"
|
|
38
38
|
|
|
39
39
|
# pyright: reportMissingTypeStubs=false
|
|
@@ -41,6 +41,7 @@ from linuxmonitor import LinuxMonitor
|
|
|
41
41
|
|
|
42
42
|
import discord
|
|
43
43
|
from discord.app_commands.models import AppCommand
|
|
44
|
+
from discord import app_commands
|
|
44
45
|
from discord.ext import commands
|
|
45
46
|
import json
|
|
46
47
|
from typing import List, Union, Awaitable, Callable, Any, Dict, Optional
|
|
@@ -71,7 +72,6 @@ class DiscordBotLinuxMonitor:
|
|
|
71
72
|
|
|
72
73
|
# Initialize the bot
|
|
73
74
|
self.force_sync_on_startup: bool = force_sync_on_startup
|
|
74
|
-
self.MAX_LENGTH_OF_DISCORD_MESSAGE = 2000 # Forced by Discord API
|
|
75
75
|
intents: discord.Intents = discord.Intents.default()
|
|
76
76
|
self.bot = commands.Bot(command_prefix=self.command_prefix, intents=intents)
|
|
77
77
|
|
|
@@ -208,68 +208,6 @@ class DiscordBotLinuxMonitor:
|
|
|
208
208
|
res: bool = (self.channel_name_for_public_commands == channel.name) # type: ignore
|
|
209
209
|
return res # type: ignore
|
|
210
210
|
|
|
211
|
-
async def _channel_send_no_limit(self, channel: discord.TextChannel, msg: str) -> None:
|
|
212
|
-
logging.info(msg=f"Sending message to channel '{channel.name}':\n{msg}")
|
|
213
|
-
|
|
214
|
-
while len(msg) > self.MAX_LENGTH_OF_DISCORD_MESSAGE:
|
|
215
|
-
# Find the last newline within the limit
|
|
216
|
-
split_point = msg.rfind('\n', 0, self.MAX_LENGTH_OF_DISCORD_MESSAGE)
|
|
217
|
-
if split_point == -1: # No newline found, split at max_length
|
|
218
|
-
split_point = self.MAX_LENGTH_OF_DISCORD_MESSAGE
|
|
219
|
-
|
|
220
|
-
# Send the chunk and remove it from the message
|
|
221
|
-
await channel.send(content=msg[:split_point].rstrip())
|
|
222
|
-
msg = msg[split_point:].lstrip()
|
|
223
|
-
|
|
224
|
-
# Send the remaining message
|
|
225
|
-
if msg:
|
|
226
|
-
await channel.send(content=msg)
|
|
227
|
-
|
|
228
|
-
async def _interaction_followup_send_no_limit(self, interaction: discord.Interaction, msg: str) -> None:
|
|
229
|
-
logging.info(msg=f"Sending follow-up message:\n{msg}")
|
|
230
|
-
|
|
231
|
-
# Send the first chunk as a follow-up message
|
|
232
|
-
try:
|
|
233
|
-
if msg == "":
|
|
234
|
-
# Send generic interaction response if no message is returned
|
|
235
|
-
msg = "No answer."
|
|
236
|
-
|
|
237
|
-
if len(msg) > self.MAX_LENGTH_OF_DISCORD_MESSAGE:
|
|
238
|
-
# Find the last newline within the limit or just split at max_length
|
|
239
|
-
split_point = msg.rfind('\n', 0, self.MAX_LENGTH_OF_DISCORD_MESSAGE)
|
|
240
|
-
if split_point == -1: # No newline found, split at max_length
|
|
241
|
-
split_point = self.MAX_LENGTH_OF_DISCORD_MESSAGE
|
|
242
|
-
|
|
243
|
-
await interaction.followup.send(content=msg[:split_point].rstrip())
|
|
244
|
-
msg = msg[split_point:].lstrip()
|
|
245
|
-
else:
|
|
246
|
-
await interaction.followup.send(content=msg)
|
|
247
|
-
return # Exit if the message fits within the limit
|
|
248
|
-
|
|
249
|
-
except Exception as e:
|
|
250
|
-
logging.error(msg=f"Error while sending follow-up message: {e}")
|
|
251
|
-
return
|
|
252
|
-
|
|
253
|
-
# Handle additional messages that exceed the initial follow-up limit
|
|
254
|
-
while len(msg) > self.MAX_LENGTH_OF_DISCORD_MESSAGE:
|
|
255
|
-
split_point = msg.rfind('\n', 0, self.MAX_LENGTH_OF_DISCORD_MESSAGE)
|
|
256
|
-
if split_point == -1:
|
|
257
|
-
split_point = self.MAX_LENGTH_OF_DISCORD_MESSAGE
|
|
258
|
-
|
|
259
|
-
try:
|
|
260
|
-
await interaction.channel.send(content=msg[:split_point].rstrip()) # type: ignore
|
|
261
|
-
except Exception as e:
|
|
262
|
-
logging.error(msg=f"Error while sending message to channel: {e}")
|
|
263
|
-
|
|
264
|
-
msg = msg[split_point:].lstrip()
|
|
265
|
-
|
|
266
|
-
# Send any remaining message directly to the channel
|
|
267
|
-
if msg:
|
|
268
|
-
try:
|
|
269
|
-
await interaction.channel.send(content=msg) # type: ignore
|
|
270
|
-
except Exception as e:
|
|
271
|
-
logging.error(msg=f"Error while sending message to channel: {e}")
|
|
272
|
-
|
|
273
211
|
async def _force_sync(self) -> str:
|
|
274
212
|
try:
|
|
275
213
|
logging.info(msg="Forcing the sync of the bot's commands...")
|
|
@@ -390,6 +328,69 @@ class DiscordBotLinuxMonitor:
|
|
|
390
328
|
embed.set_footer(text=f"Bot v{__version__} • Last update: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
391
329
|
return embed
|
|
392
330
|
|
|
331
|
+
def _infer_embed_color(self, text: str, is_error: bool = False) -> "discord.Color":
|
|
332
|
+
# Color-code the embed depending on the status hints found in the text.
|
|
333
|
+
if is_error or "❌" in text or "🔴" in text:
|
|
334
|
+
return discord.Color.red()
|
|
335
|
+
if "⚠️" in text or "🟠" in text or "🟡" in text:
|
|
336
|
+
return discord.Color.orange()
|
|
337
|
+
return discord.Color.green()
|
|
338
|
+
|
|
339
|
+
def _build_result_embed(self, title: str, description: str, color: "discord.Color") -> "discord.Embed":
|
|
340
|
+
embed = discord.Embed(title=title[:256], color=color)
|
|
341
|
+
embed.description = (description if description.strip() != "" else "No answer.")[:4096]
|
|
342
|
+
embed.set_footer(text=f"Bot v{__version__} • {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
343
|
+
return embed
|
|
344
|
+
|
|
345
|
+
def _split_text_for_embed(self, text: str, max_length: int = 4000) -> List[str]:
|
|
346
|
+
chunks: List[str] = []
|
|
347
|
+
remaining: str = text
|
|
348
|
+
while len(remaining) > max_length:
|
|
349
|
+
split_point = remaining.rfind('\n', 0, max_length)
|
|
350
|
+
if split_point == -1:
|
|
351
|
+
split_point = max_length
|
|
352
|
+
chunks.append(remaining[:split_point].rstrip())
|
|
353
|
+
remaining = remaining[split_point:].lstrip()
|
|
354
|
+
if remaining:
|
|
355
|
+
chunks.append(remaining)
|
|
356
|
+
return chunks
|
|
357
|
+
|
|
358
|
+
async def _interaction_followup_send_embed(self, interaction: discord.Interaction, title: str, msg: str, icon: str = "", is_error: bool = False, ephemeral: bool = False) -> None:
|
|
359
|
+
if msg is None or msg.strip() == "":
|
|
360
|
+
msg = "No answer."
|
|
361
|
+
|
|
362
|
+
logging.info(msg=f"Sending embed follow-up '{title}':\n{msg}")
|
|
363
|
+
|
|
364
|
+
color: "discord.Color" = self._infer_embed_color(text=msg, is_error=is_error)
|
|
365
|
+
display_title: str = f"{icon} {title}".strip()
|
|
366
|
+
chunks: List[str] = self._split_text_for_embed(text=msg)
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
for index, chunk in enumerate(chunks):
|
|
370
|
+
page_title: str = display_title if len(chunks) == 1 else f"{display_title} ({index + 1}/{len(chunks)})"
|
|
371
|
+
embed: "discord.Embed" = self._build_result_embed(title=page_title, description=chunk, color=color)
|
|
372
|
+
await interaction.followup.send(embed=embed, ephemeral=ephemeral)
|
|
373
|
+
except Exception as e:
|
|
374
|
+
logging.error(msg=f"Error while sending embed follow-up message: {e}")
|
|
375
|
+
|
|
376
|
+
async def _channel_send_embed(self, channel: discord.TextChannel, title: str, msg: str, icon: str = "", is_error: bool = False, color: Optional["discord.Color"] = None) -> None:
|
|
377
|
+
if msg is None or msg.strip() == "":
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
logging.info(msg=f"Sending embed to channel '{channel.name}' ('{title}'):\n{msg}")
|
|
381
|
+
|
|
382
|
+
embed_color: "discord.Color" = color if color is not None else self._infer_embed_color(text=msg, is_error=is_error)
|
|
383
|
+
display_title: str = f"{icon} {title}".strip()
|
|
384
|
+
chunks: List[str] = self._split_text_for_embed(text=msg)
|
|
385
|
+
|
|
386
|
+
for index, chunk in enumerate(chunks):
|
|
387
|
+
page_title: str = display_title if len(chunks) == 1 else f"{display_title} ({index + 1}/{len(chunks)})"
|
|
388
|
+
embed: "discord.Embed" = self._build_result_embed(title=page_title, description=chunk, color=embed_color)
|
|
389
|
+
try:
|
|
390
|
+
await channel.send(embed=embed)
|
|
391
|
+
except Exception as e:
|
|
392
|
+
logging.error(msg=f"Error while sending embed message to channel: {e}")
|
|
393
|
+
|
|
393
394
|
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:
|
|
394
395
|
for attempt in range(max_retries + 1):
|
|
395
396
|
try:
|
|
@@ -462,24 +463,24 @@ class DiscordBotLinuxMonitor:
|
|
|
462
463
|
logging.info(msg="Found the public channel, it will be possible to do public commands.")
|
|
463
464
|
public_channel_cmd_ready = True
|
|
464
465
|
if self.welcome_message_for_public_commands != "":
|
|
465
|
-
await self.
|
|
466
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_public_commands), color=discord.Color.blurple())
|
|
466
467
|
|
|
467
468
|
if self.channel_name_for_private_commands != "" and channel.name == self.channel_name_for_private_commands:
|
|
468
469
|
logging.info(msg="Found the private channel, it will be possible to do private commands.")
|
|
469
470
|
private_channel_cmd_ready = True
|
|
470
471
|
if self.welcome_message_for_private_commands != "":
|
|
471
|
-
await self.
|
|
472
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_private_commands), color=discord.Color.blurple())
|
|
472
473
|
|
|
473
474
|
if self.channel_name_for_public_error_tasks != "" and channel.name == self.channel_name_for_public_error_tasks:
|
|
474
475
|
logging.info(msg="Found the public channel for error task, it will be possible to show public status issues if found periodically.")
|
|
475
476
|
public_channel_error_task_ready = True
|
|
476
477
|
|
|
477
478
|
if self.welcome_message_for_public_error_tasks != "":
|
|
478
|
-
await self.
|
|
479
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_public_error_tasks), color=discord.Color.blurple())
|
|
479
480
|
|
|
480
481
|
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}').")
|
|
481
482
|
public_channel_for_error_task: discord.TextChannel = channel
|
|
482
|
-
send_message_public_error_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self.
|
|
483
|
+
send_message_public_error_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self._channel_send_embed(channel=public_channel_for_error_task, title="Monitoring Alert", icon="🚨", msg=msg, is_error=True))
|
|
483
484
|
|
|
484
485
|
# Start the public schedule task
|
|
485
486
|
self.bot.loop.create_task(self.monitoring.schedule_task(handle_error_message=send_message_public_error_task_func, is_private=False))
|
|
@@ -489,11 +490,11 @@ class DiscordBotLinuxMonitor:
|
|
|
489
490
|
public_channel_info_task_ready = True
|
|
490
491
|
|
|
491
492
|
if self.welcome_message_for_public_infos_tasks != "":
|
|
492
|
-
await self.
|
|
493
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_public_infos_tasks), color=discord.Color.blurple())
|
|
493
494
|
|
|
494
495
|
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}').")
|
|
495
496
|
public_channel_for_info_task: discord.TextChannel = channel
|
|
496
|
-
send_message_public_info_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self.
|
|
497
|
+
send_message_public_info_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self._channel_send_embed(channel=public_channel_for_info_task, title="Periodic Status", icon="📊", msg=msg))
|
|
497
498
|
|
|
498
499
|
# Start the public schedule task
|
|
499
500
|
self.bot.loop.create_task(self.monitoring.schedule_task_show_info(show_message=send_message_public_info_task_func, is_private=False))
|
|
@@ -503,11 +504,11 @@ class DiscordBotLinuxMonitor:
|
|
|
503
504
|
private_channel_error_task_ready = True
|
|
504
505
|
|
|
505
506
|
if self.welcome_message_for_private_error_tasks != "":
|
|
506
|
-
await self.
|
|
507
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_private_error_tasks), color=discord.Color.blurple())
|
|
507
508
|
|
|
508
509
|
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}').")
|
|
509
510
|
private_channel_for_error_task: discord.TextChannel = channel
|
|
510
|
-
send_message_private_error_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self.
|
|
511
|
+
send_message_private_error_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self._channel_send_embed(channel=private_channel_for_error_task, title="Monitoring Alert", icon="🚨", msg=msg, is_error=True))
|
|
511
512
|
|
|
512
513
|
# Start the private schedule task
|
|
513
514
|
self.bot.loop.create_task(self.monitoring.schedule_task(handle_error_message=send_message_private_error_task_func, is_private=True))
|
|
@@ -517,11 +518,11 @@ class DiscordBotLinuxMonitor:
|
|
|
517
518
|
private_channel_info_task_ready = True
|
|
518
519
|
|
|
519
520
|
if self.welcome_message_for_private_infos_tasks != "":
|
|
520
|
-
await self.
|
|
521
|
+
await self._channel_send_embed(channel=channel, title="Welcome", icon="👋", msg=self._welcome_message_with_version(self.welcome_message_for_private_infos_tasks), color=discord.Color.blurple())
|
|
521
522
|
|
|
522
523
|
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}').")
|
|
523
524
|
private_channel_for_info_task: discord.TextChannel = channel
|
|
524
|
-
send_message_private_info_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self.
|
|
525
|
+
send_message_private_info_task_func: Callable[[str], Awaitable[None]] = lambda msg: asyncio.create_task(self._channel_send_embed(channel=private_channel_for_info_task, title="Periodic Status", icon="📊", msg=msg))
|
|
525
526
|
|
|
526
527
|
# Start the private schedule task
|
|
527
528
|
self.bot.loop.create_task(self.monitoring.schedule_task_show_info(show_message=send_message_private_info_task_func, is_private=True))
|
|
@@ -576,7 +577,7 @@ class DiscordBotLinuxMonitor:
|
|
|
576
577
|
out_msg: str = await self._force_sync()
|
|
577
578
|
|
|
578
579
|
# Respond to the user
|
|
579
|
-
await self.
|
|
580
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Command Synchronization", icon="🔄", msg=out_msg)
|
|
580
581
|
|
|
581
582
|
async def version(self, interaction: discord.Interaction) -> None:
|
|
582
583
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -586,9 +587,11 @@ class DiscordBotLinuxMonitor:
|
|
|
586
587
|
|
|
587
588
|
out_msg: str = (
|
|
588
589
|
f"🤖 Bot version: {__version__}\n"
|
|
589
|
-
f"
|
|
590
|
+
f"� Linux Monitor library version: {self.monitoring.get_raw_version()}\n"
|
|
591
|
+
f"�🐍 Python compatibility: {__python_version__}"
|
|
590
592
|
)
|
|
591
|
-
|
|
593
|
+
embed = self._build_result_embed(title="🤖 Bot Version", description=out_msg, color=discord.Color.blurple())
|
|
594
|
+
await interaction.response.send_message(embed=embed, ephemeral=True)
|
|
592
595
|
|
|
593
596
|
async def usage(self, interaction: discord.Interaction) -> None:
|
|
594
597
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -619,11 +622,11 @@ class DiscordBotLinuxMonitor:
|
|
|
619
622
|
out_msg += self.monitoring.get_network_info()
|
|
620
623
|
|
|
621
624
|
# Respond to the user
|
|
622
|
-
await self.
|
|
625
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="System Usage", icon="📊", msg=out_msg)
|
|
623
626
|
except Exception as e:
|
|
624
627
|
out_msg = f"**Internal error retrieving usage info**:\n```sh\n{e}\n```"
|
|
625
628
|
logging.exception(msg=out_msg)
|
|
626
|
-
await self.
|
|
629
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="System Usage", icon="📊", msg=out_msg, is_error=True)
|
|
627
630
|
|
|
628
631
|
async def os_infos(self, interaction: discord.Interaction) -> None:
|
|
629
632
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -642,11 +645,11 @@ class DiscordBotLinuxMonitor:
|
|
|
642
645
|
out_msg += self.monitoring.get_server_datetime()
|
|
643
646
|
|
|
644
647
|
# Respond to the user
|
|
645
|
-
await self.
|
|
648
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="OS Information", icon="🖥️", msg=out_msg)
|
|
646
649
|
except Exception as e:
|
|
647
650
|
out_msg = f"**Internal error retrieving OS info**:\n```sh\n{e}\n```"
|
|
648
651
|
logging.exception(msg=out_msg)
|
|
649
|
-
await self.
|
|
652
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="OS Information", icon="🖥️", msg=out_msg, is_error=True)
|
|
650
653
|
|
|
651
654
|
async def users(self, interaction: discord.Interaction) -> None:
|
|
652
655
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -664,11 +667,11 @@ class DiscordBotLinuxMonitor:
|
|
|
664
667
|
out_msg: str = self.monitoring.get_connected_users()
|
|
665
668
|
|
|
666
669
|
# Respond to the user
|
|
667
|
-
await self.
|
|
670
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Connected Users", icon="👥", msg=out_msg)
|
|
668
671
|
except Exception as e:
|
|
669
672
|
out_msg = f"**Internal error retrieving connected users**:\n```sh\n{e}\n```"
|
|
670
673
|
logging.exception(msg=out_msg)
|
|
671
|
-
await self.
|
|
674
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Connected Users", icon="👥", msg=out_msg, is_error=True)
|
|
672
675
|
|
|
673
676
|
async def user_logins(self, interaction: discord.Interaction) -> None:
|
|
674
677
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -688,12 +691,12 @@ class DiscordBotLinuxMonitor:
|
|
|
688
691
|
out_msg: str = self.monitoring.check_all_recent_user_logins(display_only_if_critical=False)
|
|
689
692
|
|
|
690
693
|
# Répondre à l'utilisateur
|
|
691
|
-
await self.
|
|
694
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Recent User Logins", icon="🔐", msg=out_msg)
|
|
692
695
|
|
|
693
696
|
except Exception as e:
|
|
694
697
|
out_msg = f"**Internal error retrieving last user connections**:\n```sh\n{e}\n```"
|
|
695
698
|
logging.exception(msg=out_msg)
|
|
696
|
-
await self.
|
|
699
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Recent User Logins", icon="🔐", msg=out_msg, is_error=True)
|
|
697
700
|
|
|
698
701
|
async def ping(self, interaction: discord.Interaction) -> None:
|
|
699
702
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -709,11 +712,11 @@ class DiscordBotLinuxMonitor:
|
|
|
709
712
|
out_msg: str = await self.monitoring.ping_all_websites(is_private=is_private, display_only_if_critical=False)
|
|
710
713
|
|
|
711
714
|
# Respond to the user
|
|
712
|
-
await self.
|
|
715
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Websites Ping", icon="📡", msg=out_msg)
|
|
713
716
|
except Exception as e:
|
|
714
717
|
out_msg = f"**Internal error during websites ping **:\n```sh\n{e}\n```"
|
|
715
718
|
logging.exception(msg=out_msg)
|
|
716
|
-
await self.
|
|
719
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Websites Ping", icon="📡", msg=out_msg, is_error=True)
|
|
717
720
|
|
|
718
721
|
async def websites(self, interaction: discord.Interaction) -> None:
|
|
719
722
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -729,11 +732,11 @@ class DiscordBotLinuxMonitor:
|
|
|
729
732
|
out_msg: str = await self.monitoring.check_all_websites(is_private=is_private, display_only_if_critical=False)
|
|
730
733
|
|
|
731
734
|
# Respond to the user
|
|
732
|
-
await self.
|
|
735
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Websites Access", icon="🌐", msg=out_msg)
|
|
733
736
|
except Exception as e:
|
|
734
737
|
out_msg = f"**Internal error during websites access check **:\n```sh\n{e}\n```"
|
|
735
738
|
logging.exception(msg=out_msg)
|
|
736
|
-
await self.
|
|
739
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Websites Access", icon="🌐", msg=out_msg, is_error=True)
|
|
737
740
|
|
|
738
741
|
async def certificates(self, interaction: discord.Interaction) -> None:
|
|
739
742
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -749,11 +752,11 @@ class DiscordBotLinuxMonitor:
|
|
|
749
752
|
out_msg: str = self.monitoring.check_all_certificates(is_private=is_private, display_only_if_critical=False)
|
|
750
753
|
|
|
751
754
|
# Respond to the user
|
|
752
|
-
await self.
|
|
755
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="SSL Certificates", icon="🔒", msg=out_msg)
|
|
753
756
|
except Exception as e:
|
|
754
757
|
out_msg = f"**Internal error during SSL certificate checks **:\n```sh\n{e}\n```"
|
|
755
758
|
logging.exception(msg=out_msg)
|
|
756
|
-
await self.
|
|
759
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="SSL Certificates", icon="🔒", msg=out_msg, is_error=True)
|
|
757
760
|
|
|
758
761
|
async def reboot(self, interaction: discord.Interaction) -> None:
|
|
759
762
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -769,12 +772,12 @@ class DiscordBotLinuxMonitor:
|
|
|
769
772
|
|
|
770
773
|
try:
|
|
771
774
|
out_msg: str = await self.monitoring.reboot_server()
|
|
772
|
-
await self.
|
|
775
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Server Reboot", icon="🔁", msg=out_msg)
|
|
773
776
|
|
|
774
777
|
except Exception as e:
|
|
775
778
|
out_msg = f"**Internal error during server reboot**:\n```sh\n{e}\n```"
|
|
776
779
|
logging.exception(msg=out_msg)
|
|
777
|
-
await self.
|
|
780
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Server Reboot", icon="🔁", msg=out_msg, is_error=True)
|
|
778
781
|
|
|
779
782
|
async def services_status(self, interaction: discord.Interaction) -> None:
|
|
780
783
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -790,11 +793,11 @@ class DiscordBotLinuxMonitor:
|
|
|
790
793
|
out_msg: str = await self.monitoring.check_all_services_status(is_private=is_private)
|
|
791
794
|
|
|
792
795
|
# Respond to the user
|
|
793
|
-
await self.
|
|
796
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Services Status", icon="⚙️", msg=out_msg)
|
|
794
797
|
except Exception as e:
|
|
795
798
|
out_msg = f"**Internal error checking services are running**:\n```sh\n{e}\n```"
|
|
796
799
|
logging.exception(msg=out_msg)
|
|
797
|
-
await self.
|
|
800
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Services Status", icon="⚙️", msg=out_msg, is_error=True)
|
|
798
801
|
|
|
799
802
|
async def restart_all(self, interaction: discord.Interaction) -> None:
|
|
800
803
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -811,11 +814,11 @@ class DiscordBotLinuxMonitor:
|
|
|
811
814
|
out_msg: str = await self.monitoring.restart_all_services(is_private=is_private)
|
|
812
815
|
|
|
813
816
|
# Respond to the user
|
|
814
|
-
await self.
|
|
817
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Restart All Services", icon="🔄", msg=out_msg)
|
|
815
818
|
except Exception as e:
|
|
816
819
|
out_msg = f"**Internal error restarting all services**:\n```sh\n{e}\n```"
|
|
817
820
|
logging.exception(msg=out_msg)
|
|
818
|
-
await self.
|
|
821
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Restart All Services", icon="🔄", msg=out_msg, is_error=True)
|
|
819
822
|
|
|
820
823
|
async def restart_service(self, interaction: discord.Interaction, service_name: str) -> None:
|
|
821
824
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -832,12 +835,12 @@ class DiscordBotLinuxMonitor:
|
|
|
832
835
|
out_msg: str = await self.monitoring.restart_service(is_private=is_private, service_name=service_name, force_restart=True)
|
|
833
836
|
|
|
834
837
|
# Répondre à l'utilisateur
|
|
835
|
-
await self.
|
|
838
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Restart Service", icon="🔄", msg=out_msg)
|
|
836
839
|
|
|
837
840
|
except Exception as e:
|
|
838
841
|
out_msg = f"**Internal error restarting service {service_name}**:\n```sh\n{e}\n```"
|
|
839
842
|
logging.exception(msg=out_msg)
|
|
840
|
-
await self.
|
|
843
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Restart Service", icon="🔄", msg=out_msg, is_error=True)
|
|
841
844
|
|
|
842
845
|
async def stop_service(self, interaction: discord.Interaction, service_name: str) -> None:
|
|
843
846
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -854,11 +857,11 @@ class DiscordBotLinuxMonitor:
|
|
|
854
857
|
out_msg: str = await self.monitoring.stop_service(is_private=is_private, service_name=service_name)
|
|
855
858
|
|
|
856
859
|
# Répondre à l'utilisateur
|
|
857
|
-
await self.
|
|
860
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Stop Service", icon="🛑", msg=out_msg)
|
|
858
861
|
except Exception as e:
|
|
859
862
|
out_msg = f"**Internal error stopping service {service_name}**:\n```sh\n{e}\n```"
|
|
860
863
|
logging.exception(msg=out_msg)
|
|
861
|
-
await self.
|
|
864
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Stop Service", icon="🛑", msg=out_msg, is_error=True)
|
|
862
865
|
|
|
863
866
|
async def list_services(self, interaction: discord.Interaction) -> None:
|
|
864
867
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -875,11 +878,11 @@ class DiscordBotLinuxMonitor:
|
|
|
875
878
|
out_msg: str = self.monitoring.get_all_services(is_private=is_private)
|
|
876
879
|
|
|
877
880
|
# Répondre à l'utilisateur
|
|
878
|
-
await self.
|
|
881
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Available Services", icon="📋", msg=out_msg)
|
|
879
882
|
except Exception as e:
|
|
880
883
|
out_msg = f"**Internal error retrieving available services**:\n```sh\n{e}\n```"
|
|
881
884
|
logging.exception(msg=out_msg)
|
|
882
|
-
await self.
|
|
885
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Available Services", icon="📋", msg=out_msg, is_error=True)
|
|
883
886
|
|
|
884
887
|
async def ports(self, interaction: discord.Interaction) -> None:
|
|
885
888
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -896,11 +899,11 @@ class DiscordBotLinuxMonitor:
|
|
|
896
899
|
out_msg: str = await self.monitoring.check_all_ports(is_private=is_private, display_only_if_critical=False)
|
|
897
900
|
|
|
898
901
|
# Répondre à l'utilisateur
|
|
899
|
-
await self.
|
|
902
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Ports Status", icon="🔌", msg=out_msg)
|
|
900
903
|
except Exception as e:
|
|
901
904
|
out_msg = f"**Internal error checking ports**:\n```sh\n{e}\n```"
|
|
902
905
|
logging.exception(msg=out_msg)
|
|
903
|
-
await self.
|
|
906
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Ports Status", icon="🔌", msg=out_msg, is_error=True)
|
|
904
907
|
|
|
905
908
|
async def list_processes(self, interaction: discord.Interaction, order_by_ram: bool) -> None:
|
|
906
909
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -919,11 +922,11 @@ class DiscordBotLinuxMonitor:
|
|
|
919
922
|
out_msg: str = await self.monitoring.get_ordered_processes(get_non_consuming_processes=False, order_by_ram=order_by_ram, max_processes=20)
|
|
920
923
|
|
|
921
924
|
# Répondre à l'utilisateur
|
|
922
|
-
await self.
|
|
925
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Processes List", icon="🧮", msg=out_msg)
|
|
923
926
|
except Exception as e:
|
|
924
927
|
out_msg = f"**Internal error retrieving active processes**:\n```sh\n{e}\n```"
|
|
925
928
|
logging.exception(msg=out_msg)
|
|
926
|
-
await self.
|
|
929
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Processes List", icon="🧮", msg=out_msg, is_error=True)
|
|
927
930
|
|
|
928
931
|
async def kill_process(self, interaction: discord.Interaction, pid: int) -> None:
|
|
929
932
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -942,11 +945,11 @@ class DiscordBotLinuxMonitor:
|
|
|
942
945
|
out_msg: str = await self.monitoring.kill_process(pid=pid)
|
|
943
946
|
|
|
944
947
|
# Répondre à l'utilisateur
|
|
945
|
-
await self.
|
|
948
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Kill Process", icon="☠️", msg=out_msg)
|
|
946
949
|
except Exception as e:
|
|
947
950
|
out_msg = f"**Internal error stopping process of PID {pid}**:\n```sh\n{e}\n```"
|
|
948
951
|
logging.exception(msg=out_msg)
|
|
949
|
-
await self.
|
|
952
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Kill Process", icon="☠️", msg=out_msg, is_error=True)
|
|
950
953
|
|
|
951
954
|
async def clear_channel_messages(self, interaction: discord.Interaction, channel: discord.TextChannel) -> None:
|
|
952
955
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -1118,6 +1121,55 @@ class DiscordBotLinuxMonitor:
|
|
|
1118
1121
|
except asyncio.CancelledError:
|
|
1119
1122
|
pass
|
|
1120
1123
|
|
|
1124
|
+
async def list_clearable_channels(self, interaction: discord.Interaction) -> None:
|
|
1125
|
+
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
1126
|
+
return
|
|
1127
|
+
if not (await self._is_bot_channel_interaction(interaction=interaction, send_message_if_not_bot=True)):
|
|
1128
|
+
return
|
|
1129
|
+
if not self._is_private_channel(channel=interaction.channel): # type: ignore
|
|
1130
|
+
await interaction.response.send_message(content="❌ Public channels do not allow this command.", ephemeral=True)
|
|
1131
|
+
return
|
|
1132
|
+
|
|
1133
|
+
await interaction.response.defer(ephemeral=True)
|
|
1134
|
+
|
|
1135
|
+
guild = interaction.guild
|
|
1136
|
+
bot_member = guild.me if guild is not None else None # type: ignore
|
|
1137
|
+
if guild is None or bot_member is None:
|
|
1138
|
+
await interaction.followup.send(content="❌ Unable to resolve the guild or bot member.", ephemeral=True)
|
|
1139
|
+
return
|
|
1140
|
+
|
|
1141
|
+
try:
|
|
1142
|
+
clearable_lines: List[str] = []
|
|
1143
|
+
blocked_lines: List[str] = []
|
|
1144
|
+
|
|
1145
|
+
for text_channel in guild.text_channels:
|
|
1146
|
+
perms = text_channel.permissions_for(bot_member)
|
|
1147
|
+
can_view: bool = perms.view_channel
|
|
1148
|
+
can_read_history: bool = perms.read_message_history
|
|
1149
|
+
can_manage: bool = perms.manage_messages
|
|
1150
|
+
is_clearable: bool = can_view and can_read_history and can_manage
|
|
1151
|
+
|
|
1152
|
+
line = (
|
|
1153
|
+
f"{'✅' if is_clearable else '❌'} #{text_channel.name} "
|
|
1154
|
+
f"(View: {'✔' if can_view else '✘'}, "
|
|
1155
|
+
f"History: {'✔' if can_read_history else '✘'}, "
|
|
1156
|
+
f"Manage: {'✔' if can_manage else '✘'})"
|
|
1157
|
+
)
|
|
1158
|
+
if is_clearable:
|
|
1159
|
+
clearable_lines.append(line)
|
|
1160
|
+
else:
|
|
1161
|
+
blocked_lines.append(line)
|
|
1162
|
+
|
|
1163
|
+
out_msg = f"🧹 **Clearable channels report** ({len(clearable_lines)} clearable / {len(guild.text_channels)} text channels)\n\n"
|
|
1164
|
+
out_msg += "**✅ Clearable:**\n" + ("\n".join(clearable_lines) if clearable_lines else "None") + "\n\n"
|
|
1165
|
+
out_msg += "**❌ Blocked (missing bot permission):**\n" + ("\n".join(blocked_lines) if blocked_lines else "None")
|
|
1166
|
+
|
|
1167
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Clearable Channels", icon="🧹", msg=out_msg, ephemeral=True)
|
|
1168
|
+
except Exception as e:
|
|
1169
|
+
out_msg = f"**Internal error listing clearable channels**:\n```sh\n{e}\n```"
|
|
1170
|
+
logging.exception(msg=out_msg)
|
|
1171
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Clearable Channels", icon="🧹", msg=out_msg, is_error=True, ephemeral=True)
|
|
1172
|
+
|
|
1121
1173
|
async def list_commands(self, interaction: discord.Interaction) -> None:
|
|
1122
1174
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
1123
1175
|
return
|
|
@@ -1133,12 +1185,29 @@ class DiscordBotLinuxMonitor:
|
|
|
1133
1185
|
out_msg: str = await self.monitoring.list_commands(is_private=is_private)
|
|
1134
1186
|
|
|
1135
1187
|
# Répondre à l'utilisateur
|
|
1136
|
-
await self.
|
|
1188
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Available Commands", icon="📖", msg=out_msg)
|
|
1137
1189
|
except Exception as e:
|
|
1138
1190
|
out_msg = f"**Internal error retrieving available commands**:\n```sh\n{e}\n```"
|
|
1139
1191
|
logging.exception(msg=out_msg)
|
|
1140
|
-
await self.
|
|
1192
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Available Commands", icon="📖", msg=out_msg, is_error=True)
|
|
1193
|
+
|
|
1141
1194
|
|
|
1195
|
+
async def autocomplete_command_name(self, interaction: discord.Interaction, current: str) -> List["app_commands.Choice[str]"]:
|
|
1196
|
+
# Suggest the configured command names so the user picks from a list instead of typing them.
|
|
1197
|
+
try:
|
|
1198
|
+
is_private: bool = self._is_private_channel(channel=interaction.channel) # type: ignore
|
|
1199
|
+
current_lower: str = current.lower()
|
|
1200
|
+
choices: List["app_commands.Choice[str]"] = []
|
|
1201
|
+
for command_name, display_name in self.monitoring.get_command_names(is_private=is_private):
|
|
1202
|
+
if current_lower == "" or current_lower in command_name.lower() or current_lower in display_name.lower():
|
|
1203
|
+
label: str = f"{command_name} — {display_name}"
|
|
1204
|
+
choices.append(app_commands.Choice(name=label[:100], value=command_name))
|
|
1205
|
+
if len(choices) >= 25: # Discord limits autocomplete to 25 choices
|
|
1206
|
+
break
|
|
1207
|
+
return choices
|
|
1208
|
+
except Exception as e:
|
|
1209
|
+
logging.error(msg=f"Error while building command autocomplete: {e}")
|
|
1210
|
+
return []
|
|
1142
1211
|
|
|
1143
1212
|
async def execute_command(self, interaction: discord.Interaction, command_name: str, parameters: str = "") -> None:
|
|
1144
1213
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -1155,11 +1224,11 @@ class DiscordBotLinuxMonitor:
|
|
|
1155
1224
|
out_msg: str = await self.monitoring.execute_command(is_private=is_private, command_name=command_name, parameters=parameters)
|
|
1156
1225
|
|
|
1157
1226
|
# Répondre à l'utilisateur
|
|
1158
|
-
await self.
|
|
1227
|
+
await self._interaction_followup_send_embed(interaction=interaction, title=f"Execute Command — {command_name}", icon="▶️", msg=out_msg)
|
|
1159
1228
|
except Exception as e:
|
|
1160
1229
|
out_msg = f"**Internal error executing command '{command_name}'**:\n```sh\n{e}\n```"
|
|
1161
1230
|
logging.exception(msg=out_msg)
|
|
1162
|
-
await self.
|
|
1231
|
+
await self._interaction_followup_send_embed(interaction=interaction, title=f"Execute Command — {command_name}", icon="▶️", msg=out_msg, is_error=True)
|
|
1163
1232
|
|
|
1164
1233
|
async def execute_all_commands(self, interaction: discord.Interaction) -> None:
|
|
1165
1234
|
if not self._check_if_valid_guild(guild=interaction.guild):
|
|
@@ -1176,10 +1245,10 @@ class DiscordBotLinuxMonitor:
|
|
|
1176
1245
|
out_msg: str = await self.monitoring.execute_all_commands(is_private=is_private)
|
|
1177
1246
|
|
|
1178
1247
|
# Répondre à l'utilisateur
|
|
1179
|
-
await self.
|
|
1248
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Execute All Commands", icon="⏩", msg=out_msg)
|
|
1180
1249
|
except Exception as e:
|
|
1181
1250
|
out_msg = f"**Internal error executing all commands**:\n```sh\n{e}\n```"
|
|
1182
1251
|
logging.exception(msg=out_msg)
|
|
1183
|
-
await self.
|
|
1252
|
+
await self._interaction_followup_send_embed(interaction=interaction, title="Execute All Commands", icon="⏩", msg=out_msg, is_error=True)
|
|
1184
1253
|
|
|
1185
1254
|
#endregion
|
|
@@ -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.7",
|
|
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",
|
|
@@ -36,7 +36,7 @@ setup(
|
|
|
36
36
|
keywords='discord bot warning info linux monitor monitoring server service port ping ssl certificate disk folder cpu ram swap usage ip connection',
|
|
37
37
|
platforms='Linux',
|
|
38
38
|
install_requires=[
|
|
39
|
-
"linuxmonitor~=1.5.
|
|
39
|
+
"linuxmonitor~=1.5.9", # follows the {MAJOR}.{MINOR}.x version range (because LinuxMonitor and DiscordBotLinuxMonitor are tightly coupled and should have same major and minor version)
|
|
40
40
|
"discord.py",
|
|
41
41
|
"typing",
|
|
42
42
|
"asyncio",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{discordbotlinuxmonitor-1.6.5 → discordbotlinuxmonitor-1.6.7}/discordbotlinuxmonitor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|