hdhdhdhehd
This commit is contained in:
parent
f341ea0aa7
commit
e077a1fbba
122
command_context.py
Normal file
122
command_context.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
"""
|
||||||
|
Utility module for handling command contexts in Discord.py.
|
||||||
|
|
||||||
|
This module provides functions for setting up commands with different contexts,
|
||||||
|
allowing them to work in DMs, private channels, guilds, or any combination.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
from enum import Enum, auto
|
||||||
|
from typing import Optional, List, Dict, Any, Union, Callable, Awaitable
|
||||||
|
|
||||||
|
class AppCommandContext(Enum):
|
||||||
|
"""
|
||||||
|
Enum representing the allowed contexts for application commands.
|
||||||
|
|
||||||
|
This enum defines where commands can be used:
|
||||||
|
- GUILD: Commands can only be used in guilds (servers)
|
||||||
|
- GUILD_INSTALL: Commands can only be used in guilds where the app is installed
|
||||||
|
- PRIVATE: Commands can only be used in private contexts (DMs and private channels)
|
||||||
|
- ALL: Commands can be used in both guilds and private contexts
|
||||||
|
"""
|
||||||
|
GUILD = auto()
|
||||||
|
GUILD_INSTALL = auto()
|
||||||
|
PRIVATE = auto()
|
||||||
|
ALL = auto()
|
||||||
|
|
||||||
|
def set_command_context(
|
||||||
|
command: app_commands.Command,
|
||||||
|
context: AppCommandContext = AppCommandContext.ALL
|
||||||
|
) -> app_commands.Command:
|
||||||
|
"""
|
||||||
|
Set the context for a command, determining where it can be used.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command: The command to modify
|
||||||
|
context: The context to set for the command
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The modified command
|
||||||
|
"""
|
||||||
|
# Set guild_only and dm_permission based on the context
|
||||||
|
if context == AppCommandContext.GUILD or context == AppCommandContext.GUILD_INSTALL:
|
||||||
|
command.guild_only = True
|
||||||
|
# dm_permission is automatically set to False when guild_only is True
|
||||||
|
elif context == AppCommandContext.PRIVATE:
|
||||||
|
command.guild_only = False
|
||||||
|
# We need to override the extras to set dm_permission to True and guild_permission to False
|
||||||
|
command.extras["dm_permission"] = True
|
||||||
|
command.extras["default_member_permissions"] = "0" # No permissions in guild
|
||||||
|
elif context == AppCommandContext.ALL:
|
||||||
|
command.guild_only = False
|
||||||
|
command.extras["dm_permission"] = True
|
||||||
|
# Allow default permissions in guilds
|
||||||
|
|
||||||
|
return command
|
||||||
|
|
||||||
|
def create_global_command(
|
||||||
|
bot_tree: app_commands.CommandTree,
|
||||||
|
name: str,
|
||||||
|
description: str,
|
||||||
|
callback: Callable[[discord.Interaction], Awaitable[None]],
|
||||||
|
context: AppCommandContext = AppCommandContext.ALL,
|
||||||
|
**kwargs
|
||||||
|
) -> app_commands.Command:
|
||||||
|
"""
|
||||||
|
Create a command that can be used globally with the specified context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot_tree: The command tree to add the command to
|
||||||
|
name: The name of the command
|
||||||
|
description: The description of the command
|
||||||
|
callback: The function to call when the command is used
|
||||||
|
context: The context to set for the command
|
||||||
|
**kwargs: Additional arguments to pass to the command constructor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created command
|
||||||
|
"""
|
||||||
|
# Create the command
|
||||||
|
command = app_commands.Command(
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
callback=callback,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set the context
|
||||||
|
set_command_context(command, context)
|
||||||
|
|
||||||
|
# Add the command to the tree
|
||||||
|
bot_tree.add_command(command)
|
||||||
|
|
||||||
|
return command
|
||||||
|
|
||||||
|
def sync_commands(
|
||||||
|
bot_tree: app_commands.CommandTree,
|
||||||
|
guild_specific: bool = True,
|
||||||
|
global_commands: bool = True
|
||||||
|
) -> Awaitable[List[app_commands.Command]]:
|
||||||
|
"""
|
||||||
|
Sync commands with Discord.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot_tree: The command tree to sync
|
||||||
|
guild_specific: Whether to sync guild-specific commands
|
||||||
|
global_commands: Whether to sync global commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A coroutine that resolves to the list of synced commands
|
||||||
|
"""
|
||||||
|
if guild_specific and not global_commands:
|
||||||
|
# Only sync guild-specific commands
|
||||||
|
# This is handled by command_customization.register_all_guild_commands
|
||||||
|
return None
|
||||||
|
elif not guild_specific and global_commands:
|
||||||
|
# Only sync global commands
|
||||||
|
return bot_tree.sync()
|
||||||
|
else:
|
||||||
|
# Sync both guild-specific and global commands
|
||||||
|
# This is the default behavior of bot_tree.sync()
|
||||||
|
return bot_tree.sync()
|
120
command_sync_utils.py
Normal file
120
command_sync_utils.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"""
|
||||||
|
Utility module for syncing commands with Discord.
|
||||||
|
|
||||||
|
This module provides functions for syncing both global and guild-specific commands,
|
||||||
|
allowing for more flexibility in command registration and supporting commands in
|
||||||
|
both guild and private contexts (DMs and private channels).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
from discord.ext import commands
|
||||||
|
import logging
|
||||||
|
import command_customization
|
||||||
|
from typing import Dict, List, Optional, Union, Any
|
||||||
|
from command_context import AppCommandContext
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
async def sync_global_and_guild_commands(
|
||||||
|
bot: commands.Bot,
|
||||||
|
global_only: bool = False,
|
||||||
|
guild_only: bool = False
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Sync both global and guild-specific commands with Discord.
|
||||||
|
|
||||||
|
This function allows for more control over command syncing, enabling
|
||||||
|
both global commands (that work in DMs) and guild-specific commands
|
||||||
|
with customizations.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot: The bot instance
|
||||||
|
global_only: If True, only sync global commands
|
||||||
|
guild_only: If True, only sync guild-specific commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dictionary containing the results of the sync operations
|
||||||
|
"""
|
||||||
|
results = {
|
||||||
|
"global": [],
|
||||||
|
"guild": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Sync global commands if requested
|
||||||
|
if not guild_only:
|
||||||
|
log.info("Syncing global commands...")
|
||||||
|
global_synced = await bot.tree.sync()
|
||||||
|
results["global"] = global_synced
|
||||||
|
log.info(f"Synced {len(global_synced)} global command(s)")
|
||||||
|
|
||||||
|
# List the synced commands
|
||||||
|
global_commands = [cmd.name for cmd in global_synced]
|
||||||
|
log.info(f"Global commands: {', '.join(global_commands)}")
|
||||||
|
|
||||||
|
# Sync guild-specific commands if requested
|
||||||
|
if not global_only:
|
||||||
|
log.info("Syncing guild-specific command customizations...")
|
||||||
|
guild_syncs = await command_customization.register_all_guild_commands(bot)
|
||||||
|
results["guild"] = guild_syncs
|
||||||
|
|
||||||
|
total_guild_syncs = sum(len(cmds) for cmds in guild_syncs.values())
|
||||||
|
log.info(f"Synced commands for {len(guild_syncs)} guilds with a total of {total_guild_syncs} customized commands")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log.error(f"Failed to sync commands: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def register_global_command(
|
||||||
|
bot: commands.Bot,
|
||||||
|
name: str,
|
||||||
|
description: str,
|
||||||
|
callback: Any,
|
||||||
|
context: AppCommandContext = AppCommandContext.ALL,
|
||||||
|
**kwargs
|
||||||
|
) -> app_commands.Command:
|
||||||
|
"""
|
||||||
|
Register a global command with the specified context.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot: The bot instance
|
||||||
|
name: The name of the command
|
||||||
|
description: The description of the command
|
||||||
|
callback: The function to call when the command is used
|
||||||
|
context: The context to set for the command
|
||||||
|
**kwargs: Additional arguments to pass to the command constructor
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The created command
|
||||||
|
"""
|
||||||
|
from command_context import create_global_command
|
||||||
|
|
||||||
|
return create_global_command(
|
||||||
|
bot.tree,
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
callback=callback,
|
||||||
|
context=context,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_command_contexts(bot: commands.Bot, contexts: Dict[str, AppCommandContext]) -> None:
|
||||||
|
"""
|
||||||
|
Set the contexts for multiple commands.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bot: The bot instance
|
||||||
|
contexts: A dictionary mapping command names to contexts
|
||||||
|
"""
|
||||||
|
from command_context import set_command_context
|
||||||
|
|
||||||
|
for cmd in bot.tree.get_commands():
|
||||||
|
if cmd.name in contexts:
|
||||||
|
set_command_context(cmd, contexts[cmd.name])
|
||||||
|
log.info(f"Set context for command '{cmd.name}' to {contexts[cmd.name]}")
|
97
example_global_commands.py
Normal file
97
example_global_commands.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
"""
|
||||||
|
Example module demonstrating how to create global commands that work in DMs and private channels.
|
||||||
|
|
||||||
|
This module shows how to use the AppCommandContext system to create commands
|
||||||
|
that can be used in both guild and private contexts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import app_commands
|
||||||
|
from discord.ext import commands
|
||||||
|
from command_context import AppCommandContext, create_global_command
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class GlobalCommandsCog(commands.Cog):
|
||||||
|
"""A cog that demonstrates global commands that work in DMs and private channels."""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
self.bot = bot
|
||||||
|
print("GlobalCommandsCog initialized!")
|
||||||
|
|
||||||
|
# Register global commands
|
||||||
|
self.register_global_commands()
|
||||||
|
|
||||||
|
def register_global_commands(self):
|
||||||
|
"""Register global commands that work in DMs and private channels."""
|
||||||
|
|
||||||
|
# Create a help command that works in DMs and private channels
|
||||||
|
create_global_command(
|
||||||
|
self.bot.tree,
|
||||||
|
name="dmhelp",
|
||||||
|
description="Get help with bot commands (works in DMs)",
|
||||||
|
callback=self.dm_help_callback,
|
||||||
|
context=AppCommandContext.ALL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a ping command that works in DMs and private channels
|
||||||
|
create_global_command(
|
||||||
|
self.bot.tree,
|
||||||
|
name="dmping",
|
||||||
|
description="Check if the bot is responsive (works in DMs)",
|
||||||
|
callback=self.dm_ping_callback,
|
||||||
|
context=AppCommandContext.ALL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a command that only works in private contexts
|
||||||
|
create_global_command(
|
||||||
|
self.bot.tree,
|
||||||
|
name="privateonly",
|
||||||
|
description="This command only works in DMs and private channels",
|
||||||
|
callback=self.private_only_callback,
|
||||||
|
context=AppCommandContext.PRIVATE
|
||||||
|
)
|
||||||
|
|
||||||
|
print("GlobalCommandsCog: Registered global commands")
|
||||||
|
|
||||||
|
async def dm_help_callback(self, interaction: discord.Interaction):
|
||||||
|
"""Callback for the /dmhelp command."""
|
||||||
|
is_dm = isinstance(interaction.channel, discord.DMChannel)
|
||||||
|
is_private = not interaction.guild
|
||||||
|
|
||||||
|
help_text = (
|
||||||
|
"# Bot Help\n\n"
|
||||||
|
"This help command works in both DMs and servers!\n\n"
|
||||||
|
f"**Current context:** {'DM' if is_dm else 'Private Channel' if is_private else 'Server'}\n\n"
|
||||||
|
"## Available Commands\n"
|
||||||
|
"- `/dmhelp` - This help command\n"
|
||||||
|
"- `/dmping` - Check if the bot is responsive\n"
|
||||||
|
"- `/privateonly` - Only works in DMs and private channels\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
await interaction.response.send_message(help_text, ephemeral=True)
|
||||||
|
|
||||||
|
async def dm_ping_callback(self, interaction: discord.Interaction):
|
||||||
|
"""Callback for the /dmping command."""
|
||||||
|
is_dm = isinstance(interaction.channel, discord.DMChannel)
|
||||||
|
is_private = not interaction.guild
|
||||||
|
|
||||||
|
await interaction.response.send_message(
|
||||||
|
f"🏓 Pong! Bot is responsive.\n"
|
||||||
|
f"**Current context:** {'DM' if is_dm else 'Private Channel' if is_private else 'Server'}",
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def private_only_callback(self, interaction: discord.Interaction):
|
||||||
|
"""Callback for the /privateonly command."""
|
||||||
|
is_dm = isinstance(interaction.channel, discord.DMChannel)
|
||||||
|
|
||||||
|
await interaction.response.send_message(
|
||||||
|
f"This command only works in private contexts.\n"
|
||||||
|
f"**Current context:** {'DM' if is_dm else 'Private Channel'}",
|
||||||
|
ephemeral=True
|
||||||
|
)
|
||||||
|
|
||||||
|
async def setup(bot: commands.Bot):
|
||||||
|
"""Add the GlobalCommandsCog to the bot."""
|
||||||
|
await bot.add_cog(GlobalCommandsCog(bot))
|
||||||
|
print("GlobalCommandsCog setup complete.")
|
39
gurt_bot.py
39
gurt_bot.py
@ -28,8 +28,43 @@ async def on_ready():
|
|||||||
# Sync commands
|
# Sync commands
|
||||||
try:
|
try:
|
||||||
print("Starting command sync process...")
|
print("Starting command sync process...")
|
||||||
synced = await bot.tree.sync()
|
|
||||||
print(f"Synced {len(synced)} command(s)")
|
# Import our command sync utilities
|
||||||
|
try:
|
||||||
|
import command_sync_utils
|
||||||
|
from command_context import AppCommandContext
|
||||||
|
|
||||||
|
# Sync both global and guild-specific commands
|
||||||
|
print("Syncing both global and guild-specific commands...")
|
||||||
|
sync_results = await command_sync_utils.sync_global_and_guild_commands(bot)
|
||||||
|
|
||||||
|
# Report results
|
||||||
|
global_syncs = sync_results["global"]
|
||||||
|
guild_syncs = sync_results["guild"]
|
||||||
|
|
||||||
|
print(f"Synced {len(global_syncs)} global command(s)")
|
||||||
|
if guild_syncs:
|
||||||
|
total_guild_syncs = sum(len(cmds) for cmds in guild_syncs.values())
|
||||||
|
print(f"Synced commands for {len(guild_syncs)} guilds with a total of {total_guild_syncs} customized commands")
|
||||||
|
|
||||||
|
# Set context for specific commands to allow them in private channels and DMs
|
||||||
|
private_commands = [
|
||||||
|
# Add command names that should work in private contexts here
|
||||||
|
"gurtmood", # Allow mood command in DMs
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create a dictionary mapping command names to contexts
|
||||||
|
command_contexts = {name: AppCommandContext.ALL for name in private_commands}
|
||||||
|
|
||||||
|
# Apply the contexts to the commands
|
||||||
|
command_sync_utils.set_command_contexts(bot, command_contexts)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# Fall back to regular sync if the utility modules aren't available
|
||||||
|
print("Command sync utilities not available, falling back to regular sync...")
|
||||||
|
synced = await bot.tree.sync()
|
||||||
|
print(f"Synced {len(synced)} command(s)")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to sync commands: {e}")
|
print(f"Failed to sync commands: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
|
39
main.py
39
main.py
@ -230,19 +230,46 @@ async def on_ready():
|
|||||||
commands_before = [cmd.name for cmd in bot.tree.get_commands()]
|
commands_before = [cmd.name for cmd in bot.tree.get_commands()]
|
||||||
print(f"Commands before sync: {commands_before}")
|
print(f"Commands before sync: {commands_before}")
|
||||||
|
|
||||||
# Skip global command sync to avoid duplication
|
# Import our new command sync utilities
|
||||||
print("Skipping global command sync to avoid command duplication...")
|
import command_sync_utils
|
||||||
|
from command_context import AppCommandContext
|
||||||
|
|
||||||
# Only sync guild-specific commands with customizations
|
# Sync both global and guild-specific commands
|
||||||
print("Syncing guild-specific command customizations...")
|
print("Syncing both global and guild-specific commands...")
|
||||||
guild_syncs = await command_customization.register_all_guild_commands(bot)
|
sync_results = await command_sync_utils.sync_global_and_guild_commands(bot)
|
||||||
|
|
||||||
|
# Report results
|
||||||
|
global_syncs = sync_results["global"]
|
||||||
|
guild_syncs = sync_results["guild"]
|
||||||
|
|
||||||
|
print(f"Synced {len(global_syncs)} global command(s)")
|
||||||
total_guild_syncs = sum(len(cmds) for cmds in guild_syncs.values())
|
total_guild_syncs = sum(len(cmds) for cmds in guild_syncs.values())
|
||||||
print(f"Synced commands for {len(guild_syncs)} guilds with a total of {total_guild_syncs} customized commands")
|
print(f"Synced commands for {len(guild_syncs)} guilds with a total of {total_guild_syncs} customized commands")
|
||||||
|
|
||||||
# List commands after sync
|
# List commands after sync
|
||||||
commands_after = [cmd.name for cmd in bot.tree.get_commands()]
|
commands_after = [cmd.name for cmd in bot.tree.get_commands()]
|
||||||
print(f"Commands registered in command tree: {commands_after}")
|
print(f"Commands registered in command tree: {', '.join(commands_after)}")
|
||||||
|
|
||||||
|
# Set context for specific commands to allow them in private channels and DMs
|
||||||
|
private_commands = [
|
||||||
|
# Add command names that should work in private contexts here
|
||||||
|
"dmhelp",
|
||||||
|
"dmping",
|
||||||
|
"privateonly",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Load the example global commands cog
|
||||||
|
try:
|
||||||
|
await bot.load_extension("example_global_commands")
|
||||||
|
print("Loaded example_global_commands cog")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to load example_global_commands cog: {e}")
|
||||||
|
|
||||||
|
# Create a dictionary mapping command names to contexts
|
||||||
|
command_contexts = {name: AppCommandContext.ALL for name in private_commands}
|
||||||
|
|
||||||
|
# Apply the contexts to the commands
|
||||||
|
command_sync_utils.set_command_contexts(bot, command_contexts)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to sync commands: {e}")
|
print(f"Failed to sync commands: {e}")
|
||||||
|
@ -28,8 +28,43 @@ async def on_ready():
|
|||||||
# Sync commands
|
# Sync commands
|
||||||
try:
|
try:
|
||||||
print("Starting command sync process...")
|
print("Starting command sync process...")
|
||||||
synced = await bot.tree.sync()
|
|
||||||
print(f"Synced {len(synced)} command(s)")
|
# Import our command sync utilities
|
||||||
|
try:
|
||||||
|
import command_sync_utils
|
||||||
|
from command_context import AppCommandContext
|
||||||
|
|
||||||
|
# Sync both global and guild-specific commands
|
||||||
|
print("Syncing both global and guild-specific commands...")
|
||||||
|
sync_results = await command_sync_utils.sync_global_and_guild_commands(bot)
|
||||||
|
|
||||||
|
# Report results
|
||||||
|
global_syncs = sync_results["global"]
|
||||||
|
guild_syncs = sync_results["guild"]
|
||||||
|
|
||||||
|
print(f"Synced {len(global_syncs)} global command(s)")
|
||||||
|
if guild_syncs:
|
||||||
|
total_guild_syncs = sum(len(cmds) for cmds in guild_syncs.values())
|
||||||
|
print(f"Synced commands for {len(guild_syncs)} guilds with a total of {total_guild_syncs} customized commands")
|
||||||
|
|
||||||
|
# Set context for specific commands to allow them in private channels and DMs
|
||||||
|
private_commands = [
|
||||||
|
# Add command names that should work in private contexts here
|
||||||
|
"wheatleymemory", # Allow memory command in DMs
|
||||||
|
]
|
||||||
|
|
||||||
|
# Create a dictionary mapping command names to contexts
|
||||||
|
command_contexts = {name: AppCommandContext.ALL for name in private_commands}
|
||||||
|
|
||||||
|
# Apply the contexts to the commands
|
||||||
|
command_sync_utils.set_command_contexts(bot, command_contexts)
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
# Fall back to regular sync if the utility modules aren't available
|
||||||
|
print("Command sync utilities not available, falling back to regular sync...")
|
||||||
|
synced = await bot.tree.sync()
|
||||||
|
print(f"Synced {len(synced)} command(s)")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to sync commands: {e}")
|
print(f"Failed to sync commands: {e}")
|
||||||
import traceback
|
import traceback
|
||||||
|
Loading…
x
Reference in New Issue
Block a user