From 8c1c94a2748da1beeeae43f028f11abf64bef276 Mon Sep 17 00:00:00 2001 From: Dustin Pianalto Date: Sun, 15 Dec 2019 22:46:05 -0900 Subject: [PATCH] Add functionality for prefixes to be case insensitive --- geeksbot/imports/geeksbot.py | 73 ++++++++++++++++++++++++++++++++++++ geeksbot/imports/strings.py | 11 ++++++ 2 files changed, 84 insertions(+) create mode 100644 geeksbot/imports/strings.py diff --git a/geeksbot/imports/geeksbot.py b/geeksbot/imports/geeksbot.py index bb3656c..7e881de 100644 --- a/geeksbot/imports/geeksbot.py +++ b/geeksbot/imports/geeksbot.py @@ -6,6 +6,8 @@ import logging import discord from discord.ext import commands +from discord.ext.commands.context import Context +from geeksbot.imports.strings import MyStringView import redis import aiohttp @@ -69,6 +71,77 @@ class Geeksbot(commands.Bot): async def get_prefixes(self, bot, message): return self.default_prefix.casefold() + async def get_context(self, message, *, cls=Context): + r"""|coro| + + Returns the invocation context from the message. + + This is a more low-level counter-part for :meth:`.process_commands` + to allow users more fine grained control over the processing. + + The returned context is not guaranteed to be a valid invocation + context, :attr:`.Context.valid` must be checked to make sure it is. + If the context is not valid then it is not a valid candidate to be + invoked under :meth:`~.Bot.invoke`. + + Parameters + ----------- + message: :class:`discord.Message` + The message to get the invocation context from. + cls + The factory class that will be used to create the context. + By default, this is :class:`.Context`. Should a custom + class be provided, it must be similar enough to :class:`.Context`\'s + interface. + + Returns + -------- + :class:`.Context` + The invocation context. The type of this can change via the + ``cls`` parameter. + """ + + view = MyStringView(message.content) + ctx = cls(prefix=None, view=view, bot=self, message=message) + + if self._skip_check(message.author.id, self.user.id): + return ctx + + prefix = await self.get_prefix(message) + invoked_prefix = prefix + + if isinstance(prefix, str): + if not view.skip_string(prefix): + return ctx + else: + try: + # if the context class' __init__ consumes something from the view this + # will be wrong. That seems unreasonable though. + if message.content.casefold().startswith(tuple(prefix)): + invoked_prefix = discord.utils.find(view.skip_string, prefix) + else: + return ctx + + except TypeError: + if not isinstance(prefix, list): + raise TypeError("get_prefix must return either a string or a list of string, " + "not {}".format(prefix.__class__.__name__)) + + # It's possible a bad command_prefix got us here. + for value in prefix: + if not isinstance(value, str): + raise TypeError("Iterable command_prefix or list returned from get_prefix must " + "contain only strings, not {}".format(value.__class__.__name__)) + + # Getting here shouldn't happen + raise + + invoker = view.get_word() + ctx.invoked_with = invoker + ctx.prefix = invoked_prefix + ctx.command = self.all_commands.get(invoker) + return ctx + async def close(self): try: await super().close() diff --git a/geeksbot/imports/strings.py b/geeksbot/imports/strings.py new file mode 100644 index 0000000..2d4a53b --- /dev/null +++ b/geeksbot/imports/strings.py @@ -0,0 +1,11 @@ +from discord.ext.commands.view import StringView + + +class MyStringView(StringView): + def skip_string(self, string): + strlen = len(string) + if self.buffer[self.index:self.index + strlen].casefold() == string: + self.previous = self.index + self.index += strlen + return True + return False