Skip to content

Commit e5269c7

Browse files
Modernize codebase (#34)
* Switch to a trunk-based release * Include Nanika's shutdown handlers * Update `?about` with fresh info * Rename `HajPages` to `RoboPages` This is for consistency with `RoboView` and others. Plus, Haj is an internal reference to a dear mentor of mine * Include basic help command * Move `DevTools` cog into `Admin` * Include Catherine-Chan's reload-all command * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Align to best practices * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Check if docker env and send diff revision text * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Include requirements.txt * Upgrade Dockerfile to 3.12 and remove poetry installation * Fix `?about` command not working on Docker * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add ping command * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix logging not working for files * Update .gitignore --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent aff9334 commit e5269c7

19 files changed

+634
-160
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ celerybeat.pid
124124
.venv
125125
env/
126126
venv/
127+
rodhaj-venv/
127128
ENV/
128129
env.bak/
129130
venv.bak/

bot/cogs/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ class VersionInfo(NamedTuple):
66
major: int
77
minor: int
88
micro: int
9-
releaselevel: Literal["alpha", "beta", "final"]
9+
releaselevel: Literal["main", "alpha", "beta", "final"]
1010

1111
def __str__(self) -> str:
1212
return f"{self.major}.{self.minor}.{self.micro}-{self.releaselevel}"
1313

1414

1515
EXTENSIONS = [module.name for module in iter_modules(__path__, f"{__package__}.")]
16-
VERSION: VersionInfo = VersionInfo(major=0, minor=1, micro=0, releaselevel="alpha")
16+
VERSION: VersionInfo = VersionInfo(major=0, minor=1, micro=0, releaselevel="main")

bot/cogs/admin.py

+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import asyncio
2+
import importlib
3+
import os
4+
import re
5+
import subprocess # nosec # We already know this is dangerous, but it's needed
6+
import sys
7+
from typing import Literal, Optional
8+
9+
import discord
10+
from discord.ext import commands
11+
from discord.ext.commands import Greedy
12+
from libs.utils import RoboContext
13+
from rodhaj import Rodhaj
14+
15+
GIT_PULL_REGEX = re.compile(r"\s+(?P<filename>.*)\b\s+\|\s+[\d]")
16+
17+
18+
class Admin(commands.Cog, command_attrs=dict(hidden=True)):
19+
"""Administrative commands for Rodhaj"""
20+
21+
def __init__(self, bot: Rodhaj) -> None:
22+
self.bot = bot
23+
24+
@property
25+
def display_emoji(self) -> discord.PartialEmoji:
26+
return discord.PartialEmoji(name="\U00002699")
27+
28+
async def cog_check(self, ctx: RoboContext) -> bool:
29+
return await self.bot.is_owner(ctx.author)
30+
31+
async def reload_or_load_extension(self, module: str) -> None:
32+
try:
33+
await self.bot.reload_extension(module)
34+
except commands.ExtensionNotLoaded:
35+
await self.bot.load_extension(module)
36+
37+
def find_modules_from_git(self, output: str) -> list[tuple[int, str]]:
38+
files = GIT_PULL_REGEX.findall(output)
39+
ret: list[tuple[int, str]] = []
40+
for file in files:
41+
root, ext = os.path.splitext(file)
42+
if ext != ".py" or root.endswith("__init__"):
43+
continue
44+
45+
true_root = ".".join(root.split("/")[1:])
46+
47+
if true_root.startswith("cogs") or true_root.startswith("libs"):
48+
# A subdirectory within these are a part of the codebase
49+
50+
ret.append((true_root.count(".") + 1, true_root))
51+
52+
# For reload order, the submodules should be reloaded first
53+
ret.sort(reverse=True)
54+
return ret
55+
56+
async def run_process(self, command: str) -> list[str]:
57+
process = await asyncio.create_subprocess_shell(
58+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
59+
)
60+
result = await process.communicate()
61+
62+
return [output.decode() for output in result]
63+
64+
def tick(self, opt: Optional[bool], label: Optional[str] = None) -> str:
65+
lookup = {
66+
True: "\U00002705",
67+
False: "\U0000274c",
68+
None: "\U000023e9",
69+
}
70+
emoji = lookup.get(opt, "\U0000274c")
71+
if label is not None:
72+
return f"{emoji}: {label}"
73+
return emoji
74+
75+
def format_results(self, statuses: list) -> str:
76+
desc = "\U00002705 - Successful reload | \U0000274c - Failed reload | \U000023e9 - Skipped\n\n"
77+
status = "\n".join(f"- {status}: `{module}`" for status, module in statuses)
78+
desc += status
79+
return desc
80+
81+
async def reload_exts(self, module: str) -> list[tuple[str, str]]:
82+
statuses = []
83+
try:
84+
await self.reload_or_load_extension(module)
85+
statuses.append((self.tick(True), module))
86+
except commands.ExtensionError:
87+
statuses.append((self.tick(False), module))
88+
89+
return statuses
90+
91+
def reload_lib_modules(self, module: str) -> list[tuple[str, str]]:
92+
statuses = []
93+
try:
94+
actual_module = sys.modules[module]
95+
importlib.reload(actual_module)
96+
statuses.append((self.tick(True), module))
97+
except KeyError:
98+
statuses.append((self.tick(None), module))
99+
except Exception:
100+
statuses.append((self.tick(False), module))
101+
return statuses
102+
103+
# Umbra's sync command
104+
# To learn more about it, see the link below (and ?tag ass on the dpy server):
105+
# https://about.abstractumbra.dev/discord.py/2023/01/29/sync-command-example.html
106+
@commands.guild_only()
107+
@commands.command(name="sync")
108+
async def sync(
109+
self,
110+
ctx: RoboContext,
111+
guilds: Greedy[discord.Object],
112+
spec: Optional[Literal["~", "*", "^"]] = None,
113+
) -> None:
114+
"""Performs a sync of the tree. This will sync, copy globally, or clear the tree."""
115+
await ctx.defer()
116+
if not guilds:
117+
if spec == "~":
118+
synced = await self.bot.tree.sync(guild=ctx.guild)
119+
elif spec == "*":
120+
self.bot.tree.copy_global_to(guild=ctx.guild) # type: ignore
121+
synced = await self.bot.tree.sync(guild=ctx.guild)
122+
elif spec == "^":
123+
self.bot.tree.clear_commands(guild=ctx.guild)
124+
await self.bot.tree.sync(guild=ctx.guild)
125+
synced = []
126+
else:
127+
synced = await self.bot.tree.sync()
128+
129+
await ctx.send(
130+
f"Synced {len(synced)} commands {'globally' if spec is None else 'to the current guild.'}"
131+
)
132+
return
133+
134+
ret = 0
135+
for guild in guilds:
136+
try:
137+
await self.bot.tree.sync(guild=guild)
138+
except discord.HTTPException:
139+
pass
140+
else:
141+
ret += 1
142+
143+
await ctx.send(f"Synced the tree to {ret}/{len(guilds)}.")
144+
145+
@commands.command(name="reload-all", hidden=True)
146+
async def reload(self, ctx: RoboContext) -> None:
147+
"""Reloads all cogs and utils"""
148+
async with ctx.typing():
149+
stdout, _ = await self.run_process("git pull")
150+
151+
# progress and stuff is redirected to stderr in git pull
152+
# however, things like "fast forward" and files
153+
# along with the text "already up-to-date" are in stdout
154+
155+
if stdout.startswith("Already up-to-date."):
156+
await ctx.send(stdout)
157+
return
158+
159+
modules = self.find_modules_from_git(stdout)
160+
161+
mods_text = "\n".join(
162+
f"{index}. `{module}`" for index, (_, module) in enumerate(modules, start=1)
163+
)
164+
prompt_text = (
165+
f"This will update the following modules, are you sure?\n{mods_text}"
166+
)
167+
168+
confirm = await ctx.prompt(prompt_text)
169+
if not confirm:
170+
await ctx.send("Aborting....")
171+
return
172+
173+
statuses = []
174+
for is_submodule, module in modules:
175+
if is_submodule:
176+
statuses = self.reload_lib_modules(module)
177+
else:
178+
statuses = await self.reload_exts(module)
179+
180+
await ctx.send(self.format_results(statuses))
181+
182+
183+
async def setup(bot: Rodhaj) -> None:
184+
await bot.add_cog(Admin(bot))

bot/cogs/config.py

+4
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def __init__(self, bot: Rodhaj) -> None:
8989
self.bot = bot
9090
self.pool = self.bot.pool
9191

92+
@property
93+
def display_emoji(self) -> discord.PartialEmoji:
94+
return discord.PartialEmoji(name="\U0001f6e0")
95+
9296
@alru_cache()
9397
async def get_guild_config(self, guild_id: int) -> Optional[GuildConfig]:
9498
# Normally using the star is bad practice but...

bot/cogs/dev_tools.py

-98
This file was deleted.

0 commit comments

Comments
 (0)