diff options
author | 2019-09-30 14:48:49 +0200 | |
---|---|---|
committer | 2019-09-30 14:48:49 +0200 | |
commit | 6120f9d90272cab33aa1da857dbfaeb1b5adbd9a (patch) | |
tree | 740c8bd789e24ca22913caad26bcd5897cd6d270 /bot/utils/__init__.py | |
parent | Edit dates (diff) | |
parent | Merge pull request #276 from python-discord/update-flake8-annotations (diff) |
Merge branch 'master' into date-fix
Diffstat (limited to 'bot/utils/__init__.py')
-rw-r--r-- | bot/utils/__init__.py | 74 |
1 files changed, 62 insertions, 12 deletions
diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index ef18a1b9..0aa50af6 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -1,4 +1,6 @@ import asyncio +import re +import string from typing import List import discord @@ -9,21 +11,15 @@ from bot.pagination import LinePaginator async def disambiguate( ctx: Context, entries: List[str], *, timeout: float = 30, - per_page: int = 20, empty: bool = False, embed: discord.Embed = None -): + entries_per_page: int = 20, empty: bool = False, embed: discord.Embed = None +) -> str: """ Has the user choose between multiple entries in case one could not be chosen automatically. + Disambiguation will be canceled after `timeout` seconds. + This will raise a BadArgument if entries is empty, if the disambiguation event times out, or if the user makes an invalid choice. - - :param ctx: Context object from discord.py - :param entries: List of items for user to choose from - :param timeout: Number of seconds to wait before canceling disambiguation - :param per_page: Entries per embed page - :param empty: Whether the paginator should have an extra line between items - :param embed: The embed that the paginator will use. - :return: Users choice for correct entry. """ if len(entries) == 0: raise BadArgument('No matches found.') @@ -33,7 +29,7 @@ async def disambiguate( choices = (f'{index}: {entry}' for index, entry in enumerate(entries, start=1)) - def check(message): + def check(message: discord.Message) -> bool: return (message.content.isdigit() and message.author == ctx.author and message.channel == ctx.channel) @@ -43,7 +39,7 @@ async def disambiguate( embed = discord.Embed() coro1 = ctx.bot.wait_for('message', check=check, timeout=timeout) - coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=per_page, + coro2 = LinePaginator.paginate(choices, ctx, embed=embed, max_lines=entries_per_page, empty=empty, max_size=6000, timeout=9000) # wait_for timeout will go to except instead of the wait_for thing as I expected @@ -77,3 +73,57 @@ async def disambiguate( return entries[index - 1] except IndexError: raise BadArgument('Invalid choice.') + + +def replace_many( + sentence: str, replacements: dict, *, ignore_case: bool = False, match_case: bool = False +) -> str: + """ + Replaces multiple substrings in a string given a mapping of strings. + + By default replaces long strings before short strings, and lowercase before uppercase. + Example: + var = replace_many("This is a sentence", {"is": "was", "This": "That"}) + assert var == "That was a sentence" + + If `ignore_case` is given, does a case insensitive match. + Example: + var = replace_many("THIS is a sentence", {"IS": "was", "tHiS": "That"}, ignore_case=True) + assert var == "That was a sentence" + + If `match_case` is given, matches the case of the replacement with the replaced word. + Example: + var = replace_many( + "This IS a sentence", {"is": "was", "this": "that"}, ignore_case=True, match_case=True + ) + assert var == "That WAS a sentence" + """ + if ignore_case: + replacements = dict( + (word.lower(), replacement) for word, replacement in replacements.items() + ) + + words_to_replace = sorted(replacements, key=lambda s: (-len(s), s)) + + # Join and compile words to replace into a regex + pattern = "|".join(re.escape(word) for word in words_to_replace) + regex = re.compile(pattern, re.I if ignore_case else 0) + + def _repl(match: re.Match) -> str: + """Returns replacement depending on `ignore_case` and `match_case`.""" + word = match.group(0) + replacement = replacements[word.lower() if ignore_case else word] + + if not match_case: + return replacement + + # Clean punctuation from word so string methods work + cleaned_word = word.translate(str.maketrans('', '', string.punctuation)) + if cleaned_word.isupper(): + return replacement.upper() + elif cleaned_word[0].isupper(): + return replacement.capitalize() + else: + return replacement.lower() + + return regex.sub(_repl, sentence) |