Skip to content

Commit

Permalink
Add top-level sync command and sync script
Browse files Browse the repository at this point in the history
It is generally best practice to not rate limit the slash command
registration endpoint, so syncing should be done manually rather than on
ready. A script is provided to simply login the bot and sync commands in
case the sync command signature ever gets modified.
  • Loading branch information
Nydauron committed Apr 24, 2024
1 parent 30f07f1 commit b92109a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
69 changes: 68 additions & 1 deletion bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Serves as the initial file to launch the bot. Loads all needed extensions and maintains
core functionality.
"""

from __future__ import annotations

import asyncio
Expand All @@ -19,13 +20,15 @@
from discord.ext import commands
from rich.logging import RichHandler

from commandchecks import is_staff_from_ctx
from env import env
from src.discord.globals import (
CHANNEL_BOTSPAM,
CHANNEL_DELETEDM,
CHANNEL_DMLOG,
CHANNEL_EDITEDM,
CHANNEL_RULES,
SLASH_COMMAND_GUILDS,
)
from src.discord.reporter import Reporter
from src.mongo.mongo import MongoDatabase
Expand All @@ -37,6 +40,7 @@

intents = discord.Intents.all()
logger = logging.getLogger(__name__)
SYNC_COMMAND_NAME = "sync"

BOT_PREFIX = "?" if env.dev_mode else "!"

Expand Down Expand Up @@ -268,7 +272,11 @@ async def on_message(self, message: discord.Message) -> None:
spam: commands.Cog | SpamManager = self.get_cog("SpamManager")
await spam.store_and_validate(message)

if message.content and len(re.findall(r"^[!\?]\s*\w+$", message.content)):
legacy_command: list[str] = re.findall(r"^[!\?]\s*(\w+)", message.content)
if message.content and len(legacy_command):
if legacy_command[0].startswith(SYNC_COMMAND_NAME):
await bot.process_commands(message)
return
botspam_channel = discord.utils.get(
message.guild.channels,
name=CHANNEL_BOTSPAM,
Expand Down Expand Up @@ -319,6 +327,28 @@ async def listen_for_response(
return message
return None

async def sync_commands(
self,
only_guild_commands: bool,
) -> tuple[list[app_commands.AppCommand], dict[int, list[app_commands.AppCommand]]]:
logger.info(
f"Beginning to sync {'guild-only' if only_guild_commands else 'all'} commands ...",
)
global_cmds_synced = []
if not only_guild_commands:
global_cmds_synced = await self.tree.sync()
logger.info(f"{len(global_cmds_synced)} global commands were synced")
guild_commands = {}
for command_guild in SLASH_COMMAND_GUILDS:
guild_cmds_synced = await self.tree.sync(
guild=discord.Object(id=command_guild),
)
guild_commands[command_guild] = guild_cmds_synced
logger.info(
f"{len(guild_cmds_synced)} guild commands were synced for guild with id {command_guild}",
)
return (global_cmds_synced, guild_commands)


bot = PiBot()
KB = 1024
Expand All @@ -332,6 +362,43 @@ async def listen_for_response(
discord.utils.setup_logging(handler=handler)


@bot.command(
name=SYNC_COMMAND_NAME,
description="Syncs command list. Any new commands will be available for use.",
)
@commands.check(is_staff_from_ctx)
async def sync(ctx: commands.Context, only_guild_commands: bool = False):
"""
Syncs and registers and new commands with Discord. This command is a
top-level command to prevent disabled cogs from disabling sync
functionality.
Note: Any changes to this command's signature will require
`Pibot.sync_commands()` which can be done by running:
```
python sync_commands.py
```
within your venv.
"""
async with ctx.typing(ephemeral=True):
global_cmds_synced, guild_cmds_synced = await bot.sync_commands(
only_guild_commands,
)
res_msg = (
f"{len(global_cmds_synced)} global commands were synced."
if not only_guild_commands
else ""
)
for guild_id, cmds in guild_cmds_synced.items():
guild_info = bot.get_guild(guild_id)
guild_name_id = f"guild with id {guild_id}"
if guild_info:
guild_name_id = f'"{guild_info.name}" (id: {guild_id})'
res_msg += f"\n{len(cmds)} guild commands were synced in {guild_name_id}."
res_msg = res_msg.strip("\n")
await ctx.send(res_msg)


async def main(token: str):
"""
Main event loop for the bot.
Expand Down
32 changes: 32 additions & 0 deletions sync_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
A script file to manually sync the file
Run:
```
python sync_commands.py
```
within your venv and it will sync all commands including the /sync command
"""

import asyncio
import logging

from rich.logging import RichHandler

import bot
from src.discord.globals import DEV_TOKEN, TOKEN, dev_mode


async def main():
logger = logging.getLogger()
logger.addHandler(RichHandler(rich_tracebacks=True))
token = DEV_TOKEN if dev_mode else TOKEN
async with bot.bot:
await bot.bot.login(token)
await bot.bot.sync_commands()

exit(0)


if __name__ == "__main__":
asyncio.run(main())

0 comments on commit b92109a

Please sign in to comment.