Compare commits

...

10 Commits

Author SHA1 Message Date
stijndcl 105cee7e6e Fix memegen preview 2022-09-25 18:54:18 +02:00
stijndcl 5528ce7c2e Make memegen preview ephemeral 2022-09-25 18:40:26 +02:00
stijndcl 60181aadea Allow admins to set user's birthdays 2022-09-25 18:21:53 +02:00
stijndcl fa129efd0c Reduce db load for custom commands 2022-09-25 17:59:41 +02:00
stijndcl 5f72701714 Fix page splitting for menus 2022-09-25 17:41:22 +02:00
stijndcl 89b345c61b Improve error message handling 2022-09-25 17:22:02 +02:00
stijndcl ce7ba4285a Put slots in set at the end 2022-09-25 15:58:36 +02:00
stijndcl 3a44a1b679 Fix duplicate schedule slots 2022-09-25 15:57:02 +02:00
Stijn De Clercq ca38d06d98
Merge pull request #135 from stijndcl/didier-v3
Remove unnecessary setting, add check around schedule parsing
2022-09-25 15:29:19 +02:00
stijndcl 1df345838d Remove unnecessary setting, add check around schedule parsing 2022-09-25 15:25:29 +02:00
8 changed files with 46 additions and 39 deletions

View File

@ -63,12 +63,20 @@ 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):
async def birthday_set(self, ctx: commands.Context, day: str, user: Optional[discord.User] = None):
"""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"])
@ -81,7 +89,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, ctx.author.id, date)
await birthdays.add_birthday(session, user.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,8 +102,7 @@ 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():
fields = [f"Field #{i + 1}" for i in range(20)]
meme = await self._do_generate_meme(template, fields)
meme = await self._do_generate_meme(template, [])
return await ctx.reply(meme, mention_author=False)
@memes_slash.command(name="generate")
@ -119,10 +118,9 @@ 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()
await interaction.response.defer(ephemeral=True)
fields = [f"Field #{i + 1}" for i in range(20)]
meme_url = await self._do_generate_meme(template, fields)
meme_url = await self._do_generate_meme(template, [])
await interaction.followup.send(meme_url, ephemeral=True)

View File

@ -10,6 +10,10 @@ __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.replace(r"*", r"\*").replace(r"_", r"\_")
error_string += line
if line.strip():
error_string += "\n"
@ -45,7 +45,12 @@ def create_error_embed(ctx: Optional[commands.Context], exception: Exception) ->
invocation = f"{ctx.author.display_name} in {origin}"
embed.add_field(name="Command", value=f"{ctx.message.content}", inline=True)
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="Context", value=invocation, inline=True)
if message:

View File

@ -52,7 +52,7 @@ class Schedule(EmbedBaseModel):
if not self.slots:
return Schedule()
personal_slots = set()
personal_slots = []
for slot in self.slots:
alt_id = slot.alternative_overarching_role_id
@ -65,24 +65,9 @@ class Schedule(EmbedBaseModel):
alt_id is not None and alt_id in roles
)
if role_found or overarching_role_found:
personal_slots.add(slot)
personal_slots.append(slot)
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
return Schedule(set(personal_slots))
@overrides
def to_embed(self, **kwargs) -> discord.Embed:
@ -131,9 +116,11 @@ class ScheduleSlot:
def __post_init__(self):
"""Fix some properties to display more nicely"""
# Re-format the location data
room, building, campus = re.search(r"(.*)\. (?:Gebouw )?(.*)\. (?:Campus )?(.*)\. ", self.location).groups()
room = room.replace("PC / laptoplokaal ", "PC-lokaal")
self.location = f"{campus} {building} {room}"
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}"
# The same course can only start once at the same moment,
# so this is guaranteed to be unique
@ -177,7 +164,8 @@ class ScheduleSlot:
return False
if self.start_time == other.end_time:
other.end_time = self.end_time
self.start_time = other.start_time
self._hash = hash(f"{self.course.course_id} {str(self.start_time)}")
return True
elif self.end_time == other.start_time:
self.end_time = other.end_time
@ -234,7 +222,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: set[ScheduleSlot] = set()
slots: list[ScheduleSlot] = []
for event in events:
code = parse_course_code(event.name)
@ -260,9 +248,10 @@ async def parse_schedule_from_content(content: str, *, database_session: AsyncSe
if any(s.could_merge_with(slot) for s in slots):
continue
slots.add(slot)
slots.append(slot)
return Schedule(slots=slots)
# Cast to set at the END because the __hash__ can change while merging with others
return Schedule(slots=set(slots))
async def parse_schedule(name: ScheduleType, *, database_session: AsyncSession) -> Optional[Schedule]:

View File

@ -215,9 +215,14 @@ 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 : page + self.per_page]
return self.dataset[page * self.per_page : (page + 1) * 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,7 +29,6 @@ __all__ = [
"DISCORD_CUSTOM_COMMAND_PREFIX",
"UFORA_ANNOUNCEMENTS_CHANNEL",
"UFORA_RSS_TOKEN",
"URBAN_DICTIONARY_TOKEN",
"IMGFLIP_NAME",
"IMGFLIP_PASSWORD",
"ScheduleType",
@ -77,7 +76,6 @@ 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)