Skip to content

Commit

Permalink
Merge pull request #93 from discord-modmail/feat/utils-time
Browse files Browse the repository at this point in the history
Feat/utils time

add discord timestamp utils
  • Loading branch information
onerandomusername committed Oct 28, 2021
2 parents 1a77549 + b721e3a commit ca172af
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 0 deletions.
41 changes: 41 additions & 0 deletions modmail/utils/time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import datetime
import enum
import typing

import arrow


class TimeStampEnum(enum.Enum):
"""
Timestamp modes for discord.
Full docs on this format are viewable here:
https://discord.com/developers/docs/reference#message-formatting
"""

# fmt: off
SHORT_TIME = "t" # 16:20
LONG_TIME = "T" # 16:20:30
SHORT_DATE = "d" # 20/04/2021
LONG_DATE = "D" # 20 April 2021
SHORT_DATE_TIME = "f" # 20 April 2021 16:20
LONG_DATE_TIME = "F" # Tuesday, 20 April 2021 16:20
RELATIVE_TIME = "R" # 2 months ago

# fmt: on
# DEFAULT alised to the default, so for all purposes, it behaves like SHORT_DATE_TIME, including the name
DEFAULT = SHORT_DATE_TIME


TypeTimes = typing.Union[arrow.Arrow, datetime.datetime]


def get_discord_formatted_timestamp(
timestamp: TypeTimes, format: TimeStampEnum = TimeStampEnum.DEFAULT
) -> str:
"""
Return a discord formatted timestamp from a datetime compatiable datatype.
`format` must be an enum member of TimeStampEnum. Default style is SHORT_DATE_TIME
"""
return f"<t:{int(timestamp.timestamp())}:{format.value}>"
Empty file.
174 changes: 174 additions & 0 deletions tests/modmail/extensions/utils/test_error_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import inspect
import typing
import unittest.mock

import discord
import pytest
from discord.ext import commands

from modmail.extensions.utils import error_handler
from modmail.extensions.utils.error_handler import ErrorHandler
from tests import mocks


@pytest.fixture
def cog():
"""Pytest fixture for error_handler."""
return ErrorHandler(mocks.MockBot())


@pytest.fixture
def ctx():
"""Pytest fixture for MockContext."""
return mocks.MockContext(channel=mocks.MockTextChannel())


def test_error_embed():
"""Test the error embed method creates the correct embed."""
title = "Something very drastic went very wrong!"
message = "seven southern seas are ready to collapse."
embed = ErrorHandler.error_embed(title=title, message=message)

assert embed.title == title
assert embed.description == message
assert embed.colour == error_handler.ERROR_COLOUR


@pytest.mark.parametrize(
["exception_or_str", "expected_str"],
[
[commands.NSFWChannelRequired(mocks.MockTextChannel()), "NSFW Channel Required"],
[commands.CommandNotFound(), "Command Not Found"],
["someWEIrdName", "some WE Ird Name"],
],
)
def test_get_title_from_name(exception_or_str: typing.Union[Exception, str], expected_str: str):
"""Test the regex works properly for the title from name."""
result = ErrorHandler.get_title_from_name(exception_or_str)
assert expected_str == result


@pytest.mark.parametrize(
["error", "title", "description"],
[
[
commands.UserInputError("some interesting information."),
"User Input Error",
"some interesting information.",
],
[
commands.MissingRequiredArgument(inspect.Parameter("SomethingSpecial", kind=1)),
"Missing Required Argument",
"SomethingSpecial is a required argument that is missing.",
],
[
commands.GuildNotFound("ImportantGuild"),
"Guild Not Found",
'Guild "ImportantGuild" not found.',
],
],
)
@pytest.mark.asyncio
async def test_handle_user_input_error(
cog: ErrorHandler, ctx: mocks.MockContext, error: commands.UserInputError, title: str, description: str
):
"""Test user input errors are handled properly. Does not test with BadUnionArgument."""
embed = await cog.handle_user_input_error(ctx=ctx, error=error, reset_cooldown=False)

assert title == embed.title
assert description == embed.description


@pytest.mark.asyncio
async def test_handle_bot_missing_perms(cog: ErrorHandler):
"""
Test error_handler.handle_bot_missing_perms.
There are some cases here where the bot is unable to send messages, and that should be clear.
"""
...


@pytest.mark.asyncio
async def test_handle_check_failure(cog: ErrorHandler):
"""
Test check failures.
In some cases, this method should result in calling a bot_missing_perms method
because the bot cannot send messages.
"""
...


@pytest.mark.asyncio
async def test_on_command_error(cog: ErrorHandler):
"""Test the general command error method."""
...


class TestErrorHandler:
"""
Test class for the error handler. The problem here is a lot of the errors need to be raised.
Thankfully, most of them do not have extra attributes that we use, and can be easily faked.
"""

errors = {
commands.CommandError: [
commands.ConversionError,
{
commands.UserInputError: [
commands.MissingRequiredArgument,
commands.TooManyArguments,
{
commands.BadArgument: [
commands.MessageNotFound,
commands.MemberNotFound,
commands.GuildNotFound,
commands.UserNotFound,
commands.ChannelNotFound,
commands.ChannelNotReadable,
commands.BadColourArgument,
commands.RoleNotFound,
commands.BadInviteArgument,
commands.EmojiNotFound,
commands.GuildStickerNotFound,
commands.PartialEmojiConversionFailure,
commands.BadBoolArgument,
commands.ThreadNotFound,
]
},
commands.BadUnionArgument,
commands.BadLiteralArgument,
{
commands.ArgumentParsingError: [
commands.UnexpectedQuoteError,
commands.InvalidEndOfQuotedStringError,
commands.ExpectedClosingQuoteError,
]
},
]
},
commands.CommandNotFound,
{
commands.CheckFailure: [
commands.CheckAnyFailure,
commands.PrivateMessageOnly,
commands.NoPrivateMessage,
commands.NotOwner,
commands.MissingPermissions,
commands.BotMissingPermissions,
commands.MissingRole,
commands.BotMissingRole,
commands.MissingAnyRole,
commands.BotMissingAnyRole,
commands.NSFWChannelRequired,
]
},
commands.DisabledCommand,
commands.CommandInvokeError,
commands.CommandOnCooldown,
commands.MaxConcurrencyReached,
]
}
26 changes: 26 additions & 0 deletions tests/modmail/utils/test_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import arrow
import pytest

from modmail.utils import time as utils_time
from modmail.utils.time import TimeStampEnum


@pytest.mark.parametrize(
["timestamp", "expected", "mode"],
[
[arrow.get(1634593650), "<t:1634593650:f>", TimeStampEnum.SHORT_DATE_TIME],
[arrow.get(1), "<t:1:f>", TimeStampEnum.DEFAULT],
[arrow.get(12356941), "<t:12356941:R>", TimeStampEnum.RELATIVE_TIME],
[arrow.get(8675309).datetime, "<t:8675309:D>", TimeStampEnum.LONG_DATE],
],
)
def test_timestamp(timestamp, expected: str, mode: utils_time.TimeStampEnum):
"""Test the timestamp is of the proper form."""
fmtted_timestamp = utils_time.get_discord_formatted_timestamp(timestamp, mode)
assert expected == fmtted_timestamp


def test_enum_default():
"""Ensure that the default mode is of the correct mode, and works properly."""
assert TimeStampEnum.DEFAULT.name == TimeStampEnum.SHORT_DATE_TIME.name
assert TimeStampEnum.DEFAULT.value == TimeStampEnum.SHORT_DATE_TIME.value

0 comments on commit ca172af

Please sign in to comment.