async def on_message(self, message: discord.Message) -> None: """Move an available channel to the In Use category and replace it with a dormant one.""" if message.author.bot: return # Ignore messages sent by bots. channel = message.channel await self.check_for_answer(message) is_available = channel_utils.is_in_category( channel, constants.Categories.help_available) if not is_available or self.is_excluded_channel(channel): return # Ignore messages outside the Available category or in excluded channels. log.trace( "Waiting for the cog to be ready before processing messages.") await self.ready.wait() log.trace( "Acquiring lock to prevent a channel from being processed twice..." ) async with self.on_message_lock: log.trace(f"on_message lock acquired for {message.id}.") if not channel_utils.is_in_category( channel, constants.Categories.help_available): log.debug( f"Message {message.id} will not make #{channel} ({channel.id}) in-use " f"because another message in the channel already triggered that." ) return log.info( f"Channel #{channel} was claimed by `{message.author.id}`.") await self.move_to_in_use(channel) await self.revoke_send_permissions(message.author) await self.pin(message) # Add user with channel for dormant check. await self.help_channel_claimants.set(channel.id, message.author.id) self.bot.stats.incr("help.claimed") # Must use a timezone-aware datetime to ensure a correct POSIX timestamp. timestamp = datetime.now(timezone.utc).timestamp() await self.claim_times.set(channel.id, timestamp) await self.unanswered.set(channel.id, True) log.trace(f"Releasing on_message lock for {message.id}.") # Move a dormant channel to the Available category to fill in the gap. # This is done last and outside the lock because it may wait indefinitely for a channel to # be put in the queue. await self.move_to_available()
async def on_message_delete(self, msg: discord.Message) -> None: """ Reschedule an in-use channel to become dormant sooner if the channel is empty. The new time for the dormant task is configured with `HelpChannels.deleted_idle_minutes`. """ await self.init_task if not channel_utils.is_in_category(msg.channel, constants.Categories.help_in_use): return if not await _message.is_empty(msg.channel): return log.info( f"Claimant of #{msg.channel} ({msg.author}) deleted message, channel is empty now. Rescheduling task." ) # Cancel existing dormant task before scheduling new. self.scheduler.cancel(msg.channel.id) delay = constants.HelpChannels.deleted_idle_minutes * 60 self.scheduler.schedule_later(delay, msg.channel.id, self.move_idle_channel(msg.channel))
async def on_message(self, message: discord.Message) -> None: """Move an available channel to the In Use category and replace it with a dormant one.""" if message.author.bot: return # Ignore messages sent by bots. await self.init_task if channel_utils.is_in_category(message.channel, constants.Categories.help_available): if not _channel.is_excluded_channel(message.channel): await self.claim_channel(message) elif channel_utils.is_in_category(message.channel, constants.Categories.help_in_use): await self.notify_session_participants(message) await _message.update_message_caches(message)
async def on_message(self, message: Message) -> None: """Report message events in the server to statsd.""" if message.guild is None: return if message.guild.id != Guild.id: return if is_in_category(message.channel, Categories.modmail): if message.channel.id != Channels.incidents: # Do not report modmail channels to stats, there are too many # of them for interesting statistics to be drawn out of this. return reformatted_name = message.channel.name.replace('-', '_') if CHANNEL_NAME_OVERRIDES.get(message.channel.id): reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id) reformatted_name = "".join(char for char in reformatted_name if char in ALLOWED_CHARS) stat_name = f"channels.{reformatted_name}" self.bot.stats.incr(stat_name) # Increment the total message count self.bot.stats.incr("messages")
async def check_for_answer(message: discord.Message) -> None: """Checks for whether new content in a help channel comes from non-claimants.""" channel = message.channel # Confirm the channel is an in use help channel if is_in_category(channel, constants.Categories.help_in_use): log.trace(f"Checking if #{channel} ({channel.id}) has been answered.") # Check if there is an entry in unanswered if await _caches.unanswered.contains(channel.id): claimant_id = await _caches.claimants.get(channel.id) if not claimant_id: # The mapping for this channel doesn't exist, we can't do anything. return # Check the message did not come from the claimant if claimant_id != message.author.id: # Mark the channel as answered await _caches.unanswered.set(channel.id, False)
async def update_message_caches(message: discord.Message) -> None: """Checks the source of new content in a help channel and updates the appropriate cache.""" channel = message.channel # Confirm the channel is an in use help channel if is_in_category(channel, constants.Categories.help_in_use): log.trace(f"Checking if #{channel} ({channel.id}) has had a reply.") claimant_id = await _caches.claimants.get(channel.id) if not claimant_id: # The mapping for this channel doesn't exist, we can't do anything. return # datetime.timestamp() would assume it's local, despite d.py giving a (naïve) UTC time. timestamp = Arrow.fromdatetime(message.created_at).timestamp() # Overwrite the appropriate last message cache depending on the author of the message if message.author.id == claimant_id: await _caches.claimant_last_message_times.set( channel.id, timestamp) else: await _caches.non_claimant_last_message_times.set( channel.id, timestamp)