Skip to content

Commit

Permalink
Merge pull request #8 from doodlebunnyhops/base-player-commands
Browse files Browse the repository at this point in the history
Base Player Commands
  • Loading branch information
doodlebunnyhops authored Oct 17, 2024
2 parents 1cfa89e + 0f497f6 commit 7d6eda1
Show file tree
Hide file tree
Showing 27 changed files with 1,695 additions and 227 deletions.
File renamed without changes.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ Not paitent enough to wait on a cauldron event, no worries try your hand at pump

## Commands

- [Moderator](/docs/moderator_commands.md)
- [Moderator](/docs/moderator_commands.md): Locked commands to a specific role set by server admin.
- [Player](/docs/player_commands.md)
- [Active Commands](/docs/all_current_commands.md):


## Development

So glad you want to help! Refer to these docs on setup for dev.
- [Development requirements and setup](/docs/development.md)
- [Contributing Guidlines](/docs/CONTRIBUTION.md)
- [Contributing Guidlines](CONTRIBUTION.md)

## Attribution

Expand Down
113 changes: 91 additions & 22 deletions discord-bot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import discord
from discord.ext import commands
from discord import app_commands
import context_menu.player
import context_menu.player as player
from db_utils import initialize_database, get_game_join_msg_settings,is_player_active,create_player_data
from cogs.mod import Mod
from cogs.game import Game
from utils.messages import MessageLoader
import settings
import context_menu
# import context_menu
from modals.player import Treat

print(discord.__version__)
print(discord.__file__)
Expand All @@ -30,7 +32,10 @@
class MyBot(commands.Bot):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.guild_id = settings.GUILDS_ID
if settings.GUILDS_ID is not None:
self.guild_id = settings.GUILDS_ID
else:
self.guild_id = None
# self.tree = discord.app_commands.CommandTree(self)
self.message_loader = None

Expand All @@ -43,43 +48,106 @@ async def setup_hook(self):
print("Loading spooky messages...")
self.message_loader = MessageLoader('utils/messages.json')

join_cm = app_commands.ContextMenu(name="Join Game", callback=context_menu.player.join)
trick_cm = app_commands.ContextMenu(name="Trick Player", callback=context_menu.player.trick)

#Set Context Menus callback
join_cm = app_commands.ContextMenu(name="Join Game", callback=player.join)
trick_cm = app_commands.ContextMenu(name="Trick Player", callback=player.trick)
bucket_cm = app_commands.ContextMenu(name="Check Bucket", callback=player.bucket)

@bot.tree.context_menu(name="Treat Player")
async def treat_modal(interaction: discord.Interaction, user: discord.Member):
# Show the modal for user input
modal = Treat(target_user=user)
await interaction.response.send_modal(modal)

# @self.tree.context_menu(name="Join Game")
# @checks.must_target_self()
# async def join(interaction: discord.Interaction, user: discord.Member):
# await interaction.response.send_message(f"{user.display_name} you are trying to join!", ephemeral=True)

# Load all cogs
# Load cogs and cm's
print("Loading group commands...")
#the additional options were the trick to force guild update
self.tree.add_command(Mod.cmds_group,guild=self.guild_id,override=True)
self.tree.add_command(join_cm,guild=self.guild_id,override=True)
self.tree.add_command(trick_cm,guild=self.guild_id,override=True)
await self.load_extension("cogs.player")
if self.guild_id is None:
self.tree.add_command(Mod.cmds_group,override=True)
self.tree.add_command(Game.game_group,override=True)
self.tree.add_command(join_cm,override=True)
self.tree.add_command(trick_cm,override=True)
self.tree.add_command(bucket_cm,override=True)
self.tree.add_command(treat_modal,override=True)
await self.load_extension("cogs.player")
# await self.load_extension("cogs.game")
else:
self.tree.add_command(Mod.cmds_group,guild=self.guild_id,override=True)
self.tree.add_command(Game.game_group,guild=self.guild_id,override=True)
self.tree.add_command(join_cm,guild=self.guild_id,override=True)
self.tree.add_command(trick_cm,guild=self.guild_id,override=True)
self.tree.add_command(bucket_cm,guild=self.guild_id,override=True)
self.tree.add_command(treat_modal,guild=self.guild_id,override=True)
await self.load_extension("cogs.player")
# await self.load_extension("cogs.game")

print("Syncing tree...")
try:
# Sync the commands
logger.info(f"Attempting to sync commands for guild ID: {self.guild_id}")
self.tree.copy_global_to(guild=self.guild_id) #this is what loaded in my slash command to the guild i wanted
synced = await self.tree.sync(guild=self.guild_id)

# Log detailed info about synced commands
logger.info(f"Successfully synced {len(synced)} commands for guild ID: {self.guild_id}")
for command in synced:
logger.info(f"Command synced: {command.name} - {command.description}")
if hasattr(command, 'options'):
for option in command.options:
logger.info(f" - Option: {option.name} ({option.type}) - {option.description}")
if self.guild_id is None:
# self.tree.copy_global_to()
synced = await self.tree.sync()
logger.info(f"Attempting to sync commands globally")
else:
self.tree.copy_global_to(guild=self.guild_id)
synced = await self.tree.sync(guild=self.guild_id)
logger.info(f"Attempting to sync commands for guild ID: {self.guild_id.id}")

