Skip to content

Commit 4c64b09

Browse files
ToothyDevCopilotPaillat-dev
authored
feat: ✨ Add info command (#30)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Paillat <me@paillat.dev>
1 parent 2dd914e commit 4c64b09

3 files changed

Lines changed: 67 additions & 1 deletion

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extend-ignore = [
3737
"COM812", # missing-trailing-comma conflicts with formatter
3838
"D10", # missing docstrings is overkill
3939
]
40-
pydocstyle.convention = "numpy"
40+
pydocstyle.convention = "pep257"
4141

4242
[tool.ruff.lint.per-file-ignores]
4343
"src/cogs/*" = [

src/cogs/slash_commands.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import asyncio
22
import io
3+
import time
34
from pathlib import Path
45

56
import discord
67
from discord import option, slash_command
78
from PIL import Image, UnidentifiedImageError
89

10+
from src.exceptions import UnexpectedNoneError
11+
912
MAX_IMAGE_FILESIZE = 50_000_000 # 50 MB
1013
SUPPORTED_IMAGE_FORMATS = {"jpeg", "png", "gif", "webp", "tiff", "bmp"}
1114
TRANSPARENT_FORMATS = {"png", "webp", "tiff"}
@@ -14,6 +17,35 @@
1417
class SlashCommands(discord.Cog, name="slash_commands"):
1518
def __init__(self, bot: discord.Bot) -> None:
1619
self.bot = bot
20+
self.started_time = time.time()
21+
22+
@slash_command()
23+
async def info(self, ctx: discord.ApplicationContext) -> None:
24+
"""Display information about the bot."""
25+
if self.bot.user is None:
26+
raise UnexpectedNoneError(self.bot, "user")
27+
28+
container = discord.ui.Container()
29+
container.add_text(f"""
30+
{self.bot.user.name} is a bot developed by [Versa Bots](https://github.com/Versa-Bots/) offering utility commands.""")
31+
container.add_separator()
32+
container.add_section(
33+
discord.ui.TextDisplay(
34+
f"""**Users:** {len(self.bot.users)}
35+
**Servers:** {len(self.bot.guilds)}
36+
**API Latency:** {round(self.bot.latency * 1000)}ms
37+
**Pycord Version:** {discord.__version__}
38+
**Uptime:** {self.format_uptime(time.time() - self.started_time)}
39+
**Code:** https://github.com/Versa-Bots/versa/"""
40+
),
41+
accessory=discord.ui.Thumbnail(url=self.bot.user.display_avatar.url),
42+
)
43+
inv_button_row = discord.ui.ActionRow(
44+
discord.ui.Button(
45+
label="Invite", url=f"https://discord.com/api/oauth2/authorize?client_id={self.bot.user.id}"
46+
)
47+
)
48+
await ctx.respond(view=discord.ui.DesignerView(container, inv_button_row))
1749

1850
@slash_command()
1951
@option("image", discord.Attachment, description="The image to convert")
@@ -86,6 +118,24 @@ def convert_image(image_bytes: bytes, target_filetype: str) -> io.BytesIO:
86118
buffer.seek(0)
87119
return buffer
88120

121+
@staticmethod
122+
def format_uptime(seconds: float) -> str:
123+
seconds = int(seconds)
124+
days, seconds = divmod(seconds, 86400)
125+
hours, seconds = divmod(seconds, 3600)
126+
minutes, seconds = divmod(seconds, 60)
127+
128+
parts = []
129+
if days:
130+
parts.append(f"{days}d")
131+
if hours:
132+
parts.append(f"{hours}h")
133+
if minutes:
134+
parts.append(f"{minutes}m")
135+
parts.append(f"{seconds}s")
136+
137+
return " ".join(parts)
138+
89139

90140
def setup(bot: discord.Bot) -> None:
91141
bot.add_cog(SlashCommands(bot))

src/exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class UnexpectedNoneError(TypeError):
2+
"""
3+
Exception raised when an attribute of an object is unexpectedly None.
4+
5+
This is raised when access of an optional class member that should never be None is required, and the value
6+
of said member is unexpectedly None
7+
"""
8+
9+
def __init__(self, obj: object, attr: str) -> None:
10+
"""
11+
Initialize the custom exception raised when an attribute of an object is unexpectedly None.
12+
13+
:param obj: The object containing the attribute that is None.
14+
:param attr: The name of the attribute that is unexpectedly None.
15+
"""
16+
super().__init__(f"{obj.__class__.__name__}.{attr} is None unexpectedly")

0 commit comments

Comments
 (0)