wdiscordbotserver/cogs/serverconfig.py
Slipstream f84c82ec2a
Groups app commands; adds local LLM for AI reactions
- Reorganizes application commands across cogs into logical groups and subgroups for improved usability and maintainability.
- Introduces a local Hugging Face Transformer model (Gemma 2B IT) to provide context-aware emoji suggestions, enhancing bot interaction.
- Converts remaining hybrid commands to pure application commands.
- Updates global command disable check registration and includes minor UI and error handling improvements.
2025-05-09 18:05:27 -06:00

162 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import discord
from discord.ext import commands
from discord import app_commands
import os
import json
# Path to the JSON config file
CONFIG_FILE = "/home/server/serverconfig.json"
def load_config() -> dict:
"""Load the server configuration from file.
If the file does not exist or is invalid, create a new empty configuration."""
if not os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "w") as f:
json.dump({}, f)
return {}
try:
with open(CONFIG_FILE, "r") as f:
return json.load(f)
except json.JSONDecodeError:
return {}
def save_config(data: dict) -> None:
"""Save the configuration JSON to file."""
with open(CONFIG_FILE, "w") as f:
json.dump(data, f, indent=4)
async def global_disabled_check(interaction: discord.Interaction) -> bool:
"""
Global check for all app (slash) commands.
If the command (except for serverconfig itself) is marked as disabled in this servers config,
send an ephemeral message and prevent execution.
"""
# If interaction comes from a DM, allow it.
if interaction.guild is None:
return True
# Always allow the serverconfig command so admins can change settings.
if interaction.command and interaction.command.name == "serverconfig":
return True
config = load_config()
guild_id = str(interaction.guild.id)
disabled_commands = config.get(guild_id, [])
if interaction.command and interaction.command.name in disabled_commands:
if not interaction.response.is_done():
await interaction.response.send_message(
"This command has been disabled by server admins.", ephemeral=True
)
# Raising a CheckFailure prevents the command from running.
raise app_commands.CheckFailure("Command disabled.")
return True
class ServerConfigCog(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
@app_commands.command(
name="serverconfig",
description="Enable or disable a command in this server."
)
@app_commands.describe(
command="The name of the command to configure",
enabled="Type 'yes' to enable or 'no' to disable."
)
async def serverconfig(
self,
interaction: discord.Interaction,
command: str,
enabled: str
):
# Check if the user has admin permissions.
if not interaction.user.guild_permissions.administrator:
await interaction.response.send_message(
"You do not have permission to use this command.",
ephemeral=True
)
return
# Normalize the enabled flag.
enabled_flag = enabled.lower()
if enabled_flag not in ["yes", "no"]:
await interaction.response.send_message(
"Invalid 'enabled' option. Please use 'yes' or 'no'.",
ephemeral=True
)
return
# Verify that the provided command exists.
found = False
# Check the classic text commands.
for cmd in self.bot.commands:
if cmd.name == command:
found = True
break
# Also check application (slash) commands from the tree.
if not found:
for cmd in self.bot.tree.get_commands():
if cmd.name == command:
found = True
break
if not found:
await interaction.response.send_message(
f"The command '{command}' was not found.",
ephemeral=True
)
return
# Load the configuration.
config = load_config()
guild_id = str(interaction.guild.id)
if guild_id not in config:
config[guild_id] = []
if enabled_flag == "no":
# Add the command to the disabled list if not already present.
if command not in config[guild_id]:
config[guild_id].append(command)
save_config(config)
await interaction.response.send_message(
f"Command '{command}' has been **disabled** in this server.",
ephemeral=True
)
else: # enabled_flag == "yes"
# Remove the command from the disabled list if present.
if command in config[guild_id]:
config[guild_id].remove(command)
save_config(config)
await interaction.response.send_message(
f"Command '{command}' has been **enabled** in this server.",
ephemeral=True
)
@serverconfig.autocomplete("command")
async def command_autocomplete(
self, interaction: discord.Interaction, current: str
) -> list[app_commands.Choice[str]]:
"""
Autocomplete for the 'command' parameter.
It searches both classic and slash commands for matches.
"""
choices = set()
# Get names of text commands.
for cmd in self.bot.commands:
choices.add(cmd.name)
# Get names of app commands.
for cmd in self.bot.tree.get_commands():
choices.add(cmd.name)
# Filter and send at most 25 matching choices.
filtered = [
app_commands.Choice(name=cmd, value=cmd)
for cmd in choices
if current.lower() in cmd.lower()
]
return filtered[:25]
async def setup(bot: commands.Bot):
# Register the global check it will run for every application (slash) command.
bot.tree.interaction_check = global_disabled_check
await bot.add_cog(ServerConfigCog(bot))