fully re-implement contest cog with sqlalchemy, begin planning proper period logic in TODOs

This commit is contained in:
Xevion
2021-02-13 08:28:57 -06:00
parent 5978068e9b
commit ca10389aea

View File

@@ -1,5 +1,4 @@
import logging
from datetime import datetime
import discord
from discord.ext import commands
@@ -7,7 +6,7 @@ from discord.ext.commands import Context
from bot import checks, constants
from bot.bot import ContestBot
from bot.db import Period
from bot.models import Guild, Period, PeriodStates, Submission
logger = logging.getLogger(__file__)
logger.setLevel(constants.LOGGING_LEVEL)
@@ -24,61 +23,75 @@ class ContestCog(commands.Cog):
@checks.privileged()
async def prefix(self, ctx, new_prefix: str):
"""Changes the bot's saved prefix."""
guild = await self.bot.db.get_guild(ctx.guild.id)
if 1 <= len(new_prefix) <= 2:
if guild.prefix == new_prefix:
return await ctx.send(f':no_entry_sign: The prefix is already `{new_prefix}`.')
with self.bot.get_session() as session:
guild = session.query(Guild).filter_by(id=ctx.guild.id).first()
if 1 <= len(new_prefix) <= 2:
if guild.prefix == new_prefix:
return await ctx.send(f':no_entry_sign: The prefix is already `{new_prefix}`.')
else:
guild.prefix = new_prefix
return await ctx.send(f':white_check_mark: Prefix changed to `{new_prefix}`.')
else:
await self.bot.db.set_prefix(ctx.guild.id, new_prefix)
return await ctx.send(f':white_check_mark: Prefix changed to `{new_prefix}`.')
else:
return await ctx.send(':no_entry_sign: Invalid argument. Prefix must be 1 or 2 characters long.')
return await ctx.send(':no_entry_sign: Invalid argument. Prefix must be 1 or 2 characters long.')
@commands.command()
@commands.guild_only()
@checks.privileged()
async def submission(self, ctx: Context, new_submission: discord.TextChannel) -> None:
"""Changes the bot's saved submission channel."""
guild = await self.bot.db.get_guild(ctx.guild.id)
if guild.submission is not None and guild.submission == new_submission.id:
await ctx.send(
f':no_entry_sign: The submission channel is already set to {new_submission.mention}.')
else:
await self.bot.db.set_submission_channel(ctx.guild.id, new_submission.id)
await ctx.send(f':white_check_mark: Submission channel changed to {new_submission.mention}.')
with self.bot.get_session() as session:
guild = session.query(Guild).filter_by(id=ctx.guild.id).first()
if guild.submission is not None and guild.submission == new_submission.id:
await ctx.send(f':no_entry_sign: The submission channel is already set to {new_submission.mention}.')
else:
guild.submission_channel = new_submission
await ctx.send(f':white_check_mark: Submission channel changed to {new_submission.mention}.')
@commands.command()
@commands.guild_only()
@checks.privileged()
async def submissions(self, ctx: Context, duration: float = None) -> None:
"""Opens up the submissions channel."""
async def advance(self, ctx: Context, duration: float = None, pingback: bool = True) -> None:
"""
Advance the state of the current period pertaining to this Guild.
:param ctx:
:param duration: If given,
:param pingback: Whether or not the user should be pinged back when the duration is passed.
"""
assert duration == -1 or duration >= 0, "Duration must"
cur = await self.bot.db.conn.cursor()
try:
period = await self.bot.db.get_current_period(ctx.guild.id)
with self.bot.get_session() as session:
guild: Guild = session.query(Guild).get(ctx.guild.id)
period: Period = guild.current_period
# Handle non-existent or final-state period
if period is None:
new_period = Period(guild=ctx.guild.id, current_state=0, started_at=datetime.now(), voting_at=None, finished_at=None)
await self.bot.db.new_period(new_period)
# Handle non-existent or previously completed period in the current guild
if period is None or not period.active:
session.add(Period(id=ctx.guild.id))
# Handle previous period being completed.
elif period.state == PeriodStates.READY:
# TODO: Open the channel to messages
pass
# Handle submissions state
elif period.current_state == 0:
await self.bot.db.update(period)
elif period.state == PeriodStates.SUBMISSIONS:
# TODO: Close the channel to messages
return
# Handle voting state
elif period.current_state == 1:
elif period.state == PeriodStates.PAUSED:
# TODO: Add all reactions to every submission
# TODO: Unlock channel reactions
# TODO: Close channel submissions
return
# Print period submissions
elif period.current_state == 2:
elif period.state == PeriodStates.VOTING:
# TODO: Fetch all submissions related to this period
# TODO: Create new period for Guild at
return
finally:
await cur.close()
@commands.command()
@commands.guild_only()
@@ -104,39 +117,47 @@ class ContestCog(commands.Cog):
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author == self.bot.user or message.author.bot or not message.guild: return
guild = await self.bot.db.get_guild(message.guild.id)
channel: discord.TextChannel = message.channel
if channel.id == guild.submission:
attachments = message.attachments
if len(attachments) == 0:
await message.delete(delay=1)
warning = await channel.send(
f':no_entry_sign: {message.author.mention} Each submission must contain exactly one image.')
await warning.delete(delay=5)
elif len(attachments) > 1:
await message.delete(delay=1)
warning = await channel.send(
f':no_entry_sign: {message.author.mention} Each submission must contain exactly one image.')
await warning.delete(delay=5)
else:
last_submission = await self.bot.db.get_submission(message.guild.id, message.author.id)
if last_submission is not None:
# delete last submission
submission_msg = await channel.fetch_message(last_submission)
if submission_msg is None:
logger.error(f'Unexpected: submission message {last_submission} could not be found.')
else:
await submission_msg.delete()
logger.info(f'Old submission deleted. {last_submission} (Old) -> {message.id} (New)')
with self.bot.get_session() as session:
guild = session.query(Guild).get(message.guild.id)
print(session.query(Guild).all)
# Delete the old submission row
await self.bot.db.conn.execute('''DELETE FROM submission WHERE id = ?''', [last_submission])
await self.bot.db.conn.commit()
channel: discord.TextChannel = message.channel
if channel.id == guild.submission:
attachments = message.attachments
# Add the new submission row
await self.bot.db.add_submission(message.id, channel.guild.id, message.author.id, message.created_at)
logger.info(f'New submission created ({message.id}).')
# TODO: Do attachment filtering between videos/files/audio etc.
# Ensure that the submission contains at least one attachment
if len(attachments) == 0:
await message.delete(delay=1)
warning = await channel.send(
f':no_entry_sign: {message.author.mention} Each submission must contain exactly one image.')
await warning.delete(delay=5)
# Ensure the image contains no more than one attachment
elif len(attachments) > 1:
await message.delete(delay=1)
warning = await channel.send(
f':no_entry_sign: {message.author.mention} Each submission must contain exactly one image.')
await warning.delete(delay=5)
else:
last_submission = session.query(Submission).filter_by(id=message.guild.id, user=message.author.id)
if last_submission is not None:
# delete last submission
submission_msg = await channel.fetch_message(last_submission)
if submission_msg is None:
logger.error(f'Unexpected: submission message {last_submission} could not be found.')
else:
await submission_msg.delete()
logger.info(f'Old submission deleted. {last_submission} (Old) -> {message.id} (New)')
# Delete the old submission row
session.delete(last_submission)
# Add the new submission row
session.add(
Submission(id=message.id, user=message.author.id, period=guild.current_period, timestamp=message.created_at))
logger.info(f'New submission created ({message.id}).')
@commands.Cog.listener()
async def on_raw_message_delete(self, payload: discord.RawMessageDeleteEvent) -> None:
@@ -148,22 +169,20 @@ class ContestCog(commands.Cog):
expected_deletions.remove(payload.message_id)
return
# If the message was cached, check that it's in the correct channel.
if payload.cached_message is not None:
guild = await self.bot.db.get_guild(payload.guild_id)
if payload.cached_message.channel.id != guild.submission:
with self.bot.get_session() as session:
guild: Guild = session.query(Guild).get(payload.guild_id)
# If the message was cached, check that it's in the correct channel.
if payload.cached_message is not None and payload.cached_message.channel.id != guild.submission_channel:
return
cur = await self.bot.db.conn.cursor()
try:
await cur.execute('''DELETE FROM submission WHERE id = ? AND guild = ?''',
[payload.message_id, payload.guild_id])
if cur.rowcount > 0:
author = payload.cached_message.author.display_name if payload.cached_message is not None else 'Unknown'
submission = session.query(Submission).get(payload.message_id)
if submission is None:
logger.error(f'Submission {payload.message_id} could not be deleted from database as it was not found.')
else:
author: str = payload.cached_message.author.display_name if payload.cached_message is not None else 'Unknown'
logger.info(f'Submission {payload.message_id} by {author} deleted by outside source.')
await self.bot.db.conn.commit()
finally:
await cur.close()
session.delete(submission)
@commands.Cog.listener()
async def on_raw_bulk_message_delete(self, payload: discord.RawBulkMessageDeleteEvent) -> None: