Move Submission votes count logic into model, add improved verification system, add verification that the bot is always reacting to the message (on every reaction add/removal)

This commit is contained in:
Xevion
2021-02-14 01:48:56 -06:00
parent bb28f38fd9
commit 2178a3ec99
2 changed files with 46 additions and 14 deletions

View File

@@ -232,16 +232,20 @@ class ContestCog(commands.Cog):
with self.bot.get_session() as session: with self.bot.get_session() as session:
guild: Guild = session.query(Guild).get(payload.guild_id) guild: Guild = session.query(Guild).get(payload.guild_id)
if payload.channel_id == guild.submission_channel: if payload.channel_id == guild.submission_channel:
channel: discord.TextChannel = self.bot.get_channel(payload.channel_id)
message: discord.PartialMessage = channel.get_partial_message(payload.message_id)
if payload.emoji.id != constants.Emoji.UPVOTE: if payload.emoji.id != constants.Emoji.UPVOTE:
channel: discord.TextChannel = self.bot.get_channel(payload.channel_id)
message: discord.PartialMessage = channel.get_partial_message(payload.message_id)
await message.remove_reaction(payload.emoji, payload.member) await message.remove_reaction(payload.emoji, payload.member)
else: else:
submission: Submission = session.query(Submission).get(payload.message_id) submission: Submission = session.query(Submission).get(payload.message_id)
if submission is None: if submission is None:
logger.warning(f'Upvote reaction added to message {payload.message_id}, but no Submission found in database.') logger.warning(f'Upvote reaction added to message {payload.message_id}, but no Submission found in database.')
else: else:
submission.votes += 1 submission.increment()
# Make sure our reaction exists, verify vote count
self_reacted = await submission.verify(await message.fetch(), self.bot.user)
if not self_reacted:
await message.add_reaction(self.bot.get_emoji(constants.Emoji.UPVOTE))
@commands.Cog.listener() @commands.Cog.listener()
async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent) -> None: async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent) -> None:
@@ -262,16 +266,16 @@ class ContestCog(commands.Cog):
if submission is None: if submission is None:
logger.warning(f'Upvote reaction removed from message {payload.message_id}, but no Submission found in database.') logger.warning(f'Upvote reaction removed from message {payload.message_id}, but no Submission found in database.')
else: else:
submission.votes -= 1 submission.decrement()
# Get the actual number of votes from the message # Get the actual number of votes from the message
channel: discord.TextChannel = self.bot.get_channel(payload.channel_id) channel: discord.TextChannel = self.bot.get_channel(payload.channel_id)
message: discord.Message = await channel.fetch_message(payload.message_id) message: discord.Message = await channel.fetch_message(payload.message_id)
reaction: discord.Reaction self_reacted = await submission.verify(await message.fetch(), self.bot.user)
for reaction in filter(lambda _reaction: isinstance(_reaction.emoji, (discord.Emoji, discord.PartialEmoji))
and _reaction.emoji.id == constants.Emoji.UPVOTE, # Make sure our reaction exists
message.reactions): if not self_reacted:
submission.set_votes(reaction.count) await message.add_reaction(self.bot.get_emoji(constants.Emoji.UPVOTE))
@commands.Cog.listener() @commands.Cog.listener()
async def on_raw_reaction_clear(self, payload: discord.RawReactionActionEvent) -> None: async def on_raw_reaction_clear(self, payload: discord.RawReactionActionEvent) -> None:

View File

@@ -2,8 +2,9 @@ import datetime
import enum import enum
import functools import functools
import logging import logging
from typing import List from typing import List, Optional, Union
import discord
from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Integer, Text from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Integer, Text
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
@@ -80,11 +81,38 @@ class Submission(Base):
period_id = Column(Integer, ForeignKey("period.id")) # The id of the period this Submission relates to. period_id = Column(Integer, ForeignKey("period.id")) # The id of the period this Submission relates to.
period = relationship("Period", back_populates="submissions") # The period this submission was made in. period = relationship("Period", back_populates="submissions") # The period this submission was made in.
def set_votes(self, n: int) -> None: def increment(self) -> None:
"""Increase the number of votes by one."""
self.votes += 1
def decrement(self) -> None:
"""Decrease the number of votes by one."""
self.votes -= 1
async def verify(self, message: discord.Message, user: Optional[Union[discord.ClientUser, discord.User]] = None) -> bool:
"""Sets the number of votes for this Submission.""" """Sets the number of votes for this Submission."""
if self.votes != n: saw_user = False
logger.warning(f'True vote count was off for Submission {self.id} by {n - self.votes}.') for reaction in message.reactions:
self.votes = n # Check that it's a custom Emoji and that the Emoji is the expected Upvote emoji
if isinstance(reaction.emoji, (discord.Emoji, discord.PartialEmoji)):
if reaction.emoji.id == constants.Emoji.UPVOTE:
# If a user was specified, look for him in the reactions
if user is not None:
reacting_user: Union[discord.Member, discord.User]
async for reacting_user in reaction.users():
# Check if the user who reacted to this emoji is the user we are looking for
if reacting_user.id == user.id:
saw_user = True
break
# Tally the number of votes
votes = reaction.count - 1
if votes != self.votes:
# Make a racket if we counted wrong or somehow missed reactions
logger.warning(f'True vote count was off for Submission {self.id} by {votes - self.votes}.')
self.votes = votes
return saw_user
class Period(Base): class Period(Base):