Use aware objects when storing and reading UTC timestamps (#4017)

* Use aware objects instead of naive ones

* Use aware objects when storing and reading UTC timestamps

* Remove unneeded parentheses

* Fixed naive and aware objects unable to be compared here

* Address feedback

* Fix the newly added `modlog.create_case()` calls

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
This commit is contained in:
DevilXD 2020-08-12 10:46:32 +02:00 committed by GitHub
parent 73a34eacd6
commit 6e63ed4e60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 63 additions and 59 deletions

View File

@ -1,5 +1,6 @@
import discord import discord
import re import re
from datetime import timezone
from typing import Union, Set, Literal from typing import Union, Set, Literal
from redbot.core import checks, Config, modlog, commands from redbot.core import checks, Config, modlog, commands
@ -339,10 +340,11 @@ class Filter(commands.Cog):
filter_time = guild_data["filterban_time"] filter_time = guild_data["filterban_time"]
user_count = member_data["filter_count"] user_count = member_data["filter_count"]
next_reset_time = member_data["next_reset_time"] next_reset_time = member_data["next_reset_time"]
created_at = message.created_at.replace(tzinfo=timezone.utc)
if filter_count > 0 and filter_time > 0: if filter_count > 0 and filter_time > 0:
if message.created_at.timestamp() >= next_reset_time: if created_at.timestamp() >= next_reset_time:
next_reset_time = message.created_at.timestamp() + filter_time next_reset_time = created_at.timestamp() + filter_time
async with self.config.member(author).all() as member_data: async with self.config.member(author).all() as member_data:
member_data["next_reset_time"] = next_reset_time member_data["next_reset_time"] = next_reset_time
if user_count > 0: if user_count > 0:
@ -361,10 +363,7 @@ class Filter(commands.Cog):
if filter_count > 0 and filter_time > 0: if filter_count > 0 and filter_time > 0:
user_count += 1 user_count += 1
await self.config.member(author).filter_count.set(user_count) await self.config.member(author).filter_count.set(user_count)
if ( if user_count >= filter_count and created_at.timestamp() < next_reset_time:
user_count >= filter_count
and message.created_at.timestamp() < next_reset_time
):
reason = _("Autoban (too many filtered messages.)") reason = _("Autoban (too many filtered messages.)")
try: try:
await guild.ban(author, reason=reason) await guild.ban(author, reason=reason)
@ -374,7 +373,7 @@ class Filter(commands.Cog):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
message.created_at, message.created_at.replace(tzinfo=timezone.utc),
"filterban", "filterban",
author, author,
guild.me, guild.me,

View File

@ -1,5 +1,5 @@
import logging import logging
from datetime import datetime from datetime import timezone
from collections import defaultdict, deque from collections import defaultdict, deque
import discord import discord
@ -60,7 +60,7 @@ class Events(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
message.created_at, message.created_at.replace(tzinfo=timezone.utc),
"ban", "ban",
author, author,
guild.me, guild.me,
@ -84,7 +84,7 @@ class Events(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
message.created_at, message.created_at.replace(tzinfo=timezone.utc),
"kick", "kick",
author, author,
guild.me, guild.me,
@ -116,7 +116,7 @@ class Events(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
message.created_at, message.created_at.replace(tzinfo=timezone.utc),
"warning", "warning",
author, author,
guild.me, guild.me,

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
import contextlib import contextlib
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from typing import Optional, Union from typing import Optional, Union
import discord import discord
@ -120,7 +120,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"ban", "ban",
user, user,
author, author,
@ -142,10 +142,11 @@ class KickBanMixin(MixinMeta):
async with self.config.guild(guild).current_tempbans() as guild_tempbans: async with self.config.guild(guild).current_tempbans() as guild_tempbans:
for uid in guild_tempbans.copy(): for uid in guild_tempbans.copy():
unban_time = datetime.utcfromtimestamp( unban_time = datetime.fromtimestamp(
await self.config.member_from_ids(guild.id, uid).banned_until() await self.config.member_from_ids(guild.id, uid).banned_until(),
timezone.utc,
) )
if datetime.utcnow() > unban_time: # Time to unban the user if datetime.now(timezone.utc) > unban_time: # Time to unban the user
queue_entry = (guild.id, uid) queue_entry = (guild.id, uid)
try: try:
await guild.unban( await guild.unban(
@ -228,7 +229,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"kick", "kick",
user, user,
author, author,
@ -410,7 +411,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"hackban", "hackban",
user_id, user_id,
author, author,
@ -436,7 +437,7 @@ class KickBanMixin(MixinMeta):
"""Temporarily ban a user from this server.""" """Temporarily ban a user from this server."""
guild = ctx.guild guild = ctx.guild
author = ctx.author author = ctx.author
unban_time = datetime.utcnow() + duration unban_time = datetime.now(timezone.utc) + duration
if author == user: if author == user:
await ctx.send( await ctx.send(
@ -491,7 +492,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"tempban", "tempban",
user, user,
author, author,
@ -574,7 +575,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"softban", "softban",
user, user,
author, author,
@ -621,7 +622,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"vkick", "vkick",
member, member,
author, author,
@ -660,7 +661,7 @@ class KickBanMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"unban", "unban",
user, user,
author, author,

View File

@ -1,4 +1,5 @@
import asyncio import asyncio
from datetime import timezone
from typing import cast, Optional from typing import cast, Optional
import discord import discord
@ -107,7 +108,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"voiceunban", "voiceunban",
user, user,
author, author,
@ -148,7 +149,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"voiceban", "voiceban",
user, user,
author, author,
@ -188,7 +189,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"vmute", "vmute",
user, user,
author, author,
@ -232,7 +233,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"cmute", "cmute",
user, user,
author, author,
@ -262,7 +263,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"smute", "smute",
user, user,
author, author,
@ -305,7 +306,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"vunmute", "vunmute",
user, user,
author, author,
@ -349,7 +350,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"cunmute", "cunmute",
user, user,
author, author,
@ -383,7 +384,7 @@ class MuteMixin(MixinMeta):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"sunmute", "sunmute",
user, user,
author, author,

View File

@ -1,3 +1,4 @@
from datetime import timezone
from typing import Optional, Union from typing import Optional, Union
import discord import discord
@ -181,7 +182,7 @@ class ModLog(commands.Cog):
to_modify = {"reason": reason} to_modify = {"reason": reason}
if case_obj.moderator != author: if case_obj.moderator != author:
to_modify["amended_by"] = author to_modify["amended_by"] = author
to_modify["modified_at"] = ctx.message.created_at.timestamp() to_modify["modified_at"] = ctx.message.created_at.replace(tzinfo=timezone.utc).timestamp()
await case_obj.edit(to_modify) await case_obj.edit(to_modify)
await ctx.send( await ctx.send(
_("Reason for case #{num} has been updated.").format(num=case_obj.case_number) _("Reason for case #{num} has been updated.").format(num=case_obj.case_number)

View File

@ -1,5 +1,6 @@
import asyncio import asyncio
import contextlib import contextlib
from datetime import timezone
from collections import namedtuple from collections import namedtuple
from copy import copy from copy import copy
from typing import Union, Optional, Literal from typing import Union, Optional, Literal
@ -494,7 +495,7 @@ class Warnings(commands.Cog):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
ctx.guild, ctx.guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"warning", "warning",
user, user,
ctx.message.author, ctx.message.author,
@ -616,7 +617,7 @@ class Warnings(commands.Cog):
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
ctx.guild, ctx.guild,
ctx.message.created_at, ctx.message.created_at.replace(tzinfo=timezone.utc),
"unwarned", "unwarned",
member, member,
ctx.message.author, ctx.message.author,

View File

@ -1,8 +1,8 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import datetime
import logging import logging
from datetime import datetime, timezone
from typing import Union, List, Optional, TYPE_CHECKING, Literal from typing import Union, List, Optional, TYPE_CHECKING, Literal
from functools import wraps from functools import wraps
@ -109,7 +109,7 @@ class Account:
This class should ONLY be instantiated by the bank itself.""" This class should ONLY be instantiated by the bank itself."""
def __init__(self, name: str, balance: int, created_at: datetime.datetime): def __init__(self, name: str, balance: int, created_at: datetime):
self.name = name self.name = name
self.balance = balance self.balance = balance
self.created_at = created_at self.created_at = created_at
@ -124,11 +124,11 @@ def _encoded_current_time() -> int:
The current UTC timestamp. The current UTC timestamp.
""" """
now = datetime.datetime.utcnow() now = datetime.now(timezone.utc)
return _encode_time(now) return _encode_time(now)
def _encode_time(time: datetime.datetime) -> int: def _encode_time(time: datetime) -> int:
"""Convert a datetime object to a serializable int. """Convert a datetime object to a serializable int.
Parameters Parameters
@ -146,7 +146,7 @@ def _encode_time(time: datetime.datetime) -> int:
return ret return ret
def _decode_time(time: int) -> datetime.datetime: def _decode_time(time: int) -> datetime:
"""Convert a timestamp to a datetime object. """Convert a timestamp to a datetime object.
Parameters Parameters
@ -160,7 +160,7 @@ def _decode_time(time: int) -> datetime.datetime:
The datetime object from the timestamp. The datetime object from the timestamp.
""" """
return datetime.datetime.utcfromtimestamp(time) return datetime.utcfromtimestamp(time)
async def get_balance(member: discord.Member) -> int: async def get_balance(member: discord.Member) -> int:

View File

@ -3,10 +3,9 @@ import contextlib
import platform import platform
import sys import sys
import codecs import codecs
import datetime
import logging import logging
import traceback import traceback
from datetime import timedelta from datetime import datetime, timedelta
import aiohttp import aiohttp
import discord import discord
@ -55,7 +54,7 @@ def init_events(bot, cli_flags):
if bot._uptime is not None: if bot._uptime is not None:
return return
bot._uptime = datetime.datetime.utcnow() bot._uptime = datetime.utcnow()
guilds = len(bot.guilds) guilds = len(bot.guilds)
users = len(set([m for m in bot.get_all_members()])) users = len(set([m for m in bot.get_all_members()]))
@ -313,7 +312,7 @@ def init_events(bot, cli_flags):
not bot._checked_time_accuracy not bot._checked_time_accuracy
or (discord_now - timedelta(minutes=60)) > bot._checked_time_accuracy or (discord_now - timedelta(minutes=60)) > bot._checked_time_accuracy
): ):
system_now = datetime.datetime.utcnow() system_now = datetime.utcnow()
diff = abs((discord_now - system_now).total_seconds()) diff = abs((discord_now - system_now).total_seconds())
if diff > 60: if diff > 60:
log.warning( log.warning(

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import asyncio import asyncio
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from typing import List, Literal, Union, Optional, cast, TYPE_CHECKING from typing import List, Literal, Union, Optional, cast, TYPE_CHECKING
import discord import discord
@ -127,7 +127,8 @@ async def _init(bot: Red):
if entry: if entry:
if entry.user.id != guild.me.id: if entry.user.id != guild.me.id:
# Don't create modlog entires for the bot's own bans, cogs do this. # Don't create modlog entires for the bot's own bans, cogs do this.
mod, reason, date = entry.user, entry.reason, entry.created_at mod, reason = entry.user, entry.reason
date = entry.created_at.replace(tzinfo=timezone.utc)
await create_case(_bot_ref, guild, date, "ban", member, mod, reason) await create_case(_bot_ref, guild, date, "ban", member, mod, reason)
return return
@ -163,7 +164,8 @@ async def _init(bot: Red):
if entry: if entry:
if entry.user.id != guild.me.id: if entry.user.id != guild.me.id:
# Don't create modlog entires for the bot's own unbans, cogs do this. # Don't create modlog entires for the bot's own unbans, cogs do this.
mod, reason, date = entry.user, entry.reason, entry.created_at mod, reason = entry.user, entry.reason
date = entry.created_at.replace(tzinfo=timezone.utc)
await create_case(_bot_ref, guild, date, "unban", user, mod, reason) await create_case(_bot_ref, guild, date, "unban", user, mod, reason)
return return
@ -351,9 +353,9 @@ class Case:
until = None until = None
duration = None duration = None
if self.until: if self.until:
start = datetime.fromtimestamp(self.created_at) start = datetime.utcfromtimestamp(self.created_at)
end = datetime.fromtimestamp(self.until) end = datetime.utcfromtimestamp(self.until)
end_fmt = end.strftime("%Y-%m-%d %H:%M:%S") end_fmt = end.strftime("%Y-%m-%d %H:%M:%S UTC")
duration = end - start duration = end - start
dur_fmt = _strfdelta(duration) dur_fmt = _strfdelta(duration)
until = end_fmt until = end_fmt
@ -374,7 +376,7 @@ class Case:
last_modified = None last_modified = None
if self.modified_at: if self.modified_at:
last_modified = "{}".format( last_modified = "{}".format(
datetime.fromtimestamp(self.modified_at).strftime("%Y-%m-%d %H:%M:%S") datetime.utcfromtimestamp(self.modified_at).strftime("%Y-%m-%d %H:%M:%S UTC")
) )
if isinstance(self.user, int): if isinstance(self.user, int):
@ -413,7 +415,7 @@ class Case:
emb.add_field(name=_("Amended by"), value=amended_by) emb.add_field(name=_("Amended by"), value=amended_by)
if last_modified: if last_modified:
emb.add_field(name=_("Last modified at"), value=last_modified) emb.add_field(name=_("Last modified at"), value=last_modified)
emb.timestamp = datetime.fromtimestamp(self.created_at) emb.timestamp = datetime.utcfromtimestamp(self.created_at)
return emb return emb
else: else:
user = filter_mass_mentions(filter_urls(user)) # Further sanitization outside embeds user = filter_mass_mentions(filter_urls(user)) # Further sanitization outside embeds

View File

@ -12,7 +12,7 @@ async def test_modlog_register_casetype(mod):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_modlog_case_create(mod, ctx, member_factory): async def test_modlog_case_create(mod, ctx, member_factory):
from datetime import datetime as dt from datetime import datetime, timezone
# Run casetype register test to register casetype in this test too # Run casetype register test to register casetype in this test too
await test_modlog_register_casetype(mod) await test_modlog_register_casetype(mod)
@ -23,7 +23,7 @@ async def test_modlog_case_create(mod, ctx, member_factory):
case_type = "ban" case_type = "ban"
moderator = ctx.author moderator = ctx.author
reason = "Test 12345" reason = "Test 12345"
created_at = dt.utcnow() created_at = datetime.now(timezone.utc)
case = await mod.create_case(bot, guild, created_at, case_type, usr, moderator, reason) case = await mod.create_case(bot, guild, created_at, case_type, usr, moderator, reason)
assert case is not None assert case is not None
assert case.user == usr assert case.user == usr