# 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( self, *, after: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None, ) -> 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( self, *, after: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None, ) -> int: ret = len(self._filter(after=after, message_check=message_check)) return ret def conditional_remove( self, *, before: Optional[datetime] = None, message_check: Optional[MessagePredicate] = None, ): if before: self.activities = [a for a in self.activities if a > before] if message_check: self.messages = [m for m in self.messages if m.created_at > before and message_check(m)] 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( self, *, guild: discord.Guild, after: datetime, message_check: Optional[MessagePredicate] = None, ) -> 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)