From 56e5a0b8795a56b47b2b84df238afa29385265b0 Mon Sep 17 00:00:00 2001 From: Brandon Date: Wed, 19 Jan 2022 21:41:23 -0500 Subject: [PATCH] add timehelper --- timehelper/__init__.py | 7 ++ timehelper/info.json | 16 ++++ timehelper/timehelper.py | 176 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 timehelper/__init__.py create mode 100644 timehelper/info.json create mode 100644 timehelper/timehelper.py diff --git a/timehelper/__init__.py b/timehelper/__init__.py new file mode 100644 index 0000000..8170419 --- /dev/null +++ b/timehelper/__init__.py @@ -0,0 +1,7 @@ +from .timehelper import TimeHelper + +__red_end_user_data_statement__ = "This cog stores a user's timezone, if set by the user." + + +def setup(bot): + bot.add_cog(TimeHelper(bot)) diff --git a/timehelper/info.json b/timehelper/info.json new file mode 100644 index 0000000..fcd6a73 --- /dev/null +++ b/timehelper/info.json @@ -0,0 +1,16 @@ +{ + "author": [ + "Brandons209", + ], + "description": "Timehelper provides commands for users to easily get discord formatted timestamps for a specific date or interval, and also compare timezone's times easily.", + "hidden": false, + "install_msg": "Thanks for using my cog! Use [p]time to get started.", + "requirements": ["dateparser", "tzdata"], + "tags": [ + "brandons209", + "timestamp", + "timezone", + "time" + ], + "end_user_data_statement": "This cog stores a user's timezone, if set by the user." +} diff --git a/timehelper/timehelper.py b/timehelper/timehelper.py new file mode 100644 index 0000000..d178fea --- /dev/null +++ b/timehelper/timehelper.py @@ -0,0 +1,176 @@ +# redbot/discord +from redbot.core.utils.chat_formatting import * +from redbot.core import Config, checks, commands +from redbot.core.commands import Converter, BadArgument +import discord + +import dateparser + +from zoneinfo import ZoneInfo +from datetime import datetime +from typing import Literal, Union + + +class TimezoneConverter(Converter): + """ + Checks timezone is correct and converts it to a timezone object + """ + + async def convert(self, ctx, arg: str) -> List[ZoneInfo]: + zones = [z.strip() for z in arg.split(",")] + + for i, z in enumerate(zones): + # adding in my own fixes to make certain codes not in the database work + if z.upper() == "PDT" or z.upper() == "PST": + z = "PST8PDT" + elif z.upper() == "CST" or z.upper() == "CDT": + z = "CST6CDT" + + try: + zones[i] = ZoneInfo(z) + except: + raise BadArgument( + error( + f"Unrecongized timezone `{z}`, please find your timezone name under `TZ database name` column here: " + ) + ) + + return zones + + +class TimeHelper(commands.Cog): + """ + Command suite for comparing timezones and getting discord formated time stamps + """ + + def __init__(self, bot): + self.bot = bot + self.config = Config.get_conf(self, identifier=789646516315646, force_registration=True) + + default_user = {"timezone": "UTC"} + self.config.register_user(**default_user) + + @staticmethod + def format_datetime(date: datetime) -> str: + """ + Formats datetime into user readable string + + Args: + date (datetime): Datetime object representing date + + Returns: + str: The date formatted as a string + """ + return date.strftime("%b %d, %Y %I:%M %p %Z") + + async def get_date(self, user: Union[discord.Member, discord.User], date: str, timezone: str = None) -> datetime: + """ + Returns the date in the user's timezone, if set + + Args: + user (discord.Member, discord.User): The user calling the function + date (str): Date as a string + timezone (str, Optional): Convert date to this timezone + """ + user_timezone = await self.config.user(user).timezone() + + # since the settings timezone overwrites the timezone in the string, need to change this so timezone in string overrides it + # its not a solid approach but it allows us to use the features of dateparser + # first, find if there is a timezone in the date + date_timezone = None + for possible_timezone in date.split(" "): + try: + date_timezone = str(ZoneInfo(possible_timezone)) + except: + pass + + date_timezone = user_timezone if date_timezone is None else date_timezone + + if timezone is not None: + parsed = dateparser.parse( + date, settings={"TIMEZONE": date_timezone, "TO_TIMEZONE": timezone, "RETURN_AS_TIMEZONE_AWARE": True} + ) + else: + parsed = dateparser.parse(date, settings={"TIMEZONE": date_timezone, "RETURN_AS_TIMEZONE_AWARE": True}) + + return parsed + + @commands.group(aliases=["ti"]) + async def time(self, ctx): + """ + Time helper tools + + **Set your timezone with `myzone` command so you don't need to specify your timezone using `time`!** + """ + pass + + @time.command(name="myzone", usage="") + async def myzone(self, ctx, *, timezone: TimezoneConverter): + """ + (Optional) Set your timezone + + Other time commands will use your timezone if you don't provide a timezone when converting a time. + + Timezone formats: + - Timezone code: EST, EDT, UTC, etc + - Timezone name: America/New_York, America/Bogota, Asia/Rangoon, etc + + See timezone names here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + """ + await self.config.user(ctx.author).timezone.set(str(timezone[0])) + await ctx.tick() + + @time.command(usage="") + async def stamp(self, ctx, *, date: str): + """ + Convert time into discord timestamp + + Discord timestamps are shown in the timezone of each user who views it + + The date can either be an exact date or an interval from now + **Example Usage** + - `[p]time stamp 2021-05-12 23:00:00 EST` + - `[p]time stamp 21 July 2013 10:15 pm +0500` + - `[p]time stamp 1st of October, 2021` + - `[p]time stamp 20 hours ago EST` + - `[p]time stamp in 50 minutes` + - `[p]time stamp 01/10/2021` + - `[p]time stamp now` + + See timezone names here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + """ + # convert timezone to local zone, otherwise timestamp will be off since discord timestamp will convert from local timezone automatically + parsed = await self.get_date(ctx.author, date, timezone=str(datetime.now().astimezone().tzinfo)) + + if not parsed: + return ctx.send(error("Unrecognized date/time!"), delete_after=30) + + timestamp = int(parsed.timestamp()) + + msg = f"**Timestamp formats for **:\n" + for frmt in "fdtFDTR": + msg += f"\t- `` = \n" + + await ctx.send(msg) + + @time.command(name="zone", usage=" ") + async def time_zone(self, ctx, zones: TimezoneConverter, *, date: str): + """ + Convert time to specific timezones + + See timezone names here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + """ + parsed = await self.get_date(ctx.author, date) + if not parsed: + return ctx.send(error("Unrecognized date/time!"), delete_after=30) + + msg = f"**Timezones for `{self.format_datetime(parsed)}`:**\n" + for zone in zones: + new_date = await self.get_date(ctx.author, date, timezone=str(zone)) + + msg += f"\t- `{zone}` = `{self.format_datetime(new_date)}`\n" + + msgs = pagify(msg) + + for m in msgs: + await ctx.send(m)