mirror of
https://github.com/nexy7574/LCC-bot.git
synced 2024-09-19 18:16:34 +01:00
Blackify code (formatting)
This commit is contained in:
parent
c2e5e71270
commit
a41670b437
11 changed files with 144 additions and 243 deletions
|
@ -8,16 +8,13 @@ from discord.ext import commands, tasks
|
||||||
import config
|
import config
|
||||||
from utils import Assignments, Tutors, simple_embed_paginator, get_or_none, Student, hyperlink, console
|
from utils import Assignments, Tutors, simple_embed_paginator, get_or_none, Student, hyperlink, console
|
||||||
|
|
||||||
BOOL_EMOJI = {
|
BOOL_EMOJI = {True: "\N{white heavy check mark}", False: "\N{cross mark}"}
|
||||||
True: "\N{white heavy check mark}",
|
|
||||||
False: "\N{cross mark}"
|
|
||||||
}
|
|
||||||
|
|
||||||
TUTOR_OPTION = discord.Option(
|
TUTOR_OPTION = discord.Option(
|
||||||
str,
|
str,
|
||||||
"The tutor who assigned the project",
|
"The tutor who assigned the project",
|
||||||
default=None,
|
default=None,
|
||||||
choices=[x.title() for x in dir(Tutors) if not x.startswith("__") and x not in ("name", "value")]
|
choices=[x.title() for x in dir(Tutors) if not x.startswith("__") and x not in ("name", "value")],
|
||||||
)
|
)
|
||||||
__MARK_AS_OPTION_OPTIONS = ("unfinished", "finished", "unsubmitted", "submitted")
|
__MARK_AS_OPTION_OPTIONS = ("unfinished", "finished", "unsubmitted", "submitted")
|
||||||
MARK_AS_OPTION = discord.Option(
|
MARK_AS_OPTION = discord.Option(
|
||||||
|
@ -29,7 +26,7 @@ MARK_AS_OPTION = discord.Option(
|
||||||
value=__MARK_AS_OPTION_OPTIONS.index(x),
|
value=__MARK_AS_OPTION_OPTIONS.index(x),
|
||||||
)
|
)
|
||||||
for x in __MARK_AS_OPTION_OPTIONS
|
for x in __MARK_AS_OPTION_OPTIONS
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,12 +36,8 @@ class TutorSelector(discord.ui.View):
|
||||||
@discord.ui.select(
|
@discord.ui.select(
|
||||||
placeholder="Select a tutor name",
|
placeholder="Select a tutor name",
|
||||||
options=[
|
options=[
|
||||||
discord.SelectOption(
|
discord.SelectOption(label=x.title(), value=x.upper()) for x in [y.name for y in TUTOR_OPTION.choices]
|
||||||
label=x.title(),
|
],
|
||||||
value=x.upper()
|
|
||||||
)
|
|
||||||
for x in [y.name for y in TUTOR_OPTION.choices]
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
async def select_tutor(self, select: discord.ui.Select, interaction2: discord.Interaction):
|
async def select_tutor(self, select: discord.ui.Select, interaction2: discord.Interaction):
|
||||||
await interaction2.response.defer(invisible=True)
|
await interaction2.response.defer(invisible=True)
|
||||||
|
@ -56,13 +49,10 @@ async def assignment_autocomplete(ctx: discord.AutocompleteContext) -> list[str]
|
||||||
if not ctx.value:
|
if not ctx.value:
|
||||||
results: list[Assignments] = await Assignments.objects.order_by("-due_by").limit(7).all()
|
results: list[Assignments] = await Assignments.objects.order_by("-due_by").limit(7).all()
|
||||||
else:
|
else:
|
||||||
results: list[Assignments] = await Assignments.objects.filter(
|
results: list[Assignments] = (
|
||||||
title__icontains=ctx.value
|
await Assignments.objects.filter(title__icontains=ctx.value).limit(30).order_by("-entry_id").all()
|
||||||
).limit(30).order_by("-entry_id").all()
|
)
|
||||||
return [
|
return [textwrap.shorten(f"{x.entry_id}: {x.title}", 100, placeholder="...") for x in results]
|
||||||
textwrap.shorten(f"{x.entry_id}: {x.title}", 100, placeholder="...")
|
|
||||||
for x in results
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection DuplicatedCode
|
# noinspection DuplicatedCode
|
||||||
|
@ -80,16 +70,10 @@ class AssignmentsCog(commands.Cog):
|
||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
try:
|
try:
|
||||||
view_command = "</{0.name} view:{0.id}>".format(
|
view_command = "</{0.name} view:{0.id}>".format(
|
||||||
self.bot.get_application_command(
|
self.bot.get_application_command("assignments", type=discord.SlashCommandGroup)
|
||||||
"assignments",
|
|
||||||
type=discord.SlashCommandGroup
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
edit_command = "</{0.name} edit:{0.id}>".format(
|
edit_command = "</{0.name} edit:{0.id}>".format(
|
||||||
self.bot.get_application_command(
|
self.bot.get_application_command("assignments", type=discord.SlashCommandGroup)
|
||||||
"assignments",
|
|
||||||
type=discord.SlashCommandGroup
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
view_command = "`/assignments view`"
|
view_command = "`/assignments view`"
|
||||||
|
@ -100,10 +84,12 @@ class AssignmentsCog(commands.Cog):
|
||||||
if not general.can_send():
|
if not general.can_send():
|
||||||
return
|
return
|
||||||
|
|
||||||
msg_format = "@everyone {reminder_name} reminder for project {project_title} for **{project_tutor}**!\n" \
|
msg_format = (
|
||||||
"Run '%s {project_title}' to view information on the assignment.\n" \
|
"@everyone {reminder_name} reminder for project {project_title} for **{project_tutor}**!\n"
|
||||||
"*You can mark this assignment as complete with '%s {project_title}', which will prevent" \
|
"Run '%s {project_title}' to view information on the assignment.\n"
|
||||||
" further reminders.*" % (view_command, edit_command)
|
"*You can mark this assignment as complete with '%s {project_title}', which will prevent"
|
||||||
|
" further reminders.*" % (view_command, edit_command)
|
||||||
|
)
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
assignments: list[Assignments] = await Assignments.objects.filter(submitted=False).all()
|
assignments: list[Assignments] = await Assignments.objects.filter(submitted=False).all()
|
||||||
|
@ -126,9 +112,9 @@ class AssignmentsCog(commands.Cog):
|
||||||
msg_format.format(
|
msg_format.format(
|
||||||
reminder_name=reminder_name,
|
reminder_name=reminder_name,
|
||||||
project_title=textwrap.shorten(assignment.title, 100, placeholder="..."),
|
project_title=textwrap.shorten(assignment.title, 100, placeholder="..."),
|
||||||
project_tutor=assignment.tutor.name.title()
|
project_tutor=assignment.tutor.name.title(),
|
||||||
),
|
),
|
||||||
allowed_mentions=allowed_mentions
|
allowed_mentions=allowed_mentions,
|
||||||
)
|
)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
|
@ -142,9 +128,9 @@ class AssignmentsCog(commands.Cog):
|
||||||
msg_format.format(
|
msg_format.format(
|
||||||
reminder_name=reminder_name,
|
reminder_name=reminder_name,
|
||||||
project_title=textwrap.shorten(assignment.title, 100, placeholder="..."),
|
project_title=textwrap.shorten(assignment.title, 100, placeholder="..."),
|
||||||
project_tutor=assignment.tutor.name.title()
|
project_tutor=assignment.tutor.name.title(),
|
||||||
),
|
),
|
||||||
allowed_mentions=allowed_mentions
|
allowed_mentions=allowed_mentions,
|
||||||
)
|
)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
|
@ -156,7 +142,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title=f"Assignment #{assignment.entry_id}",
|
title=f"Assignment #{assignment.entry_id}",
|
||||||
description=f"**Title:**\n>>> {assignment.title}",
|
description=f"**Title:**\n>>> {assignment.title}",
|
||||||
colour=discord.Colour.random()
|
colour=discord.Colour.random(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if assignment.classroom:
|
if assignment.classroom:
|
||||||
|
@ -172,37 +158,29 @@ class AssignmentsCog(commands.Cog):
|
||||||
embed.add_field(name="Classroom URL:", value=classroom, inline=False),
|
embed.add_field(name="Classroom URL:", value=classroom, inline=False),
|
||||||
embed.add_field(name="Shared Document URL:", value=shared_doc)
|
embed.add_field(name="Shared Document URL:", value=shared_doc)
|
||||||
embed.add_field(name="Tutor:", value=assignment.tutor.name.title(), inline=False)
|
embed.add_field(name="Tutor:", value=assignment.tutor.name.title(), inline=False)
|
||||||
user_id = getattr(assignment.created_by, 'user_id', assignment.entry_id)
|
user_id = getattr(assignment.created_by, "user_id", assignment.entry_id)
|
||||||
embed.add_field(
|
embed.add_field(name="Created:", value=f"<t:{assignment.created_at:.0f}:R> by <@{user_id}>", inline=False)
|
||||||
name="Created:",
|
|
||||||
value=f"<t:{assignment.created_at:.0f}:R> by <@{user_id}>",
|
|
||||||
inline=False
|
|
||||||
)
|
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
name="Due:",
|
name="Due:",
|
||||||
value=f"<t:{assignment.due_by:.0f}:R> "
|
value=f"<t:{assignment.due_by:.0f}:R> "
|
||||||
f"(finished: {BOOL_EMOJI[assignment.finished]} | Submitted: {BOOL_EMOJI[assignment.submitted]})",
|
f"(finished: {BOOL_EMOJI[assignment.finished]} | Submitted: {BOOL_EMOJI[assignment.submitted]})",
|
||||||
inline=False
|
inline=False,
|
||||||
)
|
)
|
||||||
if assignment.reminders:
|
if assignment.reminders:
|
||||||
embed.set_footer(text="Reminders sent: " + ", ".join(assignment.reminders))
|
embed.set_footer(text="Reminders sent: " + ", ".join(assignment.reminders))
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
assignments_command = discord.SlashCommandGroup(
|
assignments_command = discord.SlashCommandGroup("assignments", "Assignment/project management", guild_only=True)
|
||||||
"assignments",
|
|
||||||
"Assignment/project management",
|
|
||||||
guild_only=True
|
|
||||||
)
|
|
||||||
|
|
||||||
@assignments_command.command(name="list")
|
@assignments_command.command(name="list")
|
||||||
async def list_assignments(
|
async def list_assignments(
|
||||||
self,
|
self,
|
||||||
ctx: discord.ApplicationContext,
|
ctx: discord.ApplicationContext,
|
||||||
limit: int = 20,
|
limit: int = 20,
|
||||||
upcoming_only: bool = True,
|
upcoming_only: bool = True,
|
||||||
tutor_name: TUTOR_OPTION = None,
|
tutor_name: TUTOR_OPTION = None,
|
||||||
unfinished_only: bool = False,
|
unfinished_only: bool = False,
|
||||||
unsubmitted_only: bool = False
|
unsubmitted_only: bool = False,
|
||||||
):
|
):
|
||||||
"""Lists assignments."""
|
"""Lists assignments."""
|
||||||
tutor_name: Optional[str]
|
tutor_name: Optional[str]
|
||||||
|
@ -231,13 +209,9 @@ class AssignmentsCog(commands.Cog):
|
||||||
)
|
)
|
||||||
|
|
||||||
embeds = simple_embed_paginator(lines, assert_ten=True, colour=ctx.author.colour)
|
embeds = simple_embed_paginator(lines, assert_ten=True, colour=ctx.author.colour)
|
||||||
embeds = embeds or [
|
embeds = embeds or [discord.Embed(description="No projects match the provided criteria.")]
|
||||||
discord.Embed(description="No projects match the provided criteria.")
|
|
||||||
]
|
|
||||||
|
|
||||||
return await ctx.respond(
|
return await ctx.respond(embeds=embeds)
|
||||||
embeds=embeds
|
|
||||||
)
|
|
||||||
|
|
||||||
@assignments_command.command(name="add")
|
@assignments_command.command(name="add")
|
||||||
async def create_assignment(self, ctx: discord.ApplicationContext):
|
async def create_assignment(self, ctx: discord.ApplicationContext):
|
||||||
|
@ -255,7 +229,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
"classroom": None,
|
"classroom": None,
|
||||||
"shared_doc": None,
|
"shared_doc": None,
|
||||||
"due_by": None,
|
"due_by": None,
|
||||||
"tutor": None
|
"tutor": None,
|
||||||
}
|
}
|
||||||
super().__init__(
|
super().__init__(
|
||||||
discord.ui.InputText(
|
discord.ui.InputText(
|
||||||
|
@ -263,7 +237,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
label="Assignment Title",
|
label="Assignment Title",
|
||||||
min_length=2,
|
min_length=2,
|
||||||
max_length=2000,
|
max_length=2000,
|
||||||
value=self.create_kwargs["title"]
|
value=self.create_kwargs["title"],
|
||||||
),
|
),
|
||||||
discord.ui.InputText(
|
discord.ui.InputText(
|
||||||
custom_id="classroom",
|
custom_id="classroom",
|
||||||
|
@ -271,7 +245,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
max_length=4000,
|
max_length=4000,
|
||||||
required=False,
|
required=False,
|
||||||
placeholder="Optional, can be added later.",
|
placeholder="Optional, can be added later.",
|
||||||
value=self.create_kwargs["classroom"]
|
value=self.create_kwargs["classroom"],
|
||||||
),
|
),
|
||||||
discord.ui.InputText(
|
discord.ui.InputText(
|
||||||
custom_id="shared_doc",
|
custom_id="shared_doc",
|
||||||
|
@ -279,7 +253,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
max_length=4000,
|
max_length=4000,
|
||||||
required=False,
|
required=False,
|
||||||
placeholder="Google docs, slides, powerpoint, etc. Optional.",
|
placeholder="Google docs, slides, powerpoint, etc. Optional.",
|
||||||
value=self.create_kwargs["shared_doc"]
|
value=self.create_kwargs["shared_doc"],
|
||||||
),
|
),
|
||||||
discord.ui.InputText(
|
discord.ui.InputText(
|
||||||
custom_id="due_by",
|
custom_id="due_by",
|
||||||
|
@ -289,11 +263,12 @@ class AssignmentsCog(commands.Cog):
|
||||||
placeholder="dd/mm/yy hh:mm".upper(),
|
placeholder="dd/mm/yy hh:mm".upper(),
|
||||||
value=(
|
value=(
|
||||||
self.create_kwargs["due_by"].strftime("%d/%m/%y %H:%M")
|
self.create_kwargs["due_by"].strftime("%d/%m/%y %H:%M")
|
||||||
if self.create_kwargs["due_by"] else None
|
if self.create_kwargs["due_by"]
|
||||||
)
|
else None
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title="Add an assignment",
|
title="Add an assignment",
|
||||||
timeout=300
|
timeout=300,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
@ -304,9 +279,10 @@ class AssignmentsCog(commands.Cog):
|
||||||
try:
|
try:
|
||||||
self.create_kwargs["due_by"] = datetime.datetime.strptime(
|
self.create_kwargs["due_by"] = datetime.datetime.strptime(
|
||||||
self.children[3].value,
|
self.children[3].value,
|
||||||
"%d/%m/%y %H:%M" if len(self.children[3].value) == 14 else "%d/%m/%Y %H:%M"
|
"%d/%m/%y %H:%M" if len(self.children[3].value) == 14 else "%d/%m/%Y %H:%M",
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|
||||||
class TryAgainView(discord.ui.View):
|
class TryAgainView(discord.ui.View):
|
||||||
def __init__(self, kw):
|
def __init__(self, kw):
|
||||||
self._mod = None
|
self._mod = None
|
||||||
|
@ -327,10 +303,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
v = TryAgainView(self.create_kwargs)
|
v = TryAgainView(self.create_kwargs)
|
||||||
msg = await interaction.followup.send(
|
msg = await interaction.followup.send("\N{cross mark} Failed to parse date - try again?", view=v)
|
||||||
"\N{cross mark} Failed to parse date - try again?",
|
|
||||||
view=v
|
|
||||||
)
|
|
||||||
await v.wait()
|
await v.wait()
|
||||||
if v.modal:
|
if v.modal:
|
||||||
self.create_kwargs = v.modal.create_kwargs
|
self.create_kwargs = v.modal.create_kwargs
|
||||||
|
@ -338,10 +311,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
view = TutorSelector()
|
view = TutorSelector()
|
||||||
msg = await interaction.followup.send(
|
msg = await interaction.followup.send("Which tutor assigned this project?", view=view)
|
||||||
"Which tutor assigned this project?",
|
|
||||||
view=view
|
|
||||||
)
|
|
||||||
await view.wait()
|
await view.wait()
|
||||||
self.create_kwargs["tutor"] = view.value
|
self.create_kwargs["tutor"] = view.value
|
||||||
|
|
||||||
|
@ -353,25 +323,18 @@ class AssignmentsCog(commands.Cog):
|
||||||
await modal.wait()
|
await modal.wait()
|
||||||
if not modal.msg:
|
if not modal.msg:
|
||||||
return
|
return
|
||||||
await modal.msg.edit(
|
await modal.msg.edit(content="Creating assignment...", view=None)
|
||||||
content="Creating assignment...",
|
|
||||||
view=None
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
modal.create_kwargs["due_by"] = modal.create_kwargs["due_by"].timestamp()
|
modal.create_kwargs["due_by"] = modal.create_kwargs["due_by"].timestamp()
|
||||||
await Assignments.objects.create(**modal.create_kwargs)
|
await Assignments.objects.create(**modal.create_kwargs)
|
||||||
except sqlite3.Error as e:
|
except sqlite3.Error as e:
|
||||||
return await modal.msg.edit(content="SQL Error: %s.\nAssignment not saved." % e)
|
return await modal.msg.edit(content="SQL Error: %s.\nAssignment not saved." % e)
|
||||||
else:
|
else:
|
||||||
return await modal.msg.edit(
|
return await modal.msg.edit(content=f"\N{white heavy check mark} Created assignment!")
|
||||||
content=f"\N{white heavy check mark} Created assignment!"
|
|
||||||
)
|
|
||||||
|
|
||||||
@assignments_command.command(name="view")
|
@assignments_command.command(name="view")
|
||||||
async def get_assignment(
|
async def get_assignment(
|
||||||
self,
|
self, ctx: discord.ApplicationContext, title: discord.Option(str, autocomplete=assignment_autocomplete)
|
||||||
ctx: discord.ApplicationContext,
|
|
||||||
title: discord.Option(str, autocomplete=assignment_autocomplete)
|
|
||||||
):
|
):
|
||||||
"""Views an assignment's details"""
|
"""Views an assignment's details"""
|
||||||
try:
|
try:
|
||||||
|
@ -389,9 +352,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
|
|
||||||
@assignments_command.command(name="edit")
|
@assignments_command.command(name="edit")
|
||||||
async def edit_assignment(
|
async def edit_assignment(
|
||||||
self,
|
self, ctx: discord.ApplicationContext, title: discord.Option(str, autocomplete=assignment_autocomplete)
|
||||||
ctx: discord.ApplicationContext,
|
|
||||||
title: discord.Option(str, autocomplete=assignment_autocomplete)
|
|
||||||
):
|
):
|
||||||
"""Edits an assignment"""
|
"""Edits an assignment"""
|
||||||
try:
|
try:
|
||||||
|
@ -432,15 +393,14 @@ class AssignmentsCog(commands.Cog):
|
||||||
min_length=2,
|
min_length=2,
|
||||||
max_length=4000,
|
max_length=4000,
|
||||||
),
|
),
|
||||||
title="Update assignment title"
|
title="Update assignment title",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, _interaction: discord.Interaction):
|
async def callback(self, _interaction: discord.Interaction):
|
||||||
await _interaction.response.defer()
|
await _interaction.response.defer()
|
||||||
await assignment.update(title=self.children[0].value)
|
await assignment.update(title=self.children[0].value)
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
"\N{white heavy check mark} Changed assignment title!",
|
"\N{white heavy check mark} Changed assignment title!", delete_after=5
|
||||||
delete_after=5
|
|
||||||
)
|
)
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
|
@ -460,7 +420,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
required=False,
|
required=False,
|
||||||
max_length=4000,
|
max_length=4000,
|
||||||
),
|
),
|
||||||
title="Update Classroom url"
|
title="Update Classroom url",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, _interaction: discord.Interaction):
|
async def callback(self, _interaction: discord.Interaction):
|
||||||
|
@ -468,8 +428,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
try:
|
try:
|
||||||
await assignment.update(classroom=self.children[0].value)
|
await assignment.update(classroom=self.children[0].value)
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
"\N{white heavy check mark} Changed classroom URL!",
|
"\N{white heavy check mark} Changed classroom URL!", delete_after=5
|
||||||
delete_after=5
|
|
||||||
)
|
)
|
||||||
except sqlite3.Error:
|
except sqlite3.Error:
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
|
@ -494,7 +453,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
required=False,
|
required=False,
|
||||||
max_length=4000,
|
max_length=4000,
|
||||||
),
|
),
|
||||||
title="Update shared document url"
|
title="Update shared document url",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, _interaction: discord.Interaction):
|
async def callback(self, _interaction: discord.Interaction):
|
||||||
|
@ -502,8 +461,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
try:
|
try:
|
||||||
await assignment.update(shared_doc=self.children[0].value)
|
await assignment.update(shared_doc=self.children[0].value)
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
"\N{white heavy check mark} Changed shared doc URL!",
|
"\N{white heavy check mark} Changed shared doc URL!", delete_after=5
|
||||||
delete_after=5
|
|
||||||
)
|
)
|
||||||
except sqlite3.Error:
|
except sqlite3.Error:
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
|
@ -521,14 +479,12 @@ class AssignmentsCog(commands.Cog):
|
||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
view = TutorSelector()
|
view = TutorSelector()
|
||||||
msg: discord.WebhookMessage = await interaction.followup.send(
|
msg: discord.WebhookMessage = await interaction.followup.send(
|
||||||
"Which tutor assigned this project?",
|
"Which tutor assigned this project?", view=view
|
||||||
view=view
|
|
||||||
)
|
)
|
||||||
await view.wait()
|
await view.wait()
|
||||||
await assignment.update(tutor=view.value)
|
await assignment.update(tutor=view.value)
|
||||||
await msg.edit(
|
await msg.edit(
|
||||||
content=f"\N{white heavy check mark} Changed tutor to {view.value.name.title()}",
|
content=f"\N{white heavy check mark} Changed tutor to {view.value.name.title()}", view=None
|
||||||
view=None
|
|
||||||
)
|
)
|
||||||
await msg.delete(delay=5)
|
await msg.delete(delay=5)
|
||||||
await self.update_display(interaction)
|
await self.update_display(interaction)
|
||||||
|
@ -544,9 +500,9 @@ class AssignmentsCog(commands.Cog):
|
||||||
placeholder=self.date.strftime("%d/%m/%y %H:%M"),
|
placeholder=self.date.strftime("%d/%m/%y %H:%M"),
|
||||||
value=self.date.strftime("%d/%m/%y %H:%M"),
|
value=self.date.strftime("%d/%m/%y %H:%M"),
|
||||||
min_length=14,
|
min_length=14,
|
||||||
max_length=16
|
max_length=16,
|
||||||
),
|
),
|
||||||
title="Change due by date"
|
title="Change due by date",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, _interaction: discord.Interaction):
|
async def callback(self, _interaction: discord.Interaction):
|
||||||
|
@ -554,7 +510,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
try:
|
try:
|
||||||
new = datetime.datetime.strptime(
|
new = datetime.datetime.strptime(
|
||||||
self.children[1].value,
|
self.children[1].value,
|
||||||
"%d/%m/%y %H:%M" if len(self.children[1].value) == 14 else "%d/%m/%Y %H:%M"
|
"%d/%m/%y %H:%M" if len(self.children[1].value) == 14 else "%d/%m/%Y %H:%M",
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
|
@ -566,15 +522,13 @@ class AssignmentsCog(commands.Cog):
|
||||||
try:
|
try:
|
||||||
await assignment.update(due_by=new.timestamp(), reminders=[])
|
await assignment.update(due_by=new.timestamp(), reminders=[])
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send(
|
||||||
"\N{white heavy check mark} Changed due by date & reset reminders.",
|
"\N{white heavy check mark} Changed due by date & reset reminders.", delete_after=5
|
||||||
delete_after=5
|
|
||||||
)
|
)
|
||||||
except sqlite3.Error:
|
except sqlite3.Error:
|
||||||
await _interaction.followup.send(
|
await _interaction.followup.send("\N{cross mark} Failed to apply changes.")
|
||||||
"\N{cross mark} Failed to apply changes."
|
|
||||||
)
|
|
||||||
finally:
|
finally:
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
await interaction.response.send_modal(UpdateDateModal())
|
await interaction.response.send_modal(UpdateDateModal())
|
||||||
await self.update_display(interaction)
|
await self.update_display(interaction)
|
||||||
|
|
||||||
|
@ -599,7 +553,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
if assignment.finished is False and assignment.submitted is False:
|
if assignment.finished is False and assignment.submitted is False:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(
|
||||||
"\N{cross mark} You cannot mark an assignment as submitted if it is not marked as complete!",
|
"\N{cross mark} You cannot mark an assignment as submitted if it is not marked as complete!",
|
||||||
delete_after=10
|
delete_after=10,
|
||||||
)
|
)
|
||||||
await assignment.update(submitted=not assignment.submitted)
|
await assignment.update(submitted=not assignment.submitted)
|
||||||
await self.update_display(interaction)
|
await self.update_display(interaction)
|
||||||
|
@ -607,7 +561,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
"\N{white heavy check mark} Assignment is now marked as {}submitted.".format(
|
"\N{white heavy check mark} Assignment is now marked as {}submitted.".format(
|
||||||
"in" if assignment.submitted is False else ""
|
"in" if assignment.submitted is False else ""
|
||||||
),
|
),
|
||||||
delete_after=5
|
delete_after=5,
|
||||||
)
|
)
|
||||||
|
|
||||||
@discord.ui.button(label="Save & Exit")
|
@discord.ui.button(label="Save & Exit")
|
||||||
|
@ -621,8 +575,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
await interaction.response.defer(ephemeral=True)
|
await interaction.response.defer(ephemeral=True)
|
||||||
await assignment.created_by.load()
|
await assignment.created_by.load()
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
embed=AssignmentsCog.generate_assignment_embed(assignment),
|
embed=AssignmentsCog.generate_assignment_embed(assignment), ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
await self.update_display(interaction)
|
await self.update_display(interaction)
|
||||||
|
|
||||||
|
@ -630,9 +583,7 @@ class AssignmentsCog(commands.Cog):
|
||||||
|
|
||||||
@assignments_command.command(name="remove")
|
@assignments_command.command(name="remove")
|
||||||
async def remove_assignment(
|
async def remove_assignment(
|
||||||
self,
|
self, ctx: discord.ApplicationContext, title: discord.Option(str, autocomplete=assignment_autocomplete)
|
||||||
ctx: discord.ApplicationContext,
|
|
||||||
title: discord.Option(str, autocomplete=assignment_autocomplete)
|
|
||||||
):
|
):
|
||||||
"""Edits an assignment"""
|
"""Edits an assignment"""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -37,11 +37,7 @@ class Events(commands.Cog):
|
||||||
if self.bot.activity.name == text:
|
if self.bot.activity.name == text:
|
||||||
return
|
return
|
||||||
await self.bot.change_presence(
|
await self.bot.change_presence(
|
||||||
activity=discord.Activity(
|
activity=discord.Activity(name=text, type=discord.ActivityType.playing), status=status
|
||||||
name=text,
|
|
||||||
type=discord.ActivityType.playing
|
|
||||||
),
|
|
||||||
status=status
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await self.bot.change_presence()
|
await self.bot.change_presence()
|
||||||
|
@ -71,9 +67,7 @@ class Events(commands.Cog):
|
||||||
|
|
||||||
channel: discord.TextChannel = discord.utils.get(member.guild.text_channels, name="general")
|
channel: discord.TextChannel = discord.utils.get(member.guild.text_channels, name="general")
|
||||||
if channel and channel.can_send():
|
if channel and channel.can_send():
|
||||||
await channel.send(
|
await channel.send(f"{LTR} {member.mention} {f'({student.id})' if student else '(pending verification)'}")
|
||||||
f"{LTR} {member.mention} {f'({student.id})' if student else '(pending verification)'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_member_remove(self, member: discord.Member):
|
async def on_member_remove(self, member: discord.Member):
|
||||||
|
@ -83,9 +77,7 @@ class Events(commands.Cog):
|
||||||
student: Optional[Student] = await get_or_none(Student, user_id=member.id)
|
student: Optional[Student] = await get_or_none(Student, user_id=member.id)
|
||||||
channel: discord.TextChannel = discord.utils.get(member.guild.text_channels, name="general")
|
channel: discord.TextChannel = discord.utils.get(member.guild.text_channels, name="general")
|
||||||
if channel and channel.can_send():
|
if channel and channel.can_send():
|
||||||
await channel.send(
|
await channel.send(f"{RTL} {member.mention} {f'({student.id})' if student else '(pending verification)'}")
|
||||||
f"{RTL} {member.mention} {f'({student.id})' if student else '(pending verification)'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
async def on_message(self, message: discord.Message):
|
async def on_message(self, message: discord.Message):
|
||||||
|
|
12
cogs/mod.py
12
cogs/mod.py
|
@ -26,8 +26,7 @@ class Mod(commands.Cog):
|
||||||
|
|
||||||
await member.ban(reason=f"Banned ID {ban.student_id} by {ctx.author}")
|
await member.ban(reason=f"Banned ID {ban.student_id} by {ctx.author}")
|
||||||
return await ctx.respond(
|
return await ctx.respond(
|
||||||
f"\N{white heavy check mark} Banned {ban.student_id} (and {member.mention})",
|
f"\N{white heavy check mark} Banned {ban.student_id} (and {member.mention})", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@commands.slash_command(name="unban-student-number")
|
@commands.slash_command(name="unban-student-number")
|
||||||
|
@ -45,18 +44,13 @@ class Mod(commands.Cog):
|
||||||
return await ctx.respond(f"\N{white heavy check mark} Unbanned {student_id}. No user to unban.")
|
return await ctx.respond(f"\N{white heavy check mark} Unbanned {student_id}. No user to unban.")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
await ctx.guild.unban(
|
await ctx.guild.unban(discord.Object(user_id), reason=f"Unbanned by {ctx.author}")
|
||||||
discord.Object(user_id),
|
|
||||||
reason=f"Unbanned by {ctx.author}"
|
|
||||||
)
|
|
||||||
except discord.HTTPException as e:
|
except discord.HTTPException as e:
|
||||||
return await ctx.respond(
|
return await ctx.respond(
|
||||||
f"\N{white heavy check mark} Unbanned {student_id}. Failed to unban {user_id} - HTTP {e.status}."
|
f"\N{white heavy check mark} Unbanned {student_id}. Failed to unban {user_id} - HTTP {e.status}."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return await ctx.respond(
|
return await ctx.respond(f"\N{white heavy check mark} Unbanned {student_id}. Unbanned {user_id}.")
|
||||||
f"\N{white heavy check mark} Unbanned {student_id}. Unbanned {user_id}."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|
|
@ -32,11 +32,7 @@ class TimeTableCog(commands.Cog):
|
||||||
start_date = datetime.strptime(dates["start"], "%d/%m/%Y")
|
start_date = datetime.strptime(dates["start"], "%d/%m/%Y")
|
||||||
end_date = datetime.strptime(dates["end"], "%d/%m/%Y")
|
end_date = datetime.strptime(dates["end"], "%d/%m/%Y")
|
||||||
if date.timestamp() <= end_date.timestamp() and date.timestamp() >= start_date.timestamp():
|
if date.timestamp() <= end_date.timestamp() and date.timestamp() >= start_date.timestamp():
|
||||||
return {
|
return {"name": name, "start": start_date, "end": end_date}
|
||||||
"name": name,
|
|
||||||
"start": start_date,
|
|
||||||
"end": end_date
|
|
||||||
}
|
|
||||||
|
|
||||||
def cog_unload(self):
|
def cog_unload(self):
|
||||||
self.update_status.stop()
|
self.update_status.stop()
|
||||||
|
@ -92,7 +88,9 @@ class TimeTableCog(commands.Cog):
|
||||||
if lesson is None:
|
if lesson is None:
|
||||||
# Loop until we find the next day when it isn't the weekend, and we aren't on break.
|
# Loop until we find the next day when it isn't the weekend, and we aren't on break.
|
||||||
next_available_date = date.replace(hour=0, minute=0, second=0)
|
next_available_date = date.replace(hour=0, minute=0, second=0)
|
||||||
while self.are_on_break(next_available_date) or not self.timetable.get(next_available_date.strftime("%A").lower()):
|
while self.are_on_break(next_available_date) or not self.timetable.get(
|
||||||
|
next_available_date.strftime("%A").lower()
|
||||||
|
):
|
||||||
next_available_date += timedelta(days=1)
|
next_available_date += timedelta(days=1)
|
||||||
if next_available_date.year >= 2024:
|
if next_available_date.year >= 2024:
|
||||||
raise RuntimeError("Failed to fetch absolute next lesson")
|
raise RuntimeError("Failed to fetch absolute next lesson")
|
||||||
|
@ -103,60 +101,60 @@ class TimeTableCog(commands.Cog):
|
||||||
return lesson
|
return lesson
|
||||||
|
|
||||||
async def update_timetable_message(
|
async def update_timetable_message(
|
||||||
self,
|
self,
|
||||||
message: Union[discord.Message, discord.ApplicationContext],
|
message: Union[discord.Message, discord.ApplicationContext],
|
||||||
date: datetime = None,
|
date: datetime = None,
|
||||||
*,
|
*,
|
||||||
no_prefix: bool = False,
|
no_prefix: bool = False,
|
||||||
):
|
):
|
||||||
date = date or datetime.now()
|
date = date or datetime.now()
|
||||||
_break = self.are_on_break(date)
|
_break = self.are_on_break(date)
|
||||||
if _break:
|
if _break:
|
||||||
next_lesson = self.next_lesson(_break["end"] + timedelta(days=1, hours=7))
|
next_lesson = self.next_lesson(_break["end"] + timedelta(days=1, hours=7))
|
||||||
next_lesson = next_lesson or {
|
next_lesson = next_lesson or {"name": "Unknown", "tutor": "Unknown", "room": "Unknown"}
|
||||||
"name": "Unknown",
|
text = (
|
||||||
"tutor": "Unknown",
|
"[tt] On break {!r} from {} until {}. Break ends {}, and the first lesson back is "
|
||||||
"room": "Unknown"
|
"{lesson[name]!r} with {lesson[tutor]} in {lesson[room]}.".format(
|
||||||
}
|
_break["name"],
|
||||||
text = "[tt] On break {!r} from {} until {}. Break ends {}, and the first lesson back is " \
|
discord.utils.format_dt(_break["start"], "d"),
|
||||||
"{lesson[name]!r} with {lesson[tutor]} in {lesson[room]}.".format(
|
discord.utils.format_dt(_break["end"], "d"),
|
||||||
_break["name"],
|
discord.utils.format_dt(_break["end"], "R"),
|
||||||
discord.utils.format_dt(_break["start"], "d"),
|
lesson=next_lesson,
|
||||||
discord.utils.format_dt(_break["end"], "d"),
|
)
|
||||||
discord.utils.format_dt(_break["end"], "R"),
|
|
||||||
lesson=next_lesson
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
lesson = self.current_lesson(date)
|
lesson = self.current_lesson(date)
|
||||||
if not lesson:
|
if not lesson:
|
||||||
next_lesson = self.next_lesson(date)
|
next_lesson = self.next_lesson(date)
|
||||||
if not next_lesson:
|
if not next_lesson:
|
||||||
next_lesson = await asyncio.to_thread(
|
next_lesson = await asyncio.to_thread(self.absolute_next_lesson, new_method=True)
|
||||||
self.absolute_next_lesson,
|
|
||||||
new_method=True
|
|
||||||
)
|
|
||||||
next_lesson = next_lesson or {
|
next_lesson = next_lesson or {
|
||||||
"name": "unknown",
|
"name": "unknown",
|
||||||
"tutor": "unknown",
|
"tutor": "unknown",
|
||||||
"room": "unknown",
|
"room": "unknown",
|
||||||
"start_datetime": datetime.max
|
"start_datetime": datetime.max,
|
||||||
}
|
}
|
||||||
text = "[tt] No more lessons today!\n" \
|
text = (
|
||||||
f"[tt] Next Lesson: {next_lesson['name']!r} with {next_lesson['tutor']} in " \
|
"[tt] No more lessons today!\n"
|
||||||
f"{next_lesson['room']} - " \
|
f"[tt] Next Lesson: {next_lesson['name']!r} with {next_lesson['tutor']} in "
|
||||||
f"Starts {discord.utils.format_dt(next_lesson['start_datetime'], 'R')}"
|
f"{next_lesson['room']} - "
|
||||||
|
f"Starts {discord.utils.format_dt(next_lesson['start_datetime'], 'R')}"
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
text = f"[tt] Next Lesson: {next_lesson['name']!r} with {next_lesson['tutor']} in " \
|
text = (
|
||||||
f"{next_lesson['room']} - Starts {discord.utils.format_dt(next_lesson['start_datetime'], 'R')}"
|
f"[tt] Next Lesson: {next_lesson['name']!r} with {next_lesson['tutor']} in "
|
||||||
|
f"{next_lesson['room']} - Starts {discord.utils.format_dt(next_lesson['start_datetime'], 'R')}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
text = f"[tt] Current Lesson: {lesson['name']!r} with {lesson['tutor']} in {lesson['room']} - " \
|
text = (
|
||||||
f"ends {discord.utils.format_dt(lesson['end_datetime'], 'R')}"
|
f"[tt] Current Lesson: {lesson['name']!r} with {lesson['tutor']} in {lesson['room']} - "
|
||||||
|
f"ends {discord.utils.format_dt(lesson['end_datetime'], 'R')}"
|
||||||
|
)
|
||||||
next_lesson = self.next_lesson(date)
|
next_lesson = self.next_lesson(date)
|
||||||
if next_lesson:
|
if next_lesson:
|
||||||
text += "\n[tt] Next lesson: {0[name]!r} with {0[tutor]} in {0[room]} - starts {1}".format(
|
text += "\n[tt] Next lesson: {0[name]!r} with {0[tutor]} in {0[room]} - starts {1}".format(
|
||||||
next_lesson,
|
next_lesson, discord.utils.format_dt(next_lesson["start_datetime"], "R")
|
||||||
discord.utils.format_dt(next_lesson["start_datetime"], 'R')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if no_prefix:
|
if no_prefix:
|
||||||
|
@ -215,9 +213,11 @@ class TimeTableCog(commands.Cog):
|
||||||
for lesson in lessons:
|
for lesson in lessons:
|
||||||
start_datetime = date.replace(hour=lesson["start"][0], minute=lesson["start"][1])
|
start_datetime = date.replace(hour=lesson["start"][0], minute=lesson["start"][1])
|
||||||
end_datetime = date.replace(hour=lesson["end"][0], minute=lesson["end"][1])
|
end_datetime = date.replace(hour=lesson["end"][0], minute=lesson["end"][1])
|
||||||
text = f"{discord.utils.format_dt(start_datetime, 't')} to {discord.utils.format_dt(end_datetime, 't')}" \
|
text = (
|
||||||
f":\n> Lesson Name: {lesson['name']!r}\n" \
|
f"{discord.utils.format_dt(start_datetime, 't')} to {discord.utils.format_dt(end_datetime, 't')}"
|
||||||
f"> Tutor: **{lesson['tutor']}**\n> Room: `{lesson['room']}`"
|
f":\n> Lesson Name: {lesson['name']!r}\n"
|
||||||
|
f"> Tutor: **{lesson['tutor']}**\n> Room: `{lesson['room']}`"
|
||||||
|
)
|
||||||
blocks.append(text)
|
blocks.append(text)
|
||||||
await ctx.respond("\n\n".join(blocks))
|
await ctx.respond("\n\n".join(blocks))
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,13 @@ class VerifyCog(commands.Cog):
|
||||||
return await ctx.respond(
|
return await ctx.respond(
|
||||||
f"{member.mention}'s B number is saved as {student.id!r}.",
|
f"{member.mention}'s B number is saved as {student.id!r}.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
allowed_mentions=discord.AllowedMentions.none()
|
allowed_mentions=discord.AllowedMentions.none(),
|
||||||
)
|
)
|
||||||
except orm.NoMatch:
|
except orm.NoMatch:
|
||||||
return await ctx.respond(
|
return await ctx.respond(
|
||||||
f"{member.mention} has no saved B number.",
|
f"{member.mention} has no saved B number.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
allowed_mentions=discord.AllowedMentions.none()
|
allowed_mentions=discord.AllowedMentions.none(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@commands.command(name="rebind")
|
@commands.command(name="rebind")
|
||||||
|
|
11
main.py
11
main.py
|
@ -8,17 +8,10 @@ bot = commands.Bot(
|
||||||
commands.when_mentioned_or("h!"),
|
commands.when_mentioned_or("h!"),
|
||||||
debug_guilds=config.guilds,
|
debug_guilds=config.guilds,
|
||||||
allowed_mentions=discord.AllowedMentions.none(),
|
allowed_mentions=discord.AllowedMentions.none(),
|
||||||
intents=discord.Intents.default() + discord.Intents.members
|
intents=discord.Intents.default() + discord.Intents.members,
|
||||||
)
|
)
|
||||||
|
|
||||||
extensions = [
|
extensions = ["jishaku", "cogs.verify", "cogs.mod", "cogs.events", "cogs.assignments", "cogs.timetable"]
|
||||||
"jishaku",
|
|
||||||
"cogs.verify",
|
|
||||||
"cogs.mod",
|
|
||||||
"cogs.events",
|
|
||||||
"cogs.assignments",
|
|
||||||
"cogs.timetable"
|
|
||||||
]
|
|
||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
bot.load_extension(ext)
|
bot.load_extension(ext)
|
||||||
console.log(f"Loaded extension [green]{ext}")
|
console.log(f"Loaded extension [green]{ext}")
|
||||||
|
|
|
@ -16,11 +16,10 @@ def is_sane_time(time: list[int, int]) -> Union[bool, AssertionError]:
|
||||||
assert minute in range(0, 60), "Invalid minute range - must be between (inclusive) 0 & 59"
|
assert minute in range(0, 60), "Invalid minute range - must be between (inclusive) 0 & 59"
|
||||||
if minute % 15 != 0:
|
if minute % 15 != 0:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
UserWarning(
|
UserWarning("Time '%s:%s' is probably not a valid timetable time, as lessons are every 15 minutes.")
|
||||||
"Time '%s:%s' is probably not a valid timetable time, as lessons are every 15 minutes."
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return inner()
|
return inner()
|
||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
|
|
|
@ -8,11 +8,7 @@ from .views import *
|
||||||
|
|
||||||
|
|
||||||
def simple_embed_paginator(
|
def simple_embed_paginator(
|
||||||
lines: list[str],
|
lines: list[str], *, assert_ten: bool = False, empty_is_none: bool = True, **kwargs
|
||||||
*,
|
|
||||||
assert_ten: bool = False,
|
|
||||||
empty_is_none: bool = True,
|
|
||||||
**kwargs
|
|
||||||
) -> Optional[list[discord.Embed]]:
|
) -> Optional[list[discord.Embed]]:
|
||||||
"""Paginates x lines into x embeds."""
|
"""Paginates x lines into x embeds."""
|
||||||
if not lines and empty_is_none is True:
|
if not lines and empty_is_none is True:
|
||||||
|
|
|
@ -19,6 +19,7 @@ class _FakeUser:
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
import random
|
import random
|
||||||
|
|
||||||
return f"{random.choice(self.names)}#{str(random.randint(1, 9999)).zfill(4)}"
|
return f"{random.choice(self.names)}#{str(random.randint(1, 9999)).zfill(4)}"
|
||||||
|
|
||||||
|
|
||||||
|
|
16
utils/db.py
16
utils/db.py
|
@ -21,17 +21,9 @@ class Tutors(IntEnum):
|
||||||
os.chdir(Path(__file__).parent.parent)
|
os.chdir(Path(__file__).parent.parent)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ["registry", "get_or_none", "VerifyCode", "Student", "BannedStudentID", "Assignments", "Tutors"]
|
||||||
"registry",
|
|
||||||
"get_or_none",
|
|
||||||
"VerifyCode",
|
|
||||||
"Student",
|
|
||||||
"BannedStudentID",
|
|
||||||
"Assignments",
|
|
||||||
"Tutors"
|
|
||||||
]
|
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar("T")
|
||||||
T_co = TypeVar("T_co", covariant=True)
|
T_co = TypeVar("T_co", covariant=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +75,7 @@ class BannedStudentID(orm.Model):
|
||||||
"entry_id": orm.UUID(primary_key=True, default=uuid.uuid4),
|
"entry_id": orm.UUID(primary_key=True, default=uuid.uuid4),
|
||||||
"student_id": orm.String(min_length=7, max_length=7, unique=True),
|
"student_id": orm.String(min_length=7, max_length=7, unique=True),
|
||||||
"associated_account": orm.BigInteger(default=None),
|
"associated_account": orm.BigInteger(default=None),
|
||||||
"banned_at_timestamp": orm.Float(default=lambda: datetime.datetime.utcnow().timestamp())
|
"banned_at_timestamp": orm.Float(default=lambda: datetime.datetime.utcnow().timestamp()),
|
||||||
}
|
}
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
entry_id: uuid.UUID
|
entry_id: uuid.UUID
|
||||||
|
@ -105,7 +97,7 @@ class Assignments(orm.Model):
|
||||||
"tutor": orm.Enum(Tutors),
|
"tutor": orm.Enum(Tutors),
|
||||||
"reminders": orm.JSON(default=[]),
|
"reminders": orm.JSON(default=[]),
|
||||||
"finished": orm.Boolean(default=False),
|
"finished": orm.Boolean(default=False),
|
||||||
"submitted": orm.Boolean(default=False)
|
"submitted": orm.Boolean(default=False),
|
||||||
}
|
}
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
entry_id: int
|
entry_id: int
|
||||||
|
|
|
@ -12,11 +12,7 @@ class VerifyView(discord.ui.View):
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
super().__init__(timeout=300, disable_on_timeout=True)
|
super().__init__(timeout=300, disable_on_timeout=True)
|
||||||
|
|
||||||
@discord.ui.button(
|
@discord.ui.button(label="I have a verification code!", emoji="\U0001f4e7", custom_id="have")
|
||||||
label="I have a verification code!",
|
|
||||||
emoji="\U0001f4e7",
|
|
||||||
custom_id="have"
|
|
||||||
)
|
|
||||||
async def have(self, _, interaction1: discord.Interaction):
|
async def have(self, _, interaction1: discord.Interaction):
|
||||||
class Modal(discord.ui.Modal):
|
class Modal(discord.ui.Modal):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -25,8 +21,8 @@ class VerifyView(discord.ui.View):
|
||||||
custom_id="code",
|
custom_id="code",
|
||||||
label="Verification Code",
|
label="Verification Code",
|
||||||
placeholder="e.g: " + secrets.token_hex(TOKEN_LENGTH),
|
placeholder="e.g: " + secrets.token_hex(TOKEN_LENGTH),
|
||||||
min_length=TOKEN_LENGTH*2,
|
min_length=TOKEN_LENGTH * 2,
|
||||||
max_length=TOKEN_LENGTH*2,
|
max_length=TOKEN_LENGTH * 2,
|
||||||
),
|
),
|
||||||
title="Enter the verification code in your inbox",
|
title="Enter the verification code in your inbox",
|
||||||
)
|
)
|
||||||
|
@ -51,7 +47,7 @@ class VerifyView(discord.ui.View):
|
||||||
self.stop()
|
self.stop()
|
||||||
return await interaction.user.ban(
|
return await interaction.user.ban(
|
||||||
reason=f"Attempted to verify with banned student ID {ban.student_id}"
|
reason=f"Attempted to verify with banned student ID {ban.student_id}"
|
||||||
f" (originally associated with account {ban.associated_account})"
|
f" (originally associated with account {ban.associated_account})"
|
||||||
)
|
)
|
||||||
await Student.objects.create(id=existing.student_id, user_id=interaction.user.id)
|
await Student.objects.create(id=existing.student_id, user_id=interaction.user.id)
|
||||||
await existing.delete()
|
await existing.delete()
|
||||||
|
@ -62,8 +58,7 @@ class VerifyView(discord.ui.View):
|
||||||
console.log(f"[green]{interaction.user} verified ({interaction.user.id}/{existing.student_id})")
|
console.log(f"[green]{interaction.user} verified ({interaction.user.id}/{existing.student_id})")
|
||||||
self.stop()
|
self.stop()
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(
|
||||||
"\N{white heavy check mark} Verification complete!",
|
"\N{white heavy check mark} Verification complete!", ephemeral=True
|
||||||
ephemeral=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await interaction1.response.send_modal(Modal())
|
await interaction1.response.send_modal(Modal())
|
||||||
|
@ -71,10 +66,7 @@ class VerifyView(discord.ui.View):
|
||||||
await interaction1.edit_original_response(view=self)
|
await interaction1.edit_original_response(view=self)
|
||||||
await interaction1.delete_original_response(delay=1)
|
await interaction1.delete_original_response(delay=1)
|
||||||
|
|
||||||
@discord.ui.button(
|
@discord.ui.button(label="Send me a verification code.", emoji="\U0001f4e5")
|
||||||
label="Send me a verification code.",
|
|
||||||
emoji="\U0001f4e5"
|
|
||||||
)
|
|
||||||
async def send(self, btn: discord.ui.Button, interaction1: discord.Interaction):
|
async def send(self, btn: discord.ui.Button, interaction1: discord.Interaction):
|
||||||
class Modal(discord.ui.Modal):
|
class Modal(discord.ui.Modal):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -87,7 +79,7 @@ class VerifyView(discord.ui.View):
|
||||||
max_length=7,
|
max_length=7,
|
||||||
),
|
),
|
||||||
title="Enter your student ID number",
|
title="Enter your student ID number",
|
||||||
timeout=120
|
timeout=120,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
|
@ -98,17 +90,13 @@ class VerifyView(discord.ui.View):
|
||||||
|
|
||||||
if not re.match(r"^B\d{6}$", st):
|
if not re.match(r"^B\d{6}$", st):
|
||||||
btn.disabled = False
|
btn.disabled = False
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send("\N{cross mark} Invalid student ID.", delete_after=60)
|
||||||
"\N{cross mark} Invalid student ID.",
|
|
||||||
delete_after=60
|
|
||||||
)
|
|
||||||
|
|
||||||
ex = await get_or_none(Student, id=st)
|
ex = await get_or_none(Student, id=st)
|
||||||
if ex:
|
if ex:
|
||||||
btn.disabled = False
|
btn.disabled = False
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(
|
||||||
"\N{cross mark} Student ID is already associated.",
|
"\N{cross mark} Student ID is already associated.", delete_after=60
|
||||||
delete_after=60
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -117,8 +105,9 @@ class VerifyView(discord.ui.View):
|
||||||
return await interaction.followup.send(f"\N{cross mark} Failed to send email - {e}. Try again?")
|
return await interaction.followup.send(f"\N{cross mark} Failed to send email - {e}. Try again?")
|
||||||
console.log(f"Sending verification email to {interaction.user} ({interaction.user.id}/{st})...")
|
console.log(f"Sending verification email to {interaction.user} ({interaction.user.id}/{st})...")
|
||||||
__code = await VerifyCode.objects.create(code=_code, bind=interaction.id, student_id=st)
|
__code = await VerifyCode.objects.create(code=_code, bind=interaction.id, student_id=st)
|
||||||
console.log(f"[green]Sent verification email to {interaction.user} ({interaction.user.id}/{st}): "
|
console.log(
|
||||||
f"{_code!r}")
|
f"[green]Sent verification email to {interaction.user} ({interaction.user.id}/{st}): " f"{_code!r}"
|
||||||
|
)
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
"\N{white heavy check mark} Verification email sent to your college email "
|
"\N{white heavy check mark} Verification email sent to your college email "
|
||||||
f"({st}@my.leedscitycollege.ac.uk)\n"
|
f"({st}@my.leedscitycollege.ac.uk)\n"
|
||||||
|
@ -140,14 +129,9 @@ class VerifyView(discord.ui.View):
|
||||||
await modal.wait()
|
await modal.wait()
|
||||||
await interaction1.edit_original_response(view=self)
|
await interaction1.edit_original_response(view=self)
|
||||||
|
|
||||||
@discord.ui.button(
|
@discord.ui.button(label="Why do I need a verification code?", emoji="\U0001f616")
|
||||||
label="Why do I need a verification code?",
|
|
||||||
emoji="\U0001f616"
|
|
||||||
)
|
|
||||||
async def why(self, _, interaction: discord.Interaction):
|
async def why(self, _, interaction: discord.Interaction):
|
||||||
await interaction.response.defer(
|
await interaction.response.defer(ephemeral=True)
|
||||||
ephemeral=True
|
|
||||||
)
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
"In order to access this server, you need to enter your student ID.\n"
|
"In order to access this server, you need to enter your student ID.\n"
|
||||||
"We require this to make sure only **students** in our course can access the server.\n"
|
"We require this to make sure only **students** in our course can access the server.\n"
|
||||||
|
@ -155,6 +139,5 @@ class VerifyView(discord.ui.View):
|
||||||
"This is not invading your privacy, your B number is publicly visible, as it is the start of your email,"
|
"This is not invading your privacy, your B number is publicly visible, as it is the start of your email,"
|
||||||
" plus can be found on google chat.",
|
" plus can be found on google chat.",
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
delete_after=60
|
delete_after=60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue