Compare commits

..

No commits in common. "105cee7e6e7c71bafd02adfc5c8c1830dabab860" and "98b18f7ee3f4099e459d51d0b48096f93faff50a" have entirely different histories.

8 changed files with 39 additions and 46 deletions

View File

@ -63,20 +63,12 @@ class Discord(commands.Cog):
return await ctx.reply(f"{name or 'Your'} birthday is set to **{day}/{month}**.", mention_author=False)
@birthday.command(name="set", aliases=["config"])
async def birthday_set(self, ctx: commands.Context, day: str, user: Optional[discord.User] = None):
async def birthday_set(self, ctx: commands.Context, day: str):
"""Set your birthday to `day`.
Parsing of the `day`-argument happens in the following order: `DD/MM/YYYY`, `DD/MM/YY`, `DD/MM`.
Other formats will not be accepted.
"""
# Let owners set other people's birthdays
if user is not None and not await self.client.is_owner(ctx.author):
return await ctx.reply("You don't have permission to set other people's birthdays.", mention_author=False)
# For regular users: default to your own birthday
if user is None:
user = ctx.author
try:
default_year = 2001
date = str_to_date(day, formats=["%d/%m/%Y", "%d/%m/%y", "%d/%m"])
@ -89,7 +81,7 @@ class Discord(commands.Cog):
return await ctx.reply(f"`{day}` is not a valid date.", mention_author=False)
async with self.client.postgres_session as session:
await birthdays.add_birthday(session, user.id, date)
await birthdays.add_birthday(session, ctx.author.id, date)
await self.client.confirm_message(ctx.message)
@commands.group(name="bookmark", aliases=["bm", "bookmarks"], case_insensitive=True, invoke_without_command=True)

View File

@ -102,7 +102,8 @@ class Fun(commands.Cog):
async def memegen_preview_msg(self, ctx: commands.Context, template: str):
"""Generate a preview for the meme template `template`, to see how the fields are structured."""
async with ctx.typing():
meme = await self._do_generate_meme(template, [])
fields = [f"Field #{i + 1}" for i in range(20)]
meme = await self._do_generate_meme(template, fields)
return await ctx.reply(meme, mention_author=False)
@memes_slash.command(name="generate")
@ -118,9 +119,10 @@ class Fun(commands.Cog):
@app_commands.describe(template="The meme template to use in the preview.")
async def memegen_preview_slash(self, interaction: discord.Interaction, template: str):
"""Generate a preview for a meme, to see how the fields are structured."""
await interaction.response.defer(ephemeral=True)
await interaction.response.defer()
meme_url = await self._do_generate_meme(template, [])
fields = [f"Field #{i + 1}" for i in range(20)]
meme_url = await self._do_generate_meme(template, fields)
await interaction.followup.send(meme_url, ephemeral=True)

View File

@ -10,10 +10,6 @@ __all__ = ["generate_meme"]
def generate_boxes(meme: MemeTemplate, fields: list[str]) -> list[str]:
"""Generate the template boxes for Imgflip"""
# If no fields were passed, generate a template instead
if not fields:
fields = [f"Field #{i + 1}" for i in range(meme.field_count)]
# If a meme only has 1 field, join all the arguments together into one string
if meme.field_count == 1:
fields = [" ".join(fields)]

View File

@ -20,7 +20,7 @@ def _get_traceback(exception: Exception) -> str:
break
# Escape Discord Markdown formatting
error_string += line
error_string += line.replace(r"*", r"\*").replace(r"_", r"\_")
if line.strip():
error_string += "\n"
@ -45,12 +45,7 @@ def create_error_embed(ctx: Optional[commands.Context], exception: Exception) ->
invocation = f"{ctx.author.display_name} in {origin}"
if ctx.interaction is not None and ctx.interaction.command is not None:
command = ctx.interaction.command.name
else:
command = ctx.message.content or "N/A"
embed.add_field(name="Command", value=command, inline=True)
embed.add_field(name="Command", value=f"{ctx.message.content}", inline=True)
embed.add_field(name="Context", value=invocation, inline=True)
if message:

View File