# Log detailed info about synced commands
logger.info(f"Successfully synced {len(synced)} commands for guild ID: {self.guild_id.id}")

for i, command in enumerate(synced):
is_last_command = i == len(synced) - 1
log_command(command, is_last=is_last_command)

except Exception as e:
logger.error(f"Error syncing commands for guild ID {self.guild_id}: {str(e)}")

async def on_ready(self):
print(f'Logged in as {self.user} and bot is ready!')


def log_command(command, depth=0, parent_cmd=None, is_last=False, parent_has_args=False):
if depth > 0:
indent = "│ " * (depth - 1) + ("└── " if is_last else "├── ")
else:
indent = ""

full_command = f"{parent_cmd} {command.name}" if parent_cmd else f"/{command.name}"

# Display the command or subcommand description
if depth == 0:
logger.info(f"{full_command} : {command.description}")
else:
logger.info(f"{indent}{command.name} : {command.description}")

# Handle subcommands and options
if hasattr(command, 'options') and command.options:
subcommand_found = False
for i, option in enumerate(command.options):
is_last_option = i == len(command.options) - 1
if option.type in (discord.AppCommandOptionType.subcommand, discord.AppCommandOptionType.subcommand_group):
log_command(option, depth + 1, full_command, is_last=is_last_option, parent_has_args=False)
subcommand_found = True

# Display options with properly connected lines if no subcommands were found
if not subcommand_found:
args_indent = "│ " * depth if not is_last else " " * depth
logger.info(f"{args_indent}Arguments:")
for i, opt in enumerate(command.options):
is_last_arg = i == len(command.options) - 1
opt_indent = "│ " * depth + ("└── " if is_last_arg else "├── ")

# Log option name, description, and the type of option
opt_type = opt.type.name if hasattr(opt.type, 'name') else str(opt.type)
logger.info(f"{opt_indent}{opt.name} : {opt.description} (Type: {opt_type})")


intents = discord.Intents.all()
# intents = discord.Intents.default()
# intents.message_content = True
Expand Down Expand Up @@ -172,6 +240,7 @@ async def on_app_command_error(interaction: discord.Interaction, error):
if interaction.response.is_done():
await interaction.followup.send("An error occurred while processing the command.", ephemeral=True)
else:
print(f"An error occurred while processing:\n\tError: {error}")
await interaction.response.send_message("An error occurred while processing the command.", ephemeral=True)
# await utils.post_admin_message(bot, interaction.guild.id, f"An error occurred while processing:\n\tError: {error}.\n\tInvoked by: {interaction.user.name}\n\tAttempted: {interaction.command.name}")

Expand Down
18 changes: 18 additions & 0 deletions discord-bot/cogs/game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from discord import app_commands
from discord.ext import commands

from cogs.game_commands.set import set_group

class Game(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.commands_to_toggle = ["join", "trick", "treat"]

game_group = app_commands.Group(name="game",description="Game options and commands")
game_group.add_command(set_group)




async def setup(bot):
await bot.tree.add_command(Game.game_group)
47 changes: 47 additions & 0 deletions discord-bot/cogs/game_commands/set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@

import discord
from discord import app_commands
import utils.checks as checks
from db_utils import get_game_settings, set_game_disabled

from modals.settings import Game

# Subcommand group for setting (used within the server group)
set_group = app_commands.Group(name="set", description="Set commands")

@set_group.command(name="settings", description="Update the game settings.")
@checks.check_if_has_permission_or_role()
async def set_game_settings(interaction: discord.Interaction):
modal = Game()
await interaction.response.send_modal(modal)


@set_group.command(name="state", description="Enable or disable the game.")
@app_commands.describe(
state="Enable or disable the game."
)
@app_commands.choices(state=[
app_commands.Choice(name="Enable", value="enable"),
app_commands.Choice(name="Disable", value="disable")
])
@checks.check_if_has_permission_or_role()
async def set_game_state( interaction: discord.Interaction, state: app_commands.Choice[str]):
guild_id = interaction.guild.id

# Fetch current settings from the db using db_utils
game_disabled, _, _ = get_game_settings(guild_id)

# Logic based on the selected option and current game state
if state.value == "enable":
if not game_disabled:
await interaction.response.send_message("The game commands are already enabled.", ephemeral=True)
return
set_game_disabled(guild_id, False)
await interaction.response.send_message("The game commands have been enabled.", ephemeral=True)

elif state.value == "disable":
if game_disabled:
await interaction.response.send_message("The game commands are already disabled.", ephemeral=True)
return
set_game_disabled(guild_id, True)
await interaction.response.send_message("The game commands have been disabled.", ephemeral=True)
2 changes: 1 addition & 1 deletion discord-bot/cogs/mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, bot):
self.bot = bot

