[Core] Improve API token converter (#2692)

* improve api converter

* make usage more clear
This commit is contained in:
Michael H 2019-05-25 17:58:14 -04:00 committed by Kowlin
parent 2e271d695b
commit 68590dfdb8
2 changed files with 59 additions and 7 deletions

View File

@ -1,7 +1,9 @@
import re import re
from typing import TYPE_CHECKING import functools
from typing import TYPE_CHECKING, Optional, List, Dict
import discord import discord
from discord.ext import commands as dpy_commands
from . import BadArgument from . import BadArgument
from ..i18n import Translator from ..i18n import Translator
@ -9,7 +11,7 @@ from ..i18n import Translator
if TYPE_CHECKING: if TYPE_CHECKING:
from .context import Context from .context import Context
__all__ = ["GuildConverter"] __all__ = ["GuildConverter", "APIToken", "DictConverter", "get_dict_converter"]
_ = Translator("commands.converter", __file__) _ = Translator("commands.converter", __file__)
@ -47,16 +49,20 @@ class APIToken(discord.ext.commands.Converter):
This will parse the input argument separating the key value pairs into a This will parse the input argument separating the key value pairs into a
format to be used for the core bots API token storage. format to be used for the core bots API token storage.
This will split the argument by either `;` or `,` and return a dict This will split the argument by either `;` ` `, or `,` and return a dict
to be stored. Since all API's are different and have different naming convention, to be stored. Since all API's are different and have different naming convention,
this leaves the onus on the cog creator to clearly define how to setup the correct this leaves the onus on the cog creator to clearly define how to setup the correct
credential names for their cogs. credential names for their cogs.
Note: Core usage of this has been replaced with DictConverter use instead.
This may be removed at a later date (with warning)
""" """
async def convert(self, ctx, argument) -> dict: async def convert(self, ctx, argument) -> dict:
bot = ctx.bot bot = ctx.bot
result = {} result = {}
match = re.split(r";|,", argument) match = re.split(r";|,| ", argument)
# provide two options to split incase for whatever reason one is part of the api key we're using # provide two options to split incase for whatever reason one is part of the api key we're using
if len(match) > 1: if len(match) > 1:
result[match[0]] = "".join(r for r in match[1:]) result[match[0]] = "".join(r for r in match[1:])
@ -65,3 +71,48 @@ class APIToken(discord.ext.commands.Converter):
if not result: if not result:
raise BadArgument(_("The provided tokens are not in a valid format.")) raise BadArgument(_("The provided tokens are not in a valid format."))
return result return result
class DictConverter(dpy_commands.Converter):
"""
Converts pairs of space seperated values to a dict
"""
def __init__(self, *expected_keys: str, delims: Optional[List[str]] = None):
self.expected_keys = expected_keys
self.delims = delims or [" "]
self.pattern = re.compile(r"|".join(re.escape(d) for d in self.delims))
async def convert(self, ctx: "Context", argument: str) -> Dict[str, str]:
ret: Dict[str, str] = {}
args = self.pattern.split(argument)
if len(args) % 2 != 0:
raise BadArgument()
iterator = iter(args)
for key in iterator:
if self.expected_keys and key not in self.expected_keys:
raise BadArgument(_("Unexpected key {key}").format(key))
ret[key] = next(iterator)
return ret
def get_dict_converter(*expected_keys: str, delims: Optional[List[str]] = None) -> type:
"""
Returns a typechecking safe `DictConverter` suitable for use with discord.py
"""
class PartialMeta(type(DictConverter)):
__call__ = functools.partialmethod(
type(DictConverter).__call__, *expected_keys, delims=delims
)
class ValidatedConverter(DictConverter, metaclass=PartialMeta):
pass
return ValidatedConverter

View File

@ -45,6 +45,8 @@ log = logging.getLogger("red")
_ = i18n.Translator("Core", __file__) _ = i18n.Translator("Core", __file__)
TokenConverter = commands.get_dict_converter(delims=[" ", ",", ";"])
class CoreLogic: class CoreLogic:
def __init__(self, bot: "Red"): def __init__(self, bot: "Red"):
@ -1063,7 +1065,7 @@ class Core(commands.Cog, CoreLogic):
@_set.command() @_set.command()
@checks.is_owner() @checks.is_owner()
async def api(self, ctx: commands.Context, service: str, *tokens: commands.converter.APIToken): async def api(self, ctx: commands.Context, service: str, *, tokens: TokenConverter):
"""Set various external API tokens. """Set various external API tokens.
This setting will be asked for by some 3rd party cogs and some core cogs. This setting will be asked for by some 3rd party cogs and some core cogs.
@ -1076,8 +1078,7 @@ class Core(commands.Cog, CoreLogic):
""" """
if ctx.channel.permissions_for(ctx.me).manage_messages: if ctx.channel.permissions_for(ctx.me).manage_messages:
await ctx.message.delete() await ctx.message.delete()
entry = {k: v for t in tokens for k, v in t.items()} await ctx.bot.db.api_tokens.set_raw(service, value=tokens)
await ctx.bot.db.api_tokens.set_raw(service, value=entry)
await ctx.send(_("`{service}` API tokens have been set.").format(service=service)) await ctx.send(_("`{service}` API tokens have been set.").format(service=service))
@commands.group() @commands.group()