diff --git a/src/cogs/galtinn.py b/src/cogs/galtinn.py new file mode 100644 index 0000000..7e25a97 --- /dev/null +++ b/src/cogs/galtinn.py @@ -0,0 +1,136 @@ +from datetime import datetime +import requests + +import discord +from discord import app_commands +from discord.ext import commands + +from cogs.utils import embed_templates + + +class Galtinn(commands.Cog): + """Galtinn - Membership database for the Norwegian Student Society""" + + def __init__(self, bot: commands.Bot): + """ + Parameters + ---------- + bot (commands.Bot): The bot instance + """ + + self.bot = bot + + galtinn_group = app_commands.Group( + name="galtinn", description="Kommandoer for Galtinn - Medlemsdatabasen til Det Norske Studentersamfund" + ) + + @app_commands.checks.cooldown(1, 5) + @galtinn_group.command(name="medlemskap", description="Sjekk medlemskap i Det Norske Studentersamfund") + async def medlemskap( + self, interaction: discord.Interaction, brukernavn: str | None = None, discordbruker: discord.User | None = None + ): + """ + Check someone's membership status in the norwegian student society + + Parameters + ---------- + interaction (discord.Interaction): Slash command context object + """ + + if discordbruker and brukernavn: + embed = embed_templates.error_fatal("Du kan ikke oppgi både brukernavn og discordbruker") + await interaction.response.send_message(embed=embed, ephemeral=True) + + if not brukernavn and not discordbruker: + discordbruker = interaction.user + + # Check if the user has registered their Discord account in Galtinn. + # We do this because we don't want just anyone fetching data + # from the database. We only want the users who have registered + data = requests.get( + f"{self.bot.galtinn['api_url']}/users/?discord_profile__discord_id={interaction.user.id}&format=json", + headers={"Authorization": f"Token {self.bot.galtinn['auth_token']}"}, + ) + if data.status_code != 200 or not data.json(): + self.logger.error(f"Failed to fetch data from Galtinn API: {data.status_code} - {data.text}") + embed = embed_templates.error_fatal("Noe gikk galt under henting av data fra Galtinn") + return await interaction.response.send_message(embed=embed, ephemeral=True) + + data = data.json() + + if not data["results"]: + embed = embed_templates.error_warning( + """ + Du har ikke registrert din Discord-konto i Galtinn. Vi lar bare folk med brukere i Galtinn se data. + + For å koble Galtinnbrukeren og Discordbrukeren din sammen kan du besøke [DNS sin Discord](https://discord.gg/SE2ChXjVeV) og skrive: + `/galtinn registrer` + """ + ) + return await interaction.response.send_message(embed=embed, ephemeral=True) + + # Only make new request if given user is different from command invoker + # This is done in order to avoid redundant requests + if (discordbruker and discordbruker.id != data["results"][0]["discord_profile"]["discord_id"]) or brukernavn: + params = { + "discord_profile__discord_id": discordbruker.id if discordbruker else None, + "username": brukernavn, + "format": "json", + } + data = requests.get( + f"{self.bot.galtinn['api_url']}/users/", + params=params, + headers={"Authorization": f"Token {self.bot.galtinn['auth_token']}"}, + ) + if data.status_code != 200 or not data.json(): + self.logger.error(f"Failed to make second Galtinn API request: {data.status_code} - {data.text}") + embed = embed_templates.error_fatal("Noe gikk galt under henting av data fra Galtinn") + return await interaction.response.send_message(embed=embed, ephemeral=True) + + data = data.json() + + if not data["results"]: + if discordbruker: + embed = embed_templates.error_warning( + "Brukeren du forespurte har ikke koblet Discordbrukeren sin til Galtinn" + ) + else: + embed = embed_templates.error_warning("Brukeren du forespurte finnes ikke") + + user = data["results"][0] + username = user["username"] + last_membership = user["last_membership"] + + if not last_membership: + membership_status = "Har aldri vært medlem" + else: + membership_status = "Ja" if last_membership["is_valid"] else "Nei" + if last_membership["membership_type"] != "lifelong": + expiration_date = datetime.strptime(last_membership["end_date"], "%Y-%m-%d") + expires = discord.utils.format_dt(expiration_date, "D") + time_left = discord.utils.format_dt(expiration_date, "R") + + embed = discord.Embed() + embed.set_author(name=username) + embed.color = discord.Color.green() if last_membership["is_valid"] else discord.Color.red() + if user["discord_profile"]: + embed.description = f"<@{user['discord_profile']['discord_id']}>" + embed.add_field(name="Medlem?", value=membership_status) + if last_membership: + embed.add_field(name="Medlemskapstype", value=last_membership["membership_type"]) + if last_membership["membership_type"] != "lifelong": + embed.add_field(name="Gyldig til", value=f"{expires}\n{time_left}") + + await interaction.response.send_message(embed=embed, ephemeral=True) + + +async def setup(bot: commands.Bot): + """ + Add the cog to the bot on extension load + + Parameters + ---------- + bot (commands.Bot): Bot instance + """ + + await bot.add_cog(Galtinn(bot)) diff --git a/src/config/config.yaml.example b/src/config/config.yaml.example index 98cf329..d960dae 100644 --- a/src/config/config.yaml.example +++ b/src/config/config.yaml.example @@ -28,6 +28,10 @@ sanity: project_id: dataset: production +galtinn: + auth_token: + api_url: https://galtinn.neuf.no/api + # Emoji emoji: online: <:online:516328785910431754> diff --git a/src/run.py b/src/run.py index 318e137..948c715 100644 --- a/src/run.py +++ b/src/run.py @@ -19,6 +19,7 @@ DATABASE_RELIANT_COGS = { "user_facts.py", "word_cloud.py", } +GALTINN_RELIANT_COGS = {"galtinn.py"} SANITY_RELIANT_COGS = {"website_events.py"} MINECRAFT_RELIANT_COGS = {"mc_whitelist.py"} @@ -56,6 +57,9 @@ class Bot(commands.Bot): if self.check_credentials(config["sanity"], SANITY_RELIANT_COGS): self.sanity = config["sanity"] + if self.check_credentials(config["galtinn"], GALTINN_RELIANT_COGS): + self.galtinn = config["galtinn"] + if self.check_credentials(config["minecraft"], MINECRAFT_RELIANT_COGS): self.mc_rcon_password = config["minecraft"]["rcon_password"]