# Top-level group
cmds_group = app_commands.Group(name="zmod", description="Moderator Commands")
cmds_group = app_commands.Group(name="bot", description="Moderator Commands")

cmds_group.add_command(set_group)
cmds_group.add_command(get_group)
Expand Down
60 changes: 59 additions & 1 deletion discord-bot/cogs/mod_commands/get.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import db_utils
import utils.checks as checks
from utils.utils import post_to_target_channel
from utils.helper import calculate_thief_success_rate

# Subcommand group for setting (used within the server group)
get_group = app_commands.Group(name="get", description="get commands")
Expand Down Expand Up @@ -89,13 +90,16 @@ async def get_game_join_msg(interaction: discord.Interaction):
await interaction.response.send_message("An error occurred while fetching the game invite message.", ephemeral=True)

# Slash Command to get the event or admin channel
@checks.check_if_has_permission_or_role()
@get_group.command(name="channel", description="Get the channel where event or admin messages will be posted.")
@checks.check_if_has_permission_or_role()
@app_commands.choices(channel_type=[
app_commands.Choice(name="Event", value="event"),
app_commands.Choice(name="Admin", value="admin"),
app_commands.Choice(name="Both", value="both")
])
@app_commands.describe(
channel_type="The type of channel to get (event, admin, or both)"
)
async def get_channel(interaction: discord.Interaction, channel_type: app_commands.Choice[str]):
guild_id = interaction.guild.id

Expand Down Expand Up @@ -163,3 +167,57 @@ async def get_channel(interaction: discord.Interaction, channel_type: app_comman
await interaction.response.send_message(personal_message, ephemeral=True)
await post_to_target_channel(interaction,admin_message,channel_type="admin")

@get_group.command(name="player", description="View a player's stats.")
@checks.check_if_has_permission_or_role()
@app_commands.choices(get=[
app_commands.Choice(name="Stats", value="stats"),
app_commands.Choice(name="Hidden Values", value="hidden_values"),
app_commands.Choice(name="All", value="all")
])
@app_commands.describe(
user="The user whose stats you want to view",
get="The details you want to view (stats, hidden values, or both)"
)
async def get_player_stats(interaction: discord.Interaction, user: discord.Member, get: app_commands.Choice[str]):
guild_id = interaction.guild.id
# Fetch the player's stats from the database
player_data = db_utils.get_player_data(user.id, guild_id)

if player_data:
try:
candy_count, successful_steals, failed_steals, candy_given, tickets_purchased, active = list(player_data.values())
active_status = "Active" if active == 1 else "Inactive"

# Prepare the response based on the selected details option
response_message = ""

if get.value == "stats" or get.value == "all":
# Standard player stats
response_message += (
f"{user.display_name} has {candy_count} candy.\n"
f"Potions In Stock: {tickets_purchased}\n"
f"Successful steals: {successful_steals}\n"
f"Failed steals: {failed_steals}\n"
f"Candy given: {candy_given}\n"
f"Status: {active_status}\n"
)

if get.value == "hidden_values" or get.value == "all":
# Hidden values
# evilness, sweetness, trick_success_rate = hidden_values
trick_success_rate = calculate_thief_success_rate(candy_count)
response_message += (
f"Hidden Values:\n"
# f"Evilness: {evilness}\n"
# f"Sweetness: {sweetness}\n"
f"Trick Success Rate: {trick_success_rate*100}%\n"
)

await interaction.response.send_message(response_message, ephemeral=True)

except Exception as e:
logging.error(f"Error fetching player stats: {str(e)}")
await interaction.response.send_message("An error occurred while fetching player stats.", ephemeral=True)

else:
await interaction.response.send_message(f"{user.display_name} has not joined the game yet.", ephemeral=True)
9 changes: 9 additions & 0 deletions discord-bot/cogs/mod_commands/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@


@remove_group.command(name="join_game_msg", description="I cant remove messages :P")
@app_commands.describe(
channel="The channel where the invite message will be ignored."
)
@checks.check_if_has_permission_or_role()
async def update_game_join_msg(interaction: discord.Interaction, channel: discord.TextChannel):
await interaction.response.send_message(f":wave: Hey {interaction.user.name}...so I don't have perms to remove the join message in {channel.name} ;) you'll have to have someone with proper permissions manually remove it so they can /update")

@remove_group.command(name="role", description="Remove a role from the games restricted commands")
@app_commands.describe(
role="The role to be removed"
)
@checks.check_if_has_permission_or_role()
async def remove_role(interaction: discord.Interaction, role: discord.Role):

Expand All @@ -36,6 +42,9 @@ async def remove_role(interaction: discord.Interaction, role: discord.Role):

@remove_group.command(name="channel", description="Remove the event or admin channel setting.")
@checks.check_if_has_permission_or_role()
@app_commands.describe(
channel_type="The Channel Type to stop posting messages to."
)
@app_commands.choices(channel_type=[
app_commands.Choice(name="Event", value="event"),
app_commands.Choice(name="Admin", value="admin")
Expand Down
Loading

0 comments on commit 7d6eda1

Please sign in to comment.