2020-02-02 19:26:54 +13:00
|
|
|
# This probably seems like overkill
|
|
|
|
# It is for the current form, it isn't with future features in mind.
|
|
|
|
import contextlib
|
|
|
|
from datetime import datetime
|
|
|
|
from typing import List, Callable, Union, Optional, Dict, Iterator
|
|
|
|
|
|
|
|
import discord
|
|
|
|
|
|
|
|
MessagePredicate = Callable[[discord.Message], bool]
|
|
|
|
|
|
|
|
|
|
|
|
class RecentActivityRecord:
|
|
|
|
|
|
|
|
__slots__ = ("activities", "messages")
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.activities: List[datetime] = []
|
|
|
|
self.messages: List[discord.Message] = []
|
|
|
|
|
|
|
|
def add_activity(self, when: datetime):
|
|
|
|
self.activities.append(when)
|
|
|
|
|
|
|
|
def add_message(self, message: discord.Message):
|
|
|
|
self.messages.append(message)
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self.activities) + len(self.messages)
|
|
|
|
|
|
|
|
def _filter(
|
2020-02-02 20:18:16 +13:00
|
|
|
self, *, after: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None,
|
2020-02-02 19:26:54 +13:00
|
|
|
) -> List[Union[datetime, discord.Message]]:
|
|
|
|
|
|
|
|
ret: List[Union[datetime, discord.Message]] = []
|
|
|
|
|
|
|
|
for a in self.activities:
|
|
|
|
if after and a <= after:
|
|
|
|
continue
|
|
|
|
ret.append(a)
|
|
|
|
|
|
|
|
for m in self.messages:
|
|
|
|
if after and m.created_at <= after:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if message_check and not message_check(m):
|
|
|
|
continue
|
|
|
|
|
|
|
|
ret.append(m)
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def conditional_count(
|
2020-02-02 20:18:16 +13:00
|
|
|
self, *, after: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None,
|
2020-02-02 19:26:54 +13:00
|
|
|
) -> int:
|
|
|
|
|
|
|
|
ret = len(self._filter(after=after, message_check=message_check))
|
|
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def conditional_remove(
|
2020-02-02 20:18:16 +13:00
|
|
|
self, *, before: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None,
|
2020-02-02 19:26:54 +13:00
|
|
|
):
|
|
|
|
if before:
|
|
|
|
self.activities = [a for a in self.activities if a > before]
|
|
|
|
|
|
|
|
if message_check:
|
2020-02-02 20:18:16 +13:00
|
|
|
self.messages = [m for m in self.messages if m.created_at > before and message_check(m)]
|
2020-02-02 19:26:54 +13:00
|
|
|
else:
|
|
|
|
self.messages = [m for m in self.messages if m.created_at > before]
|
|
|
|
|
|
|
|
elif message_check:
|
|
|
|
self.messages = [m for m in self.messages if message_check(m)]
|
|
|
|
|
|
|
|
|
|
|
|
RecordDict = Dict[discord.Guild, Dict[discord.Member, RecentActivityRecord]]
|
|
|
|
|
|
|
|
|
|
|
|
class RecordHandler:
|
|
|
|
|
|
|
|
__slots__ = ("records",)
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.records: RecordDict = {}
|
|
|
|
|
|
|
|
def proccess_message(self, message):
|
|
|
|
|
|
|
|
try:
|
|
|
|
member = message.author
|
|
|
|
guild = member.guild
|
|
|
|
if not (guild and member and not member.bot):
|
|
|
|
return
|
|
|
|
except AttributeError:
|
|
|
|
return
|
|
|
|
|
|
|
|
if guild not in self.records:
|
|
|
|
self.records[guild] = {}
|
|
|
|
|
|
|
|
if member not in self.records[guild]:
|
|
|
|
self.records[guild][member] = RecentActivityRecord()
|
|
|
|
|
|
|
|
self.records[guild][member].add_message(message)
|
|
|
|
|
|
|
|
def get_active_for_guild(
|
2020-02-02 20:18:16 +13:00
|
|
|
self, *, guild: discord.Guild, after: datetime, message_check: Optional[MessagePredicate] = None,
|
2020-02-02 19:26:54 +13:00
|
|
|
) -> Iterator[discord.Member]:
|
|
|
|
|
|
|
|
with contextlib.suppress(KeyError):
|
|
|
|
for member, rec in self.records[guild].items():
|
|
|
|
if rec.conditional_count(after=after, message_check=message_check):
|
|
|
|
yield member
|
|
|
|
|
|
|
|
def clear_before(self, *, guild: discord.Guild, before: datetime):
|
|
|
|
with contextlib.suppress(KeyError):
|
|
|
|
for rec in self.records[guild].values():
|
|
|
|
rec.conditional_remove(before=before)
|