make docstrings consistent

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leanderandClaude Opus 5.5 committed 2026-11-03 02:07:01 +01:00
1 parent a4b889cf54
commit 4d58d33063
13 files changed
+185 -85

No files matched your search

+6 -6
View File
@@ -31,7 +31,7 @@ class BotInfo(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# Memory usage # Memory usage
@@ -99,7 +99,7 @@ class BotInfo(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
embed = discord.Embed(color=interaction.client.user.color) embed = discord.Embed(color=interaction.client.user.color)
@@ -115,7 +115,7 @@ class BotInfo(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
embed = discord.Embed(color=interaction.client.user.color) embed = discord.Embed(color=interaction.client.user.color)
@@ -128,7 +128,7 @@ class BotInfo(commands.Cog):
Returns Returns
---------- ----------
(int) The bot's ping in milliseconds (int): The bot's ping in milliseconds
""" """
return int(self.bot.latency * 1000) return int(self.bot.latency * 1000)
@@ -139,7 +139,7 @@ class BotInfo(commands.Cog):
Returns Returns
---------- ----------
(str) The bot's uptime in human readable format (str): The bot's uptime in human readable format
""" """
now = time() now = time()
@@ -157,7 +157,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(BotInfo(bot)) await bot.add_cog(BotInfo(bot))
+9 -9
View File
@@ -29,7 +29,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
activity_type (str): The type of activity to set the bot's status to activity_type (str): The type of activity to set the bot's status to
message (str): The message to set the bot's status to message (str): The message to set the bot's status to
status_type (str): The type of status to set the bot's status to status_type (str): The type of status to set the bot's status to
@@ -60,7 +60,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
guild_id (int): The ID of the guild to leave guild_id (int): The ID of the guild to leave
""" """
@@ -104,7 +104,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
""" """
if not ctx.invoked_subcommand: if not ctx.invoked_subcommand:
@@ -117,7 +117,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
cog (str): The name of the cog to disable cog (str): The name of the cog to disable
""" """
@@ -141,7 +141,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
cog (str): The name of the cog to enable cog (str): The name of the cog to enable
""" """
@@ -165,7 +165,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
cog (str): The name of the cog to reload cog (str): The name of the cog to reload
""" """
@@ -189,7 +189,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
""" """
extensions = [f"cogs.{file[:-3]}" for file in listdir("./src/cogs") if file.endswith(".py")] extensions = [f"cogs.{file[:-3]}" for file in listdir("./src/cogs") if file.endswith(".py")]
@@ -205,7 +205,7 @@ class DevTools(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
""" """
# Only loaded cogs can be reloaded. Copied to a list since reloading modifies bot.extensions # Only loaded cogs can be reloaded. Copied to a list since reloading modifies bot.extensions
@@ -267,7 +267,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(DevTools(bot)) await bot.add_cog(DevTools(bot))
+7 -7
View File
@@ -27,7 +27,7 @@ class Errors(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
""" """
self.bot.logger.info( self.bot.logger.info(
@@ -45,7 +45,7 @@ class Errors(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
command (app_commands.Command | app_commands.ContextMenu): Command object command (app_commands.Command | app_commands.ContextMenu): Command object
""" """
@@ -62,8 +62,8 @@ class Errors(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Context object ctx (commands.Context): The command context
error (commands.CommandError): Eror context object error (commands.CommandError): The error the command raised
""" """
# Failed commands don't count towards the cooldown, unless the failure is the cooldown itself. # Failed commands don't count towards the cooldown, unless the failure is the cooldown itself.
@@ -133,8 +133,8 @@ class Errors(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
error (app_commands.AppCommandError): Eror context object error (app_commands.AppCommandError): The error the command raised
""" """
# The command may have deferred or responded before it failed # The command may have deferred or responded before it failed
@@ -196,7 +196,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(Errors(bot)) await bot.add_cog(Errors(bot))
+33 -5
View File
@@ -42,6 +42,10 @@ class Galtinn(commands.Cog):
self.session: aiohttp.ClientSession | None = None self.session: aiohttp.ClientSession | None = None
async def cog_load(self): async def cog_load(self):
"""
Open the Galtinn session, set up the database listener and start the background tasks
"""
# One session for all Galtinn requests, so connections are reused # One session for all Galtinn requests, so connections are reused
self.session = aiohttp.ClientSession( self.session = aiohttp.ClientSession(
headers={"Authorization": f"Token {self.bot.galtinn['auth_token']}"}, headers={"Authorization": f"Token {self.bot.galtinn['auth_token']}"},
@@ -55,6 +59,10 @@ class Galtinn(commands.Cog):
self.verification_cleanup.start() self.verification_cleanup.start()
async def cog_unload(self): async def cog_unload(self):
"""
Stop the background tasks and close the database listener and the Galtinn session
"""
self.bot.logger.info("Unloading cog") self.bot.logger.info("Unloading cog")
self.membership_check.cancel() self.membership_check.cancel()
self.verification_cleanup.cancel() self.verification_cleanup.cancel()
@@ -122,6 +130,10 @@ class Galtinn(commands.Cog):
def on_listen_conn_terminated(self, conn: asyncpg.Connection): def on_listen_conn_terminated(self, conn: asyncpg.Connection):
""" """
Starts reconnecting the listener if its connection is lost, e.g. on a database restart Starts reconnecting the listener if its connection is lost, e.g. on a database restart
Parameters
----------
conn (asyncpg.Connection): The connection that was lost
""" """
# Connections closed on cog unload are detached before closing # Connections closed on cog unload are detached before closing
@@ -153,6 +165,13 @@ class Galtinn(commands.Cog):
async def process_notification(self, conn: asyncpg.Connection, pid: int, channel: str, payload: str): async def process_notification(self, conn: asyncpg.Connection, pid: int, channel: str, payload: str):
""" """
Applies Galtinn roles to a user that has just completed verification Applies Galtinn roles to a user that has just completed verification
Parameters
----------
conn (asyncpg.Connection): The connection that received the notification
pid (int): The ID of the database process that sent the notification
channel (str): The notification channel
payload (str): The Discord user ID and the Galtinn user ID, separated by a space
""" """
self.bot.logger.info("Received galtinn_auth_complete event from database") self.bot.logger.info("Received galtinn_auth_complete event from database")
@@ -242,6 +261,10 @@ class Galtinn(commands.Cog):
async def fetch_galtinn_users(self, discord_id: int | None = None) -> Users | None: async def fetch_galtinn_users(self, discord_id: int | None = None) -> Users | None:
""" """
Attempts to fetch user(s) from Galtinn. If no Discord ID is given, all users are fetched Attempts to fetch user(s) from Galtinn. If no Discord ID is given, all users are fetched
Parameters
----------
discord_id (int | None): The Discord ID of the user to fetch
""" """
params = {"no_discord_id": False} params = {"no_discord_id": False}
@@ -255,6 +278,11 @@ class Galtinn(commands.Cog):
) -> DiscordProfiles | None: ) -> DiscordProfiles | None:
""" """
Attempts to fetch discord profile(s) from Galtinn. If no parameters are given, all profiles are fetched Attempts to fetch discord profile(s) from Galtinn. If no parameters are given, all profiles are fetched
Parameters
----------
galtinn_user_id (int | None): Only fetch the profile of this Galtinn user
discord_id (int | None): Only fetch the profile with this Discord ID
""" """
params = {} params = {}
@@ -278,7 +306,7 @@ class Galtinn(commands.Cog):
Returns Returns
---------- ----------
set(int): Set of role ids (set[int]): Set of role ids
""" """
all_roles = {self.bot.galtinn_roles["volunteer"], self.bot.galtinn_roles["member"]} all_roles = {self.bot.galtinn_roles["volunteer"], self.bot.galtinn_roles["member"]}
@@ -493,7 +521,7 @@ class Galtinn(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
await interaction.response.defer(ephemeral=True) await interaction.response.defer(ephemeral=True)
@@ -566,7 +594,7 @@ class Galtinn(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# TODO: add confirmation button # TODO: add confirmation button
@@ -627,7 +655,7 @@ class Galtinn(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# Discord ignores guild_only on subcommands, and the rest of the group has to work in DMs # Discord ignores guild_only on subcommands, and the rest of the group has to work in DMs
@@ -686,7 +714,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(Galtinn(bot)) await bot.add_cog(Galtinn(bot))
+24 -24
View File
@@ -31,7 +31,7 @@ class Info(commands.Cog):
roles (list[discord.Role]): List of roles roles (list[discord.Role]): List of roles
Returns Returns
------- ----------
(str): String of roles (str): String of roles
""" """
@@ -50,11 +50,11 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
join_method (callable): Function to join the boosters with join_method (callable): Function to join the boosters with
Returns Returns
------- ----------
(str): String of boosters (str): String of boosters
""" """
@@ -78,7 +78,7 @@ class Info(commands.Cog):
members (list[discord.Member]): List of members members (list[discord.Member]): List of members
Returns Returns
------- ----------
(str): String of members (str): String of members
""" """
@@ -98,7 +98,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# Days since guild was created # Days since guild was created
@@ -254,7 +254,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
roles = self.construct_role_string(interaction.guild.roles) roles = self.construct_role_string(interaction.guild.roles)
@@ -277,7 +277,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
if len(interaction.guild.premium_subscribers) == 0: if len(interaction.guild.premium_subscribers) == 0:
@@ -302,7 +302,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
if not interaction.guild.icon: if not interaction.guild.icon:
@@ -323,7 +323,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
if not interaction.guild.splash: if not interaction.guild.splash:
@@ -344,7 +344,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
if not interaction.guild.banner: if not interaction.guild.banner:
@@ -367,7 +367,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
role (discord.Role): Role to fetch information about role (discord.Role): Role to fetch information about
""" """
@@ -413,7 +413,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
channel (discord.TextChannel): Text channel to fetch information about channel (discord.TextChannel): Text channel to fetch information about
""" """
@@ -447,7 +447,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
channel (discord.VoiceChannel): Voice channel to fetch information about channel (discord.VoiceChannel): Voice channel to fetch information about
""" """
@@ -472,7 +472,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
roles = sorted( roles = sorted(
@@ -501,7 +501,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
ctx (commands.Context): Command context object ctx (commands.Context): The command context
emoji (discord.Emoji): Emoji to fetch information about emoji (discord.Emoji): Emoji to fetch information about
""" """
@@ -534,7 +534,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# Sort members by creation date # Sort members by creation date
@@ -566,7 +566,7 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
# Sort members by creation date # Sort members by creation date
@@ -597,8 +597,8 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
user (discord.Member, optional): User to get information about. Defaults to None (invoker). user (discord.Member | None): User to get information about. Defaults to the invoking user
""" """
if not user: if not user:
@@ -693,8 +693,8 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
user (discord.Member, optional): User to get roles of. Defaults to None (invoker). user (discord.Member | None): User to get roles of. Defaults to the invoking user
""" """
if not user: if not user:
@@ -724,8 +724,8 @@ class Info(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
user (discord.User, optional): User to get avatar of. Defaults to None (invoker). user (discord.User | None): User to get avatar of. Defaults to the invoking user
""" """
if not user: if not user:
@@ -743,7 +743,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(Info(bot)) await bot.add_cog(Info(bot))
+16 -15
View File
@@ -12,7 +12,7 @@ async def send_as_txt_file(interaction: discord.Interaction, content: str, filen
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
content (str): String that's too long to send content (str): String that's too long to send
filename (str): Name of the file shown in Discord filename (str): Name of the file shown in Discord
""" """
@@ -27,7 +27,7 @@ async def get_discord_guild(bot: commands.Bot, id: int) -> discord.Guild | None:
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
id (int): ID of the object id (int): ID of the object
Returns Returns
@@ -56,7 +56,7 @@ async def get_guild_member(bot: commands.Bot, guild: discord.Guild, user_id: int
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
guild (discord.Guild): The discord guild to fetch the member from guild (discord.Guild): The discord guild to fetch the member from
user_id (int): ID of the user user_id (int): ID of the user
@@ -87,7 +87,7 @@ async def get_guild_roles(bot: commands.Bot, guild: discord.Guild, role_ids: set
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
guild (discord.Guild): The discord guild to fetch the roles from guild (discord.Guild): The discord guild to fetch the roles from
role_ids (set[int]): IDs of the roles role_ids (set[int]): IDs of the roles
@@ -123,11 +123,13 @@ class ScrollerButton(discord.ui.Button):
): ):
""" """
Parameters Parameters
----------- ----------
paginator (Paginator): The paginator object that contains the data to be paginated paginator (Paginator): The paginator object that contains the data to be paginated
button_action (callable): The function that returns the requested page button_action (callable): The function that returns the requested page
content_constructor (callable): A function that takes a paginator object and a page number and returns an embed content_constructor (callable): A function that takes a paginator object and a page number and returns an embed
owner (discord.User|discord.Member): The user that invoked the paginator. Only this user can use the button owner (discord.User | discord.Member): The user that invoked the paginator. Only this user can use the button
label (str): The text on the button
disabled (bool): Whether the button is greyed out
""" """
super().__init__(label=label, disabled=disabled) super().__init__(label=label, disabled=disabled)
@@ -141,8 +143,8 @@ class ScrollerButton(discord.ui.Button):
What to do when the button is pressed What to do when the button is pressed
Parameters Parameters
----------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The interaction that triggered this
""" """
if interaction.user.id != self.owner.id: if interaction.user.id != self.owner.id:
@@ -163,20 +165,20 @@ class Scroller(discord.ui.View):
def __init__( def __init__(
self, self,
paginatior: misc_utils.Paginator, paginator: misc_utils.Paginator,
owner: discord.User | discord.Member, owner: discord.User | discord.Member,
content_constructor: callable = None, content_constructor: callable = None,
): ):
""" """
Parameters Parameters
----------- ----------
paginator (Paginator): The paginator object that contains the data to be paginated paginator (Paginator): The paginator object that contains the data to be paginated
owner (discord.User|discord.Member): The user that invoked the paginator. Only this user can use the buttons owner (discord.User | discord.Member): The user that invoked the paginator. Only this user can use the buttons
content_constructor (callable): A function that takes a paginator object and a page number and returns an embed content_constructor (callable): A function that takes a paginator object and a page number and returns an embed
""" """
super().__init__() super().__init__()
self.paginator = paginatior self.paginator = paginator
self.content_constructor = content_constructor if content_constructor else self.__default_content_constructor self.content_constructor = content_constructor if content_constructor else self.__default_content_constructor
self.add_item( self.add_item(
@@ -225,7 +227,7 @@ class Scroller(discord.ui.View):
Constructs the embed to be displayed Constructs the embed to be displayed
Parameters Parameters
----------- ----------
base_embed (discord.Embed): The base embed to add fields to base_embed (discord.Embed): The base embed to add fields to
""" """
@@ -237,8 +239,7 @@ class Scroller(discord.ui.View):
Parameters Parameters
---------- ----------
paginator (Paginator): Paginator dataclass page (list): The items to show on the page
page (list): List of streaks to display on a page
embed (discord.Embed): Embed to add fields to embed (discord.Embed): Embed to add fields to
""" """
+7 -7
View File
@@ -6,11 +6,11 @@ def error_warning(text: str) -> discord.Embed:
Creates an embed with a specified error message based on a warning template Creates an embed with a specified error message based on a warning template
Parameters Parameters
----------- ----------
text (str): The error message text (str): The error message
Returns Returns
----------- ----------
(discord.Embed): An embed object based on the template with the specified text (discord.Embed): An embed object based on the template with the specified text
""" """
@@ -23,11 +23,11 @@ def error_fatal(text: str) -> discord.Embed:
Creates an embed with a specified error message based on a warning template Creates an embed with a specified error message based on a warning template
Parameters Parameters
----------- ----------
text (str): The error message text (str): The error message
Returns Returns
----------- ----------
(discord.Embed): An embed object based on the template with the specified text (discord.Embed): An embed object based on the template with the specified text
""" """
@@ -40,12 +40,12 @@ def success(text: str) -> discord.Embed:
Creates an embed with a specified message using a template signifying success. Creates an embed with a specified message using a template signifying success.
Parameters Parameters
----------- ----------
text (str): The message text (str): The message
Returns Returns
----------- ----------
discord.Embed: An embed object based on the template with the specified text (discord.Embed): An embed object based on the template with the specified text
""" """
embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}") embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}")
+1 -1
View File
@@ -66,7 +66,7 @@ class Paginator:
Returns Returns
---------- ----------
(list|None): The page you requested (list | None): The page you requested
""" """
if page < 1 or page > self.total_page_count: if page < 1 or page > self.total_page_count:
+11 -11
View File
@@ -26,7 +26,7 @@ async def query_api(session: aiohttp.ClientSession, params: dict) -> dict:
Returns Returns
---------- ----------
dict: The JSON response (dict): The JSON response
Raises Raises
---------- ----------
@@ -53,7 +53,7 @@ async def fetch_article(session: aiohttp.ClientSession, title: str):
Returns Returns
---------- ----------
tuple: The title, url, text and image of the article (tuple): The title, url, text and image of the article
""" """
# The wiki renders the article to HTML for us, so templates are already expanded # The wiki renders the article to HTML for us, so templates are already expanded
@@ -194,7 +194,7 @@ def viteboka_embed(title: str, url: str, text: str, image: str):
Returns Returns
---------- ----------
discord.Embed: The embed (discord.Embed): The embed
""" """
embed = discord.Embed(color=discord.Color.orange(), title=title, url=url, description=text) embed = discord.Embed(color=discord.Color.orange(), title=title, url=url, description=text)
@@ -205,7 +205,7 @@ def viteboka_embed(title: str, url: str, text: str, image: str):
class VitebokaException(Exception): class VitebokaException(Exception):
pass """Raised when an article can't be fetched. The message is shown to the user"""
class Viteboka(commands.Cog): class Viteboka(commands.Cog):
@@ -233,7 +233,7 @@ class Viteboka(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
query (str): The search query query (str): The search query
""" """
@@ -292,7 +292,7 @@ class Viteboka(commands.Cog):
Parameters Parameters
---------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The slash command interaction
""" """
await interaction.response.defer() await interaction.response.defer()
@@ -336,8 +336,8 @@ class ArticleButton(discord.ui.Button):
): ):
""" """
Parameters Parameters
----------- ----------
owner (discord.User|discord.Member): The user that invoked the paginator. Only this user can use the button owner (discord.User | discord.Member): The user that invoked the paginator. Only this user can use the button
label (str): The label of the button label (str): The label of the button
article_title (str): The title of the article the button is associated with article_title (str): The title of the article the button is associated with
""" """
@@ -351,8 +351,8 @@ class ArticleButton(discord.ui.Button):
What to do when the button is pressed What to do when the button is pressed
Parameters Parameters
----------- ----------
interaction (discord.Interaction): Slash command context object interaction (discord.Interaction): The interaction that triggered this
""" """
await interaction.response.defer() await interaction.response.defer()
@@ -378,7 +378,7 @@ async def setup(bot: commands.Bot):
Parameters Parameters
---------- ----------
bot (commands.Bot): Bot instance bot (commands.Bot): The bot instance
""" """
await bot.add_cog(Viteboka(bot)) await bot.add_cog(Viteboka(bot))
+4
View File
@@ -8,6 +8,10 @@ class BotLogger:
"""Logging config for the bot""" """Logging config for the bot"""
def __init__(self): def __init__(self):
"""
Log to the console and to a file that rotates at midnight
"""
logger = logging.getLogger("") logger = logging.getLogger("")
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
formatter = logging.Formatter( formatter = logging.Formatter(
+18
View File
@@ -5,12 +5,16 @@ from pydantic import Field
class GroupDiscordRole(BaseModel): class GroupDiscordRole(BaseModel):
"""A Discord role connected to a Galtinn group"""
id: int id: int
discord_id: int discord_id: int
description: str description: str
class GroupProfile(BaseModel): class GroupProfile(BaseModel):
"""Extra information about a Galtinn group, including its Discord roles"""
id: int id: int
posix_name: str posix_name: str
description: str description: str
@@ -19,18 +23,24 @@ class GroupProfile(BaseModel):
class Group(BaseModel): class Group(BaseModel):
"""A Galtinn group"""
id: int id: int
name: str name: str
profile: Optional[GroupProfile] profile: Optional[GroupProfile]
class UserDiscordProfile(BaseModel): class UserDiscordProfile(BaseModel):
"""The link between a Galtinn user and a Discord user"""
id: int id: int
discord_id: int discord_id: int
user: int user: int
class DuskenUser(BaseModel): class DuskenUser(BaseModel):
"""A Galtinn user"""
id: int id: int
username: str username: str
is_volunteer: bool is_volunteer: bool
@@ -40,6 +50,8 @@ class DuskenUser(BaseModel):
class BasicResponse(BaseModel): class BasicResponse(BaseModel):
"""A page of results from a paginated Galtinn endpoint"""
count: int count: int
next: Optional[str] next: Optional[str]
previous: Optional[str] previous: Optional[str]
@@ -47,12 +59,18 @@ class BasicResponse(BaseModel):
class Users(BasicResponse): class Users(BasicResponse):
"""A page of Galtinn users"""
results: list[DuskenUser] results: list[DuskenUser]
class DiscordProfiles(BasicResponse): class DiscordProfiles(BasicResponse):
"""A page of Discord profiles"""
results: list[UserDiscordProfile] results: list[UserDiscordProfile]
class Groups(BasicResponse): class Groups(BasicResponse):
"""A page of Galtinn groups"""
results: list[Group] results: list[Group]
+18
View File
@@ -16,7 +16,13 @@ load_dotenv()
class Bot(commands.Bot): class Bot(commands.Bot):
"""The bot, configured from environment variables"""
def __init__(self): def __init__(self):
"""
Read the configuration from the environment
"""
super().__init__( super().__init__(
command_prefix=commands.when_mentioned_or(os.environ["BOT_PREFIX"]), command_prefix=commands.when_mentioned_or(os.environ["BOT_PREFIX"]),
case_insensitive=True, case_insensitive=True,
@@ -48,6 +54,10 @@ class Bot(commands.Bot):
} }
async def setup_hook(self): async def setup_hook(self):
"""
Open the HTTP session and database pool, load the cogs and sync the slash commands
"""
self.http_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15)) self.http_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15))
# DB needs to be setup here because contructor is not async # DB needs to be setup here because contructor is not async
@@ -97,6 +107,10 @@ class Bot(commands.Bot):
""" """
Close the HTTP session and database pool after the bot has shut down. This isn't done in close(), Close the HTTP session and database pool after the bot has shut down. This isn't done in close(),
because bot.run doesn't wait for a close() started by the SIGTERM handler to finish because bot.run doesn't wait for a close() started by the SIGTERM handler to finish
Parameters
----------
*args (tuple): The exception type, value and traceback if the bot stopped because of an error
""" """
await super().__aexit__(*args) await super().__aexit__(*args)
@@ -112,6 +126,10 @@ bot = Bot()
@bot.event @bot.event
async def on_ready(): async def on_ready():
"""
Print the bot's info and set its presence once it's connected
"""
if not hasattr(bot, "uptime"): if not hasattr(bot, "uptime"):
bot.uptime = time() bot.uptime = time()
+31
View File
@@ -21,6 +21,14 @@ GALTINN_AUTH_TOKEN = os.environ["GALTINN_AUTH_TOKEN"]
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
"""
Open the database pool and HTTP session on startup, and close them on shutdown
Parameters
----------
app (FastAPI): The app
"""
credentials = { credentials = {
"host": os.environ["DATABASE_HOST"], "host": os.environ["DATABASE_HOST"],
"database": os.environ["DATABASE_NAME"], "database": os.environ["DATABASE_NAME"],
@@ -45,11 +53,25 @@ templates = Jinja2Templates(directory="src/templates")
@app.get("/") @app.get("/")
async def index(): async def index():
"""
Tell visitors there's nothing to see here
"""
return "Hei du! Her skal ikke du drive og luske!" return "Hei du! Her skal ikke du drive og luske!"
@app.get("/callback") @app.get("/callback")
async def callback(request: Request, state: str, code: str | None = None): async def callback(request: Request, state: str, code: str | None = None):
"""
Finish a Galtinn login: link the Galtinn user to the Discord user and tell the bot
Parameters
----------
request (Request): The request
state (str): Identifies the pending verification
code (str | None): The authorization code from Galtinn. Missing if the login was cancelled or failed
"""
# Claim the pending verification. Deleting it up front makes the state single use # Claim the pending verification. Deleting it up front makes the state single use
verification = await app.state.pool.fetchrow( verification = await app.state.pool.fetchrow(
""" """
@@ -175,4 +197,13 @@ async def callback(request: Request, state: str, code: str | None = None):
@app.get("/success/{name}") @app.get("/success/{name}")
async def success(request: Request, name: str): async def success(request: Request, name: str):
"""
Show the success page after a verification
Parameters
----------
request (Request): The request
name (str): The Galtinn username to show
"""
return templates.TemplateResponse(request, "success.html", {"name": name}) return templates.TemplateResponse(request, "success.html", {"name": name})