Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve cooldown UX #2412

Merged
merged 2 commits into from Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 17 additions & 2 deletions redbot/core/events.py
Expand Up @@ -4,6 +4,7 @@
import datetime
import logging
import traceback
import asyncio
from datetime import timedelta
from typing import List

Expand All @@ -15,7 +16,7 @@

from . import __version__ as red_version, version_info as red_version_info, VersionInfo, commands
from .data_manager import storage_type
from .utils.chat_formatting import inline, bordered, format_perms_list
from .utils.chat_formatting import inline, bordered, format_perms_list, humanize_timedelta
from .utils import fuzzy_command_search, format_fuzzy_results

log = logging.getLogger("red")
Expand Down Expand Up @@ -242,8 +243,22 @@ async def on_command_error(ctx, error):
elif isinstance(error, commands.NoPrivateMessage):
await ctx.send("That command is not available in DMs.")
elif isinstance(error, commands.CommandOnCooldown):
if error.retry_after < 1:
async with ctx.typing():
# the sleep here is so that commands using this for ratelimit purposes
# are not made more lenient than intended, while still being
# more convienient for the user than redoing it less than a second later.
await asyncio.sleep(error.retry_after)
await ctx.bot.invoke(ctx)
# done this way so checks still occur if there are other
# failures possible than just cooldown.
# do not change to ctx.reinvoke()
return

await ctx.send(
"This command is on cooldown. Try again in {:.2f}s".format(error.retry_after)
"This command is on cooldown. Try again in {}".format(
humanize_timedelta(seconds=error.retry_after)
)
)
else:
log.exception(type(error).__name__, exc_info=error)
Expand Down
39 changes: 37 additions & 2 deletions redbot/core/utils/chat_formatting.py
@@ -1,5 +1,6 @@
import itertools
from typing import Sequence, Iterator, List
import datetime
from typing import Sequence, Iterator, List, Optional

import discord

Expand Down Expand Up @@ -204,7 +205,7 @@ def pagify(
priority: bool = False,
escape_mass_mentions: bool = True,
shorten_by: int = 8,
page_length: int = 2000
page_length: int = 2000,
) -> Iterator[str]:
"""Generate multiple pages from the given text.

Expand Down Expand Up @@ -386,3 +387,37 @@ def format_perms_list(perms: discord.Permissions) -> str:
perm_name = '"' + perm.replace("_", " ").title() + '"'
perm_names.append(perm_name)
return humanize_list(perm_names).replace("Guild", "Server")


def humanize_timedelta(
*, timedelta: Optional[datetime.timedelta] = None, seconds: Optional[int] = None
) -> str:
"""
Get a human timedelta representation
"""

try:
obj = seconds or timedelta.total_seconds()
except AttributeError:
raise ValueError("You must provide either a timedelta or a number of seconds")

seconds = int(obj)
periods = [
(_("year"), _("years"), 60 * 60 * 24 * 365),
(_("month"), _("months"), 60 * 60 * 24 * 30),
(_("day"), _("days"), 60 * 60 * 24),
(_("hour"), _("hours"), 60 * 60),
(_("minute"), _("minutes"), 60),
(_("second"), _("seconds"), 1),
]

strings = []
for period_name, plural_period_name, period_seconds in periods:
if seconds >= period_seconds:
period_value, seconds = divmod(seconds, period_seconds)
if period_value == 0:
continue
unit = plural_period_name if period_value > 1 else period_name
strings.append(f"{period_value} {unit}")

return ", ".join(strings)