From 9b4eb98e29b8d9c2828a7a19797da3f5ac450210 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sun, 29 Aug 2021 15:10:27 -0400 Subject: [PATCH 1/4] Moved AppCmd stuff to .commands --- discord/bot.py | 62 +++++++------------------------------------------- 1 file changed, 8 insertions(+), 54 deletions(-) diff --git a/discord/bot.py b/discord/bot.py index 9b854dff7e..953bc20f9a 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -22,6 +22,8 @@ DEALINGS IN THE SOFTWARE. """ +from __future__ import annotations # will probably need in future for type hinting + import asyncio import inspect from typing import Callable @@ -29,65 +31,17 @@ from .client import Client from .shard import AutoShardedClient from .utils import get - - -class SlashCommand: - type = 1 - - def __new__(cls, *args, **kwargs): - self = super().__new__(cls) - - self.__original_kwargs__ = kwargs.copy() - return self - - def __init__(self, func, *args, **kwargs): - if not asyncio.iscoroutinefunction(func): - raise TypeError("Callback must be a coroutine.") - - name = kwargs.get("name") or func.__name__ - if not isinstance(name, str): - raise TypeError("Name of a command must be a string.") - self.name = name - - description = ( - kwargs.get("description") - or inspect.cleandoc(func.__doc__) - or "No description set" - ) - if not isinstance(name, str): - raise TypeError("Description of a command must be a string.") - self.description = description - - self.callback = func - - def to_dict(self): - return {"name": self.name, "description": self.description} - - def __eq__(self, other): - return ( - isinstance(other, SlashCommand) - and other.name == self.name - and other.description == self.description - ) - - -class UserCommand: - type = 2 - - -class MessageCommand: - type = 3 - +from .commands import SlashCommand, MessageCommand, UserCommand class ApplicationCommandMixin: def __init__(self): self.to_register = [] self.app_commands = {} - def add_command(self, command): + def add_application_command(self, command): self.to_register.append(command) - def remove_command(self, command): + def remove_application_command(self, command): self.app_commands.remove(command) async def sync_commands(self): @@ -126,9 +80,6 @@ async def start(self, token, *, reconnect=True) -> None: await self.login(token) await self.connect(reconnect=reconnect) await self.register_commands() - - -class Bot(BotBase, Client): def slash(self, **kwargs): def wrap(func: Callable) -> SlashCommand: command = SlashCommand(func, **kwargs) @@ -140,5 +91,8 @@ def wrap(func: Callable) -> SlashCommand: command = slash +class Bot(BotBase, Client): + + class AutoShardedBot(BotBase, AutoShardedClient): pass From d71971034cab54d8341f7a2aa797f936d8fd2b42 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sun, 29 Aug 2021 15:11:00 -0400 Subject: [PATCH 2/4] SLASH COMMAND OPTIONS!!! yes --- discord/commands.py | 133 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 discord/commands.py diff --git a/discord/commands.py b/discord/commands.py new file mode 100644 index 0000000000..bb8791d5be --- /dev/null +++ b/discord/commands.py @@ -0,0 +1,133 @@ +""" +The MIT License (MIT) + +Copyright (c) 2015-present Rapptz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +""" + +from __future__ import annotations + +import asyncio +import inspect +from enum import Enum +from collections import OrderedDict + +from .member import Member +from .abc import GuildChannel +from .role import Role + +class SlashCommand: + type = 1 + + def __new__(cls, *args, **kwargs): + self = super().__new__(cls) + + self.__original_kwargs__ = kwargs.copy() + return self + def __init__(self, func, *args, **kwargs): + if not asyncio.iscoroutinefunction(func): + raise TypeError('Callback must be a coroutine.') + + name = kwargs.get('name') or func.__name__ + if not isinstance(name, str): + raise TypeError('Name of a command must be a string.') + self.name = name + + description = kwargs.get('description') or inspect.cleandoc(func.__doc__) + if description == None: + description = "No description set" + elif not isinstance(name, str): + raise TypeError('Description of a command must be a string.') + self.description = description + + options = OrderedDict(inspect.signature(func).parameters) + options.pop(list(options)[0]) + self.options = dict(options) + + + self.callback = func + + def to_dict(self): + return { + "name":self.name, + "description":self.description, + "options":[o.to_dict() for o in self.options] + } + def __eq__(self, other): + return ( + isinstance(other, SlashCommand) + and other.name == self.name + and other.description == self.description + ) + +class SlashCommandOptionType(Enum): + custom = 0 + #sub_command = 1 + #sub_command_group = 2 + string = 3 + integer = 4 + boolean = 5 + user = 6 + channel = 7 + role = 8 + mentionable = 9 + number = 10 + + @classmethod + def from_datatype(cls, datatype): + if isinstance(datatype, str): + return cls.string + if isinstance(datatype, str): + return cls.integer + if isinstance(datatype, str): + return cls.boolean + if isinstance(datatype, Member): + return cls.user + if isinstance(datatype, GuildChannel): + return cls.channel + if isinstance(datatype, Role): + return cls.role + if isinstance(datatype, None): # FIXME uhm + return cls.mentionable + if isinstance(datatype, float): + return cls.number + return cls.custom + + +class Option: + def __init__(self, type, /, **kwargs): + self.name = kwargs.pop("name", None) + self.description = kwargs.pop("description") + if not isinstance(type, SlashCommandOptionType): + type = SlashCommandOptionType.from_datatype(type) + self.type = type + self.required = kwargs.pop("required", False) + self.choices = list(i for i in kwargs.pop("choices", list())) + +class OptionChoice: + def __init__(self, name, value=None): + self.name = name + self.value = value or name + +class UserCommand: + type = 2 + +class MessageCommand: + type = 3 From 7ac75e5292581f3a3dd4caf513038a2d4ecc6284 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sun, 29 Aug 2021 15:29:12 -0400 Subject: [PATCH 3/4] Added `to_dict()` for Option/OptionChoice As well as default naming for Option names --- discord/commands.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/discord/commands.py b/discord/commands.py index bb8791d5be..6e0bf6c5af 100644 --- a/discord/commands.py +++ b/discord/commands.py @@ -59,6 +59,9 @@ def __init__(self, func, *args, **kwargs): options = OrderedDict(inspect.signature(func).parameters) options.pop(list(options)[0]) + for a, o in options: + if o.name == None: + o.name == a self.options = dict(options) @@ -120,11 +123,24 @@ def __init__(self, type, /, **kwargs): self.type = type self.required = kwargs.pop("required", False) self.choices = list(i for i in kwargs.pop("choices", list())) + def to_dict(self): + return { + "name":self.name, + "description":self.description, + "type":int(self.type), + "required":self.required, + "choices":[c.to_dict() for c in self.choices] + } class OptionChoice: def __init__(self, name, value=None): self.name = name self.value = value or name + def to_dict(self): + return { + "name":self.name", + "value":self.value + } class UserCommand: type = 2 From da7d95b00f662b481892133395362dee4f321501 Mon Sep 17 00:00:00 2001 From: Middledot <78228142+Middledot@users.noreply.github.com> Date: Sun, 29 Aug 2021 15:29:26 -0400 Subject: [PATCH 4/4] smh --- discord/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/bot.py b/discord/bot.py index 953bc20f9a..544ce19843 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -92,7 +92,7 @@ def wrap(func: Callable) -> SlashCommand: class Bot(BotBase, Client): - + pass class AutoShardedBot(BotBase, AutoShardedClient): pass