@ -52,7 +52,7 @@ class Schedule(EmbedBaseModel):
if not self.slots:
return Schedule()
personal_slots = []
personal_slots = set()
for slot in self.slots:
alt_id = slot.alternative_overarching_role_id
@ -65,9 +65,24 @@ class Schedule(EmbedBaseModel):
alt_id is not None and alt_id in roles
)
if role_found or overarching_role_found:
personal_slots.append(slot)
personal_slots.add(slot)
return Schedule(set(personal_slots))
return Schedule(personal_slots)
def simplify(self):
"""Merge sequential slots in the same location into one
Note: this is done in-place instead of returning a new schedule!
(The operation is O(n^2))
Example:
13:00 - 14:30: AD3 in S9
14:30 - 1600: AD3 in S9
"""
for first in self.slots:
for second in self.slots:
if first == second:
continue
@overrides
def to_embed(self, **kwargs) -> discord.Embed:
@ -116,11 +131,9 @@ class ScheduleSlot:
def __post_init__(self):
"""Fix some properties to display more nicely"""
# Re-format the location data
match = re.search(r"(.*)\. (?:Gebouw )?(.*)\. (?:Campus )?(.*)\. ", self.location)
if match is not None:
room, building, campus = match.groups()
room = room.replace("PC / laptoplokaal ", "PC-lokaal")
self.location = f"{campus} {building} {room}"
room, building, campus = re.search(r"(.*)\. (?:Gebouw )?(.*)\. (?:Campus )?(.*)\. ", self.location).groups()
room = room.replace("PC / laptoplokaal ", "PC-lokaal")
self.location = f"{campus} {building} {room}"
# The same course can only start once at the same moment,
# so this is guaranteed to be unique
@ -164,8 +177,7 @@ class ScheduleSlot:
return False
if self.start_time == other.end_time:
self.start_time = other.start_time
self._hash = hash(f"{self.course.course_id} {str(self.start_time)}")
other.end_time = self.end_time
return True
elif self.end_time == other.start_time:
self.end_time = other.end_time
@ -222,7 +234,7 @@ async def parse_schedule_from_content(content: str, *, database_session: AsyncSe
calendar = Calendar(content)
events = list(calendar.events)
course_codes: dict[str, UforaCourse] = {}
slots: list[ScheduleSlot] = []
slots: set[ScheduleSlot] = set()
for event in events:
code = parse_course_code(event.name)
@ -248,10 +260,9 @@ async def parse_schedule_from_content(content: str, *, database_session: AsyncSe
if any(s.could_merge_with(slot) for s in slots):
continue
slots.append(slot)
slots.add(slot)
# Cast to set at the END because the __hash__ can change while merging with others
return Schedule(slots=set(slots))
return Schedule(slots=slots)
async def parse_schedule(name: ScheduleType, *, database_session: AsyncSession) -> Optional[Schedule]:

View File

@ -215,14 +215,9 @@ class Didier(commands.Bot):
if not message.content.startswith(settings.DISCORD_CUSTOM_COMMAND_PREFIX):
return False
# Remove the prefix
content = message.content[len(settings.DISCORD_CUSTOM_COMMAND_PREFIX) :].strip()
# Message was just "?" (or whatever the prefix was configured to)
if not content:
return False
async with self.postgres_session as session:
# Remove the prefix
content = message.content[len(settings.DISCORD_CUSTOM_COMMAND_PREFIX) :]
command = await custom_commands.get_command(session, content)
# Command found

View File

@ -169,7 +169,7 @@ class PageSource(ABC, Generic[T]):
def get_page_data(self, page: int) -> list[T]:
"""Get the chunk of the dataset for page [page]"""
return self.dataset[page * self.per_page : (page + 1) * self.per_page]
return self.dataset[page : page + self.per_page]
async def start(self, *, ephemeral: bool = False, timeout: Optional[int] = None) -> Menu:
"""Shortcut to creating (and starting) a Menu with this source

View File

@ -29,6 +29,7 @@ __all__ = [
"DISCORD_CUSTOM_COMMAND_PREFIX",
"UFORA_ANNOUNCEMENTS_CHANNEL",
"UFORA_RSS_TOKEN",
"URBAN_DICTIONARY_TOKEN",
"IMGFLIP_NAME",
"IMGFLIP_PASSWORD",
"ScheduleType",
@ -76,6 +77,7 @@ MA_CS_ENG_2_ROLE: Optional[int] = env.int("MA_CS_ENG_2_ROLE", 102330043480016491
"""API Keys"""
UFORA_RSS_TOKEN: Optional[str] = env.str("UFORA_RSS_TOKEN", None)
URBAN_DICTIONARY_TOKEN: Optional[str] = env.str("URBAN_DICTIONARY_TOKEN", None)
IMGFLIP_NAME: Optional[str] = env.str("IMGFLIP_NAME", None)
IMGFLIP_PASSWORD: Optional[str] = env.str("IMGFLIP_PASSWORD", None)