OokamiPupV2/cmd_discord.py

258 lines
9.5 KiB
Python

# cmd_discord.py
import discord
from discord.ext import commands
from discord import app_commands
from typing import Optional
from cmd_common import common_commands as cc
from modules.permissions import has_permission
from modules.utility import handle_help_command
import globals
def setup(bot, db_conn=None):
"""
Attach commands to the Discord bot, store references to db/log.
"""
config_data = globals.load_config_file()
@bot.command()
@commands.is_owner()
async def sync_commands(ctx):
"""
Trigger manual command sync. OWNER ONLY
"""
if commands.is_owner():
primary_guild_int = int(config_data["discord_guilds"][0])
primary_guild = discord.Object(id=primary_guild_int)
await ctx.send(f"Initiating slash sync to Discord Guild '{primary_guild_int}' ...")
try:
await bot.tree.sync(guild=primary_guild)
reply = "... Commands synced!"
except Exception as e:
reply = f"... Commands failed to sync! Error message:\n{e}"
globals.log(f"'sync_commands' failed to sync command tree\n{e}", "ERROR")
else:
reply = "You're not the registered owner of me!"
await ctx.send(reply)
@bot.hybrid_command(name="available", description="List commands available to you")
async def available(ctx):
available_cmds = []
for command in bot.commands:
try:
# This will return True if the command's checks pass for the given context.
if await command.can_run(ctx):
available_cmds.append(command.name)
except commands.CheckFailure:
# The command's checks did not pass.
pass
except Exception as e:
# In case some commands fail unexpectedly during checks.
globals.log(f"Error checking command {command.name}: {e}", "ERROR")
if available_cmds:
await ctx.send("Available commands: " + ", ".join(sorted(available_cmds)))
else:
await ctx.send("No commands are available to you at this time.")
@bot.hybrid_command(name="help", description="Get information about commands")
@app_commands.describe(
command="The command to get help info about. Defaults to 'help'"
)
async def cmd_help(ctx: commands.Context, *, command: str = ""):
"""
e.g. !help
!help quote
"""
result = await handle_help_command(ctx, command, bot, is_discord=True)
await ctx.send(result)
@bot.hybrid_command(name="greet", description="Make me greet you")
async def cmd_greet(ctx):
result = cc.greet(ctx.author.display_name, "Discord")
await ctx.send(result)
@bot.hybrid_command(name="ping", description="Check my uptime")
async def cmd_ping(ctx):
result = cc.ping()
# Get heartbeat latency. Discord only
latency = round(float(bot.latency) * 1000)
result += f" (*latency: {latency}ms*)"
await ctx.send(result)
@bot.hybrid_command(name="howl", description="Attempt a howl")
async def cmd_howl(ctx):
response = cc.handle_howl_command(ctx)
await ctx.send(response)
# @monitor_cmds(bot.log)
# @bot.hybrid_command(name="reload", description="Dynamically reload commands (INOP)")
# async def cmd_reload(ctx):
# """ Dynamically reloads Discord commands. """
# try:
# import cmd_discord
# import importlib
# importlib.reload(cmd_discord)
# cmd_discord.setup(bot)
# await ctx.send("Commands reloaded on first try!")
# except Exception as e:
# try:
# await bot.reload_extension("cmd_discord")
# await ctx.send("Commands reloaded on second try!")
# except Exception as e:
# try:
# await bot.unload_extension("cmd_discord")
# await bot.load_extension("cmd_discord")
# await ctx.send("Commands reloaded on third try!")
# except Exception as e:
# await ctx.send(f"Fallback reload failed: {e}")
@bot.hybrid_command(name="hi", description="Dev command for testing permissions system")
async def cmd_hi(ctx):
user_id = str(ctx.author.id)
user_roles = [role.name.lower() for role in ctx.author.roles] # Normalize to lowercase
if not has_permission("hi", user_id, user_roles, "discord"):
await ctx.send("You don't have permission to use this command.")
return
await ctx.send("Hello there!")
# @monitor_cmds(bot.log)
# @bot.hybrid_command(name="quote", description="Interact with the quotes system")
# async def cmd_quote(ctx, query: str = None):
# """
# !quote
# !quote add <text>
# !quote remove <id>
# !quote <id>
# """
# if not bot.db_conn:
# return await ctx.send("Database is unavailable, sorry.")
# args = query.split()
# # Send to our shared logic
# await cc.handle_quote_command(
# db_conn=bot.db_conn,
# log_func=bot.log,
# is_discord=True,
# ctx=ctx,
# args=list(args),
# get_twitch_game_for_channel=None # None for Discord
# )
# @monitor_cmds(bot.log)
# @bot.hybrid_group(name="quote", description="Interact with the quotes system", with_app_command=True)
# async def cmd_quote(ctx, query: str = None):
# """
# Usage:
# !quote -> get a random quote
# !quote <id> -> get a specific quote by number
# As a slash command, leave the query blank for a random quote or type a number.
# """
# if not bot.db_conn:
# return await ctx.send("Database is unavailable, sorry.")
# # Only process the base command if no subcommand was invoked.
# # When query is provided, split it into arguments (for a specific quote lookup).
# args = query.split() if query else []
# await cc.handle_quote_command(
# db_conn=bot.db_conn,
# log_func=bot.log,
# is_discord=True,
# ctx=ctx,
# args=args,
# get_twitch_game_for_channel=None # None for Discord
# )
@bot.hybrid_group(name="quote", description="Interact with the quotes system", with_app_command=True)
async def cmd_quote(ctx, *, id: Optional[int] = None):
"""
Handles base quote commands.
- `!quote` -> Fetch a random quote
- `!quote <id>` -> Fetch a specific quote by ID
- `/quote` or `/quote <id>` -> Works for slash commands
"""
if not bot.db_conn:
return await ctx.send("Database is unavailable, sorry.")
# If no query is provided, fetch a random quote.
if not id:
args = []
else:
args = id.split() # Split query into arguments
globals.log(f"'quote' command initiated with arguments: {args}", "DEBUG")
result = await cc.handle_quote_command(
db_conn=bot.db_conn,
is_discord=True,
ctx=ctx,
args=args,
get_twitch_game_for_channel=None
)
globals.log(f"'quote' result: {result}")
await ctx.send(result)
@cmd_quote.command(name="add", description="Add a quote")
async def cmd_quote_add(ctx, *, text: str):
"""
Usage:
- `!quote add <text>`
- `/quote add text:<your quote>` for slash commands.
"""
if not bot.db_conn:
return await ctx.send("Database is unavailable, sorry.")
# Ensure text isn't empty
if not text.strip():
return await ctx.send("You must provide a quote text.")
args = ["add", text] # Properly format arguments
result = await cc.handle_quote_command(
db_conn=bot.db_conn,
is_discord=True,
ctx=ctx,
args=args,
get_twitch_game_for_channel=None
)
await ctx.send(result)
@cmd_quote.command(name="remove", description="Remove a quote by number")
async def cmd_quote_remove(ctx, id: int):
"""
Usage:
- `!quote remove <id>`
- `/quote remove id:<quote number>`
"""
if not bot.db_conn:
return await ctx.send("Database is unavailable, sorry.")
args = ["remove", str(id)] # Properly pass the ID as an argument
result = await cc.handle_quote_command(
db_conn=bot.db_conn,
is_discord=True,
ctx=ctx,
args=args,
get_twitch_game_for_channel=None
)
await ctx.send(result)
######################
# The following log entry must be last in the file to verify commands loading as they should
######################
# Debug: Print that commands are being registered
try:
command_names = [cmd.name for cmd in bot.commands] # Extract command names
globals.log(f"Registering commands for Discord: {command_names}", "DEBUG")
except Exception as e:
globals.log(f"An error occured while printing registered commands for Discord: {e}", "WARNING")