Skip to content

Commit

Permalink
feat: Support subcommands
Browse files Browse the repository at this point in the history
- Support subcommand feature in interactions
- Updated the update command to support subcommands
  • Loading branch information
Seniru committed May 24, 2023
1 parent 7f01ac8 commit b1867c1
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 31 deletions.
5 changes: 5 additions & 0 deletions src/bots/Discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ async def on_message(self, message):
"color": 0xcc0000
}))

if cmd["subcommands"]:
return await cmd["subcommands"].__dict__[args[1]](args[2:], message, self)

await cmd["f"](args[1:], message, self)
else:

Expand Down Expand Up @@ -153,6 +156,8 @@ async def on_interaction(self, interaction: discord.Interaction):
),
"color": 0xcc0000
}))
if cmd["subcommands"]:
return await cmd["subcommands"].__dict__[interaction.options[0]["name"]](interaction.args, interaction, self)
await cmd["f"](interaction.args, interaction, self)

async def on_member_join(self, member):
Expand Down
85 changes: 58 additions & 27 deletions src/bots/cmd_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,32 @@

commands = {}

def command(discord=False, tfm=False, whisper_command=False, aliases=None, allowed_roles=None):
def command(discord=False, tfm=False, whisper_command=False, aliases=None, allowed_roles=None, subcommands=None):

def decorator(f):

functools.wraps(f)
commands[f.__name__] = { "f": f, "discord": discord, "tfm": tfm, "whisper_command": whisper_command, "allowed_roles": allowed_roles }
commands[f.__name__] = {
"f": f,
"name": f.__name__,
"discord": discord,
"tfm": tfm,
"whisper_command": whisper_command,
"allowed_roles": allowed_roles,
"subcommands": subcommands
}

if aliases:
for alias in aliases:
commands[alias] = { "f": f, "discord": discord, "tfm": tfm, "whisper_command": whisper_command, "allowed_roles": allowed_roles }
commands[alias] = {
"f": f,
"name": f.__name__,
"discord": discord,
"tfm": tfm,
"whisper_command": whisper_command,
"allowed_roles": allowed_roles,
"subcommands": subcommands
}

def wrapper(*args, **kwargs):
return f(*args, **kwargs)
Expand All @@ -47,17 +63,18 @@ def wrapper(*args, **kwargs):
from .commands import qotd as qhandler


@command(discord = True, allowed_roles = [ data["roles"]["admin"], data["roles"]["mod"] ])
@command(discord = True, allowed_roles = [ data["roles"]["admin"], data["roles"]["mod"] ], subcommands = qhandler)
async def qotd(args, msg, client):
"""Question of the day
Args:
option (string): add, queue
data (string): Relevant data
"""
if len(args) > 0:
if hasattr(qhandler, args[0]):
await qhandler.__getattribute__(args[0])(args[1:], msg, client)
#if len(args) > 0:
# if hasattr(qhandler, args[0]):
# await qhandler.__getattribute__(args[0])(args[1:], msg, client)
pass

from .commands import mod

Expand Down Expand Up @@ -547,6 +564,33 @@ async def botw(args, msg, client):
await msg.reply(embed = Embed.from_dict(embed))


def get_cmd(data, subcommand=None):
"""Helper function for `update`"""

doc = parse_doc(data["f"])
cmd_data = {
"name": data["name"],
"type": 1,
"description": doc.short_description,
"options": []
}
if data["subcommands"]:
for scmd, f in data["subcommands"].__dict__.items():
if scmd.startswith("__"): continue
# shitty implementation for subcommands I know but it should be fine for now
cmd_data["options"].append(get_cmd({ "name": scmd, "f": f, "subcommands": None }, subcommand=True))
else:
for param in doc.params:
param_data = {
"name": param.arg_name,
"description": param.description,
"required": not param.is_optional
}
param_data["type"] = AppCommandOptionType[param.type_name][1]
cmd_data["options"].append(param_data)
return cmd_data


@command(discord=True)
async def update(args, msg, client):
"""Updates the application commands
Expand All @@ -557,33 +601,20 @@ async def update(args, msg, client):
await msg.reply("Updating...")
i = 0
cmdlist = list(commands.keys())
headers = {
"Authorization": "Bot {}".format(os.environ["DISCORD"])
}
while i < len(cmdlist):
cmd = cmdlist[i]
data = commands[cmd]
doc = parse_doc(data["f"])
cmd_data = {
"name": cmd,
"type": 1,
"description": doc.short_description,
"options": []
}
for param in doc.params:
param_data = {
"name": param.arg_name,
"description": param.description,
"required": not param.is_optional
}
param_data["type"] = AppCommandOptionType[param.type_name][1]
cmd_data["options"].append(param_data)
headers = {
"Authorization": "Bot {}".format(os.environ["DISCORD"])
}
r = requests.post(f"https://discord.com/api/v10/applications/{client.application_id}/guilds/{client.main_guild.id}/commands", headers=headers, json=cmd_data)
data = get_cmd(commands[cmd])

r = requests.post(f"https://discord.com/api/v10/applications/{client.application_id}/guilds/{client.main_guild.id}/commands", headers=headers, json=data)
if r.status_code == 429:
time.sleep(2)
continue
print(r.status_code, r.content)
time.sleep(1)

i += 1

await msg.channel.send(":white_check_mark: Updated!")
20 changes: 19 additions & 1 deletion src/bots/commands/qotd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
async def queue(args, msg, client):
"""Check the question queueu
"""

import json
from discord import Embed
Expand All @@ -14,6 +16,12 @@ async def queue(args, msg, client):
}))

async def add(args, msg, client):
"""Adds a question
Args:
index (integer): The place to add in the queue
question (string): The question
"""
if args[0].isnumeric():
client.questions["questions"].insert(int(args[0]), " ".join(args[1:]))
else:
Expand All @@ -23,6 +31,11 @@ async def add(args, msg, client):
await msg.reply("Added the question!")

async def remove(args, msg, client):
"""Removes a question
Args:
index (integer, optional): The question number to remove. Removes first question if none
"""
try:
client.questions["questions"].pop(int(args[0] if len(args) > 0 else "1") -1)
await client.update_qotd()
Expand All @@ -32,11 +45,16 @@ async def remove(args, msg, client):


async def ask(args, msg, client):
"""Asks the first available question in the queue
Args:
force (boolean, optional): Whether to bypass the time limit
"""
from datetime import datetime, timedelta
from discord import Embed
from data import data
import json
force = len(args) > 0 and args[0] == "force"
force = len(args) > 0 and ("force", "true") in args[0].lower()
now, lastpost = datetime.now(), datetime.fromtimestamp(client.questions["last-post"])
cooldown_over = now > (lastpost + timedelta(seconds = 1 * 60 * 60 * 24))
qotd_channel = client.get_channel(data["channels"]["qotd"])
Expand Down
8 changes: 5 additions & 3 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ def __init__(self, interaction, client):
self.reply = interaction.response.send_message
self.options = interaction.data.get("options", [])
self.content = []
for option in self.options:
# todo: see how it works with mentions later
self.content.append(option["value"])
if get(self.options, 0, None):
for option in self.options if not self.options[0].get("options") else self.options[0]["options"]:
if option.get("value") and option["value"]:
self.content.append(str(option["value"]))
self.content = " ".join(self.content)
# splitting args after joining to avoid having empty string args
self.args = self.content.split(" ")

0 comments on commit b1867c1

Please sign in to comment.