mirror of https://github.com/stijndcl/didier
Compare commits
No commits in common. "105cee7e6e7c71bafd02adfc5c8c1830dabab860" and "98b18f7ee3f4099e459d51d0b48096f93faff50a" have entirely different histories.
105cee7e6e
...
98b18f7ee3
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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]:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue