hdhdhdhehd

This commit is contained in:
Slipstream 2025-05-13 08:08:17 -06:00
parent f341ea0aa7
commit e077a1fbba
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
6 changed files with 446 additions and 10 deletions

122
command_context.py Normal file
View 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
View 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]}")

View 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.")

View File

@ -28,8 +28,43 @@ async def on_ready():
# Sync commands
try:
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:
print(f"Failed to sync commands: {e}")
import traceback

39
main.py
View File

@ -230,19 +230,46 @@ async def on_ready():
commands_before = [cmd.name for cmd in bot.tree.get_commands()]
print(f"Commands before sync: {commands_before}")
# Skip global command sync to avoid duplication
print("Skipping global command sync to avoid command duplication...")
# Import our new command sync utilities
import command_sync_utils
from command_context import AppCommandContext
# Only sync guild-specific commands with customizations
print("Syncing guild-specific command customizations...")
guild_syncs = await command_customization.register_all_guild_commands(bot)
# 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)")
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")
# List commands after sync
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:
print(f"Failed to sync commands: {e}")

View File

@ -28,8 +28,43 @@ async def on_ready():
# Sync commands
try:
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:
print(f"Failed to sync commands: {e}")
import traceback