async def get_thread_cooldown(self, author: discord.Member): thread_cooldown = self.config.get("thread_cooldown") now = datetime.utcnow() if thread_cooldown == isodate.Duration(): return last_log = await self.api.get_latest_user_logs(author.id) if last_log is None: logger.debug("Last thread wasn't found, %s.", author.name) return last_log_closed_at = last_log.get("closed_at") if not last_log_closed_at: logger.debug("Last thread was not closed, %s.", author.name) return try: cooldown = datetime.fromisoformat(last_log_closed_at) + thread_cooldown except ValueError: logger.warning("Error with 'thread_cooldown'.", exc_info=True) cooldown = datetime.fromisoformat(last_log_closed_at) + self.config.remove( "thread_cooldown" ) if cooldown > now: # User messaged before thread cooldown ended delta = human_timedelta(cooldown) logger.debug("Blocked due to thread cooldown, user %s.", author.name) return delta return
async def _restart_close_timer(self): """ This will create or restart a timer to automatically close this thread. """ timeout = await self._fetch_timeout() # Exit if timeout was not set if timeout is None: return # Set timeout seconds seconds = timeout.total_seconds() # seconds = 20 # Uncomment to debug with just 20 seconds reset_time = datetime.utcnow() + timedelta(seconds=seconds) human_time = human_timedelta(dt=reset_time) # Grab message close_message = self.bot.config.get( "thread_auto_close_response", f"This thread has been closed automatically due to inactivity " f"after {human_time}.", ) time_marker_regex = "%t" if len(re.findall(time_marker_regex, close_message)) == 1: close_message = re.sub(time_marker_regex, str(human_time), close_message) elif len(re.findall(time_marker_regex, close_message)) > 1: logger.warning( "The thread_auto_close_response should only contain one" f" '{time_marker_regex}' to specify time.") await self.close(closer=self.bot.user, after=seconds, message=close_message)
def check_guild_age(self, author: discord.Member) -> bool: guild_age = self.config.get("guild_age") now = datetime.utcnow() if not hasattr(author, "joined_at"): logger.warning("Not in guild, cannot verify guild_age, %s.", author.name) return True try: min_guild_age = author.joined_at + guild_age except ValueError: logger.warning("Error with 'guild_age'.", exc_info=True) min_guild_age = author.joined_at + self.config.remove("guild_age") if min_guild_age > now: # User has not stayed in the guild for long enough delta = human_timedelta(min_guild_age) logger.debug("Blocked due to guild age, user %s.", author.name) if str(author.id) not in self.blocked_users: new_reason = f"System Message: Recently Joined. Required to wait for {delta}." self.blocked_users[str(author.id)] = new_reason return False return True
def check_guild_age(self, author: discord.Member) -> bool: guild_age = self.config.get("guild_age") now = datetime.utcnow() if not hasattr(author, "joined_at"): logger.warning("Not in guild, cannot verify guild_age, %s.", author.name) return True try: min_guild_age = author.joined_at + guild_age except ValueError: logger.warning("Errore con la configurazione 'guild_age'.", exc_info=True) min_guild_age = author.joined_at + self.config.remove("guild_age") if min_guild_age > now: # User has not stayed in the guild for long enough delta = human_timedelta(min_guild_age) logger.debug( "L'utente %s è stato bloccato per via dell'eta dell'account", author.name) if str(author.id) not in self.blocked_users: new_reason = ( f"Messaggio di sistema: Entrato di recende. È richiesto aspettare per {delta}." ) self.blocked_users[str(author.id)] = new_reason return False return True
def check_account_age(self, author: discord.Member) -> bool: account_age = self.config.get("account_age") now = datetime.utcnow() try: min_account_age = author.created_at + account_age except ValueError: logger.warning("Errore con la configurazione 'account_age'.", exc_info=True) min_account_age = author.created_at + self.config.remove( "account_age") if min_account_age > now: # User account has not reached the required time delta = human_timedelta(min_account_age) logger.debug( "L'utente %s è stato bloccato per via dell'età del suo account.", author.name) if str(author.id) not in self.blocked_users: new_reason = ( f"Messaggio di sistema: Account nuovo. È richiesto aspettare per {delta}." ) self.blocked_users[str(author.id)] = new_reason return False return True
async def _restart_close_timer(self): """ This will create or restart a timer to automatically close this thread. """ timeout = self.bot.config.get("thread_auto_close") # Exit if timeout was not set if timeout == isodate.Duration(): return # Set timeout seconds seconds = timeout.total_seconds() # seconds = 20 # Uncomment to debug with just 20 seconds reset_time = datetime.utcnow() + timedelta(seconds=seconds) human_time = human_timedelta(dt=reset_time) if self.bot.config.get("thread_auto_close_silently"): return await self.close(closer=self.bot.user, silent=True, after=int(seconds), auto_close=True) # Grab message close_message = self.bot.formatter.format( self.bot.config["thread_auto_close_response"], timeout=human_time ) time_marker_regex = "%t" if len(re.findall(time_marker_regex, close_message)) == 1: close_message = re.sub(time_marker_regex, str(human_time), close_message) elif len(re.findall(time_marker_regex, close_message)) > 1: logger.warning( "The thread_auto_close_response should only contain one '%s' to specify time.", time_marker_regex, ) await self.close(closer=self.bot.user, after=int(seconds), message=close_message, auto_close=True)
async def send_scheduled_close_message(self, ctx, after, silent=False): human_delta = human_timedelta(after.dt) silent = '*silently* ' if silent else '' em = discord.Embed( title='Scheduled close', description=f'This thread will close {silent}in {human_delta}.', color=discord.Color.red()) if after.arg and not silent: em.add_field(name='Message', value=after.arg) em.set_footer( text='Closing will be cancelled if a thread message is sent.') em.timestamp = after.dt await ctx.send(embed=em)
async def send_scheduled_close_message(self, ctx, after, silent=False): human_delta = human_timedelta(after.dt) silent = "*silently* " if silent else "" embed = discord.Embed( title="Scheduled close", description=f"This thread will close {silent}in {human_delta}.", color=self.bot.error_color, ) if after.arg and not silent: embed.add_field(name="Message", value=after.arg) embed.set_footer(text="Closing will be cancelled if a thread message is sent.") embed.timestamp = after.dt await ctx.send(embed=embed)
async def send_scheduled_close_message(self, ctx, after, silent=False): human_delta = human_timedelta(after.dt) silent = "*silently* " if silent else "" embed = discord.Embed( title="Chiusura automatica", description=f"Questo thread si chiuderà {silent}in {human_delta}.", color=self.bot.error_color, ) if after.arg and not silent: embed.add_field(name="Message", value=after.arg) embed.set_footer(text="La chiusura sarà annullata " "se viene inviato un messaggio di thread.") embed.timestamp = after.dt await ctx.send(embed=embed)
async def send_scheduled_close_message(ctx, after, silent=False): human_delta = human_timedelta(after.dt) silent = '*silencieusement* ' if silent else '' embed = discord.Embed( title='Fermeture prévue', description=f'Ce ticket se fermera {silent}dans {human_delta}.', color=discord.Color.red() ) if after.arg and not silent: embed.add_field(name='Message', value=after.arg) embed.set_footer(text='La fermeture sera annulée ' 'si un message de discussion est envoyé.') embed.timestamp = after.dt await ctx.send(embed=embed)
def check_account_age(self, author: discord.Member) -> bool: account_age = self.config.get("account_age") now = datetime.utcnow() try: min_account_age = author.created_at + account_age except ValueError: logger.warning("Error with 'account_age'.", exc_info=True) min_account_age = author.created_at + self.config.remove("account_age") if min_account_age > now: # User account has not reached the required time delta = human_timedelta(min_account_age) logger.debug("Blocked due to account age, user %s.", author.name) if str(author.id) not in self.blocked_users: new_reason = f"System Message: New Account. Required to wait for {delta}." self.blocked_users[str(author.id)] = new_reason return False return True
async def get_thread_cooldown(self, author: discord.Member): thread_cooldown = self.config.get("thread_cooldown") now = datetime.utcnow() if thread_cooldown == isodate.Duration(): return last_log = await self.api.get_latest_user_logs(author.id) if last_log is None: logger.debug("L'ultima stanza non è stata trovata, %s.", author.name) return last_log_closed_at = last_log.get("closed_at") if not last_log_closed_at: logger.debug("L'ultima stanza non è stata chiusa., %s.", author.name) return try: cooldown = datetime.fromisoformat( last_log_closed_at) + thread_cooldown except ValueError: logger.warning("Errore con la configurazione 'thread_cooldown'.", exc_info=True) cooldown = datetime.fromisoformat( last_log_closed_at) + self.config.remove("thread_cooldown") if cooldown > now: # User messaged before thread cooldown ended delta = human_timedelta(cooldown) logger.debug( "L'utente %s è stato bloccato per il cooldown della creazione di thread.", author.name, ) return delta return
async def process_modmail(self, message: discord.Message) -> None: """Processes messages sent to the bot.""" sent_emoji, blocked_emoji = await self.retrieve_emoji() now = datetime.utcnow() account_age = self.config.get("account_age") guild_age = self.config.get("guild_age") if account_age is None: account_age = isodate.duration.Duration() else: try: account_age = isodate.parse_duration(account_age) except isodate.ISO8601Error: logger.warning( "The account age limit needs to be a " "ISO-8601 duration formatted duration string " 'greater than 0 days, not "%s".', str(account_age), ) del self.config.cache["account_age"] await self.config.update() account_age = isodate.duration.Duration() if guild_age is None: guild_age = isodate.duration.Duration() else: try: guild_age = isodate.parse_duration(guild_age) except isodate.ISO8601Error: logger.warning( "The guild join age limit needs to be a " "ISO-8601 duration formatted duration string " 'greater than 0 days, not "%s".', str(guild_age), ) del self.config.cache["guild_age"] await self.config.update() guild_age = isodate.duration.Duration() reason = self.blocked_users.get(str(message.author.id)) if reason is None: reason = "" try: min_account_age = message.author.created_at + account_age except ValueError as exc: logger.warning(exc.args[0]) del self.config.cache["account_age"] await self.config.update() min_account_age = now try: member = self.guild.get_member(message.author.id) if member: min_guild_age = member.joined_at + guild_age else: min_guild_age = now except ValueError as exc: logger.warning(exc.args[0]) del self.config.cache["guild_age"] await self.config.update() min_guild_age = now if min_account_age > now: # User account has not reached the required time reaction = blocked_emoji changed = False delta = human_timedelta(min_account_age) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: New Account. Required to wait for {delta}." ) self.config.blocked[str(message.author.id)] = new_reason await self.config.update() changed = True if reason.startswith("System Message: New Account.") or changed: await message.channel.send(embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact {self.user.mention}.", color=discord.Color.red(), )) elif min_guild_age > now: # User has not stayed in the guild for long enough reaction = blocked_emoji changed = False delta = human_timedelta(min_guild_age) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: Recently Joined. Required to wait for {delta}." ) self.config.blocked[str(message.author.id)] = new_reason await self.config.update() changed = True if reason.startswith( "System Message: Recently Joined.") or changed: await message.channel.send(embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact {self.user.mention}.", color=discord.Color.red(), )) elif str(message.author.id) in self.blocked_users: reaction = blocked_emoji if reason.startswith( "System Message: New Account.") or reason.startswith( "System Message: Recently Joined."): # Met the age limit already reaction = sent_emoji del self.config.blocked[str(message.author.id)] await self.config.update() else: end_time = re.search(r"%(.+?)%$", reason) if end_time is not None: after = (datetime.fromisoformat(end_time.group(1)) - now).total_seconds() if after <= 0: # No longer blocked reaction = sent_emoji del self.config.blocked[str(message.author.id)] await self.config.update() else: reaction = sent_emoji if reaction != "disable": try: await message.add_reaction(reaction) except (discord.HTTPException, discord.InvalidArgument): pass if str(message.author.id) not in self.blocked_users: thread = await self.threads.find_or_create(message.author) await thread.send(message)
async def _process_blocked(self, message: discord.Message) -> bool: sent_emoji, blocked_emoji = await self.retrieve_emoji() if str(message.author.id) in self.blocked_whitelisted_users: if str(message.author.id) in self.blocked_users: self.blocked_users.pop(str(message.author.id)) await self.config.update() if sent_emoji != "disable": try: await message.add_reaction(sent_emoji) except (discord.HTTPException, discord.InvalidArgument): logger.warning("Failed to add sent_emoji.", exc_info=True) return False now = datetime.utcnow() account_age = self.config["account_age"] guild_age = self.config["guild_age"] if account_age is None: account_age = isodate.Duration() if guild_age is None: guild_age = isodate.Duration() if not isinstance(account_age, isodate.Duration): try: account_age = isodate.parse_duration(account_age) except isodate.ISO8601Error: logger.warning( "The account age limit needs to be a " "ISO-8601 duration formatted duration string " 'greater than 0 days, not "%s".', str(account_age), ) account_age = self.config.remove("account_age") if not isinstance(guild_age, isodate.Duration): try: guild_age = isodate.parse_duration(guild_age) except isodate.ISO8601Error: logger.warning( "The guild join age limit needs to be a " "ISO-8601 duration formatted duration string " 'greater than 0 days, not "%s".', str(guild_age), ) guild_age = self.config.remove("guild_age") reason = self.blocked_users.get(str(message.author.id)) or "" min_guild_age = min_account_age = now try: min_account_age = message.author.created_at + account_age except ValueError: logger.warning("Error with 'account_age'.", exc_info=True) self.config.remove("account_age") try: joined_at = getattr(message.author, "joined_at", None) if joined_at is not None: min_guild_age = joined_at + guild_age except ValueError: logger.warning("Error with 'guild_age'.", exc_info=True) self.config.remove("guild_age") if min_account_age > now: # User account has not reached the required time reaction = blocked_emoji changed = False delta = human_timedelta(min_account_age) logger.debug("Blocked due to account age, user %s.", message.author.name) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: New Account. Required to wait for {delta}." ) self.blocked_users[str(message.author.id)] = new_reason changed = True if reason.startswith("System Message: New Account.") or changed: await message.channel.send(embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact me.", color=discord.Color.red(), )) elif min_guild_age > now: # User has not stayed in the guild for long enough reaction = blocked_emoji changed = False delta = human_timedelta(min_guild_age) logger.debug("Blocked due to guild age, user %s.", message.author.name) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: Recently Joined. Required to wait for {delta}." ) self.blocked_users[str(message.author.id)] = new_reason changed = True if reason.startswith( "System Message: Recently Joined.") or changed: await message.channel.send(embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact me.", color=discord.Color.red(), )) elif str(message.author.id) in self.blocked_users: if reason.startswith( "System Message: New Account.") or reason.startswith( "System Message: Recently Joined."): # Met the age limit already, otherwise it would've been caught by the previous if's reaction = sent_emoji logger.debug("No longer internally blocked, user %s.", message.author.name) self.blocked_users.pop(str(message.author.id)) else: reaction = blocked_emoji end_time = re.search(r"%(.+?)%$", reason) if end_time is not None: logger.debug("No longer blocked, user %s.", message.author.name) after = (datetime.fromisoformat(end_time.group(1)) - now).total_seconds() if after <= 0: # No longer blocked reaction = sent_emoji self.blocked_users.pop(str(message.author.id)) else: logger.debug("User blocked, user %s.", message.author.name) else: reaction = sent_emoji await self.config.update() if reaction != "disable": try: await message.add_reaction(reaction) except (discord.HTTPException, discord.InvalidArgument): logger.warning("Failed to add reaction %s.", reaction, exc_info=True) return str(message.author.id) in self.blocked_users
async def process_modmail(self, message): """Processes messages sent to the bot.""" sent_emoji, blocked_emoji = await self.retrieve_emoji() account_age = self.config.get('account_age') if account_age is None: account_age = isodate.duration.Duration() else: try: account_age = isodate.parse_duration(account_age) except isodate.ISO8601Error: logger.warning( 'The account age limit needs to be a ' 'ISO-8601 duration formatted duration string ' f'greater than 0 days, not "%s".', str(account_age)) del self.config.cache['account_age'] await self.config.update() account_age = isodate.duration.Duration() reason = self.blocked_users.get(str(message.author.id)) if reason is None: reason = '' try: min_account_age = message.author.created_at + account_age except ValueError as e: logger.warning(e.args[0]) del self.config.cache['account_age'] await self.config.update() min_account_age = message.author.created_at if min_account_age > datetime.utcnow(): # user account has not reached the required time reaction = blocked_emoji changed = False delta = human_timedelta(min_account_age) if str(message.author.id) not in self.blocked_users: new_reason = f'System Message: New Account. Required to wait for {delta}.' self.config.blocked[str(message.author.id)] = new_reason await self.config.update() changed = True if reason.startswith('System Message: New Account.') or changed: await message.channel.send(embed=discord.Embed( title='Message not sent!', description=f'Your must wait for {delta} ' f'before you can contact {self.user.mention}.', color=discord.Color.red())) elif str(message.author.id) in self.blocked_users: reaction = blocked_emoji if reason.startswith('System Message: New Account.'): # Met the age limit already reaction = sent_emoji del self.config.blocked[str(message.author.id)] await self.config.update() else: end_time = re.search(r'%(.+?)%$', reason) if end_time is not None: after = (datetime.fromisoformat(end_time.group(1)) - datetime.utcnow()).total_seconds() if after <= 0: # No longer blocked reaction = sent_emoji del self.config.blocked[str(message.author.id)] await self.config.update() else: reaction = sent_emoji if reaction != 'disable': try: await message.add_reaction(reaction) except (discord.HTTPException, discord.InvalidArgument): pass if str(message.author.id) not in self.blocked_users: thread = await self.threads.find_or_create(message.author) await thread.send(message)
async def _process_blocked( self, message: discord.Message ) -> typing.Tuple[bool, str]: sent_emoji, blocked_emoji = await self.retrieve_emoji() if str(message.author.id) in self.blocked_whitelisted_users: if str(message.author.id) in self.blocked_users: self.blocked_users.pop(str(message.author.id)) await self.config.update() return False, sent_emoji now = datetime.utcnow() account_age = self.config.get("account_age") guild_age = self.config.get("guild_age") if account_age is None: account_age = isodate.Duration() if guild_age is None: guild_age = isodate.Duration() reason = self.blocked_users.get(str(message.author.id)) or "" min_guild_age = min_account_age = now try: min_account_age = message.author.created_at + account_age except ValueError: logger.warning("Error with 'account_age'.", exc_info=True) self.config.remove("account_age") try: joined_at = getattr(message.author, "joined_at", None) if joined_at is not None: min_guild_age = joined_at + guild_age except ValueError: logger.warning("Error with 'guild_age'.", exc_info=True) self.config.remove("guild_age") if min_account_age > now: # User account has not reached the required time reaction = blocked_emoji changed = False delta = human_timedelta(min_account_age) logger.debug("Blocked due to account age, user %s.", message.author.name) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: New Account. Required to wait for {delta}." ) self.blocked_users[str(message.author.id)] = new_reason changed = True if reason.startswith("System Message: New Account.") or changed: await message.channel.send( embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact me.", color=self.error_color, ) ) elif min_guild_age > now: # User has not stayed in the guild for long enough reaction = blocked_emoji changed = False delta = human_timedelta(min_guild_age) logger.debug("Blocked due to guild age, user %s.", message.author.name) if str(message.author.id) not in self.blocked_users: new_reason = ( f"System Message: Recently Joined. Required to wait for {delta}." ) self.blocked_users[str(message.author.id)] = new_reason changed = True if reason.startswith("System Message: Recently Joined.") or changed: await message.channel.send( embed=discord.Embed( title="Message not sent!", description=f"Your must wait for {delta} " f"before you can contact me.", color=self.error_color, ) ) elif str(message.author.id) in self.blocked_users: if reason.startswith("System Message: New Account.") or reason.startswith( "System Message: Recently Joined." ): # Met the age limit already, otherwise it would've been caught by the previous if's reaction = sent_emoji logger.debug( "No longer internally blocked, user %s.", message.author.name ) self.blocked_users.pop(str(message.author.id)) else: reaction = blocked_emoji # etc "blah blah blah... until 2019-10-14T21:12:45.559948." end_time = re.search(r"until ([^`]+?)\.$", reason) if end_time is None: # backwards compat end_time = re.search(r"%([^%]+?)%", reason) if end_time is not None: logger.warning( r"Deprecated time message for user %s, block and unblock again to update.", message.author, ) if end_time is not None: after = ( datetime.fromisoformat(end_time.group(1)) - now ).total_seconds() if after <= 0: # No longer blocked reaction = sent_emoji self.blocked_users.pop(str(message.author.id)) logger.debug("No longer blocked, user %s.", message.author.name) else: logger.debug("User blocked, user %s.", message.author.name) else: logger.debug("User blocked, user %s.", message.author.name) else: reaction = sent_emoji await self.config.update() return str(message.author.id) in self.blocked_users, reaction