Merge pull request #42 from UiO-Gaming/2024-q1-refactor
2024 q1 refactor
No files matched your search
@@ -62,6 +62,18 @@ All annen kode holdes på engelsk. Dette inkluderer kommentarer.
|
|||||||
|
|
||||||
Et tips er å ha et engelsk funksjonsnavn, men spesifisere i decoratoren over med navnparameteren.
|
Et tips er å ha et engelsk funksjonsnavn, men spesifisere i decoratoren over med navnparameteren.
|
||||||
|
|
||||||
|
### Bruk av embed templates
|
||||||
|
|
||||||
|
`error_warning` skal brukes om det er feil brukerinput, mangel på data eller generelt sett en feil som er forventet/ikke av utenforstående årsåker
|
||||||
|
|
||||||
|
`error_fatal` brukes når en uventet feil skjer. Ofte på grun av utenforstående årsaker som et API som er nede osv.
|
||||||
|
|
||||||
|
`success` kan brukes til å gi brukeren en bekreftelse på at operasjonen som ble forespurt gikk fint. Det vil si at den ikke skal brukes til å vise spesifikk data, men heller brukes når det ikke er noe å vise.
|
||||||
|
|
||||||
|
### Bruk av private variabler/metoder
|
||||||
|
|
||||||
|
Generelt sett unngår vi bruk av leading underscores for variabelnavn og metoder. Ja, de har sin bruk, men Python var aldri ment til å være objektorientert på samme måte som f.eks Java. Uansett skal en ikke bruke metoder fra andre cogs. Dermed er regelen: greit overalt utenom i cogs.
|
||||||
|
|
||||||
### Avhengigheter
|
### Avhengigheter
|
||||||
|
|
||||||
Hvis en ny avhengighet trengs, sørg for å spesifisere major og minor versjon i requirementsfila.
|
Hvis en ny avhengighet trengs, sørg for å spesifisere major og minor versjon i requirementsfila.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y imagemagick ffmpeg libsm6 libxext6 grap
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN mv /app/src/assets/misc/policy.xml /etc/ImageMagick-6/policy.xml
|
RUN mv /app/src/assets/policy.xml /etc/ImageMagick-6/policy.xml
|
||||||
|
|
||||||
VOLUME logs
|
VOLUME logs
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ pypandoc==1.13.*
|
|||||||
pyyaml==6.0.*
|
pyyaml==6.0.*
|
||||||
regex==2023.12.25
|
regex==2023.12.25
|
||||||
requests==2.31.*
|
requests==2.31.*
|
||||||
|
tzdata # This only needed if run on windows. I haven't set a version either to ensure it's up to date.
|
||||||
wordcloud==1.9.*
|
wordcloud==1.9.*
|
||||||
|
After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@@ -12,12 +12,21 @@ from cogs.utils import embed_templates
|
|||||||
class Anime(commands.Cog):
|
class Anime(commands.Cog):
|
||||||
"""View information about different media on Anilist"""
|
"""View information about different media on Anilist"""
|
||||||
|
|
||||||
|
def __init__(self, bot: commands.Bot):
|
||||||
|
"""
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
bot (commands.Bot): The bot instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.anilist_logo = "https://anilist.co/img/logo_al.png"
|
||||||
|
|
||||||
anilist = app_commands.Group(name="anilist", description="Hent informasjon fra Anilist")
|
anilist = app_commands.Group(name="anilist", description="Hent informasjon fra Anilist")
|
||||||
anilist_profile = app_commands.Group(
|
anilist_profile = app_commands.Group(
|
||||||
parent=anilist, name="profil", description="Hent informasjon om en bruker på Anilist"
|
parent=anilist, name="profil", description="Hent informasjon om en bruker på Anilist"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __convert_color(self, color: str) -> int:
|
def convert_color(self, color: str) -> int:
|
||||||
"""
|
"""
|
||||||
Converts anilist colors to hext
|
Converts anilist colors to hext
|
||||||
|
|
||||||
@@ -39,10 +48,9 @@ class Anime(commands.Cog):
|
|||||||
"green": 0x4CCA51,
|
"green": 0x4CCA51,
|
||||||
"gray": 0x677B94,
|
"gray": 0x677B94,
|
||||||
}
|
}
|
||||||
|
|
||||||
return colors.get(color, 0x677B94)
|
return colors.get(color, 0x677B94)
|
||||||
|
|
||||||
def __convert_media_format(self, media_format: str) -> str:
|
def convert_media_format(self, media_format: str) -> str:
|
||||||
"""
|
"""
|
||||||
Translates media format names into norwegian
|
Translates media format names into norwegian
|
||||||
|
|
||||||
@@ -67,7 +75,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
return media_formats.get(media_format, media_format)
|
return media_formats.get(media_format, media_format)
|
||||||
|
|
||||||
def __convert_language_names(self, language_name: str) -> str:
|
def convert_language_names(self, language_name: str) -> str:
|
||||||
"""
|
"""
|
||||||
Translates language names into norwegian
|
Translates language names into norwegian
|
||||||
|
|
||||||
@@ -92,10 +100,9 @@ class Anime(commands.Cog):
|
|||||||
"HEBREW": "Hebraisk",
|
"HEBREW": "Hebraisk",
|
||||||
"HUNGARIAN": "Ungarsk",
|
"HUNGARIAN": "Ungarsk",
|
||||||
}
|
}
|
||||||
|
|
||||||
return languages.get(language_name, language_name)
|
return languages.get(language_name, language_name)
|
||||||
|
|
||||||
def __convert_role_names(self, role_name: str) -> str:
|
def onvert_role_names(self, role_name: str) -> str:
|
||||||
"""
|
"""
|
||||||
Translates role names into norwegian
|
Translates role names into norwegian
|
||||||
|
|
||||||
@@ -114,7 +121,7 @@ class Anime(commands.Cog):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return role_name
|
return role_name
|
||||||
|
|
||||||
def __convert_status(self, status: str) -> str:
|
def convert_status(self, status: str) -> str:
|
||||||
"""
|
"""
|
||||||
Translates role names into norwegian
|
Translates role names into norwegian
|
||||||
|
|
||||||
@@ -133,10 +140,9 @@ class Anime(commands.Cog):
|
|||||||
"NOT_YET_RELEASED": "Ikke utgitt enda",
|
"NOT_YET_RELEASED": "Ikke utgitt enda",
|
||||||
"CANCELLED": "Kansellert",
|
"CANCELLED": "Kansellert",
|
||||||
}
|
}
|
||||||
|
|
||||||
return statuses.get(status, status)
|
return statuses.get(status, status)
|
||||||
|
|
||||||
def __remove_html(self, text: str) -> str:
|
def remove_html(self, text: str) -> str:
|
||||||
"""
|
"""
|
||||||
Removes HTML tag elements
|
Removes HTML tag elements
|
||||||
|
|
||||||
@@ -154,7 +160,7 @@ class Anime(commands.Cog):
|
|||||||
|
|
||||||
return clean_text
|
return clean_text
|
||||||
|
|
||||||
async def __request_anilist(
|
async def request_anilist(
|
||||||
self, interaction: discord.Interaction, query: str, variables: dict, key: str
|
self, interaction: discord.Interaction, query: str, variables: dict, key: str
|
||||||
) -> tuple[dict, str] | tuple[None, None]:
|
) -> tuple[dict, str] | tuple[None, None]:
|
||||||
"""
|
"""
|
||||||
@@ -179,13 +185,13 @@ class Anime(commands.Cog):
|
|||||||
data = response["data"][key]
|
data = response["data"][key]
|
||||||
url = data["siteUrl"]
|
url = data["siteUrl"]
|
||||||
except TypeError:
|
except TypeError:
|
||||||
embed = embed_templates.error_fatal(interaction, text="Kunne ikke finne det du søkte etter!")
|
embed = embed_templates.error_fatal("Kunne ikke finne det du søkte etter!")
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
return data, url
|
return data, url
|
||||||
|
|
||||||
def __construct_favorite_media_string(self, media: list) -> str:
|
def construct_favorite_media_string(self, media: list) -> str:
|
||||||
"""
|
"""
|
||||||
Constructs a string with the user's favorite media
|
Constructs a string with the user's favorite media
|
||||||
|
|
||||||
@@ -210,7 +216,7 @@ class Anime(commands.Cog):
|
|||||||
|
|
||||||
return "\n".join(favorite_media)
|
return "\n".join(favorite_media)
|
||||||
|
|
||||||
def __construct_favorite_entity_string(self, entities: list, studio: bool = False) -> str:
|
def construct_favorite_entity_string(self, entities: list, studio: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
Constructs a string with the user's favorite entities
|
Constructs a string with the user's favorite entities
|
||||||
|
|
||||||
@@ -238,7 +244,7 @@ class Anime(commands.Cog):
|
|||||||
|
|
||||||
return "\n".join(favortie_entities)
|
return "\n".join(favortie_entities)
|
||||||
|
|
||||||
def __construct_release_schedule_string(self, numbers: dict) -> str:
|
def construct_release_schedule_string(self, numbers: dict) -> str:
|
||||||
"""
|
"""
|
||||||
Constructs a string with the media's release schedule
|
Constructs a string with the media's release schedule
|
||||||
|
|
||||||
@@ -358,7 +364,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"name": bruker}
|
variables = {"name": bruker}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "User")
|
data, url = await self.request_anilist(interaction, query, variables, "User")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
@@ -367,7 +373,7 @@ class Anime(commands.Cog):
|
|||||||
profile_pic = data["avatar"]["large"]
|
profile_pic = data["avatar"]["large"]
|
||||||
days_watched = round(data["statistics"]["anime"]["minutesWatched"] / 1440, 1)
|
days_watched = round(data["statistics"]["anime"]["minutesWatched"] / 1440, 1)
|
||||||
chapters_read = data["statistics"]["manga"]["chaptersRead"]
|
chapters_read = data["statistics"]["manga"]["chaptersRead"]
|
||||||
color = self.__convert_color(data["options"]["profileColor"])
|
color = self.convert_color(data["options"]["profileColor"])
|
||||||
|
|
||||||
anime_statuses = {
|
anime_statuses = {
|
||||||
status["status"]: {"count": status["count"]} for status in data["statistics"]["anime"]["statuses"]
|
status["status"]: {"count": status["count"]} for status in data["statistics"]["anime"]["statuses"]
|
||||||
@@ -385,14 +391,14 @@ class Anime(commands.Cog):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
manga_completed = 0
|
manga_completed = 0
|
||||||
|
|
||||||
favourite_anime = self.__construct_favorite_media_string(data["favourites"]["anime"]["nodes"])
|
favourite_anime = self.construct_favorite_media_string(data["favourites"]["anime"]["nodes"])
|
||||||
favourite_manga = self.__construct_favorite_media_string(data["favourites"]["manga"]["nodes"])
|
favourite_manga = self.construct_favorite_media_string(data["favourites"]["manga"]["nodes"])
|
||||||
favourite_character = self.__construct_favorite_entity_string(data["favourites"]["characters"]["nodes"])
|
favourite_character = self.construct_favorite_entity_string(data["favourites"]["characters"]["nodes"])
|
||||||
favourite_staff = self.__construct_favorite_entity_string(data["favourites"]["staff"]["nodes"])
|
favourite_staff = self.construct_favorite_entity_string(data["favourites"]["staff"]["nodes"])
|
||||||
favourite_studio = self.__construct_favorite_entity_string(data["favourites"]["studios"]["nodes"], studio=True)
|
favourite_studio = self.construct_favorite_entity_string(data["favourites"]["studios"]["nodes"], studio=True)
|
||||||
|
|
||||||
embed = discord.Embed(title=user_name, color=color, url=url)
|
embed = discord.Embed(title=user_name, color=color, url=url)
|
||||||
embed.set_author(name="Anilist", icon_url="https://anilist.co/img/logo_al.png")
|
embed.set_author(name="Anilist", icon_url=self.anilist_logo)
|
||||||
embed.set_thumbnail(url=profile_pic)
|
embed.set_thumbnail(url=profile_pic)
|
||||||
embed.add_field(name="Antall dager sett", value=days_watched)
|
embed.add_field(name="Antall dager sett", value=days_watched)
|
||||||
embed.add_field(name="Antall anime sett", value=anime_completed)
|
embed.add_field(name="Antall anime sett", value=anime_completed)
|
||||||
@@ -460,7 +466,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"name": bruker}
|
variables = {"name": bruker}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "User")
|
data, url = await self.request_anilist(interaction, query, variables, "User")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
@@ -469,7 +475,7 @@ class Anime(commands.Cog):
|
|||||||
profile_pic = data["avatar"]["large"]
|
profile_pic = data["avatar"]["large"]
|
||||||
days_watched = round(data["statistics"]["anime"]["minutesWatched"] / 1440, 1)
|
days_watched = round(data["statistics"]["anime"]["minutesWatched"] / 1440, 1)
|
||||||
episodes_watched = data["statistics"]["anime"]["episodesWatched"]
|
episodes_watched = data["statistics"]["anime"]["episodesWatched"]
|
||||||
color = self.__convert_color(data["options"]["profileColor"])
|
color = self.convert_color(data["options"]["profileColor"])
|
||||||
|
|
||||||
statuses = {
|
statuses = {
|
||||||
status["status"]: {"count": status["count"], "minutes": status["minutesWatched"]}
|
status["status"]: {"count": status["count"], "minutes": status["minutesWatched"]}
|
||||||
@@ -500,10 +506,10 @@ class Anime(commands.Cog):
|
|||||||
anime_mean_score = "**Ingen**"
|
anime_mean_score = "**Ingen**"
|
||||||
|
|
||||||
most_watched_genres = ", ".join([genre["genre"] for genre in data["statistics"]["anime"]["genres"]])
|
most_watched_genres = ", ".join([genre["genre"] for genre in data["statistics"]["anime"]["genres"]])
|
||||||
most_watched_studios = self.__construct_favorite_entity_string(data["statistics"]["anime"]["studios"])
|
most_watched_studios = self.construct_favorite_entity_string(data["statistics"]["anime"]["studios"])
|
||||||
|
|
||||||
embed = discord.Embed(title=user_name, color=color, url=url)
|
embed = discord.Embed(title=user_name, color=color, url=url)
|
||||||
embed.set_author(name="Anilist", icon_url="https://anilist.co/img/logo_al.png")
|
embed.set_author(name="Anilist", icon_url=self.anilist_logo)
|
||||||
embed.set_thumbnail(url=profile_pic)
|
embed.set_thumbnail(url=profile_pic)
|
||||||
embed.add_field(name="Gj.snittsvurdering gitt", value=anime_mean_score)
|
embed.add_field(name="Gj.snittsvurdering gitt", value=anime_mean_score)
|
||||||
embed.add_field(name="Antall dager sett", value=days_watched)
|
embed.add_field(name="Antall dager sett", value=days_watched)
|
||||||
@@ -570,7 +576,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"name": bruker}
|
variables = {"name": bruker}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "User")
|
data, url = await self.request_anilist(interaction, query, variables, "User")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
@@ -579,7 +585,7 @@ class Anime(commands.Cog):
|
|||||||
profile_pic = data["avatar"]["large"]
|
profile_pic = data["avatar"]["large"]
|
||||||
chapters_read = round(data["statistics"]["manga"]["chaptersRead"] / 1440, 1)
|
chapters_read = round(data["statistics"]["manga"]["chaptersRead"] / 1440, 1)
|
||||||
volumes_read = data["statistics"]["manga"]["volumesRead"]
|
volumes_read = data["statistics"]["manga"]["volumesRead"]
|
||||||
color = self.__convert_color(data["options"]["profileColor"])
|
color = self.convert_color(data["options"]["profileColor"])
|
||||||
|
|
||||||
statuses = {
|
statuses = {
|
||||||
status["status"]: {"count": status["count"], "minutes": status["chaptersRead"]}
|
status["status"]: {"count": status["count"], "minutes": status["chaptersRead"]}
|
||||||
@@ -610,10 +616,10 @@ class Anime(commands.Cog):
|
|||||||
manga_mean_score = "**Ingen**"
|
manga_mean_score = "**Ingen**"
|
||||||
|
|
||||||
most_read_genres = ", ".join([genre["genre"] for genre in data["statistics"]["manga"]["genres"]])
|
most_read_genres = ", ".join([genre["genre"] for genre in data["statistics"]["manga"]["genres"]])
|
||||||
most_read_staff = self.__construct_favorite_entity_string(data["statistics"]["manga"]["staff"])
|
most_read_staff = self.construct_favorite_entity_string(data["statistics"]["manga"]["staff"])
|
||||||
|
|
||||||
embed = discord.Embed(title=user_name, color=color, url=url)
|
embed = discord.Embed(title=user_name, color=color, url=url)
|
||||||
embed.set_author(name="Anilist", icon_url="https://anilist.co/img/logo_al.png")
|
embed.set_author(name="Anilist", icon_url=self.anilist_logo)
|
||||||
embed.set_thumbnail(url=profile_pic)
|
embed.set_thumbnail(url=profile_pic)
|
||||||
embed.add_field(name="Gj.snittsvurdering gitt", value=manga_mean_score)
|
embed.add_field(name="Gj.snittsvurdering gitt", value=manga_mean_score)
|
||||||
embed.add_field(name="Antall kapitler lest", value=chapters_read)
|
embed.add_field(name="Antall kapitler lest", value=chapters_read)
|
||||||
@@ -697,15 +703,15 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"search": navn, "isMain": True}
|
variables = {"search": navn, "isMain": True}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "Media")
|
data, url = await self.request_anilist(interaction, query, variables, "Media")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
nsfw = data["isAdult"]
|
nsfw = data["isAdult"]
|
||||||
if nsfw:
|
if nsfw:
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_warning(
|
||||||
interaction, text="Animen du søkte på er NSFW. " + "Gjør kommandoen i en NSFW-kanal i stedet"
|
"Animen du søkte på er NSFW. Gjør kommandoen i en NSFW-kanal i stedet"
|
||||||
)
|
)
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@@ -713,7 +719,7 @@ class Anime(commands.Cog):
|
|||||||
banner_image = data["bannerImage"]
|
banner_image = data["bannerImage"]
|
||||||
mean_score = data["meanScore"]
|
mean_score = data["meanScore"]
|
||||||
|
|
||||||
color = self.__convert_color(data["coverImage"]["color"])
|
color = self.convert_color(data["coverImage"]["color"])
|
||||||
|
|
||||||
title_romaji = data["title"]["romaji"]
|
title_romaji = data["title"]["romaji"]
|
||||||
title_native = data["title"]["native"]
|
title_native = data["title"]["native"]
|
||||||
@@ -721,9 +727,9 @@ class Anime(commands.Cog):
|
|||||||
titles = [title_romaji, title_native, title_english]
|
titles = [title_romaji, title_native, title_english]
|
||||||
title_romaji, title_native, title_english = [title if title else "" for title in titles]
|
title_romaji, title_native, title_english = [title if title else "" for title in titles]
|
||||||
|
|
||||||
description = self.__remove_html(data["description"]) if data["description"] else ""
|
description = self.remove_html(data["description"]) if data["description"] else ""
|
||||||
genres = ", ".join(data["genres"])
|
genres = ", ".join(data["genres"])
|
||||||
studios_string = self.__construct_favorite_entity_string(data["studios"]["nodes"], studio=True)
|
studios_string = self.construct_favorite_entity_string(data["studios"]["nodes"], studio=True)
|
||||||
|
|
||||||
staff = data["staff"]["edges"]
|
staff = data["staff"]["edges"]
|
||||||
director_string = ""
|
director_string = ""
|
||||||
@@ -751,7 +757,7 @@ class Anime(commands.Cog):
|
|||||||
"end_year": data["endDate"].get("year") if data["endDate"].get("year") else "?",
|
"end_year": data["endDate"].get("year") if data["endDate"].get("year") else "?",
|
||||||
}
|
}
|
||||||
|
|
||||||
date = self.__construct_release_schedule_string(numbers)
|
date = self.construct_release_schedule_string(numbers)
|
||||||
|
|
||||||
length_string = ""
|
length_string = ""
|
||||||
if numbers["duration"] == 1:
|
if numbers["duration"] == 1:
|
||||||
@@ -765,11 +771,11 @@ class Anime(commands.Cog):
|
|||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
status = self.__convert_status(status)
|
status = self.convert_status(status)
|
||||||
|
|
||||||
# Format
|
# Format
|
||||||
media_format = data["format"]
|
media_format = data["format"]
|
||||||
media_format = self.__convert_media_format(media_format)
|
media_format = self.convert_media_format(media_format)
|
||||||
|
|
||||||
embed = discord.Embed(color=color, title=title_romaji, url=url, description=f"{title_native}\n{title_english}")
|
embed = discord.Embed(color=color, title=title_romaji, url=url, description=f"{title_native}\n{title_english}")
|
||||||
embed.set_thumbnail(url=cover_image)
|
embed.set_thumbnail(url=cover_image)
|
||||||
@@ -855,15 +861,15 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"search": navn}
|
variables = {"search": navn}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "Media")
|
data, url = await self.request_anilist(interaction, query, variables, "Media")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
nsfw = data["isAdult"]
|
nsfw = data["isAdult"]
|
||||||
if nsfw:
|
if nsfw:
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_warning(
|
||||||
interaction, text="Mangaen du søkte på er NSFW. " + "Gjør kommandoen i en NSFW-kanal i stedet"
|
"Mangaen du søkte på er NSFW. Gjør kommandoen i en NSFW-kanal i stedet"
|
||||||
)
|
)
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@@ -871,7 +877,7 @@ class Anime(commands.Cog):
|
|||||||
banner_image = data["bannerImage"]
|
banner_image = data["bannerImage"]
|
||||||
mean_score = data["meanScore"]
|
mean_score = data["meanScore"]
|
||||||
|
|
||||||
color = self.__convert_color(data["coverImage"]["color"])
|
color = self.convert_color(data["coverImage"]["color"])
|
||||||
|
|
||||||
title_romaji = data["title"]["romaji"]
|
title_romaji = data["title"]["romaji"]
|
||||||
title_native = data["title"]["native"]
|
title_native = data["title"]["native"]
|
||||||
@@ -879,7 +885,7 @@ class Anime(commands.Cog):
|
|||||||
titles = [title_romaji, title_native, title_english]
|
titles = [title_romaji, title_native, title_english]
|
||||||
title_romaji, title_native, title_english = [title if title else "" for title in titles]
|
title_romaji, title_native, title_english = [title if title else "" for title in titles]
|
||||||
|
|
||||||
description = self.__remove_html(data["description"]) if data.get("description") else ""
|
description = self.remove_html(data["description"]) if data.get("description") else ""
|
||||||
genres = ", ".join(data["genres"])
|
genres = ", ".join(data["genres"])
|
||||||
|
|
||||||
staff = data["staff"]["edges"]
|
staff = data["staff"]["edges"]
|
||||||
@@ -901,7 +907,7 @@ class Anime(commands.Cog):
|
|||||||
"end_month": data["endDate"].get("month") if data["endDate"].get("month") else "?",
|
"end_month": data["endDate"].get("month") if data["endDate"].get("month") else "?",
|
||||||
"end_year": data["endDate"].get("year") if data["endDate"].get("year") else "?",
|
"end_year": data["endDate"].get("year") if data["endDate"].get("year") else "?",
|
||||||
}
|
}
|
||||||
date = self.__construct_release_schedule_string(numbers)
|
date = self.construct_release_schedule_string(numbers)
|
||||||
|
|
||||||
chapters_string = ""
|
chapters_string = ""
|
||||||
if numbers["chapters"] == 1:
|
if numbers["chapters"] == 1:
|
||||||
@@ -910,10 +916,10 @@ class Anime(commands.Cog):
|
|||||||
chapters_string += "kapitler"
|
chapters_string += "kapitler"
|
||||||
|
|
||||||
status = data["status"]
|
status = data["status"]
|
||||||
status = self.__convert_status(status)
|
status = self.convert_status(status)
|
||||||
|
|
||||||
media_format = data["format"]
|
media_format = data["format"]
|
||||||
media_format = self.__convert_media_format(media_format)
|
media_format = self.convert_media_format(media_format)
|
||||||
|
|
||||||
embed = discord.Embed(color=color, title=title_romaji, url=url, description=f"{title_native}\n{title_english}")
|
embed = discord.Embed(color=color, title=title_romaji, url=url, description=f"{title_native}\n{title_english}")
|
||||||
embed.set_thumbnail(url=cover_image)
|
embed.set_thumbnail(url=cover_image)
|
||||||
@@ -982,7 +988,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"search": navn}
|
variables = {"search": navn}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "Character")
|
data, url = await self.request_anilist(interaction, query, variables, "Character")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
@@ -993,7 +999,7 @@ class Anime(commands.Cog):
|
|||||||
favourites = data["favourites"]
|
favourites = data["favourites"]
|
||||||
|
|
||||||
if data["description"]:
|
if data["description"]:
|
||||||
description = self.__remove_html(data["description"])
|
description = self.remove_html(data["description"])
|
||||||
if len(description) > 1024:
|
if len(description) > 1024:
|
||||||
description = description[0:1020] + "..."
|
description = description[0:1020] + "..."
|
||||||
else:
|
else:
|
||||||
@@ -1005,10 +1011,10 @@ class Anime(commands.Cog):
|
|||||||
nsfwtag = "🔞" if media["node"]["isAdult"] else ""
|
nsfwtag = "🔞" if media["node"]["isAdult"] else ""
|
||||||
media_name = media["node"]["title"]["romaji"]
|
media_name = media["node"]["title"]["romaji"]
|
||||||
media_url = media["node"]["siteUrl"]
|
media_url = media["node"]["siteUrl"]
|
||||||
media_role = self.__convert_role_names(media["characterRole"])
|
media_role = self.onvert_role_names(media["characterRole"])
|
||||||
for voice_actor in media["voiceActors"]:
|
for voice_actor in media["voiceActors"]:
|
||||||
voice_actor_url = voice_actor["siteUrl"]
|
voice_actor_url = voice_actor["siteUrl"]
|
||||||
voice_actor_language = self.__convert_language_names(voice_actor["language"])
|
voice_actor_language = self.convert_language_names(voice_actor["language"])
|
||||||
voice_actor_name = voice_actor["name"]["full"]
|
voice_actor_name = voice_actor["name"]["full"]
|
||||||
if voice_actor["name"]["native"] != " " and voice_actor["name"]["native"] is not None:
|
if voice_actor["name"]["native"] != " " and voice_actor["name"]["native"] is not None:
|
||||||
voice_actor_name += f' ({voice_actor["name"]["native"]}'
|
voice_actor_name += f' ({voice_actor["name"]["native"]}'
|
||||||
@@ -1080,7 +1086,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"search": navn}
|
variables = {"search": navn}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "Staff")
|
data, url = await self.request_anilist(interaction, query, variables, "Staff")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
@@ -1091,7 +1097,7 @@ class Anime(commands.Cog):
|
|||||||
favourites = data["favourites"]
|
favourites = data["favourites"]
|
||||||
|
|
||||||
if data["description"]:
|
if data["description"]:
|
||||||
description = self.__remove_html(data["description"])
|
description = self.remove_html(data["description"])
|
||||||
if len(description) > 1024:
|
if len(description) > 1024:
|
||||||
description = description[0:1020] + "..."
|
description = description[0:1020] + "..."
|
||||||
else:
|
else:
|
||||||
@@ -1160,7 +1166,7 @@ class Anime(commands.Cog):
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
variables = {"search": navn}
|
variables = {"search": navn}
|
||||||
data, url = await self.__request_anilist(interaction, query, variables, "Studio")
|
data, url = await self.request_anilist(interaction, query, variables, "Studio")
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class Aurora(commands.Cog):
|
|||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
self.notified = datetime(2000, 9, 11) # Used to prevent spamming aurora alerts
|
self.notified = datetime(2000, 9, 11) # Used to prevent spamming aurora alerts
|
||||||
# self.aurora_alarm.start()
|
self.aurora_alarm.start()
|
||||||
|
self.AURORA_CHANNEL = 747542544291987599
|
||||||
|
|
||||||
async def get_forecast(self) -> dict | None:
|
async def get_forecast(self) -> dict | None:
|
||||||
"""
|
"""
|
||||||
@@ -41,7 +42,6 @@ class Aurora(commands.Cog):
|
|||||||
for interval in data["shortIntervals"]:
|
for interval in data["shortIntervals"]:
|
||||||
# Convert to datetime objects, convert timezone to utc and strip timezone info
|
# Convert to datetime objects, convert timezone to utc and strip timezone info
|
||||||
start = datetime.fromisoformat(interval["start"]).astimezone(pytz.utc).replace(tzinfo=None)
|
start = datetime.fromisoformat(interval["start"]).astimezone(pytz.utc).replace(tzinfo=None)
|
||||||
# end = datetime.fromisoformat(interval["end"]).astimezone(pytz.utc).replace(tzinfo=None)
|
|
||||||
|
|
||||||
# If sighting is not in within the next 12 hours ignore
|
# If sighting is not in within the next 12 hours ignore
|
||||||
if (start - datetime.now()) > timedelta(hours=12):
|
if (start - datetime.now()) > timedelta(hours=12):
|
||||||
@@ -58,7 +58,7 @@ class Aurora(commands.Cog):
|
|||||||
most_likely_sighting = max(valid_sightings, key=lambda x: x["auroraValue"])
|
most_likely_sighting = max(valid_sightings, key=lambda x: x["auroraValue"])
|
||||||
return most_likely_sighting
|
return most_likely_sighting
|
||||||
|
|
||||||
@tasks.loop(minutes=10)
|
@tasks.loop(minutes=60)
|
||||||
async def aurora_alarm(self):
|
async def aurora_alarm(self):
|
||||||
"""
|
"""
|
||||||
Checks if the aurora forecast is above 50% and sends a message to the aurora channel if it is
|
Checks if the aurora forecast is above 50% and sends a message to the aurora channel if it is
|
||||||
@@ -76,7 +76,7 @@ class Aurora(commands.Cog):
|
|||||||
self.bot.logger.info("aurora_alarm: Fetch forecast")
|
self.bot.logger.info("aurora_alarm: Fetch forecast")
|
||||||
return
|
return
|
||||||
|
|
||||||
channel = self.bot.get_channel(747542544291987599)
|
channel = self.bot.get_channel(self.AURORA_CHANNEL)
|
||||||
if not channel:
|
if not channel:
|
||||||
self.bot.logger.warning("aurora_alarm: Failed to get aurora channel")
|
self.bot.logger.warning("aurora_alarm: Failed to get aurora channel")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ class Birthday(commands.Cog):
|
|||||||
self.birthday_check.start()
|
self.birthday_check.start()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the birthday cog to work"""
|
"""
|
||||||
|
Create the necessary tables for the birthday cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -45,9 +47,11 @@ class Birthday(commands.Cog):
|
|||||||
name="bursdag", description="Se, endre eller fjern bursdager for brukere på serveren"
|
name="bursdag", description="Se, endre eller fjern bursdager for brukere på serveren"
|
||||||
)
|
)
|
||||||
|
|
||||||
@tasks.loop(hours=24)
|
@tasks.loop(time=misc_utils.MIDNIGHT)
|
||||||
async def birthday_check(self):
|
async def birthday_check(self):
|
||||||
"""Check if it's someone's birthday every day at midgnight and send a greeting if it is."""
|
"""
|
||||||
|
Check if it's someone's birthday every day at midgnight and send a greeting if it is
|
||||||
|
"""
|
||||||
|
|
||||||
await asyncio.sleep(1) # Prevent sending message before date has really changed
|
await asyncio.sleep(1) # Prevent sending message before date has really changed
|
||||||
|
|
||||||
@@ -64,27 +68,25 @@ class Birthday(commands.Cog):
|
|||||||
|
|
||||||
birthdays = self.cursor.fetchall()
|
birthdays = self.cursor.fetchall()
|
||||||
self.bot.logger.info(f"Found the following birthdays: {birthdays}") # TODO: Temporary. Remove
|
self.bot.logger.info(f"Found the following birthdays: {birthdays}") # TODO: Temporary. Remove
|
||||||
if birthdays:
|
|
||||||
guild = self.bot.get_guild(747542543750660178)
|
|
||||||
channel = guild.get_channel(747542544291987597)
|
|
||||||
for birthday in birthdays:
|
|
||||||
user = await guild.fetch_member(birthday[0])
|
|
||||||
if user:
|
|
||||||
await channel.send(f"Gratulerer med dagen {user.mention}! 🥳")
|
|
||||||
else:
|
|
||||||
self.bot.logger.warning(f"Could not find user with ID {birthday[0]}")
|
|
||||||
|
|
||||||
@birthday_check.before_loop
|
if not birthdays:
|
||||||
async def before_birthday_check(self):
|
return
|
||||||
"""Syncs loop with the time of day"""
|
|
||||||
|
|
||||||
self.bot.logger.info("Postponing birthday check until midnight")
|
guild = self.bot.get_guild(self.bot.UIO_GAMING_GUILD_ID)
|
||||||
await discord_utils.sleep_until_midnight(self.bot)
|
channel = guild.get_channel(747542544291987597)
|
||||||
|
for birthday in birthdays:
|
||||||
|
user = await guild.fetch_member(birthday[0])
|
||||||
|
if user:
|
||||||
|
await channel.send(f"Gratulerer med dagen {user.mention}! 🥳")
|
||||||
|
else:
|
||||||
|
self.bot.logger.warning(f"Could not find user with ID {birthday[0]}")
|
||||||
|
|
||||||
def cog_unload(self):
|
async def cog_unload(self):
|
||||||
|
self.bot.logger.info("Unloading cog")
|
||||||
self.birthday_check.cancel()
|
self.birthday_check.cancel()
|
||||||
|
self.cursor.close()
|
||||||
|
|
||||||
def __fetch_user_birthday(self, user_id: int) -> datetime | None:
|
def fetch_user_birthday(self, user_id: int) -> datetime | None:
|
||||||
"""
|
"""
|
||||||
Fetch the birthday of a user in the database
|
Fetch the birthday of a user in the database
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ class Birthday(commands.Cog):
|
|||||||
birthday = result[0]
|
birthday = result[0]
|
||||||
return datetime(birthday.year, birthday.month, birthday.day)
|
return datetime(birthday.year, birthday.month, birthday.day)
|
||||||
|
|
||||||
def __fetch_user_next_birthday(self, user_id: int) -> datetime:
|
def fetch_user_next_birthday(self, user_id: int) -> datetime:
|
||||||
"""
|
"""
|
||||||
Get date of next birthday for a user
|
Get date of next birthday for a user
|
||||||
|
|
||||||
@@ -132,7 +134,7 @@ class Birthday(commands.Cog):
|
|||||||
# This will never happen unless a database error occurs. Keeping the type checker happy
|
# This will never happen unless a database error occurs. Keeping the type checker happy
|
||||||
return datetime.now()
|
return datetime.now()
|
||||||
|
|
||||||
def __fetch_next_birthdays(self) -> list[tuple[int, datetime, datetime]]:
|
def fetch_next_birthdays(self) -> list[tuple[int, datetime, datetime]]:
|
||||||
"""
|
"""
|
||||||
Fetch the 5 first future birthdays of all users in the database
|
Fetch the 5 first future birthdays of all users in the database
|
||||||
|
|
||||||
@@ -160,7 +162,7 @@ class Birthday(commands.Cog):
|
|||||||
|
|
||||||
return birthdays
|
return birthdays
|
||||||
|
|
||||||
def __set_user_birthday(self, user_id: int, birthday: datetime):
|
def set_user_birthday(self, user_id: int, birthday: datetime):
|
||||||
"""
|
"""
|
||||||
Set the birthday of a user in the database. If it already exists, update it.
|
Set the birthday of a user in the database. If it already exists, update it.
|
||||||
|
|
||||||
@@ -201,28 +203,24 @@ class Birthday(commands.Cog):
|
|||||||
date = datetime(år, måned, dag)
|
date = datetime(år, måned, dag)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, text="ikke en gyldig dato, gjøk!"), ephemeral=True
|
embed=embed_templates.error_warning("ikke en gyldig dato, gjøk!"), ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if date.year < 1970:
|
if date.year < 1970:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(
|
embed=embed_templates.error_warning("Ok boomer, men nei. Hvorfor? Unix timestamps. Google it"),
|
||||||
interaction, text="Ok boomer, men nei. Hvorfor? Unix timestamps. Google it"
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if date > datetime.now():
|
if date > datetime.now():
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, text="Du kan ikke sette en fremtidig dato!"),
|
embed=embed_templates.error_warning("Du kan ikke sette en fremtidig dato!"),
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.__set_user_birthday(interaction.user.id, date)
|
self.set_user_birthday(interaction.user.id, date)
|
||||||
|
|
||||||
embed = embed_templates.success(
|
embed = embed_templates.success(f"Bursdag satt til {discord.utils.format_dt(date, style='D')}")
|
||||||
interaction, text=f'Bursdag satt til {discord.utils.format_dt(date, style="D")}'
|
|
||||||
)
|
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
@@ -239,7 +237,7 @@ class Birthday(commands.Cog):
|
|||||||
|
|
||||||
self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (interaction.user.id,))
|
self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (interaction.user.id,))
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Bursdag fjernet")
|
embed = embed_templates.success("Bursdag fjernet")
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
@@ -260,11 +258,11 @@ class Birthday(commands.Cog):
|
|||||||
if not bruker:
|
if not bruker:
|
||||||
bruker = interaction.user
|
bruker = interaction.user
|
||||||
|
|
||||||
user = self.__fetch_user_birthday(bruker.id)
|
user = self.fetch_user_birthday(bruker.id)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, text="Brukeren har ikke registrert bursdagen sin")
|
embed=embed_templates.error_warning("Brukeren har ikke registrert bursdagen sin")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Current age information
|
# Current age information
|
||||||
@@ -273,10 +271,10 @@ class Birthday(commands.Cog):
|
|||||||
years_old = relativedelta(datetime.now(), birthday).years
|
years_old = relativedelta(datetime.now(), birthday).years
|
||||||
|
|
||||||
# Next birthday information
|
# Next birthday information
|
||||||
next_birthday = self.__fetch_user_next_birthday(bruker.id)
|
next_birthday = self.fetch_user_next_birthday(bruker.id)
|
||||||
next_birthday_days = (next_birthday - datetime.now()).days
|
next_birthday_days = (next_birthday - datetime.now()).days
|
||||||
|
|
||||||
embed = discord.Embed(description=bruker.mention, color=discord_utils.get_color(bruker))
|
embed = discord.Embed(description=bruker.mention, color=bruker.color)
|
||||||
embed.set_thumbnail(url=bruker.display_avatar)
|
embed.set_thumbnail(url=bruker.display_avatar)
|
||||||
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
||||||
embed.add_field(name="Bursdag", value=discord.utils.format_dt(birthday, style="D"))
|
embed.add_field(name="Bursdag", value=discord.utils.format_dt(birthday, style="D"))
|
||||||
@@ -303,12 +301,10 @@ class Birthday(commands.Cog):
|
|||||||
|
|
||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
next_birthdays = self.__fetch_next_birthdays()
|
next_birthdays = self.fetch_next_birthdays()
|
||||||
|
|
||||||
if not next_birthdays:
|
if not next_birthdays:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(embed=embed_templates.error_warning("Ingen bursdager"))
|
||||||
embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")
|
|
||||||
)
|
|
||||||
|
|
||||||
birthday_strings = []
|
birthday_strings = []
|
||||||
for user in next_birthdays:
|
for user in next_birthdays:
|
||||||
@@ -325,9 +321,7 @@ class Birthday(commands.Cog):
|
|||||||
birthday_strings.append(f"* {discord_user.name} - {timestamp} ({relative_timestamp})")
|
birthday_strings.append(f"* {discord_user.name} - {timestamp} ({relative_timestamp})")
|
||||||
|
|
||||||
if not birthday_strings:
|
if not birthday_strings:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(embed=embed_templates.error_warning("Ingen bursdager"))
|
||||||
embed=embed_templates.error_fatal(interaction, text="Ingen bursdager")
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(birthday_strings)
|
paginator = misc_utils.Paginator(birthday_strings)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ class BotInfo(commands.Cog):
|
|||||||
embed.set_author(name=dev.name, icon_url=dev.display_avatar)
|
embed.set_author(name=dev.name, icon_url=dev.display_avatar)
|
||||||
embed.set_thumbnail(url=self.bot.user.display_avatar)
|
embed.set_thumbnail(url=self.bot.user.display_avatar)
|
||||||
embed.add_field(name="Dev", value=f"{dev.mention}\n{dev.display_name}")
|
embed.add_field(name="Dev", value=f"{dev.mention}\n{dev.display_name}")
|
||||||
embed.add_field(name="Oppetid", value=self.__get_uptime())
|
embed.add_field(name="Oppetid", value=self.get_uptime())
|
||||||
embed.add_field(name="Ping", value=f"Websocket ping: {self.__get_ping()} ms")
|
embed.add_field(name="Ping", value=f"Websocket ping: {self.get_ping()} ms")
|
||||||
embed.add_field(name="Servere", value=len(self.bot.guilds))
|
embed.add_field(name="Servere", value=len(self.bot.guilds))
|
||||||
embed.add_field(name="Discord.py", value=discord.__version__)
|
embed.add_field(name="Discord.py", value=discord.__version__)
|
||||||
embed.add_field(name="Python", value=platform.python_version())
|
embed.add_field(name="Python", value=platform.python_version())
|
||||||
@@ -106,7 +106,7 @@ class BotInfo(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.client.user.color)
|
embed = discord.Embed(color=interaction.client.user.color)
|
||||||
embed.add_field(name="🔌 Oppetid", value=self.__get_uptime())
|
embed.add_field(name="🔌 Oppetid", value=self.get_uptime())
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
@@ -122,10 +122,10 @@ class BotInfo(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.client.user.color)
|
embed = discord.Embed(color=interaction.client.user.color)
|
||||||
embed.add_field(name="📶 Ping", value=f"* **Websocket ping:** {self.__get_ping()} ms\n* **I hodet:** ∞")
|
embed.add_field(name="📶 Ping", value=f"* **Websocket ping:** {self.get_ping()} ms\n* **I hodet:** ∞")
|
||||||
await interaction.response.send_message(embed=embed, content=None)
|
await interaction.response.send_message(embed=embed, content=None)
|
||||||
|
|
||||||
def __get_ping(self) -> int:
|
def get_ping(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the bot's ping in milliseconds
|
Get the bot's ping in milliseconds
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ class BotInfo(commands.Cog):
|
|||||||
|
|
||||||
return int(self.bot.latency * 1000)
|
return int(self.bot.latency * 1000)
|
||||||
|
|
||||||
def __get_uptime(self) -> str:
|
def get_uptime(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns the current uptime of the bot in string format
|
Returns the current uptime of the bot in string format
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import textwrap
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import override
|
from typing import override
|
||||||
@@ -11,12 +10,9 @@ import discord
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from PIL import Image
|
|
||||||
from PIL import ImageDraw
|
|
||||||
from PIL import ImageFont
|
|
||||||
|
|
||||||
from cogs.utils import discord_utils
|
|
||||||
from cogs.utils import embed_templates
|
from cogs.utils import embed_templates
|
||||||
|
from cogs.utils import misc_utils
|
||||||
from cogs.utils.discord_utils import Lobby
|
from cogs.utils.discord_utils import Lobby
|
||||||
from cogs.utils.discord_utils import LobbyView
|
from cogs.utils.discord_utils import LobbyView
|
||||||
|
|
||||||
@@ -53,19 +49,13 @@ class CS2Bingo(commands.Cog):
|
|||||||
self.active_lobbies.pop(str(interaction.user.id))
|
self.active_lobbies.pop(str(interaction.user.id))
|
||||||
else:
|
else:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(
|
embed=embed_templates.error_warning("Du hoster allerede en lobby")
|
||||||
interaction,
|
|
||||||
text="Du hoster allerede en lobby",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for lobby in self.active_lobbies.values():
|
for lobby in self.active_lobbies.values():
|
||||||
if interaction.user.id in lobby.players:
|
if interaction.user.id in lobby.players:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(
|
embed=embed_templates.error_warning("Du er allerede i en lobby")
|
||||||
interaction,
|
|
||||||
text="Du er allerede i en lobby",
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.active_lobbies[str(interaction.user.id)] = Lobby(
|
self.active_lobbies[str(interaction.user.id)] = Lobby(
|
||||||
@@ -80,7 +70,7 @@ class CS2Bingo(commands.Cog):
|
|||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="Bingolobby",
|
title="Bingolobby",
|
||||||
description=f"Lobbyen stenger {time_left}. Bli med innen da!",
|
description=f"Lobbyen stenger {time_left}. Bli med innen da!",
|
||||||
color=discord_utils.get_color(interaction.user),
|
color=interaction.user.color,
|
||||||
)
|
)
|
||||||
embed.set_author(name=interaction.user.global_name, icon_url=interaction.user.avatar)
|
embed.set_author(name=interaction.user.global_name, icon_url=interaction.user.avatar)
|
||||||
embed.add_field(name="Spillere", value=f"* {interaction.user.mention}", inline=False)
|
embed.add_field(name="Spillere", value=f"* {interaction.user.mention}", inline=False)
|
||||||
@@ -103,77 +93,6 @@ class BingoGenerator:
|
|||||||
with open(f"{BINGO_PATH}/names.json") as f:
|
with open(f"{BINGO_PATH}/names.json") as f:
|
||||||
NAMES = json.load(f)
|
NAMES = json.load(f)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
async def put_text_in_box(
|
|
||||||
cls,
|
|
||||||
image: np.ndarray,
|
|
||||||
text: str,
|
|
||||||
top_left: tuple,
|
|
||||||
bottom_right: tuple,
|
|
||||||
font_path: str,
|
|
||||||
font_size: int = 10,
|
|
||||||
color: tuple = (0, 0, 0),
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Puts text inside a bounding box on the cv2 image with automatic text wrapping, using PIL for text rendering.
|
|
||||||
|
|
||||||
:param image: Source cv2 image (numpy array).
|
|
||||||
:param text: Text string to be put.
|
|
||||||
:param top_left: Tuple (x, y) defining the top-left corner of the bounding box.
|
|
||||||
:param bottom_right: Tuple (x, y) defining the bottom-right corner of the bounding box.
|
|
||||||
:param font_path: Path to a .ttf font file.
|
|
||||||
:param font_size: Initial font size.
|
|
||||||
:param color: Text color in RGB.
|
|
||||||
:return: Image with text (numpy array).
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: Dry. This algorithm is also seen in the memes cog
|
|
||||||
|
|
||||||
# Convert the cv2 image to a PIL image
|
|
||||||
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
|
||||||
draw = ImageDraw.Draw(image_pil)
|
|
||||||
|
|
||||||
# Get coordinates of the bounding box
|
|
||||||
x1, y1 = top_left
|
|
||||||
x2, y2 = bottom_right
|
|
||||||
|
|
||||||
# Get the width and height of the bounding box
|
|
||||||
# We multiply by 0.9 to give some padding
|
|
||||||
w, h = (x2 - x1) * 0.9, (y2 - y1) * 0.9
|
|
||||||
|
|
||||||
# Initialize font width/height to 0 in order to run the while loops below at least once
|
|
||||||
font_width = 0
|
|
||||||
font_height = 0
|
|
||||||
|
|
||||||
# Add newlines to the text. Here we have gone with 15 characters per line
|
|
||||||
# This is arbitrary, but seems to work well
|
|
||||||
text = textwrap.fill(text, width=15)
|
|
||||||
|
|
||||||
# Increase the font size until the longest line of text does not fit in the bounding box anymore
|
|
||||||
# We also have to check the height, since the text might be too tall for the box
|
|
||||||
while font_width < w and font_height < h:
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
|
|
||||||
longest_line = max(text.split("\n"), key=lambda x: font.getlength(x))
|
|
||||||
font_width = font.getlength(longest_line)
|
|
||||||
|
|
||||||
# bbox doesn't work with multiline text, so we have to calculate the height manually
|
|
||||||
# take the height of the bounding box, multiply by 1.5 (our assumed line dividor height)
|
|
||||||
# after that, we multiply by the number of lines
|
|
||||||
font_box = font.getbbox(text)
|
|
||||||
font_height = ((font_box[3] - font_box[1]) * 1.5) * text.count("\n")
|
|
||||||
|
|
||||||
font_size += 1
|
|
||||||
|
|
||||||
# Actually draw the text
|
|
||||||
draw.multiline_textbbox((w, h), text=text, font=font, align="center", anchor="mm")
|
|
||||||
center_of_box = ((x1 + x2) // 2, (y1 + y2) // 2)
|
|
||||||
draw.multiline_text(center_of_box, text, font=font, fill=color, align="center", anchor="mm")
|
|
||||||
|
|
||||||
# Convert back to cv2 image format and update the original image
|
|
||||||
cv2_image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
|
|
||||||
np.copyto(image, cv2_image)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def sample_cells(cls, players: list[str], bot: commands.Bot) -> dict[str, list[str]]:
|
async def sample_cells(cls, players: list[str], bot: commands.Bot) -> dict[str, list[str]]:
|
||||||
assert 0 < len(players) <= 10
|
assert 0 < len(players) <= 10
|
||||||
@@ -238,11 +157,11 @@ class BingoGenerator:
|
|||||||
for i in range(0, 5):
|
for i in range(0, 5):
|
||||||
for j in range(0, 5):
|
for j in range(0, 5):
|
||||||
width = 123
|
width = 123
|
||||||
await cls.put_text_in_box(
|
await misc_utils.put_text_in_box(
|
||||||
sliced,
|
sliced,
|
||||||
sample_space.pop(),
|
sample_space.pop(),
|
||||||
(i * width + 1, j * width + 1),
|
top_left=(i * width + 1, j * width + 1),
|
||||||
((i + 1) * width, (j + 1) * width),
|
bottom_right=((i + 1) * width, (j + 1) * width),
|
||||||
font_path=cls.FONT_PATH,
|
font_path=cls.FONT_PATH,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -257,7 +176,7 @@ class BingoView(LobbyView):
|
|||||||
@override
|
@override
|
||||||
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if interaction.user != self.lobby.host:
|
if interaction.user != self.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan starte bingoen")
|
embed = embed_templates.error_warning("Bare hosten kan starte bingoen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
await self.end_lobby()
|
await self.end_lobby()
|
||||||
@@ -268,8 +187,7 @@ class BingoView(LobbyView):
|
|||||||
|
|
||||||
mention_players = " ".join([f"{p.mention}" for p in self.lobby.players])
|
mention_players = " ".join([f"{p.mention}" for p in self.lobby.players])
|
||||||
embed = embed_templates.success(
|
embed = embed_templates.success(
|
||||||
interaction,
|
"Bingoen har startet\n\nDere vil nå få tilsendt bingobrettene deres på DM. "
|
||||||
text="Bingoen har startet\n\nDere vil nå få tilsendt bingobrettene deres på DM. "
|
|
||||||
+ "Sørg for at jeg kan slide inn i dem :smirk:",
|
+ "Sørg for at jeg kan slide inn i dem :smirk:",
|
||||||
)
|
)
|
||||||
await interaction.response.send_message(content=mention_players, embed=embed)
|
await interaction.response.send_message(content=mention_players, embed=embed)
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
|
|
||||||
class CameraChannel(commands.Cog):
|
|
||||||
def __init__(self, bot: commands.Bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
async def on_voice_state_update(self, member, before, after):
|
|
||||||
if after.channel is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if member.voice.channel.id != 817125310491131949:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not member.voice.self_video:
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
if not member.voice.self_video and member.voice.channel.id == 817125310491131949:
|
|
||||||
await member.move_to(channel=None)
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
|
||||||
# bot.add_listener(CameraChannel(bot).on_voice_state_update, 'on_voice_state_update')
|
|
||||||
# await bot.add_cog(CameraChannel(bot))
|
|
||||||
pass
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
import aiocron
|
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
from yr.libyr import Yr
|
|
||||||
|
|
||||||
|
|
||||||
class Vær(commands.Cog):
|
|
||||||
def __init__(self, bot: commands.Bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@aiocron.crontab("0 6 * * *")
|
|
||||||
async def forecast():
|
|
||||||
dev = await self.bot.fetch_user(162337748781367296)
|
|
||||||
guild = self.bot.get_guild(747542543750660178)
|
|
||||||
channel = guild.get_channel(750052141346979850)
|
|
||||||
|
|
||||||
weather = Yr(location_name="Norway/Oslo/Oslo/Oslo", forecast_link="forecast_hour_by_hour")
|
|
||||||
|
|
||||||
embed = discord.Embed(title=":white_sun_small_cloud: Værmelding for Oslo :white_sun_small_cloud:")
|
|
||||||
embed.description = "*Værmeldingen levert til deg av Petter*"
|
|
||||||
embed.set_author(name=dev.name, icon_url=dev.display_avatar)
|
|
||||||
|
|
||||||
half_hour = False
|
|
||||||
klokkeslett = 0
|
|
||||||
for forecast in weather.forecast(str):
|
|
||||||
if half_hour:
|
|
||||||
half_hour = False
|
|
||||||
continue
|
|
||||||
|
|
||||||
data = json.loads(forecast)
|
|
||||||
temp = data["temperature"]
|
|
||||||
rain = data["precipitation"]
|
|
||||||
regn = rain["@value"]
|
|
||||||
temperatur = temp["@value"]
|
|
||||||
embed.add_field(name=f"kl {klokkeslett}", value=f"{temperatur}°C - {regn}mm")
|
|
||||||
klokkeslett += 1
|
|
||||||
half_hour = True
|
|
||||||
|
|
||||||
await channel.send("RISE AND SHINE GAMERS!", embed=embed)
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
|
||||||
await bot.add_cog(Vær(bot))
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import discord
|
|
||||||
import wikipedia
|
|
||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
from cogs.utils import embed_templates
|
|
||||||
|
|
||||||
|
|
||||||
class Wiki(commands.Cog):
|
|
||||||
@commands.command()
|
|
||||||
async def wiki_search(self, ctx, *search_term):
|
|
||||||
search_term = " ".join(search_term)
|
|
||||||
|
|
||||||
search = wikipedia.search(search_term)
|
|
||||||
search = search[0]
|
|
||||||
search = wikipedia.page(search)
|
|
||||||
summary = wikipedia.summary(search_term, sentences=5)
|
|
||||||
url = search.url
|
|
||||||
|
|
||||||
embed = discord.Embed(title=f"Wikipediasøk for {search_term}", url=url)
|
|
||||||
embed.add_field(name=search_term, value=summary)
|
|
||||||
|
|
||||||
embed_templates.default_footer(ctx, embed)
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
|
||||||
await bot.add_cog(Wiki(bot))
|
|
||||||
@@ -42,7 +42,7 @@ class DevTools(commands.Cog):
|
|||||||
name="custommsg",
|
name="custommsg",
|
||||||
description="Send en melding til hvilken som helst kanal på hvilken som helst server botten er i",
|
description="Send en melding til hvilken som helst kanal på hvilken som helst server botten er i",
|
||||||
)
|
)
|
||||||
async def custommsg(self, ctx: commands.Context, channel: int, *text: tuple[str]):
|
async def custommsg(self, ctx: commands.Context, channel: int, text: str):
|
||||||
"""
|
"""
|
||||||
Send a message to any channel on any server the bot is in
|
Send a message to any channel on any server the bot is in
|
||||||
|
|
||||||
@@ -50,17 +50,16 @@ class DevTools(commands.Cog):
|
|||||||
----------
|
----------
|
||||||
ctx (commands.Context): Context object
|
ctx (commands.Context): Context object
|
||||||
channel (int): The channel ID to send the message to
|
channel (int): The channel ID to send the message to
|
||||||
text (tuple[str]): The text to send
|
text (str): The text to send
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Send message to the requested channel
|
# Send message to the requested channel
|
||||||
channel = self.bot.get_channel(channel)
|
channel = self.bot.get_channel(channel)
|
||||||
custommessage = " ".join(text)
|
await channel.send(text)
|
||||||
await channel.send(custommessage)
|
|
||||||
|
|
||||||
# Send confirmation message to the invoker
|
# Send confirmation message to the invoker
|
||||||
embed = discord.Embed(color=ctx.me.color)
|
embed = discord.Embed(color=ctx.me.color)
|
||||||
embed.add_field(name="Sent", value=custommessage)
|
embed.add_field(name="Sent", value=text)
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
||||||
|
|
||||||
@commands.is_owner()
|
@commands.is_owner()
|
||||||
@@ -115,7 +114,7 @@ class DevTools(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
guild = await self.bot.fetch_guild(guild_id)
|
guild = await self.bot.fetch_guild(guild_id)
|
||||||
except discord.errors.Forbidden:
|
except discord.errors.Forbidden:
|
||||||
embed = embed_templates.error_fatal(ctx, text="Bot is not a member of this guild")
|
embed = embed_templates.error_warning("Bot is not a member of this guild")
|
||||||
return await ctx.reply(embed=embed)
|
return await ctx.reply(embed=embed)
|
||||||
|
|
||||||
# Send confirmation message for leaving
|
# Send confirmation message for leaving
|
||||||
@@ -187,14 +186,16 @@ class DevTools(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
for file in listdir("./src/cogs"):
|
for file in listdir("./src/cogs"):
|
||||||
if file.endswith(".py"):
|
if not file.endswith(".py"):
|
||||||
name = file[:-3]
|
continue
|
||||||
if name == cog:
|
|
||||||
await self.bot.unload_extension(f"cogs.{name}")
|
|
||||||
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been disabled")
|
|
||||||
return await ctx.reply(embed=embed)
|
|
||||||
|
|
||||||
embed = embed_templates.error_fatal(ctx, text=f"{cog} does not exist")
|
name = file[:-3]
|
||||||
|
if name == cog:
|
||||||
|
await self.bot.unload_extension(f"cogs.{name}")
|
||||||
|
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been disabled")
|
||||||
|
return await ctx.reply(embed=embed)
|
||||||
|
|
||||||
|
embed = embed_templates.error_warning(f"{cog} does not exist")
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
||||||
|
|
||||||
@cogs.command(name="load", description="Aktiver en cog")
|
@cogs.command(name="load", description="Aktiver en cog")
|
||||||
@@ -209,14 +210,16 @@ class DevTools(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
for file in listdir("./src/cogs"):
|
for file in listdir("./src/cogs"):
|
||||||
if file.endswith(".py"):
|
if not file.endswith(".py"):
|
||||||
name = file[:-3]
|
continue
|
||||||
if name == cog:
|
|
||||||
await self.bot.load_extension(f"cogs.{name}")
|
|
||||||
embed = discord.Embed(color=ctx.me.color, description=f"{cog} loaded")
|
|
||||||
return await ctx.reply(embed=embed)
|
|
||||||
|
|
||||||
embed = embed_templates.error_fatal(ctx, text=f"{cog} does not exist")
|
name = file[:-3]
|
||||||
|
if name == cog:
|
||||||
|
await self.bot.load_extension(f"cogs.{name}")
|
||||||
|
embed = discord.Embed(color=ctx.me.color, description=f"{cog} loaded")
|
||||||
|
return await ctx.reply(embed=embed)
|
||||||
|
|
||||||
|
embed = embed_templates.error_warning(f"{cog} does not exist")
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
||||||
|
|
||||||
@cogs.command(name="reload", description="Last inn en cog på nytt")
|
@cogs.command(name="reload", description="Last inn en cog på nytt")
|
||||||
@@ -229,15 +232,18 @@ class DevTools(commands.Cog):
|
|||||||
ctx (commands.Context): Context object
|
ctx (commands.Context): Context object
|
||||||
cog (str): The name of the cog to reload
|
cog (str): The name of the cog to reload
|
||||||
"""
|
"""
|
||||||
for file in listdir("./src/cogs"):
|
|
||||||
if file.endswith(".py"):
|
|
||||||
name = file[:-3]
|
|
||||||
if name == cog:
|
|
||||||
await self.bot.reload_extension(f"cogs.{name}")
|
|
||||||
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been reloaded")
|
|
||||||
return await ctx.reply(embed=embed)
|
|
||||||
|
|
||||||
embed = embed_templates.error_fatal(ctx, text=f"{cog} does not exist")
|
for file in listdir("./src/cogs"):
|
||||||
|
if not file.endswith(".py"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
name = file[:-3]
|
||||||
|
if name == cog:
|
||||||
|
await self.bot.reload_extension(f"cogs.{name}")
|
||||||
|
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been reloaded")
|
||||||
|
return await ctx.reply(embed=embed)
|
||||||
|
|
||||||
|
embed = embed_templates.error_warning(f"{cog} does not exist")
|
||||||
await ctx.reply(embed=embed)
|
await ctx.reply(embed=embed)
|
||||||
|
|
||||||
@cogs.command(name="reloadunloaded", description="Last inn alle cogs på nytt, selv de som ikke er lastet inn")
|
@cogs.command(name="reloadunloaded", description="Last inn alle cogs på nytt, selv de som ikke er lastet inn")
|
||||||
|
|||||||
@@ -76,50 +76,50 @@ class Errors(commands.Cog):
|
|||||||
if hasattr(ctx.command, "on_error"):
|
if hasattr(ctx.command, "on_error"):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignored errors
|
|
||||||
ignored = commands.CommandNotFound
|
|
||||||
error = getattr(error, "original", error)
|
error = getattr(error, "original", error)
|
||||||
if isinstance(error, ignored):
|
|
||||||
|
# Ignored errors
|
||||||
|
ignored_errors = (commands.CommandNotFound, commands.DisabledCommand, commands.CheckFailure)
|
||||||
|
if isinstance(error, ignored_errors):
|
||||||
|
self.bot.logger.info("Ignored error", error)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Errors that should prompt a help message
|
||||||
send_help = (commands.MissingRequiredArgument, commands.TooManyArguments, commands.BadArgument)
|
send_help = (commands.MissingRequiredArgument, commands.TooManyArguments, commands.BadArgument)
|
||||||
if isinstance(error, send_help):
|
if isinstance(error, send_help):
|
||||||
self.bot.get_command(f"{ctx.command}").reset_cooldown(ctx)
|
|
||||||
return await ctx.send_help(ctx.command)
|
return await ctx.send_help(ctx.command)
|
||||||
|
|
||||||
elif isinstance(error, commands.BotMissingPermissions):
|
elif isinstance(error, commands.BotMissingPermissions):
|
||||||
permissions = ", ".join(error.missing_permissions)
|
embed = self.error_missing_perms(error.missing_permissions, "Jeg")
|
||||||
text = "Jeg mangler følgende tillatelser:\n\n" + f"```\n{permissions}\n```"
|
|
||||||
return await ctx.reply(text)
|
|
||||||
|
|
||||||
elif isinstance(error, commands.MissingPermissions):
|
elif isinstance(error, commands.MissingPermissions):
|
||||||
permissions = ", ".join(error.missing_permissions)
|
embed = self.error_missing_perms(error.missing_permissions, "Du")
|
||||||
text = "Du mangler følgende tillatelser\n\n" + f"```\n{permissions}\n```"
|
|
||||||
return await ctx.reply(text)
|
|
||||||
|
|
||||||
elif isinstance(error, commands.NotOwner):
|
elif isinstance(error, commands.NotOwner):
|
||||||
text = "Bare boteieren kan gjøre dette"
|
embed = embed_templates.error_warning("Bare boteieren kan gjøre dette")
|
||||||
return await ctx.reply(text)
|
|
||||||
|
|
||||||
elif isinstance(error, commands.CommandOnCooldown):
|
elif isinstance(error, commands.CommandOnCooldown):
|
||||||
text = "Kommandoen har nettopp blitt brukt\n" + f"Prøv igjen om `{error.retry_after:.1f}` sekunder."
|
embed = embed_templates.error_warning(
|
||||||
return await ctx.reply(text)
|
f"Kommandoen har nettopp blitt brukt\nPrøv igjen om `{error.retry_after:.1f}` sekunder.",
|
||||||
|
)
|
||||||
elif isinstance(error, commands.NoPrivateMessage):
|
elif isinstance(error, commands.NoPrivateMessage):
|
||||||
try:
|
embed = embed_templates.error_warning("Denne kommandoen kan bare utføres it servere")
|
||||||
text = "Denne kommandoen kan bare utføres i servere"
|
else:
|
||||||
return await ctx.reply(text)
|
embed = embed_templates.error_fatal("En ukjent feil oppstod!")
|
||||||
except discord.errors.Forbidden: # Thrown if bot is blocked by the user or if the user has closed their DMs
|
f = discord.File("./src/assets/edb.png")
|
||||||
self.bot.logger.info("DM Blocked!")
|
embed.set_image(url="attachment://edb.png")
|
||||||
|
|
||||||
elif isinstance(error, commands.DisabledCommand):
|
# Yeah, this fucking sucks but I want my beautiful edb picture :_;
|
||||||
pass
|
try:
|
||||||
|
f
|
||||||
|
except NameError:
|
||||||
|
f = None
|
||||||
|
|
||||||
elif isinstance(error, commands.CheckFailure):
|
try:
|
||||||
return
|
await ctx.reply(embed=embed, file=f)
|
||||||
|
except discord.errors.Forbidden:
|
||||||
embed = embed_templates.error_fatal(ctx, text="En ukjent feil oppstod!")
|
self.bot.logger.warning("Could not send error message to user")
|
||||||
await ctx.reply(embed=embed)
|
except app_commands.BotMissingPermissions:
|
||||||
|
await ctx.reply(
|
||||||
|
"Jeg mangler `embed_links` og/eller `attach_files` tillatelsen(e). Ingenting funker uten de"
|
||||||
|
)
|
||||||
|
|
||||||
# Log full exception to file
|
# Log full exception to file
|
||||||
self.bot.logger.error("".join(traceback.format_exception(type(error), error, error.__traceback__)))
|
self.bot.logger.error("".join(traceback.format_exception(type(error), error, error.__traceback__)))
|
||||||
@@ -134,45 +134,60 @@ class Errors(commands.Cog):
|
|||||||
error (app_commands.AppCommandError): Eror context object
|
error (app_commands.AppCommandError): Eror context object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO: add support for defered interactions
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Log command usage, just in case
|
# Log command usage, just in case
|
||||||
await self.on_app_command_completion(interaction, interaction.command)
|
await self.on_app_command_completion(interaction, interaction.command)
|
||||||
|
|
||||||
if isinstance(error, app_commands.BotMissingPermissions):
|
if isinstance(error, app_commands.BotMissingPermissions):
|
||||||
permissions = ", ".join(error.missing_permissions)
|
embed = self.error_missing_perms(error.missing_permissions, "Jeg")
|
||||||
embed = embed_templates.error_warning(
|
|
||||||
interaction, text="Jeg mangler følgende tillatelser:\n\n" + f"```\n{permissions}\n```"
|
|
||||||
)
|
|
||||||
|
|
||||||
elif isinstance(error, app_commands.MissingPermissions):
|
elif isinstance(error, app_commands.MissingPermissions):
|
||||||
permissions = ", ".join(error.missing_permissions)
|
embed = self.error_missing_perms(error.missing_permissions, "Du")
|
||||||
embed = embed_templates.error_warning(
|
|
||||||
interaction, text="Du mangler følgende tillatelser\n\n" + f"```\n{permissions}\n```"
|
|
||||||
)
|
|
||||||
|
|
||||||
# TODO: figure this shit out. app_commands does not support this check
|
|
||||||
# elif isinstance(error, app_commands.NotOwner):
|
|
||||||
# embed = embed_templates.error_fatal(interaction, text='Bare boteieren kan gjøre dette')
|
|
||||||
# return await interaction.response.send_message(embed=embed)
|
|
||||||
|
|
||||||
elif isinstance(error, app_commands.CommandOnCooldown):
|
elif isinstance(error, app_commands.CommandOnCooldown):
|
||||||
embed = embed_templates.error_warning(
|
embed = embed_templates.error_warning(
|
||||||
interaction,
|
f"Kommandoen har nettopp blitt brukt\nPrøv igjen om `{error.retry_after:.1f}` sekunder."
|
||||||
text="Kommandoen har nettopp blitt brukt\n" + f"Prøv igjen om `{error.retry_after:.1f}` sekunder.",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
embed = embed_templates.error_fatal(interaction, text="En ukjent feil oppstod!")
|
embed = embed_templates.error_fatal("En ukjent feil oppstod!")
|
||||||
|
f = discord.File("./src/assets/edb.png")
|
||||||
|
embed.set_image(url="attachment://edb.png")
|
||||||
|
|
||||||
if interaction.response.is_done():
|
# Yeah, this fucking sucks but I want my beautiful edb picture :_;
|
||||||
await interaction.followup.send(embed=embed)
|
try:
|
||||||
else:
|
f
|
||||||
await interaction.response.send_message(embed=embed)
|
except NameError:
|
||||||
|
f = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
await interaction.followup.send(embed=embed, file=f)
|
||||||
|
except discord.errors.Forbidden:
|
||||||
|
self.bot.logger.warning("Could not send error message to user")
|
||||||
|
except app_commands.BotMissingPermissions:
|
||||||
|
await interaction.followup.send(
|
||||||
|
"Jeg mangler `embed_links` og/eller `attach_files` tillatelsen(e). Ingenting funker uten de"
|
||||||
|
)
|
||||||
|
|
||||||
# Log full exception to file
|
# Log full exception to file
|
||||||
self.bot.logger.error("".join(traceback.format_exception(type(error), error, error.__traceback__)))
|
self.bot.logger.error("".join(traceback.format_exception(type(error), error, error.__traceback__)))
|
||||||
|
|
||||||
|
def error_missing_perms(self, permissions: list[str], client: str) -> discord.Embed:
|
||||||
|
"""
|
||||||
|
Missing permissions embed template.
|
||||||
|
Since it only applies to this cog it has been put here instead of the util file
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
permissions (list[str]): The missing permissions
|
||||||
|
client (str): Who is missing the permissions? Typically "Du" (user) or "Jeg" (bot client)
|
||||||
|
|
||||||
|
Returns
|
||||||
|
----------
|
||||||
|
(discord.Embed): The final embed
|
||||||
|
"""
|
||||||
|
|
||||||
|
permissions = ", ".join(permissions)
|
||||||
|
return embed_templates.error_warning(f"{client} mangler følgende tillatelser\n\n```{permissions}\n```")
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
async def setup(bot: commands.Bot):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -12,11 +12,9 @@ class FunReplies(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
bot (commands.Bot): The bot instance
|
bot (commands.Bot): The bot instance. In this case not used
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
# Cooldowns for trigger words
|
# Cooldowns for trigger words
|
||||||
#
|
#
|
||||||
# This is kind of a shitty way to do it but I'm too lazy to implement anything good right now
|
# This is kind of a shitty way to do it but I'm too lazy to implement anything good right now
|
||||||
@@ -32,6 +30,7 @@ class FunReplies(commands.Cog):
|
|||||||
"borgerlønn": initial_datetime,
|
"borgerlønn": initial_datetime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_message")
|
||||||
async def reply_to_triggers(self, message: discord.Message):
|
async def reply_to_triggers(self, message: discord.Message):
|
||||||
"""
|
"""
|
||||||
Replies to messages that trigger certain key words/phrases
|
Replies to messages that trigger certain key words/phrases
|
||||||
@@ -44,32 +43,51 @@ class FunReplies(commands.Cog):
|
|||||||
if message.author.bot:
|
if message.author.bot:
|
||||||
return
|
return
|
||||||
|
|
||||||
message_content = message.content.lower()
|
# TODO: add ability to disable single triggers?
|
||||||
|
# Auto assign cooldown_key?
|
||||||
|
triggers = [
|
||||||
|
(r"(^|\W)borgerlønn(\W|$)", "@ sivert DE SNAKKER OM BORGERLØNN", "borgerlønn"),
|
||||||
|
(r"(^|\W)olof palme(\W|$)", "Jeg vet hvem som drepte Olof Palme 👀", "olof palme"),
|
||||||
|
(r"(^|\W)+ye+et($|\W)+", "<:Nei:826593267642662912>", "yeet"),
|
||||||
|
(r"(^|\W)skal? aldri drikke?[\w\s]*igjen($|\W)+", ":billed_cap:", "drikke"),
|
||||||
|
(r"(^|\W)(jeg?|(e|æ)(g|j)?|i) er? sivert arntzen($|\W)+", "Nei, jeg er Sivert Arntzen", "sivert"),
|
||||||
|
]
|
||||||
|
|
||||||
if re.search(r"(^|\W)borgerlønn(\W|$)", message_content, flags=re.IGNORECASE):
|
for trigger in triggers:
|
||||||
if (datetime.now() - self.previous_invokations["borgerlønn"]).seconds > self.cooldown_seconds:
|
regex, reply, cooldown_key = trigger
|
||||||
await message.reply("@ sivert DE SNAKKER OM BORGERLØNN")
|
if await self.trigger(
|
||||||
self.previous_invokations["borgerlønn"] = datetime.now()
|
message=message, regex_match=regex, reply=reply, cooldown_key=cooldown_key, regex_flags=re.IGNORECASE
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
elif re.search(r"(^|\W)olof palme(\W|$)", message_content, flags=re.IGNORECASE):
|
async def trigger(
|
||||||
if (datetime.now() - self.previous_invokations["olof palme"]).seconds > self.cooldown_seconds:
|
self, message: discord.Message, regex_match: str, reply: str, cooldown_key: str, regex_flags=None
|
||||||
await message.reply("Jeg vet hvem som drepte Olof Palme 👀")
|
) -> bool:
|
||||||
self.previous_invokations["olof palme"] = datetime.now()
|
"""
|
||||||
|
Add a trigger to the bot
|
||||||
|
|
||||||
elif re.search(r"(^|\W)+ye+et($|\W)+", message_content, flags=re.IGNORECASE):
|
Parameters
|
||||||
if (datetime.now() - self.previous_invokations["yeet"]).seconds > self.cooldown_seconds:
|
----------
|
||||||
await message.reply("<:Nei:826593267642662912>")
|
message (discord.Message): The message object to check for triggers
|
||||||
self.previous_invokations["yeet"] = datetime.now()
|
regex_match (str): The regex pattern to match
|
||||||
|
reply (str): The reply to send
|
||||||
|
cooldown_key (str): The key to use for cooldown tracking
|
||||||
|
regex_flags (int): The regex flags to use
|
||||||
|
|
||||||
elif re.search(r"(^|\W)skal? aldri drikke?[\w\s]*igjen($|\W)+", message_content, flags=re.IGNORECASE):
|
Returns
|
||||||
if (datetime.now() - self.previous_invokations["drikke"]).seconds > self.cooldown_seconds:
|
----------
|
||||||
await message.reply(":billed_cap:")
|
bool: Whether or not the reply was triggered
|
||||||
self.previous_invokations["drikke"] = datetime.now()
|
"""
|
||||||
|
|
||||||
elif re.search(r"(^|\W)(jeg?|(e|æ)(g|j)?|i) er? sivert arntzen($|\W)+", message_content, flags=re.IGNORECASE):
|
if (datetime.now() - self.previous_invokations[cooldown_key]).seconds < self.cooldown_seconds:
|
||||||
if (datetime.now() - self.previous_invokations["sivert"]).seconds > self.cooldown_seconds:
|
return False
|
||||||
await message.reply("Nei, jeg er Sivert Arntzen!")
|
|
||||||
self.previous_invokations["sivert"] = datetime.now()
|
if re.search(regex_match, message.content, flags=regex_flags):
|
||||||
|
await message.reply(reply)
|
||||||
|
self.previous_invokations[cooldown_key] = datetime.now()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
async def setup(bot: commands.Bot):
|
||||||
@@ -81,5 +99,4 @@ async def setup(bot: commands.Bot):
|
|||||||
bot (commands.Bot): Bot instance
|
bot (commands.Bot): Bot instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot.add_listener(FunReplies(bot).reply_to_triggers, "on_message")
|
|
||||||
await bot.add_cog(FunReplies(bot))
|
await bot.add_cog(FunReplies(bot))
|
||||||
@@ -2,12 +2,11 @@ import discord
|
|||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from cogs.utils import discord_utils
|
|
||||||
from cogs.utils import embed_templates
|
from cogs.utils import embed_templates
|
||||||
|
|
||||||
|
|
||||||
class Gullkorn(commands.Cog):
|
class Gullkorn(commands.Cog):
|
||||||
"""Stand alone cog for handling whitelisting of Discord users on the Minecraft server"""
|
"""Tracks stats for gullkorn"""
|
||||||
|
|
||||||
def __init__(self, bot: commands.Bot):
|
def __init__(self, bot: commands.Bot):
|
||||||
"""
|
"""
|
||||||
@@ -21,7 +20,9 @@ class Gullkorn(commands.Cog):
|
|||||||
self.init_db()
|
self.init_db()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the gullkorn cog to work."""
|
"""
|
||||||
|
Create the necessary tables for the gullkorn cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -49,7 +50,7 @@ class Gullkorn(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
def __construct_data_string(self, data: list[tuple[int, int, int]]) -> str:
|
def construct_data_string(self, data: list[tuple[int, int, int]]) -> str:
|
||||||
"""
|
"""
|
||||||
Constructs a formatted string displaying lists of gullkorn data
|
Constructs a formatted string displaying lists of gullkorn data
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ class Gullkorn(commands.Cog):
|
|||||||
|
|
||||||
return formatted_string
|
return formatted_string
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_message")
|
||||||
async def gullkorn_listener(self, message: discord.Message):
|
async def gullkorn_listener(self, message: discord.Message):
|
||||||
"""
|
"""
|
||||||
Listens for messages in the gullkorn channel and updates the database accordingly
|
Listens for messages in the gullkorn channel and updates the database accordingly
|
||||||
@@ -122,8 +124,9 @@ class Gullkorn(commands.Cog):
|
|||||||
bruker (discord.Member, optional): Discord user to fetch stats for. Defaults to None.
|
bruker (discord.Member, optional): Discord user to fetch stats for. Defaults to None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# This looks kinda ugly, ngl
|
||||||
if bruker:
|
if bruker:
|
||||||
result = self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM gullkorn
|
FROM gullkorn
|
||||||
@@ -131,25 +134,24 @@ class Gullkorn(commands.Cog):
|
|||||||
""",
|
""",
|
||||||
(bruker.id,),
|
(bruker.id,),
|
||||||
)
|
)
|
||||||
|
|
||||||
result = self.cursor.fetchone()
|
result = self.cursor.fetchone()
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, text="Ingen data om denne brukeren funnet"),
|
embed=embed_templates.error_warning("Ingen data om denne brukeren funnet"),
|
||||||
ephemeral=False,
|
ephemeral=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title=f"Gullkornstatistikk for `{bruker.name}`",
|
title=f"Gullkornstatistikk for `{bruker.name}`",
|
||||||
color=discord_utils.get_color(bruker),
|
color=bruker.color,
|
||||||
)
|
)
|
||||||
embed.set_thumbnail(url=bruker.avatar)
|
embed.set_thumbnail(url=bruker.avatar)
|
||||||
embed.add_field(name="Antall gullkorn", value=result[1])
|
embed.add_field(name="Antall gullkorn", value=result[1])
|
||||||
embed.add_field(name="Antall gullkorn postet", value=result[2])
|
embed.add_field(name="Antall gullkorn postet", value=result[2])
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=False)
|
return await interaction.response.send_message(embed=embed, ephemeral=False)
|
||||||
|
|
||||||
summary = self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT SUM(citations_posted)
|
SELECT SUM(citations_posted)
|
||||||
FROM gullkorn;
|
FROM gullkorn;
|
||||||
@@ -157,7 +159,7 @@ class Gullkorn(commands.Cog):
|
|||||||
)
|
)
|
||||||
summary = self.cursor.fetchone()
|
summary = self.cursor.fetchone()
|
||||||
|
|
||||||
most_cited = self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM most_cited
|
FROM most_cited
|
||||||
@@ -166,7 +168,7 @@ class Gullkorn(commands.Cog):
|
|||||||
)
|
)
|
||||||
most_cited = self.cursor.fetchall()
|
most_cited = self.cursor.fetchall()
|
||||||
|
|
||||||
citations_posted = self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM most_frequent_posters
|
FROM most_frequent_posters
|
||||||
@@ -175,13 +177,13 @@ class Gullkorn(commands.Cog):
|
|||||||
)
|
)
|
||||||
citations_posted = self.cursor.fetchall()
|
citations_posted = self.cursor.fetchall()
|
||||||
|
|
||||||
most_cited_string = self.__construct_data_string(most_cited)
|
most_cited_string = self.construct_data_string(most_cited)
|
||||||
citations_posted_string = self.__construct_data_string(citations_posted)
|
citations_posted_string = self.construct_data_string(citations_posted)
|
||||||
|
|
||||||
gullkorn_first_msg = "https://canary.discord.com/channels/747542543750660178/865970753748074576/1034587913285025912" # noqa: E501
|
GULLKORN_FIRST_MSG = "https://canary.discord.com/channels/747542543750660178/865970753748074576/1034587913285025912" # noqa: E501
|
||||||
|
|
||||||
embed = discord.Embed(title="Gullkornstatistikk for serveren")
|
embed = discord.Embed(title="Gullkornstatistikk for serveren")
|
||||||
embed.description = f"Antall gullkorn siden [denne meldingen]({gullkorn_first_msg}): *{summary[0]}*"
|
embed.description = f"Antall gullkorn siden [denne meldingen]({GULLKORN_FIRST_MSG}): *{summary[0]}*"
|
||||||
embed.add_field(name="Mest sitert", value=most_cited_string, inline=False)
|
embed.add_field(name="Mest sitert", value=most_cited_string, inline=False)
|
||||||
embed.add_field(name="Postet mest", value=citations_posted_string, inline=False)
|
embed.add_field(name="Postet mest", value=citations_posted_string, inline=False)
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=False)
|
await interaction.response.send_message(embed=embed, ephemeral=False)
|
||||||
@@ -196,5 +198,4 @@ async def setup(bot: commands.Bot):
|
|||||||
bot (commands.Bot): Bot instance
|
bot (commands.Bot): Bot instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot.add_listener(Gullkorn(bot).gullkorn_listener, "on_message")
|
|
||||||
await bot.add_cog(Gullkorn(bot))
|
await bot.add_cog(Gullkorn(bot))
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
from os import remove
|
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
@@ -24,7 +22,7 @@ class Info(commands.Cog):
|
|||||||
guild_group = app_commands.Group(name="guild", description="Se ting om serveren")
|
guild_group = app_commands.Group(name="guild", description="Se ting om serveren")
|
||||||
user_group = app_commands.Group(name="bruker", description="Se ting om brukeren")
|
user_group = app_commands.Group(name="bruker", description="Se ting om brukeren")
|
||||||
|
|
||||||
def __construct_role_string(self, roles: list[discord.Role]) -> str:
|
def construct_role_string(self, roles: list[discord.Role]) -> str:
|
||||||
"""
|
"""
|
||||||
Puts all roles except @everyone into a list and joins them to a string
|
Puts all roles except @everyone into a list and joins them to a string
|
||||||
|
|
||||||
@@ -45,7 +43,7 @@ class Info(commands.Cog):
|
|||||||
|
|
||||||
return ", ".join(roles)
|
return ", ".join(roles)
|
||||||
|
|
||||||
def __construct_booster_string(self, interaction: discord.Interaction, join_method: callable = ", ".join) -> str:
|
def construct_booster_string(self, interaction: discord.Interaction, join_method: callable = ", ".join) -> str:
|
||||||
"""
|
"""
|
||||||
Joins all boosters into a string
|
Joins all boosters into a string
|
||||||
|
|
||||||
@@ -62,20 +60,17 @@ class Info(commands.Cog):
|
|||||||
if not interaction.guild.premium_subscribers:
|
if not interaction.guild.premium_subscribers:
|
||||||
return "**Ingen boostere**"
|
return "**Ingen boostere**"
|
||||||
|
|
||||||
# Sort boosters by boost date and put name#discrim as well as date of boost into a list of stirngs
|
# Sort boosters by boost date and put name as well as date of boost into a list of stirngs
|
||||||
# Use map instead of list comprehension because I can divide the code into 2 lines
|
boosters = [
|
||||||
boosters = list(
|
f"* {b.name} - {discord.utils.format_dt(b.premium_since)}"
|
||||||
map(
|
for b in sorted(interaction.guild.premium_subscribers, key=lambda m: m.premium_since)
|
||||||
lambda b: f"* {b.name} - {discord.utils.format_dt(b.premium_since)}",
|
]
|
||||||
sorted(interaction.guild.premium_subscribers, key=lambda m: m.premium_since),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return join_method(boosters)
|
return join_method(boosters)
|
||||||
|
|
||||||
def __construct_member_string(self, members: list[discord.Member]) -> str:
|
def construct_member_string(self, members: list[discord.Member]) -> str:
|
||||||
"""
|
"""
|
||||||
Joins all name#discrim into a string
|
Joins all names into a string
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@@ -93,31 +88,6 @@ class Info(commands.Cog):
|
|||||||
|
|
||||||
return ", ".join(members)
|
return ", ".join(members)
|
||||||
|
|
||||||
async def __send_as_txt_file(self, interaction: discord.Interaction, content: str, file_path: str):
|
|
||||||
"""
|
|
||||||
Sends a string as a txt file and deletes the file afterwards
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
content (str): String that's too long to send
|
|
||||||
file_path (str): Path to file
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Create file
|
|
||||||
with open(file_path, "w", encoding="utf-8") as file:
|
|
||||||
file.write(content)
|
|
||||||
|
|
||||||
# Send file
|
|
||||||
txt_file = discord.File(file_path)
|
|
||||||
await interaction.response.send_message(file=txt_file)
|
|
||||||
|
|
||||||
# Delete file
|
|
||||||
try:
|
|
||||||
remove(file_path)
|
|
||||||
except (FileNotFoundError, OSError, PermissionError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@app_commands.guild_only()
|
@app_commands.guild_only()
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True)
|
||||||
@app_commands.checks.cooldown(1, 2)
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@@ -156,7 +126,7 @@ class Info(commands.Cog):
|
|||||||
offline_members += 1
|
offline_members += 1
|
||||||
|
|
||||||
# Roles
|
# Roles
|
||||||
roles = self.__construct_role_string(interaction.guild.roles)
|
roles = self.construct_role_string(interaction.guild.roles)
|
||||||
roles = (
|
roles = (
|
||||||
roles
|
roles
|
||||||
if len(roles) < 1024
|
if len(roles) < 1024
|
||||||
@@ -164,7 +134,7 @@ class Info(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Boosts
|
# Boosts
|
||||||
boosters = self.__construct_booster_string(interaction, join_method="\n".join)
|
boosters = self.construct_booster_string(interaction, join_method="\n".join)
|
||||||
boosters = (
|
boosters = (
|
||||||
boosters
|
boosters
|
||||||
if len(boosters) < 1024
|
if len(boosters) < 1024
|
||||||
@@ -179,31 +149,35 @@ class Info(commands.Cog):
|
|||||||
|
|
||||||
# Features
|
# Features
|
||||||
features_string = ""
|
features_string = ""
|
||||||
if interaction.guild.features is not []:
|
if interaction.guild.features != []:
|
||||||
features = {
|
features = {
|
||||||
"ANIMATED_BANNER": "Animer serverbanner",
|
"ANIMATED_BANNER": "Animer serverbanner",
|
||||||
"ANIMATED_ICON": "Animert serverikon",
|
"ANIMATED_ICON": "Animert serverikon",
|
||||||
|
"APPLICATION_COMMAND_PERMISSIONS_V2": "Gamle slash command tillatelser",
|
||||||
|
"AUTO_MODERATION": "Automoderering",
|
||||||
"BANNER": "Serverbanner",
|
"BANNER": "Serverbanner",
|
||||||
"COMMERCE": "E-handel",
|
|
||||||
"COMMUNITY": "Samfunnsserver",
|
"COMMUNITY": "Samfunnsserver",
|
||||||
|
"CREATOR_MONETIZABLE_PROVISIONAL": "Betalingsmuligheter",
|
||||||
|
"CREATOR_STORE_PAGE": "Abonnomentsside",
|
||||||
|
"DEVELOPER_SUPPORT_SERVER": "Støtteserver for utviklere",
|
||||||
"DISCOVERABLE": "På utforsksiden",
|
"DISCOVERABLE": "På utforsksiden",
|
||||||
"FEATURABLE": "",
|
"FEATURABLE": "Fremhevbar",
|
||||||
|
"INVITES_DISABLED": "Invitasjoner deaktivert",
|
||||||
"INVITE_SPLASH": "Invitasjonsbilde",
|
"INVITE_SPLASH": "Invitasjonsbilde",
|
||||||
"MEMBER_VERIFICATION_GATE_ENABLED": "Medlemsverifisering",
|
"MEMBER_VERIFICATION_GATE_ENABLED": "Medlemsverifisering",
|
||||||
"MONETIZATION_ENABLED": "Betalingskanal",
|
|
||||||
"MORE_EMOJI": "Flere emoji",
|
|
||||||
"MORE_STICKERS": "Flere stickers",
|
"MORE_STICKERS": "Flere stickers",
|
||||||
"NEWS": "Nyhetskanaler",
|
"NEWS": "Nyhetskanaler",
|
||||||
"PARTNERED": "Partner",
|
"PARTNERED": "Partner",
|
||||||
"PREVIEW_ENABLED": "Forhåndsvisning",
|
"PREVIEW_ENABLED": "Forhåndsvisning",
|
||||||
"PRIVATE_THREADS": "Private tråder",
|
"RAID_ALERTS_DISABLED": "Raidvarsler deaktivert",
|
||||||
"ROLE_ICONS": "Rolleikon",
|
"ROLE_ICONS": "Rolleikon",
|
||||||
|
"ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE": "Abonomentsroller tilgjengelig",
|
||||||
|
"ROLE_SUBSCRIPTIONS_ENABLED": "Abonomentsroller aktivert",
|
||||||
"TICKETED_EVENTS_ENABLED": "Arrangmenter med billettsalg",
|
"TICKETED_EVENTS_ENABLED": "Arrangmenter med billettsalg",
|
||||||
"VANITY_URL": "Egendefinert URL",
|
"VANITY_URL": "Egendefinert URL",
|
||||||
"VERIFIED": "Verifisert",
|
"VERIFIED": "Verifisert",
|
||||||
"VIP_REGIONS": "Høyere bitrate i stemmekanaler",
|
"VIP_REGIONS": "Høyere bitrate i stemmekanaler",
|
||||||
"WELCOME_SCREEN_ENABLED": "Velkomstskjerm",
|
"WELCOME_SCREEN_ENABLED": "Velkomstskjerm",
|
||||||
"INVITES_DISABLED": "Invitasjoner deaktivert",
|
|
||||||
}
|
}
|
||||||
for feature in interaction.guild.features:
|
for feature in interaction.guild.features:
|
||||||
if translation := features.get(feature):
|
if translation := features.get(feature):
|
||||||
@@ -232,7 +206,8 @@ class Info(commands.Cog):
|
|||||||
description=f"* **Verifiseringskrav:** {verification}\n"
|
description=f"* **Verifiseringskrav:** {verification}\n"
|
||||||
+ f"* **Innholdsfilter:** {content}\n"
|
+ f"* **Innholdsfilter:** {content}\n"
|
||||||
+ f"* **Boost Tier:** {interaction.guild.premium_tier}\n"
|
+ f"* **Boost Tier:** {interaction.guild.premium_tier}\n"
|
||||||
+ f"* **Emoji:** {len(interaction.guild.emojis)}",
|
+ f"* **Emoji:** {len(interaction.guild.emojis)}\n"
|
||||||
|
+ f"* **Stickers:** {len(interaction.guild.stickers)}\n",
|
||||||
)
|
)
|
||||||
embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon)
|
embed.set_author(name=interaction.guild.name, icon_url=interaction.guild.icon)
|
||||||
embed.set_thumbnail(url=interaction.guild.icon)
|
embed.set_thumbnail(url=interaction.guild.icon)
|
||||||
@@ -271,7 +246,7 @@ class Info(commands.Cog):
|
|||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.guild_only()
|
@app_commands.guild_only()
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 2)
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@guild_group.command(name="roller", description="Se rollene på serveren")
|
@guild_group.command(name="roller", description="Se rollene på serveren")
|
||||||
async def guild_roles(self, interaction: discord.Interaction):
|
async def guild_roles(self, interaction: discord.Interaction):
|
||||||
@@ -283,11 +258,11 @@ class Info(commands.Cog):
|
|||||||
interaction (discord.Interaction): Slash command context object
|
interaction (discord.Interaction): Slash command context object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
roles = self.__construct_role_string(interaction.guild.roles)
|
roles = self.construct_role_string(interaction.guild.roles)
|
||||||
|
|
||||||
# IF roles list is longer than 2048, create text file and send it
|
# IF roles list is longer than 2048, create text file and send it
|
||||||
if len(roles) > 2048:
|
if len(roles) > 2048:
|
||||||
self.__send_as_txt_file(interaction, f"./assets/temp/{interaction.guild.id}_roles.txt", roles)
|
await discord_utils.send_as_txt_file(interaction, roles, f"./assets/temp/{interaction.guild.id}_roles.txt")
|
||||||
else:
|
else:
|
||||||
embed = discord.Embed(color=interaction.guild.me.color, description=roles)
|
embed = discord.Embed(color=interaction.guild.me.color, description=roles)
|
||||||
embed.set_author(name=f"Roller ({len(interaction.guild.roles)})", icon_url=interaction.guild.icon)
|
embed.set_author(name=f"Roller ({len(interaction.guild.roles)})", icon_url=interaction.guild.icon)
|
||||||
@@ -308,10 +283,10 @@ class Info(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if len(interaction.guild.premium_subscribers) == 0:
|
if len(interaction.guild.premium_subscribers) == 0:
|
||||||
embed = embed_templates.error_warning(interaction, text="Serveren har ikke noen boosts :(")
|
embed = embed_templates.error_warning("Serveren har ikke noen boosts :(")
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
boosters = self.__construct_booster_string(interaction, "\n".join)
|
boosters = self.construct_booster_string(interaction, "\n".join)
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.guild.me.color, description=boosters)
|
embed = discord.Embed(color=interaction.guild.me.color, description=boosters)
|
||||||
embed.set_author(
|
embed.set_author(
|
||||||
@@ -352,7 +327,7 @@ class Info(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not interaction.guild.splash:
|
if not interaction.guild.splash:
|
||||||
embed = embed_templates.error_warning(interaction, text="Serveren har ikke en splash")
|
embed = embed_templates.error_warning("Serveren har ikke en splash")
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.guild.me.color, description=f"[Lenke]({interaction.guild.splash})")
|
embed = discord.Embed(color=interaction.guild.me.color, description=f"[Lenke]({interaction.guild.splash})")
|
||||||
@@ -374,7 +349,7 @@ class Info(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if not interaction.guild.banner:
|
if not interaction.guild.banner:
|
||||||
embed = embed_templates.error_warning(interaction, text="Serveren har ikke et banner :(")
|
embed = embed_templates.error_warning("Serveren har ikke et banner :(")
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.guild.me.color, description=f"[Lenke]({interaction.guild.banner})")
|
embed = discord.Embed(color=interaction.guild.me.color, description=f"[Lenke]({interaction.guild.banner})")
|
||||||
@@ -397,7 +372,7 @@ class Info(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if rolle.name == "@everyone":
|
if rolle.name == "@everyone":
|
||||||
embed = embed_templates.error_fatal(interaction, text="Skriv inn en annen rolle enn @everyone")
|
embed = embed_templates.error_warning("Skriv inn en annen rolle enn `@everyone`")
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
# Timestamp and days since creation
|
# Timestamp and days since creation
|
||||||
@@ -405,15 +380,13 @@ class Info(commands.Cog):
|
|||||||
since_created_days = (interaction.created_at - rolle.created_at).days
|
since_created_days = (interaction.created_at - rolle.created_at).days
|
||||||
|
|
||||||
# List of members with the role
|
# List of members with the role
|
||||||
members = self.__construct_member_string(rolle.members)
|
members = self.construct_member_string(rolle.members)
|
||||||
members = members if len(members) < 1024 else "For mange medlemmer for å vise her"
|
members = members if len(members) < 1024 else "For mange medlemmer for å vise her"
|
||||||
|
|
||||||
# List of permissions
|
# List of permissions
|
||||||
permissions = ", ".join([permission for permission, value in iter(rolle.permissions) if value is True])
|
permissions = ", ".join([permission for permission, value in iter(rolle.permissions) if value is True])
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(title=rolle.name, description=f"{rolle.mention}\n**ID:** {rolle.id}", color=rolle.color)
|
||||||
title=rolle.name, description=f"{rolle.mention}\n**ID:** {rolle.id}", color=discord_utils.get_color(rolle)
|
|
||||||
)
|
|
||||||
embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon)
|
embed.set_author(name=rolle.guild.name, icon_url=rolle.guild.icon)
|
||||||
embed.add_field(name="Fargekode", value=str(rolle.color))
|
embed.add_field(name="Fargekode", value=str(rolle.color))
|
||||||
embed.add_field(
|
embed.add_field(
|
||||||
@@ -443,7 +416,7 @@ class Info(commands.Cog):
|
|||||||
kanal (discord.TextChannel): Text channel to fetch information about
|
kanal (discord.TextChannel): Text channel to fetch information about
|
||||||
"""
|
"""
|
||||||
|
|
||||||
members = self.__construct_member_string(kanal.members)
|
members = self.construct_member_string(kanal.members)
|
||||||
if len(members) > 1024:
|
if len(members) > 1024:
|
||||||
members = "For mange for å vise her"
|
members = "For mange for å vise her"
|
||||||
|
|
||||||
@@ -501,9 +474,10 @@ class Info(commands.Cog):
|
|||||||
interaction (discord.Interaction): Slash command context object
|
interaction (discord.Interaction): Slash command context object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
roles = list(filter(lambda r: r.name != "@everyone", interaction.guild.roles))
|
roles = sorted(
|
||||||
roles = sorted(roles, key=lambda x: len(x.members), reverse=True)
|
[r for r in interaction.guild.roles if r.name != "@everyone"], key=lambda x: len(x.members), reverse=True
|
||||||
roles_formatted = list(map(lambda r: f"**#{r[0] + 1}** {r[1].mention} - {len(r[1].members)}", enumerate(roles)))
|
)
|
||||||
|
roles_formatted = [f"**#{i + 1}** {r.mention} - {len(r.members)}" for i, r in enumerate(roles)]
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(roles_formatted)
|
paginator = misc_utils.Paginator(roles_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
@@ -566,18 +540,14 @@ class Info(commands.Cog):
|
|||||||
# Sort members by creation date
|
# Sort members by creation date
|
||||||
members = sorted(interaction.guild.members, key=lambda m: m.created_at)
|
members = sorted(interaction.guild.members, key=lambda m: m.created_at)
|
||||||
|
|
||||||
# Create list of members with index, name#discriminator and creation date
|
# Create list of members with index, name and creation date
|
||||||
members_formatted = list(
|
members_formatted = [
|
||||||
map(
|
f"**#{i+1}** {m.name} - {discord.utils.format_dt(m.created_at, style='F')}" for i, m in enumerate(members)
|
||||||
lambda m: f'**#{(m[0] + 1)}** {m[1].name} - {discord.utils.format_dt(m[1].created_at, style="F")}', # noqa: E501
|
]
|
||||||
enumerate(members),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(members_formatted)
|
paginator = misc_utils.Paginator(members_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
|
|
||||||
# Send first page
|
|
||||||
embed = view.construct_embed(
|
embed = view.construct_embed(
|
||||||
discord.Embed(
|
discord.Embed(
|
||||||
color=interaction.guild.me.color, title="Eldste brukere på serveren basert på når de ble lagd"
|
color=interaction.guild.me.color, title="Eldste brukere på serveren basert på når de ble lagd"
|
||||||
@@ -604,12 +574,9 @@ class Info(commands.Cog):
|
|||||||
members = sorted(interaction.guild.members, key=lambda m: m.joined_at)
|
members = sorted(interaction.guild.members, key=lambda m: m.joined_at)
|
||||||
|
|
||||||
# Create list of members with index, name#discriminator and creation date
|
# Create list of members with index, name#discriminator and creation date
|
||||||
members_formatted = list(
|
members_formatted = [
|
||||||
map(
|
f"**#{i+1}** {m.name} - {discord.utils.format_dt(m.joined_at, style='F')}" for i, m in enumerate(members)
|
||||||
lambda m: f'**#{(m[0] + 1)}** {m[1].name} - {discord.utils.format_dt(m[1].joined_at, style="F")}', # noqa: E501
|
]
|
||||||
enumerate(members),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(members_formatted)
|
paginator = misc_utils.Paginator(members_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
@@ -666,7 +633,7 @@ class Info(commands.Cog):
|
|||||||
premium_since_days = (interaction.created_at - bruker.premium_since).days
|
premium_since_days = (interaction.created_at - bruker.premium_since).days
|
||||||
|
|
||||||
# Get user roles
|
# Get user roles
|
||||||
roles = self.__construct_role_string(bruker.roles)
|
roles = self.construct_role_string(bruker.roles)
|
||||||
|
|
||||||
roles = (
|
roles = (
|
||||||
roles
|
roles
|
||||||
@@ -682,9 +649,7 @@ class Info(commands.Cog):
|
|||||||
}
|
}
|
||||||
status = statuses[str(bruker.status)]
|
status = statuses[str(bruker.status)]
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(color=bruker.color, description=f"{bruker.mention}\nID: {bruker.id}\n{status}\n{app}")
|
||||||
color=discord_utils.get_color(bruker), description=f"{bruker.mention}\nID: {bruker.id}\n{status}\n{app}"
|
|
||||||
)
|
|
||||||
if bruker.display_name == bruker.name:
|
if bruker.display_name == bruker.name:
|
||||||
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
||||||
else:
|
else:
|
||||||
@@ -736,15 +701,15 @@ class Info(commands.Cog):
|
|||||||
if not bruker:
|
if not bruker:
|
||||||
bruker = interaction.user
|
bruker = interaction.user
|
||||||
|
|
||||||
roles = self.__construct_role_string(bruker.roles)
|
roles = self.construct_role_string(bruker.roles)
|
||||||
|
|
||||||
# If the list of roles is too long, send it as a file
|
# If the list of roles is too long, send it as a file
|
||||||
if len(roles) > 2048:
|
if len(roles) > 2048:
|
||||||
self.__send_as_txt_file(
|
await discord_utils.send_as_txt_file(
|
||||||
interaction, f"./assets/temp/{interaction.guild.id}_{interaction.user.id}_roles.txt", roles
|
interaction, roles, f"./assets/temp/{interaction.guild.id}_{interaction.user.id}_roles.txt"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
embed = discord.Embed(color=discord_utils.get_color(bruker), description=roles)
|
embed = discord.Embed(color=bruker.color, description=roles)
|
||||||
embed.set_author(name=f"Roller ({len(bruker.roles)})", icon_url=bruker.display_avatar)
|
embed.set_author(name=f"Roller ({len(bruker.roles)})", icon_url=bruker.display_avatar)
|
||||||
embed.set_footer(text=bruker.name, icon_url=bruker.display_avatar)
|
embed.set_footer(text=bruker.name, icon_url=bruker.display_avatar)
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
@@ -765,7 +730,7 @@ class Info(commands.Cog):
|
|||||||
if not bruker:
|
if not bruker:
|
||||||
bruker = interaction.user
|
bruker = interaction.user
|
||||||
|
|
||||||
embed = discord.Embed(color=discord_utils.get_color(bruker), description=f"[Lenke]({bruker.display_avatar})")
|
embed = discord.Embed(color=bruker.color, description=f"[Lenke]({bruker.display_avatar})")
|
||||||
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
embed.set_author(name=bruker.name, icon_url=bruker.display_avatar)
|
||||||
embed.set_image(url=bruker.display_avatar)
|
embed.set_image(url=bruker.display_avatar)
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ class MCWhitelist(commands.Cog):
|
|||||||
self.init_db()
|
self.init_db()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the mc_whitelist cog to work."""
|
"""
|
||||||
|
Create the necessary tables for the mc_whitelist cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -50,9 +52,7 @@ class MCWhitelist(commands.Cog):
|
|||||||
data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}", timeout=10)
|
data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}", timeout=10)
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(
|
embed=embed_templates.error_warning(f"Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft"),
|
||||||
interaction, text=f"Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft"
|
|
||||||
),
|
|
||||||
ephemeral=True,
|
ephemeral=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,16 +69,25 @@ class MCWhitelist(commands.Cog):
|
|||||||
)
|
)
|
||||||
if self.cursor.fetchone():
|
if self.cursor.fetchone():
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(
|
embed=embed_templates.error_warning(
|
||||||
interaction, text="Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted"
|
"Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Whitelist user on minecraft server
|
# Whitelist user on minecraft server
|
||||||
# Unfortunately, this requires an active connection to the server, with correct credentials
|
# Unfortunately, this requires an active connection to the server, with correct credentials
|
||||||
with MCRcon(host="127.0.0.1", password=self.bot.mc_rcon_password, port=25575) as mcr:
|
# Also unfortunate, we seemingly need to wrap the context manager like this to catch any exceptions
|
||||||
mcr.command(f'whitelist add {data["name"]}')
|
try:
|
||||||
mcr.command("whitelist reload")
|
with MCRcon(host="127.0.0.1", password=self.bot.mc_rcon_password, port=25575) as mcr:
|
||||||
|
mcr.command(f'whitelist add {data["name"]}')
|
||||||
|
mcr.command("whitelist reload")
|
||||||
|
except Exception as e:
|
||||||
|
self.bot.logger.error(f"Failed to use RCON: {e}")
|
||||||
|
return await interaction.response.send_message(
|
||||||
|
embed=embed_templates.error_fatal(
|
||||||
|
"Klarte ikke å koble til minecraftserveren. Ta kontakt med din lokale teknisk ansvarlige",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Add user to db
|
# Add user to db
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
@@ -89,10 +98,10 @@ class MCWhitelist(commands.Cog):
|
|||||||
(interaction.user.id, data["id"]),
|
(interaction.user.id, data["id"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.bot.logger.info(f"Whitelisted {data['name']} for {interaction.user.name}")
|
||||||
|
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
embed=embed_templates.success(
|
embed=embed_templates.success(f'`{data["name"]}` er nå tilknyttet din discordbruker og whitelisted!')
|
||||||
interaction, text=f'`{data["name"]}` er nå tilknyttet din discordbruker og whitelisted!'
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
import textwrap
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
import cv2
|
||||||
import discord
|
import discord
|
||||||
|
import numpy as np
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from moviepy.editor import CompositeVideoClip
|
from moviepy.editor import CompositeVideoClip
|
||||||
from moviepy.editor import TextClip
|
from moviepy.editor import TextClip
|
||||||
from moviepy.editor import VideoFileClip
|
from moviepy.editor import VideoFileClip
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import ImageDraw
|
|
||||||
from PIL import ImageEnhance
|
from PIL import ImageEnhance
|
||||||
from PIL import ImageFont
|
|
||||||
from PIL import UnidentifiedImageError
|
from PIL import UnidentifiedImageError
|
||||||
|
|
||||||
from cogs.utils import discord_utils
|
from cogs.utils import discord_utils
|
||||||
from cogs.utils import embed_templates
|
from cogs.utils import embed_templates
|
||||||
|
from cogs.utils import misc_utils
|
||||||
|
|
||||||
|
|
||||||
class Meme(commands.Cog):
|
class Meme(commands.Cog):
|
||||||
@@ -31,7 +31,7 @@ class Meme(commands.Cog):
|
|||||||
|
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 5)
|
@app_commands.checks.cooldown(1, 5)
|
||||||
@app_commands.command(name="deepfry", description="Friter et bilde til hundre og helvete")
|
@app_commands.command(name="deepfry", description="Friter et bilde til hundre og helvete")
|
||||||
async def deepfry(self, interaction: discord.Interaction, bilde: discord.Attachment):
|
async def deepfry(self, interaction: discord.Interaction, bilde: discord.Attachment):
|
||||||
@@ -47,14 +47,12 @@ class Meme(commands.Cog):
|
|||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Fetch image
|
# Fetch image
|
||||||
input = await discord_utils.get_file_bytesio(bilde)
|
input_image = await discord_utils.get_file_bytesio(bilde)
|
||||||
|
|
||||||
# Open image
|
|
||||||
# Check if image is valid
|
|
||||||
try:
|
try:
|
||||||
image = Image.open(input)
|
image = Image.open(input_image)
|
||||||
except UnidentifiedImageError:
|
except UnidentifiedImageError:
|
||||||
await interaction.followup.send(embed=embed_templates.error_warning(interaction, text="Bildet er ugyldig"))
|
await interaction.followup.send(embed=embed_templates.error_warning("Bildet er ugyldig"))
|
||||||
return
|
return
|
||||||
|
|
||||||
if image.mode != "RGB":
|
if image.mode != "RGB":
|
||||||
@@ -76,79 +74,7 @@ class Meme(commands.Cog):
|
|||||||
|
|
||||||
await interaction.followup.send(file=discord.File(output, "deepfry.jpg"))
|
await interaction.followup.send(file=discord.File(output, "deepfry.jpg"))
|
||||||
|
|
||||||
@staticmethod
|
@app_commands.checks.bot_has_permissions(attach_files=True)
|
||||||
def __draw_text(image: Image.Image, text: str, offset: tuple = (0, 0)):
|
|
||||||
"""
|
|
||||||
Draws meme text on preferred meme template.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
image (Image.Image): The meme template.
|
|
||||||
text (str): The text to be drawn.
|
|
||||||
font (ImageFont.FreeTypeFont): The font, with its size set, to be used.
|
|
||||||
offset (tuple): The offset of the text from the top left corner of the image. Defaults to (0, 0).
|
|
||||||
"""
|
|
||||||
|
|
||||||
box_size = (540, 540)
|
|
||||||
box = Image.new("RGB", box_size, (255, 255, 255))
|
|
||||||
draw = ImageDraw.Draw(box)
|
|
||||||
|
|
||||||
font_path = "./src/assets/fonts/RobotoMono-Medium.ttf"
|
|
||||||
|
|
||||||
# Calculate initial font size
|
|
||||||
# This is done by increasing the font size until the text is too wide
|
|
||||||
max_width, max_height = box_size[0] * 0.9, box_size[1] * 0.9 # 90% of the box size
|
|
||||||
|
|
||||||
font_size = 20 # Initiale size. I consider this the smallest readable size
|
|
||||||
font_width = 0 # Initiale width. This is just to make sure the while loop runs at least once
|
|
||||||
while font_width < max_width:
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
|
|
||||||
font_box = font.getbbox(text)
|
|
||||||
font_width = font_box[2] - font_box[0]
|
|
||||||
|
|
||||||
font_size += 2
|
|
||||||
|
|
||||||
# Wrap the text based on our current font size
|
|
||||||
text = textwrap.fill(text, width=font_size)
|
|
||||||
|
|
||||||
# We then have to calculate the font size again
|
|
||||||
#
|
|
||||||
# The problem is, font.getbbox() doesn't work with multiline text so it returns the size without the linebreaks
|
|
||||||
# We can get around this by calculating the size of the longest line in the text
|
|
||||||
# and then increasing the font size until that line is too wide
|
|
||||||
#
|
|
||||||
# We also have to make sure it doesn't get too tall
|
|
||||||
longest_line = max(text.split("\n"), key=len)
|
|
||||||
longest_line_font_box = font.getbbox(longest_line)
|
|
||||||
font_width = longest_line_font_box[2] - longest_line_font_box[0]
|
|
||||||
|
|
||||||
# Estimate font height
|
|
||||||
# This is done by multiplying the height of the original non-wrapped text with the number of lines
|
|
||||||
# and then adding 20 to account for the line spacing.
|
|
||||||
# This of course is dependant on font size itself, but it's good enough for now
|
|
||||||
font_height = (font_box[3] - font_box[1] + 20) * text.count("\n")
|
|
||||||
max_height = box_size[1] * 0.9
|
|
||||||
|
|
||||||
while font_width < max_width and font_height < max_height:
|
|
||||||
font = ImageFont.truetype(font_path, font_size)
|
|
||||||
|
|
||||||
longest_line_font_box = font.getbbox(longest_line)
|
|
||||||
font_width = longest_line_font_box[2] - longest_line_font_box[0]
|
|
||||||
|
|
||||||
font_box = font.getbbox(text)
|
|
||||||
font_height = (font_box[3] - font_box[1] + 20) * text.count("\n")
|
|
||||||
|
|
||||||
font_size += 2
|
|
||||||
|
|
||||||
# Draw textbox and text
|
|
||||||
text_box = draw.multiline_textbbox(box_size, text=text, font=font, align="center", anchor="mm")
|
|
||||||
text_box = box.size[0] // 2, box.size[1] // 2
|
|
||||||
draw.multiline_text(text_box, text, font=font, fill=(0, 0, 0, 255), align="center", anchor="mm")
|
|
||||||
|
|
||||||
image.paste(box, offset)
|
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
|
||||||
@app_commands.checks.cooldown(1, 5)
|
@app_commands.checks.cooldown(1, 5)
|
||||||
@app_commands.command(name="preferansemem", description="Generer en drake-aktig mem basert på tekst")
|
@app_commands.command(name="preferansemem", description="Generer en drake-aktig mem basert på tekst")
|
||||||
async def prefer_meme(self, interaction: discord.Interaction, dårlig_tekst: str, bra_tekst: str):
|
async def prefer_meme(self, interaction: discord.Interaction, dårlig_tekst: str, bra_tekst: str):
|
||||||
@@ -165,21 +91,23 @@ class Meme(commands.Cog):
|
|||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Fetch meme template
|
# Fetch meme template
|
||||||
template = Image.open("./src/assets/misc/sivert_goodbad.jpg")
|
template = cv2.imread("./src/assets/sivert_goodbad.jpg")
|
||||||
|
|
||||||
# Draw text
|
# Draw text
|
||||||
Meme.__draw_text(template, dårlig_tekst, offset=(540, 0))
|
font = "./src/assets/fonts/comic.ttf"
|
||||||
Meme.__draw_text(template, bra_tekst, offset=(540, 540))
|
await misc_utils.put_text_in_box(template, dårlig_tekst, (540, 0), (1080, 540), font)
|
||||||
|
await misc_utils.put_text_in_box(template, bra_tekst, (540, 540), (1080, 1080), font)
|
||||||
|
|
||||||
# Save image to buffer
|
# Save image to buffer
|
||||||
output = BytesIO()
|
_, buffer = cv2.imencode(".jpg", template)
|
||||||
template.save(output, format="jpeg")
|
output = BytesIO(buffer)
|
||||||
|
cv2.imdecode(np.frombuffer(output.getbuffer(), np.uint8), -1)
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
|
|
||||||
# Send image
|
# Send image
|
||||||
await interaction.followup.send(file=discord.File(output, "sivert_goodbad.jpg"))
|
await interaction.followup.send(file=discord.File(output, "sivert_goodbad.jpg"))
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 60)
|
@app_commands.checks.cooldown(1, 60)
|
||||||
@app_commands.command(name="crabrave", description="Generer en crab rave video basert på tekst")
|
@app_commands.command(name="crabrave", description="Generer en crab rave video basert på tekst")
|
||||||
async def crab_rave(self, interaction: discord.Interaction, topptekst: str, bunntekst: str):
|
async def crab_rave(self, interaction: discord.Interaction, topptekst: str, bunntekst: str):
|
||||||
@@ -204,7 +132,6 @@ class Meme(commands.Cog):
|
|||||||
os.remove(temp_file)
|
os.remove(temp_file)
|
||||||
except (FileNotFoundError, PermissionError):
|
except (FileNotFoundError, PermissionError):
|
||||||
self.bot.logger.warn(f"Failed to remove temporary file {temp_file}")
|
self.bot.logger.warn(f"Failed to remove temporary file {temp_file}")
|
||||||
pass
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_crab(top_text: str, bottom_text: str, user_id: int):
|
def make_crab(top_text: str, bottom_text: str, user_id: int):
|
||||||
@@ -220,7 +147,7 @@ class Meme(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
font_path = "DejaVu-Sans-Bold"
|
font_path = "DejaVu-Sans-Bold"
|
||||||
clip = VideoFileClip("./src/assets/misc/crab.mp4")
|
clip = VideoFileClip("./src/assets/crab.mp4")
|
||||||
|
|
||||||
top_part = (
|
top_part = (
|
||||||
TextClip(top_text, fontsize=60, color="white", stroke_width=2, stroke_color="black", font=font_path)
|
TextClip(top_text, fontsize=60, color="white", stroke_width=2, stroke_color="black", font=font_path)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import urllib
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -29,7 +28,6 @@ class Misc(commands.Cog):
|
|||||||
|
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
|
||||||
@app_commands.checks.cooldown(1, 2)
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@app_commands.command(name="weeb", description="Kjeft på weebs")
|
@app_commands.command(name="weeb", description="Kjeft på weebs")
|
||||||
async def weeb(self, interaction: discord.Interaction):
|
async def weeb(self, interaction: discord.Interaction):
|
||||||
@@ -56,16 +54,24 @@ class Misc(commands.Cog):
|
|||||||
tekst (str): Text to translate
|
tekst (str): Text to translate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
owo_rules = {"r": "w", "l": "w", "R": "W", "L": "W", "n": "ny", "N": "Ny", "ove": "uv"}
|
# Decades of computer sciencetist optimizng every little detail to make
|
||||||
for key, value in owo_rules.items():
|
# our computers as fast as possible. Only for me to ruin it by doing this shit.
|
||||||
tekst = re.sub(key, value, tekst)
|
# I am sorry to our forefathers whose shoulders we stand upon.
|
||||||
# https://kaomoji.moe/
|
# I have sinned
|
||||||
|
tekst = re.sub(r"[rl]", "w", tekst)
|
||||||
|
tekst = re.sub(r"[RL]", "W", tekst)
|
||||||
|
tekst = re.sub(r"n([aeiou])", r"ny\1", tekst, flags=re.IGNORECASE)
|
||||||
|
tekst = re.sub(r"N([aeiou])", r"Ny\1", tekst)
|
||||||
|
tekst = re.sub(r"th", "d", tekst, flags=re.IGNORECASE)
|
||||||
|
tekst = tekst.replace("ove", "uv")
|
||||||
|
|
||||||
|
# TODO: add kaomoji? https://kaomoji.moe/
|
||||||
|
|
||||||
if len(tekst) >= 1000:
|
if len(tekst) >= 1000:
|
||||||
embed = embed_templates.error_warning(interaction, text="Teksten er for lang")
|
embed = embed_templates.error_warning("Teksten er for lang")
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
embed = discord.Embed(color=interaction.client.user.color, description=tekst)
|
embed = discord.Embed(description=tekst)
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
@@ -117,15 +123,11 @@ class Misc(commands.Cog):
|
|||||||
params = {"action": "autocomplete", "service": "emner", "scope": "/studier/emner", "q": emnekode, "limit": 1}
|
params = {"action": "autocomplete", "service": "emner", "scope": "/studier/emner", "q": emnekode, "limit": 1}
|
||||||
data = requests.get("https://www.uio.no/studier/", params=params, timeout=10)
|
data = requests.get("https://www.uio.no/studier/", params=params, timeout=10)
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(embed=embed_templates.error_fatal("Kunne ikke nå API"))
|
||||||
embed=embed_templates.error_fatal(interaction, text="Kunne ikke nå API")
|
|
||||||
)
|
|
||||||
|
|
||||||
results = data.text.strip("\n").split("\n")[:-1]
|
results = data.text.strip("\n").split("\n")[:-1]
|
||||||
if not results:
|
if not results:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(embed=embed_templates.error_warning("Fant ikke emnekode"))
|
||||||
embed=embed_templates.error_fatal(interaction, text="Fant ikke emnekode")
|
|
||||||
)
|
|
||||||
|
|
||||||
for result in results:
|
for result in results:
|
||||||
code, name, _ = result.split(";")
|
code, name, _ = result.split(";")
|
||||||
@@ -148,10 +150,10 @@ class Misc(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if len(tekst) >= 1000:
|
if len(tekst) >= 1000:
|
||||||
embed = embed_templates.error_warning(interation, text="Teksten er for lang")
|
embed = embed_templates.error_warning("Teksten er for lang")
|
||||||
return await interation.response.send_message(embed=embed)
|
return await interation.response.send_message(embed=embed)
|
||||||
|
|
||||||
tekst = re.sub(" ", "👏", tekst)
|
tekst = tekst.replace(" ", "👏")
|
||||||
|
|
||||||
embed = discord.Embed(color=interation.client.user.color, description=f"**{tekst.upper()}**")
|
embed = discord.Embed(color=interation.client.user.color, description=f"**{tekst.upper()}**")
|
||||||
await interation.response.send_message(embed=embed)
|
await interation.response.send_message(embed=embed)
|
||||||
@@ -169,6 +171,7 @@ class Misc(commands.Cog):
|
|||||||
tekst (str): Text to clapify
|
tekst (str): Text to clapify
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# TODO: make this not horrible. I can't be botherd to right now
|
||||||
words = tekst.split(" ")
|
words = tekst.split(" ")
|
||||||
for i, word in enumerate(words):
|
for i, word in enumerate(words):
|
||||||
karakter = 0
|
karakter = 0
|
||||||
@@ -188,60 +191,6 @@ class Misc(commands.Cog):
|
|||||||
embed = discord.Embed(color=interation.client.user.color, description=text)
|
embed = discord.Embed(color=interation.client.user.color, description=text)
|
||||||
await interation.response.send_message(content=mention, embed=embed)
|
await interation.response.send_message(content=mention, embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
|
||||||
@app_commands.checks.cooldown(1, 5)
|
|
||||||
@app_commands.command(name="imdb", description="Søk etter filmer på IMDB")
|
|
||||||
async def imdb(self, interaction: discord.Interaction, tittel: str):
|
|
||||||
"""
|
|
||||||
Search for movies on IMDB and displays information about the first result
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
tittel (str): Movie title
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Search for movie/show
|
|
||||||
url = "http://www.omdbapi.com/?" + urllib.parse.urlencode({"s": tittel, "apikey": self.bot.api_keys["omdb"]})
|
|
||||||
search = requests.get(url, timeout=10).json()
|
|
||||||
|
|
||||||
try:
|
|
||||||
best_result_id = search["Search"][0]["imdbID"]
|
|
||||||
except KeyError:
|
|
||||||
embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!")
|
|
||||||
return await interaction.response.send_message(embed=embed)
|
|
||||||
|
|
||||||
# Get movie/show info
|
|
||||||
data = requests.get(
|
|
||||||
f'http://www.omdbapi.com/?i={best_result_id}&apikey={self.bot.api_keys["omdb"]}', timeout=10
|
|
||||||
).json()
|
|
||||||
acceptable_media_types = ["movie", "series", "episode"]
|
|
||||||
if data["Type"] not in acceptable_media_types:
|
|
||||||
embed = embed_templates.error_fatal(interaction, text="Fant ikke filmen!")
|
|
||||||
return await interaction.response.send_message(embed=embed)
|
|
||||||
|
|
||||||
embed = discord.Embed(
|
|
||||||
title=f'{data["Title"]} ({data["Year"]})',
|
|
||||||
color=0xF5C518,
|
|
||||||
url=f'https://www.imdb.com/title/{data["imdbID"]}/',
|
|
||||||
)
|
|
||||||
embed.set_author(name=interaction.user.name, icon_url=interaction.user.display_avatar)
|
|
||||||
embed.add_field(name="Type", value=data["Type"].title())
|
|
||||||
embed.add_field(name="Sjanger", value=data["Genre"])
|
|
||||||
embed.add_field(name="Spilletid", value=data["Runtime"])
|
|
||||||
embed.add_field(name="Vurdering på IMDb", value=f'{data["imdbRating"]}/10')
|
|
||||||
release_date = datetime.strptime(data["Released"], "%d %b %Y")
|
|
||||||
embed.add_field(name="Utgitt", value=discord.utils.format_dt(release_date, style="D"))
|
|
||||||
|
|
||||||
if data["Poster"] != "N/A":
|
|
||||||
embed.set_thumbnail(url=data["Poster"])
|
|
||||||
if data["Director"] != "N/A":
|
|
||||||
embed.description = data["Director"]
|
|
||||||
if data["Plot"] != "N/A" and len(data["Plot"]) < 1024:
|
|
||||||
embed.add_field(name="Sammendrag", value=data["Plot"], inline=False)
|
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed)
|
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
@app_commands.checks.cooldown(1, 5)
|
@app_commands.checks.cooldown(1, 5)
|
||||||
@app_commands.command(name="helligdager", description="Se helligdager i et land for et år")
|
@app_commands.command(name="helligdager", description="Se helligdager i et land for et år")
|
||||||
@@ -257,13 +206,11 @@ class Misc(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
land = land.upper()
|
land = land.upper()
|
||||||
aar = datetime.now().year if not år else int(år)
|
år = datetime.now().year if not år else int(år)
|
||||||
|
|
||||||
data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{aar}/{land}", timeout=10)
|
data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{år}/{land}", timeout=10)
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_warning("Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`")
|
||||||
interaction, text="Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`"
|
|
||||||
)
|
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
data = data.json()
|
data = data.json()
|
||||||
@@ -277,12 +224,12 @@ class Misc(commands.Cog):
|
|||||||
holiday_str += f'* **{date}**: {day["localName"]}\n'
|
holiday_str += f'* **{date}**: {day["localName"]}\n'
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
color=interaction.client.user.color, title=f":flag_{country}: Helligdager {aar} :flag_{country}:"
|
color=interaction.client.user.color, title=f":flag_{country}: Helligdager {år} :flag_{country}:"
|
||||||
)
|
)
|
||||||
embed.description = holiday_str
|
embed.description = holiday_str
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 5)
|
@app_commands.checks.cooldown(1, 5)
|
||||||
@app_commands.command(name="match", description="Se hvor mye du matcher med en annen")
|
@app_commands.command(name="match", description="Se hvor mye du matcher med en annen")
|
||||||
async def match(self, interaction: discord.Interaction, bruker: discord.Member):
|
async def match(self, interaction: discord.Interaction, bruker: discord.Member):
|
||||||
@@ -296,9 +243,7 @@ class Misc(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if bruker == interaction.user:
|
if bruker == interaction.user:
|
||||||
embed = embed_templates.error_warning(
|
embed = embed_templates.error_warning("Jeg vet du er ensom, men du kan ikke matche med deg selv")
|
||||||
interaction, text="Jeg vet du er ensom, men du kan ikke matche med deg selv"
|
|
||||||
)
|
|
||||||
return await interaction.response.send_message(embed=embed)
|
return await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
invoker_id = int(str(interaction.user.id)[11:14])
|
invoker_id = int(str(interaction.user.id)[11:14])
|
||||||
@@ -320,8 +265,8 @@ class Misc(commands.Cog):
|
|||||||
user = user.resize((389, 389))
|
user = user.resize((389, 389))
|
||||||
|
|
||||||
# Prepare heart image
|
# Prepare heart image
|
||||||
heart = Image.open("./src/assets/misc/heart.png")
|
heart = Image.open("./src/assets/heart.png")
|
||||||
mask = Image.open("./src/assets/misc/heart.png", "r")
|
mask = Image.open("./src/assets/heart.png", "r")
|
||||||
|
|
||||||
# Putting it all together
|
# Putting it all together
|
||||||
image = Image.new("RGBA", (1024, 576))
|
image = Image.new("RGBA", (1024, 576))
|
||||||
@@ -329,7 +274,7 @@ class Misc(commands.Cog):
|
|||||||
image.paste(user, (635, 94))
|
image.paste(user, (635, 94))
|
||||||
image.paste(heart, (311, 94), mask=mask)
|
image.paste(heart, (311, 94), mask=mask)
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", 86)
|
font = ImageFont.truetype("./src/assets/fonts/comic.ttf", 86)
|
||||||
font_size = font.getbbox(f"{match_percent}%")
|
font_size = font.getbbox(f"{match_percent}%")
|
||||||
font_size = ((image.size[0] - font_size[2]) / 2, (image.size[1] - font_size[3]) / 2)
|
font_size = ((image.size[0] - font_size[2]) / 2, (image.size[1] - font_size[3]) / 2)
|
||||||
draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255))
|
draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255))
|
||||||
@@ -345,7 +290,6 @@ class Misc(commands.Cog):
|
|||||||
embed.set_image(url=f"attachment://{interaction.user.id}_{bruker.id}_match.png")
|
embed.set_image(url=f"attachment://{interaction.user.id}_{bruker.id}_match.png")
|
||||||
await interaction.response.send_message(embed=embed, file=f)
|
await interaction.response.send_message(embed=embed, file=f)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
|
||||||
@app_commands.checks.cooldown(1, 2)
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@app_commands.command(name="ifonlyyouknew", description="How bad things really are")
|
@app_commands.command(name="ifonlyyouknew", description="How bad things really are")
|
||||||
async def howbadthingsreallyare(self, interaction: discord.Interaction):
|
async def howbadthingsreallyare(self, interaction: discord.Interaction):
|
||||||
|
|||||||
@@ -10,23 +10,20 @@ class RepeatedMessages(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot: commands.Bot):
|
def __init__(self, bot: commands.Bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
self.general_channel = 747542544291987597
|
||||||
|
|
||||||
# @aiocron.crontab('37 13 * * *')
|
# Yes we have to have these inside the constructor
|
||||||
# async def leet():
|
# Probably could've implemented the waiting functions myself
|
||||||
# """Sends a message at leet gamer hours"""
|
# but that sounds like work and I don't like to work
|
||||||
|
|
||||||
# guild = self.bot.get_guild(747542543750660178)
|
|
||||||
# channel = guild.get_channel(747542544291987597)
|
|
||||||
# sticker = await guild.fetch_sticker(1046948904471633951)
|
|
||||||
# await asyncio.sleep(1)
|
|
||||||
# await channel.send(stickers=[sticker])
|
|
||||||
|
|
||||||
@aiocron.crontab("0 0 * * 5")
|
@aiocron.crontab("0 0 * * 5")
|
||||||
async def fredag():
|
async def fredag():
|
||||||
"""Sends a message on Friday at 00:00"""
|
"""
|
||||||
|
Sends a message on Friday at 00:00
|
||||||
|
"""
|
||||||
|
|
||||||
guild = self.bot.get_guild(747542543750660178)
|
guild = self.bot.get_guild(self.bot.UIO_GAMING_GUILD_ID)
|
||||||
channel = guild.get_channel(747542544291987597)
|
channel = guild.get_channel(self.general_channel)
|
||||||
|
|
||||||
videos = [
|
videos = [
|
||||||
"https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4",
|
"https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4",
|
||||||
@@ -48,10 +45,12 @@ class RepeatedMessages(commands.Cog):
|
|||||||
|
|
||||||
@aiocron.crontab("0 0 * * 1")
|
@aiocron.crontab("0 0 * * 1")
|
||||||
async def mandag():
|
async def mandag():
|
||||||
"""Sends a message on Monday at 00:00"""
|
"""
|
||||||
|
Sends a message on Monday at 00:00
|
||||||
|
"""
|
||||||
|
|
||||||
guild = self.bot.get_guild(747542543750660178)
|
guild = self.bot.get_guild(self.bot.UIO_GAMING_GUILD_ID)
|
||||||
channel = guild.get_channel(747542544291987597)
|
channel = guild.get_channel(self.general_channel)
|
||||||
await channel.send(
|
await channel.send(
|
||||||
"ENDELIG MANDAG!\n\n"
|
"ENDELIG MANDAG!\n\n"
|
||||||
# + "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" # noqa: E501
|
# + "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4" # noqa: E501
|
||||||
@@ -60,10 +59,12 @@ class RepeatedMessages(commands.Cog):
|
|||||||
|
|
||||||
@aiocron.crontab("0 0 1 1 *")
|
@aiocron.crontab("0 0 1 1 *")
|
||||||
async def new_year():
|
async def new_year():
|
||||||
"""Sends a message on New Year's Day at 00:00"""
|
"""
|
||||||
|
Sends a message on New Year's Day at 00:00
|
||||||
|
"""
|
||||||
|
|
||||||
guild = self.bot.get_guild(747542543750660178)
|
guild = self.bot.get_guild(self.bot.UIO_GAMING_GUILD_ID)
|
||||||
channel = guild.get_channel(747542544291987597)
|
channel = guild.get_channel(self.general_channel)
|
||||||
await channel.send(
|
await channel.send(
|
||||||
"GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>"
|
"GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,23 +19,10 @@ from cogs.utils import misc_utils
|
|||||||
- score for å bli pinged i uio gullkorn
|
- score for å bli pinged i uio gullkorn
|
||||||
+ score for å få melding på stjernetavla/bli stjerna. multiplier per stjerne
|
+ score for å få melding på stjernetavla/bli stjerna. multiplier per stjerne
|
||||||
- score hver dag med weebrolle (lite)
|
- score hver dag med weebrolle (lite)
|
||||||
"""
|
|
||||||
|
|
||||||
"""
|
|
||||||
------ TO-DO ------
|
------ TO-DO ------
|
||||||
+ score for guten tag
|
|
||||||
|
|
||||||
- score sitte i afk
|
- score sitte i afk
|
||||||
+ score for å sitte i voice per time
|
+ score for å sitte i voice per time
|
||||||
|
|
||||||
---------------
|
|
||||||
- score for winiie the pooh
|
|
||||||
- score for memes i general
|
|
||||||
- score for å snakke negativt om borgerlønn
|
|
||||||
- score for å snakke negativt om styret
|
|
||||||
* hvis lav score, spiller av kinesisk nasjonalsang på voicechannel når joiner
|
|
||||||
|
|
||||||
daglig uthengig av laveste score
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -55,7 +42,9 @@ class SocialCredit(commands.Cog):
|
|||||||
self.init_db()
|
self.init_db()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the social credit cog to work"""
|
"""
|
||||||
|
Create the necessary tables for the social credit cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -76,13 +65,15 @@ class SocialCredit(commands.Cog):
|
|||||||
self = args[0]
|
self = args[0]
|
||||||
self.cursor.execute("SELECT user_id FROM social_credit WHERE user_id = %s", (args[1],))
|
self.cursor.execute("SELECT user_id FROM social_credit WHERE user_id = %s", (args[1],))
|
||||||
if not self.cursor.fetchone():
|
if not self.cursor.fetchone():
|
||||||
self._add_citizen(args[1])
|
self.add_citizen(args[1])
|
||||||
await func(*args, **kwargs)
|
await func(*args, **kwargs)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def roll(percent: int = 50):
|
def roll(percent: int = 50):
|
||||||
"""Decorator that executes the function with a given percent chance"""
|
"""
|
||||||
|
Decorator that executes the function with a given percent chance
|
||||||
|
"""
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
async def wrapper(*args, **kwargs):
|
async def wrapper(*args, **kwargs):
|
||||||
@@ -93,7 +84,7 @@ class SocialCredit(commands.Cog):
|
|||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def _add_citizen(self, user_id: int):
|
def add_citizen(self, user_id: int):
|
||||||
"""
|
"""
|
||||||
Adds a user to the database
|
Adds a user to the database
|
||||||
|
|
||||||
@@ -111,21 +102,45 @@ class SocialCredit(commands.Cog):
|
|||||||
(user_id, self.START_POINTS),
|
(user_id, self.START_POINTS),
|
||||||
)
|
)
|
||||||
|
|
||||||
@tasks.loop(hours=24.0, reconnect=True)
|
@tasks.loop(time=misc_utils.MIDNIGHT, reconnect=True)
|
||||||
async def fuck_uwu(self):
|
async def fuck_uwu(self):
|
||||||
"""Deducts points from people with the weeb role every 24 hours"""
|
"""
|
||||||
|
Deducts points from people with the weeb role every 24 hours
|
||||||
|
"""
|
||||||
|
|
||||||
await self.bot.wait_until_ready()
|
await self.bot.wait_until_ready()
|
||||||
|
|
||||||
weeb_role = self.bot.get_guild(747542543750660178).get_role(803629993539403826)
|
# We need to do these checks in case the bot user does not have access to the UiO Gaming Discord server
|
||||||
|
if not (guild := self.bot.get_guild(self.bot.UIO_GAMING_GUILD_ID)):
|
||||||
|
self.bot.logger.warning(
|
||||||
|
"Could not fetch UiO Gaming guild."
|
||||||
|
+ "If the bot does not have access to the UiO Gaming server, this function won't work. "
|
||||||
|
+ "If it is, ignore this."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not (weeb_role := guild.get_role(803629993539403826)):
|
||||||
|
self.bot.logger.warning(
|
||||||
|
"Could not fetch UiO Gaming's weeb role."
|
||||||
|
+ "If the bot does not have access to the UiO Gaming server, this function won't work. "
|
||||||
|
+ "If it is, ignore this."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
for weeb in weeb_role.members:
|
for weeb in weeb_role.members:
|
||||||
await self.social_punishment(weeb.id, 1, "weeb")
|
await self.social_punishment(weeb.id, 1, "weeb")
|
||||||
|
|
||||||
@fuck_uwu.before_loop
|
@fuck_uwu.before_loop
|
||||||
async def before_fuck_uwu(self):
|
async def before_fuck_uwu(self):
|
||||||
"""Syncs loop with the time of day"""
|
"""
|
||||||
|
Wait until bot cache is ready
|
||||||
|
"""
|
||||||
|
|
||||||
await discord_utils.sleep_until_midnight(self.bot)
|
await self.bot.wait_until_ready()
|
||||||
|
|
||||||
|
async def cog_unload(self):
|
||||||
|
self.bot.logger.info("Unloading cog")
|
||||||
|
self.fuck_uwu.cancel()
|
||||||
|
self.cursor.close()
|
||||||
|
|
||||||
@add_new_citizen
|
@add_new_citizen
|
||||||
async def social_punishment(self, user_id: int, points: int, reason: str):
|
async def social_punishment(self, user_id: int, points: int, reason: str):
|
||||||
@@ -173,6 +188,8 @@ class SocialCredit(commands.Cog):
|
|||||||
|
|
||||||
social_credit_group = app_commands.Group(name="socialcredit", description="Trenger dette å forklares?")
|
social_credit_group = app_commands.Group(name="socialcredit", description="Trenger dette å forklares?")
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@social_credit_group.command(name="credits", description="Sjekk hvor dårlig menneske du er")
|
@social_credit_group.command(name="credits", description="Sjekk hvor dårlig menneske du er")
|
||||||
async def credits(self, interaction: discord.Interaction, *, bruker: discord.Member | None = None):
|
async def credits(self, interaction: discord.Interaction, *, bruker: discord.Member | None = None):
|
||||||
"""
|
"""
|
||||||
@@ -187,12 +204,18 @@ class SocialCredit(commands.Cog):
|
|||||||
if not bruker:
|
if not bruker:
|
||||||
bruker = interaction.user
|
bruker = interaction.user
|
||||||
|
|
||||||
self.cursor.execute("SELECT * FROM social_credit WHERE user_id = %s", (bruker.id,))
|
self.cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT * FROM social_credit
|
||||||
|
WHERE user_id = %s"
|
||||||
|
""",
|
||||||
|
(bruker.id,),
|
||||||
|
)
|
||||||
result = self.cursor.fetchone()
|
result = self.cursor.fetchone()
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, f"{bruker.mention} er ikke registrert i databasen")
|
embed=embed_templates.error_warning(f"{bruker.mention} er ikke registrert i databasen")
|
||||||
)
|
)
|
||||||
|
|
||||||
db_user = CreditUser(*result)
|
db_user = CreditUser(*result)
|
||||||
@@ -200,6 +223,8 @@ class SocialCredit(commands.Cog):
|
|||||||
embed = discord.Embed(description=(f"{bruker.mention} har `{db_user.credit_score}` social credits"))
|
embed = discord.Embed(description=(f"{bruker.mention} har `{db_user.credit_score}` social credits"))
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@social_credit_group.command(name="leaderboard", description="Sjekk hvem som er de beste og verste borgerne")
|
@social_credit_group.command(name="leaderboard", description="Sjekk hvem som er de beste og verste borgerne")
|
||||||
async def leaderboard(self, interaction: discord.Interaction):
|
async def leaderboard(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -218,18 +243,14 @@ class SocialCredit(commands.Cog):
|
|||||||
ORDER BY credit_score DESC
|
ORDER BY credit_score DESC
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
result = self.cursor.fetchall()
|
||||||
|
|
||||||
if not (result := self.cursor.fetchall()):
|
if not result:
|
||||||
return await interaction.send(
|
return await interaction.send(
|
||||||
embed=embed_templates.error_fatal(interaction, "Ingen brukere er registrert i databasen")
|
embed=embed_templates.error_warning("Ingen brukere er registrert i databasen")
|
||||||
)
|
)
|
||||||
|
|
||||||
leaderboard_formatted = list(
|
leaderboard_formatted = [f"**#{s[0]+1}** <@{s[1][0]}> - `{s[1][1]}` poeng" for s in enumerate(result)]
|
||||||
map(
|
|
||||||
lambda s: f"**#{s[0]+1}** <@{s[1][0]}> - `{s[1][1]}` poeng",
|
|
||||||
enumerate(result),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(leaderboard_formatted)
|
paginator = misc_utils.Paginator(leaderboard_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
@@ -321,10 +342,10 @@ class SocialCredit(commands.Cog):
|
|||||||
message (discord.Message): The message object
|
message (discord.Message): The message object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if message.channel.id == 865970753748074576:
|
# This only works in the UiO Gaming server as well
|
||||||
if message.mentions:
|
if message.channel.id == 865970753748074576 and message.mentions:
|
||||||
for mention in message.mentions:
|
for mention in message.mentions:
|
||||||
await self.social_punishment(mention.id, 10, "gullkorn")
|
await self.social_punishment(mention.id, 10, "gullkorn")
|
||||||
|
|
||||||
@commands.Cog.listener("on_reaction_add")
|
@commands.Cog.listener("on_reaction_add")
|
||||||
async def on_star_add(self, reaction: discord.Reaction, user: discord.User | discord.Member):
|
async def on_star_add(self, reaction: discord.Reaction, user: discord.User | discord.Member):
|
||||||
@@ -343,12 +364,11 @@ class SocialCredit(commands.Cog):
|
|||||||
if reaction.emoji == "⭐":
|
if reaction.emoji == "⭐":
|
||||||
if reaction.message.author == user:
|
if reaction.message.author == user:
|
||||||
await self.social_punishment(user.id, 100, "self-star")
|
await self.social_punishment(user.id, 100, "self-star")
|
||||||
else:
|
elif reaction.count == 3:
|
||||||
if len(reaction.message.reactions) >= 3:
|
await self.social_punishment(
|
||||||
await self.social_punishment(
|
user.id, (len(reaction.message.reactions) - 1) * 25, "remove already accumulated stars"
|
||||||
user.id, (len(reaction.message.reactions) - 1) * 25, "remove already accumulated stars"
|
)
|
||||||
)
|
await self.social_reward(user.id, 25 * len(reaction.message.reactions), "add new stars")
|
||||||
await self.social_reward(user.id, 25 * len(reaction.message.reactions), "add new stars")
|
|
||||||
|
|
||||||
@commands.Cog.listener("on_reaction_remove")
|
@commands.Cog.listener("on_reaction_remove")
|
||||||
async def on_star_remove(self, reaction: discord.Reaction, user: discord.User | discord.Member):
|
async def on_star_remove(self, reaction: discord.Reaction, user: discord.User | discord.Member):
|
||||||
@@ -359,9 +379,8 @@ class SocialCredit(commands.Cog):
|
|||||||
if user.bot:
|
if user.bot:
|
||||||
return
|
return
|
||||||
|
|
||||||
if reaction.emoji == "⭐":
|
if reaction.emoji == "⭐" and reaction.count >= 3:
|
||||||
if len(reaction.message.reactions) >= 3:
|
await self.social_punishment(user.id, 25, "remove star")
|
||||||
await self.social_punishment(user.id, 25, "remove star")
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
async def setup(bot: commands.Bot):
|
||||||
|
|||||||
@@ -26,14 +26,17 @@ class Streak(commands.Cog):
|
|||||||
self.cursor = self.bot.db_connection.cursor()
|
self.cursor = self.bot.db_connection.cursor()
|
||||||
self.init_db()
|
self.init_db()
|
||||||
|
|
||||||
|
# Cache to avoid having to insert to the db for every message
|
||||||
self.streak_cache = {}
|
self.streak_cache = {}
|
||||||
self.populate_cache()
|
self.populate_cache()
|
||||||
|
|
||||||
self.streak_check.start()
|
self.streak_check.start()
|
||||||
self.streak_update_loop.start()
|
self.insert_cache_loop.start()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the streak cog to work."""
|
"""
|
||||||
|
Create the necessary tables for the streak cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -47,11 +50,9 @@ class Streak(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def cog_unload(self):
|
async def cog_unload(self):
|
||||||
"""Stop the loops and close the database connection"""
|
self.bot.logger.info("Unloading cog")
|
||||||
|
self.insert_cache_loop.cancel()
|
||||||
await self.streak_update()
|
|
||||||
self.streak_check.cancel()
|
self.streak_check.cancel()
|
||||||
self.streak_update_loop.cancel()
|
|
||||||
self.cursor.close()
|
self.cursor.close()
|
||||||
|
|
||||||
def populate_cache(self):
|
def populate_cache(self):
|
||||||
@@ -98,17 +99,25 @@ class Streak(commands.Cog):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@tasks.loop(minutes=10)
|
@tasks.loop(minutes=10)
|
||||||
async def streak_update_loop(self):
|
async def insert_cache_loop(self):
|
||||||
|
"""
|
||||||
|
Inserts cache into the database every 10 minutes
|
||||||
|
"""
|
||||||
|
|
||||||
|
await self.insert_cache()
|
||||||
|
|
||||||
|
@insert_cache_loop.after_loop
|
||||||
|
async def on_insert_cache_loop_cancel(self):
|
||||||
|
self.bot.logger.info("Insert cache loop cancelled")
|
||||||
|
if self.insert_cache_loop.is_being_cancelled() and self.streak_cache:
|
||||||
|
await self.insert_cache()
|
||||||
|
|
||||||
|
async def insert_cache(self):
|
||||||
"""
|
"""
|
||||||
Inserts cache into the database
|
Inserts cache into the database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self.streak_update()
|
self.bot.logger.info("Inserting cache into database")
|
||||||
|
|
||||||
async def streak_update(self):
|
|
||||||
"""
|
|
||||||
Inserts cache into the database
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Insert into database
|
# Insert into database
|
||||||
for user_id, user_data in self.streak_cache.items():
|
for user_id, user_data in self.streak_cache.items():
|
||||||
@@ -128,14 +137,15 @@ class Streak(commands.Cog):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@tasks.loop(hours=24)
|
@tasks.loop(time=misc_utils.MIDNIGHT)
|
||||||
async def streak_check(self):
|
async def streak_check(self):
|
||||||
"""
|
"""
|
||||||
Check if anyone has lost their streak
|
Check if anyone has lost their streak
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Clear cache to make sure all streaks are up to date
|
# Clear cache to make sure all streaks are up to date
|
||||||
await self.streak_update()
|
if self.streak_cache:
|
||||||
|
await self.insert_cache()
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -156,15 +166,16 @@ class Streak(commands.Cog):
|
|||||||
(streak[1],),
|
(streak[1],),
|
||||||
)
|
)
|
||||||
|
|
||||||
@streak_check.before_loop
|
@streak_check.after_loop
|
||||||
async def before_streak_check(self):
|
async def on_streak_check_cancel(self):
|
||||||
"""Syncs loop to the time of day"""
|
self.bot.logger.info("Streak check loop cancelled")
|
||||||
|
if self.streak_check.is_being_cancelled():
|
||||||
self.bot.logger.info("Postponing streak check until midnight")
|
await self.streak_check()
|
||||||
await discord_utils.sleep_until_midnight(self.bot)
|
|
||||||
|
|
||||||
streak_group = app_commands.Group(name="streak", description="Snapchat streaks, men for Discord")
|
streak_group = app_commands.Group(name="streak", description="Snapchat streaks, men for Discord")
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@streak_group.command(name="se", description="Se din eller en annen brukers streak")
|
@streak_group.command(name="se", description="Se din eller en annen brukers streak")
|
||||||
async def streak_user(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None):
|
async def streak_user(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None):
|
||||||
"""
|
"""
|
||||||
@@ -189,13 +200,10 @@ class Streak(commands.Cog):
|
|||||||
""",
|
""",
|
||||||
(bruker.id,),
|
(bruker.id,),
|
||||||
)
|
)
|
||||||
|
|
||||||
streak = self.cursor.fetchone()
|
streak = self.cursor.fetchone()
|
||||||
|
|
||||||
if not streak:
|
if not streak:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(embed=embed_templates.error_warning("Brukeren har ikke noen streak"))
|
||||||
embed=embed_templates.error_warning(interaction, "Brukeren har ikke noen streak")
|
|
||||||
)
|
|
||||||
|
|
||||||
streak_msg_channel, streak_msg_id = streak[0].split("-")
|
streak_msg_channel, streak_msg_id = streak[0].split("-")
|
||||||
streak_msg_channel, streak_msg_id = int(streak_msg_channel), int(streak_msg_id)
|
streak_msg_channel, streak_msg_id = int(streak_msg_channel), int(streak_msg_id)
|
||||||
@@ -225,6 +233,8 @@ class Streak(commands.Cog):
|
|||||||
)
|
)
|
||||||
await interaction.followup.send(embed=embed)
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@streak_group.command(name="topp", description="Se hvem som har høyest streak på serveren")
|
@streak_group.command(name="topp", description="Se hvem som har høyest streak på serveren")
|
||||||
async def streak_top(self, interaction: discord.Interaction):
|
async def streak_top(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -244,20 +254,14 @@ class Streak(commands.Cog):
|
|||||||
ORDER BY streak_start_time;
|
ORDER BY streak_start_time;
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
streaks = self.cursor.fetchall()
|
streaks = self.cursor.fetchall()
|
||||||
|
|
||||||
if not streaks:
|
if not streaks:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(embed=embed_templates.error_warning("Ingen har noen streak enda"))
|
||||||
embed=embed_templates.error_warning(interaction, "Ingen har noen streak enda")
|
|
||||||
)
|
|
||||||
|
|
||||||
streaks_formatted = list(
|
streaks_formatted = [
|
||||||
map(
|
f"**#{s[0]+1}** <@{s[1][0]}> - {(datetime.now() - s[1][1]).days} dager" for s in enumerate(streaks)
|
||||||
lambda s: f"**#{s[0]+1}** <@{s[1][0]}> - {(datetime.now() - s[1][1]).days} dager",
|
]
|
||||||
enumerate(streaks),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(streaks_formatted)
|
paginator = misc_utils.Paginator(streaks_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class TempVoice(commands.Cog):
|
|||||||
bot (commands.Bot): The bot instance
|
bot (commands.Bot): The bot instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bot = bot
|
|
||||||
self.temp_voice_helper = TempVoiceHelper(bot)
|
self.temp_voice_helper = TempVoiceHelper(bot)
|
||||||
|
|
||||||
@commands.Cog.listener()
|
@commands.Cog.listener()
|
||||||
@@ -29,6 +28,7 @@ class TempVoice(commands.Cog):
|
|||||||
|
|
||||||
await self.temp_voice_helper.on_voice_state_update(member, before, after)
|
await self.temp_voice_helper.on_voice_state_update(member, before, after)
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(manage_channels=True, embed_links=True)
|
||||||
@app_commands.checks.cooldown(1, 300)
|
@app_commands.checks.cooldown(1, 300)
|
||||||
@app_commands.command(name="tempvoice")
|
@app_commands.command(name="tempvoice")
|
||||||
async def tempvoice(self, interaction: discord.Interaction, name: str, limit: int = 0):
|
async def tempvoice(self, interaction: discord.Interaction, name: str, limit: int = 0):
|
||||||
@@ -46,8 +46,7 @@ class TempVoice(commands.Cog):
|
|||||||
|
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
embed=embed_templates.success(
|
embed=embed_templates.success(
|
||||||
interaction,
|
f"{channel.mention} ble opprettet. Den vil bli slettet etter cirka 1 minutt uten aktivitet.",
|
||||||
text=f"{channel.mention} ble opprettet. Den vil bli slettet etter cirka 1 minutt uten aktivitet.",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import discord
|
|||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from cogs.utils import discord_utils
|
|
||||||
from cogs.utils import embed_templates
|
from cogs.utils import embed_templates
|
||||||
from cogs.utils.discord_utils import Lobby
|
from cogs.utils.discord_utils import Lobby
|
||||||
from cogs.utils.discord_utils import LobbyView
|
from cogs.utils.discord_utils import LobbyView
|
||||||
@@ -54,8 +53,7 @@ class TenMan(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(
|
embed=embed_templates.error_warning(
|
||||||
interaction,
|
"Du hoster allerede en lobby",
|
||||||
text="Du hoster allerede en lobby",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,8 +61,7 @@ class TenMan(commands.Cog):
|
|||||||
if interaction.user.id in lobby.players:
|
if interaction.user.id in lobby.players:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(
|
embed=embed_templates.error_warning(
|
||||||
interaction,
|
"Du er allerede i en lobby",
|
||||||
text="Du er allerede i en lobby",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -80,7 +77,7 @@ class TenMan(commands.Cog):
|
|||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="10 Man Lobby",
|
title="10 Man Lobby",
|
||||||
description=f"Lobbyen stenger {time_left}. Bli med innen da!",
|
description=f"Lobbyen stenger {time_left}. Bli med innen da!",
|
||||||
color=discord_utils.get_color(interaction.user),
|
color=interaction.user.color,
|
||||||
)
|
)
|
||||||
embed.set_author(name=interaction.user.global_name, icon_url=interaction.user.avatar)
|
embed.set_author(name=interaction.user.global_name, icon_url=interaction.user.avatar)
|
||||||
embed.add_field(name="Spillere", value=f"* {interaction.user.mention}", inline=False)
|
embed.add_field(name="Spillere", value=f"* {interaction.user.mention}", inline=False)
|
||||||
@@ -97,11 +94,11 @@ class TenManView(LobbyView):
|
|||||||
@override
|
@override
|
||||||
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if interaction.user != self.lobby.host:
|
if interaction.user != self.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan starte")
|
embed = embed_templates.error_warning("Bare hosten kan starte")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if len(self.lobby.players) < 3:
|
if len(self.lobby.players) < 3:
|
||||||
embed = embed_templates.error_warning(interaction, text="Det må være mer enn 2 i lobbyen")
|
embed = embed_templates.error_warning("Det må være mer enn 2 i lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
await self.end_lobby()
|
await self.end_lobby()
|
||||||
@@ -136,7 +133,7 @@ class TeamLeaderView(discord.ui.View):
|
|||||||
|
|
||||||
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
|
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
|
||||||
self.bot.logger.error("TeamLeaderView error", exc_info=error)
|
self.bot.logger.error("TeamLeaderView error", exc_info=error)
|
||||||
embed = embed_templates.error_warning(interaction, text="Oopsie woopsie, we made a fucky wucky!")
|
embed = embed_templates.error_fatal("Oopsie woopsie, we made a fucky wucky!")
|
||||||
await interaction.response.send_message(embed=embed, delete_after=10)
|
await interaction.response.send_message(embed=embed, delete_after=10)
|
||||||
|
|
||||||
|
|
||||||
@@ -151,7 +148,7 @@ class TeamLeaderSelectMenu(discord.ui.Select):
|
|||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
if interaction.user != self.parent_view.lobby.host:
|
if interaction.user != self.parent_view.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan velge lagledere")
|
embed = embed_templates.error_warning("Bare hosten kan velge lagledere")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
team_leaders = []
|
team_leaders = []
|
||||||
@@ -203,7 +200,7 @@ class TeamSelectView(discord.ui.View):
|
|||||||
|
|
||||||
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
|
async def on_error(self, interaction: discord.Interaction, error: Exception, item: discord.ui.Item):
|
||||||
self.bot.logger.error("TeamSelectView error", exc_info=error)
|
self.bot.logger.error("TeamSelectView error", exc_info=error)
|
||||||
embed = embed_templates.error_warning(interaction, text="Oopsie woopsie, we made a fucky wucky")
|
embed = embed_templates.error_warning("Oopsie woopsie, we made a fucky wucky")
|
||||||
await interaction.response.send_message(embed=embed, delete_after=10)
|
await interaction.response.send_message(embed=embed, delete_after=10)
|
||||||
|
|
||||||
def teams_ready(self, interaction):
|
def teams_ready(self, interaction):
|
||||||
@@ -246,11 +243,11 @@ class TeamSelectMenu(discord.ui.Select):
|
|||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
|
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare lagledere kan velge spillere")
|
embed = embed_templates.error_warning("Bare lagledere kan velge spillere")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if interaction.user != self.parent_view.teams[self.parent_view.turn][0]:
|
if interaction.user != self.parent_view.teams[self.parent_view.turn][0]:
|
||||||
embed = embed_templates.error_warning(interaction, text="Det er ikke din tur enda")
|
embed = embed_templates.error_warning("Det er ikke din tur enda")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
turn_in_question = self.parent_view.turn
|
turn_in_question = self.parent_view.turn
|
||||||
@@ -264,7 +261,7 @@ class TeamSelectMenu(discord.ui.Select):
|
|||||||
|
|
||||||
await self.rerender_players(interaction, turn_in_question)
|
await self.rerender_players(interaction, turn_in_question)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Bruker valgt!")
|
embed = embed_templates.success("Bruker valgt!")
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
||||||
|
|
||||||
|
|
||||||
@@ -279,7 +276,7 @@ class MoveTeamVoiceButton(discord.ui.Button):
|
|||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
|
if interaction.user != self.parent_view.teams[0][0] and interaction.user != self.parent_view.teams[1][0]:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare lagleder kan flytte laget!")
|
embed = embed_templates.error_warning("Bare lagleder kan flytte laget!")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if interaction.user == self.parent_view.teams[0][0]:
|
if interaction.user == self.parent_view.teams[0][0]:
|
||||||
@@ -295,21 +292,20 @@ class MoveTeamVoiceButton(discord.ui.Button):
|
|||||||
failed_users = await temp_voice.move_players(interaction, self.clicked[team["number"]], team["players"])
|
failed_users = await temp_voice.move_players(interaction, self.clicked[team["number"]], team["players"])
|
||||||
else:
|
else:
|
||||||
embed = embed_templates.error_warning(
|
embed = embed_templates.error_warning(
|
||||||
interaction, text=f"Kanal for lag {team['number']} allerede opprettet og spillere flyttet!"
|
f"Kanal for lag {team['number']} allerede opprettet og spillere flyttet!"
|
||||||
)
|
)
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if failed_users:
|
if failed_users:
|
||||||
failed_users = "\n".join([f"* {user.mention}" for user in failed_users])
|
failed_users = "\n".join([f"* {user.mention}" for user in failed_users])
|
||||||
embed = embed_templates.error_warning(
|
embed = embed_templates.error_warning(
|
||||||
interaction,
|
f"Klarte ikke å flytte følgende brukere til kanalen:\n{failed_users}\n\n"
|
||||||
text=f"Klarte ikke å flytte følgende brukere til kanalen:\n{failed_users}\n\n"
|
|
||||||
+ "Dette kan være fordi brukere(ne) ikke er koblet til en kanal fra før"
|
+ "Dette kan være fordi brukere(ne) ikke er koblet til en kanal fra før"
|
||||||
+ "eller at jeg ikke har tillatelse til å flytte",
|
+ "eller at jeg ikke har tillatelse til å flytte",
|
||||||
)
|
)
|
||||||
return await interaction.response.send_message(embed=embed, delete_after=10)
|
return await interaction.response.send_message(embed=embed, delete_after=10)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text=f"Flyttet lag {team['number']} til voice!")
|
embed = embed_templates.success(f"Flyttet lag {team['number']} til voice!")
|
||||||
await interaction.response.send_message(embed=embed, delete_after=10)
|
await interaction.response.send_message(embed=embed, delete_after=10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
import io
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
import discord
|
|
||||||
from discord import app_commands
|
|
||||||
from discord.ext import commands
|
|
||||||
from PIL import Image
|
|
||||||
from PIL import ImageDraw
|
|
||||||
from PIL import ImageFont
|
|
||||||
from PIL import ImageSequence
|
|
||||||
|
|
||||||
from cogs.utils import embed_templates
|
|
||||||
|
|
||||||
|
|
||||||
class The(commands.Cog):
|
|
||||||
"""
|
|
||||||
This cog lets you make a "the" barnacle boy laser eyes meme
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, bot: commands.Bot):
|
|
||||||
self.bot = bot
|
|
||||||
self.background_url = "https://i.kym-cdn.com/photos/images/newsfeed/001/777/063/d42.png"
|
|
||||||
self.x = 30
|
|
||||||
self.y = 340
|
|
||||||
|
|
||||||
async def fetch_image(self, url: str) -> io.BytesIO:
|
|
||||||
"""
|
|
||||||
Fetch the image from the URL
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
url (str): url
|
|
||||||
|
|
||||||
Returns
|
|
||||||
----------
|
|
||||||
(io.BytesIO): data buffer
|
|
||||||
"""
|
|
||||||
if not url:
|
|
||||||
return None
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
|
||||||
async with session.get(url) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
return None
|
|
||||||
data = io.BytesIO(await resp.read())
|
|
||||||
return data
|
|
||||||
|
|
||||||
def outline_text(
|
|
||||||
self,
|
|
||||||
draw: ImageDraw,
|
|
||||||
text: str,
|
|
||||||
font: ImageFont.FreeTypeFont,
|
|
||||||
x: int,
|
|
||||||
y: int,
|
|
||||||
thickness: int,
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Outlines given text
|
|
||||||
"""
|
|
||||||
|
|
||||||
for dx in range(-thickness, thickness + 1):
|
|
||||||
for dy in range(-thickness, thickness + 1):
|
|
||||||
if dx != 0 or dy != 0:
|
|
||||||
draw.text((x + dx, y + dy), text, font=font, fill="black")
|
|
||||||
draw.text((x, y), text, font=font, fill="white")
|
|
||||||
|
|
||||||
@app_commands.command()
|
|
||||||
async def the(
|
|
||||||
self, interaction: discord.Interaction, top_text: str = "", image_url: str = "", bottom_text: str = ""
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Generer en barnacle boy laser eyes THE mem basert på gitt bilde/tekst
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
caption (str): top text
|
|
||||||
image_url (str): image url
|
|
||||||
bottom_text (str): bottom text
|
|
||||||
|
|
||||||
Returns
|
|
||||||
----------
|
|
||||||
(discord.File): an image
|
|
||||||
"""
|
|
||||||
await interaction.response.defer()
|
|
||||||
gif = False
|
|
||||||
try:
|
|
||||||
bg_data = await self.fetch_image(self.background_url)
|
|
||||||
background = Image.open(bg_data)
|
|
||||||
|
|
||||||
image_data = await self.fetch_image(image_url)
|
|
||||||
|
|
||||||
if image_data:
|
|
||||||
image = Image.open(image_data)
|
|
||||||
|
|
||||||
if image.format.lower() == "gif":
|
|
||||||
gif = True
|
|
||||||
frames = []
|
|
||||||
for frame in ImageSequence.Iterator(image):
|
|
||||||
frame = frame.convert("RGBA").resize((200, 200), Image.ANTIALIAS)
|
|
||||||
new_frame = background.copy().convert("RGBA")
|
|
||||||
new_frame.alpha_composite(frame, (self.x, self.y))
|
|
||||||
frames.append(new_frame)
|
|
||||||
|
|
||||||
draw = ImageDraw.Draw(frames[0], "RGBA")
|
|
||||||
else:
|
|
||||||
image = image.convert("RGBA").resize((200, 200), Image.ANTIALIAS)
|
|
||||||
background = background.convert("RGBA")
|
|
||||||
background.alpha_composite(image, (self.x, self.y))
|
|
||||||
|
|
||||||
draw = ImageDraw.Draw(background, "RGBA")
|
|
||||||
|
|
||||||
font = ImageFont.truetype("./src/assets/fonts/impact.ttf", 120)
|
|
||||||
|
|
||||||
if top_text:
|
|
||||||
text_x, text_y = (210, -10)
|
|
||||||
self.outline_text(draw, top_text.upper(), font, text_x, text_y, thickness=5)
|
|
||||||
|
|
||||||
if bottom_text:
|
|
||||||
text_x, text_y = (150, 550)
|
|
||||||
self.outline_text(draw, bottom_text.upper(), font, text_x, text_y, thickness=5)
|
|
||||||
|
|
||||||
if gif:
|
|
||||||
with io.BytesIO() as output:
|
|
||||||
frames[0].save(
|
|
||||||
output,
|
|
||||||
format="GIF",
|
|
||||||
save_all=True,
|
|
||||||
append_images=frames[1:],
|
|
||||||
duration=image.info["duration"],
|
|
||||||
loop=0,
|
|
||||||
transparency=image.info.get("transparency", 0),
|
|
||||||
disposal=2, # Use 'restore to background color' disposal method
|
|
||||||
)
|
|
||||||
output.seek(0)
|
|
||||||
await interaction.followup.send(file=discord.File(output, "result.gif"))
|
|
||||||
else:
|
|
||||||
with io.BytesIO() as output:
|
|
||||||
background.save(output, format="PNG")
|
|
||||||
output.seek(0)
|
|
||||||
await interaction.followup.send(file=discord.File(output, "result.png"))
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.bot.logger.warning(f"An error occurred while processing the images: {e}")
|
|
||||||
traceback.print_exc()
|
|
||||||
await interaction.followup.send(
|
|
||||||
embed=embed_templates.error_fatal(
|
|
||||||
interaction, "Klarte ikke å generere mem! Har du gitt en ordentlig bildelenke?"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
"""
|
|
||||||
Add the cog to the bot on extension load
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
bot (commands.Bot): Bot instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
await bot.add_cog(The(bot))
|
|
||||||
@@ -34,10 +34,12 @@ class UserFacts(commands.Cog):
|
|||||||
"ESFP",
|
"ESFP",
|
||||||
}
|
}
|
||||||
self.mbti_list = list(self.mbti_codes)
|
self.mbti_list = list(self.mbti_codes)
|
||||||
self.similarity_matrix = self._create_similarity_matrix()
|
self.similarity_matrix = self.create_similarity_matrix()
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""Create the necessary tables for the birthday cog to work"""
|
"""
|
||||||
|
Create the necessary tables for the birthday cog to work
|
||||||
|
"""
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -51,6 +53,8 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
height_group = app_commands.Group(name="høyde", description="Se, endre eller fjern høyde for brukere på serveren")
|
height_group = app_commands.Group(name="høyde", description="Se, endre eller fjern høyde for brukere på serveren")
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@height_group.command(name="se", description="Se høyden til en bruker")
|
@height_group.command(name="se", description="Se høyden til en bruker")
|
||||||
async def height_see(self, interaction: discord.Interaction, bruker: discord.Member = None):
|
async def height_see(self, interaction: discord.Interaction, bruker: discord.Member = None):
|
||||||
"""
|
"""
|
||||||
@@ -76,7 +80,7 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
if not height:
|
if not height:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Brukeren har ikke lagt inn høyden sin")
|
embed=embed_templates.error_warning("Brukeren har ikke lagt inn høyden sin")
|
||||||
)
|
)
|
||||||
|
|
||||||
height = height[0]
|
height = height[0]
|
||||||
@@ -92,6 +96,8 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@height_group.command(name="sett", description="Sett høyden din")
|
@height_group.command(name="sett", description="Sett høyden din")
|
||||||
@app_commands.rename(height_cm="høyde_cm")
|
@app_commands.rename(height_cm="høyde_cm")
|
||||||
async def height_set(self, interaction: discord.Interaction, height_cm: app_commands.Range[int, 50, 250]):
|
async def height_set(self, interaction: discord.Interaction, height_cm: app_commands.Range[int, 50, 250]):
|
||||||
@@ -114,8 +120,10 @@ class UserFacts(commands.Cog):
|
|||||||
(interaction.user.id, height_cm, height_cm),
|
(interaction.user.id, height_cm, height_cm),
|
||||||
)
|
)
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed_templates.success(interaction, text="Høyde satt!"))
|
await interaction.response.send_message(embed=embed_templates.success("Høyde satt!"))
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@height_group.command(name="fjern", description="Fjern høyden din")
|
@height_group.command(name="fjern", description="Fjern høyden din")
|
||||||
async def height_remove(self, interaction: discord.Interaction):
|
async def height_remove(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -137,13 +145,13 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
if self.cursor.rowcount == 0:
|
if self.cursor.rowcount == 0:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.success(
|
embed=embed_templates.success("Du hadde ikke lagt inn høyden din fra før av men ok :)")
|
||||||
interaction, text="Du hadde ikke lagt inn høyden din fra før av men ok :)"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed_templates.success(interaction, text="Høyde fjernet!"))
|
await interaction.response.send_message(embed=embed_templates.success("Høyde fjernet!"))
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@height_group.command(name="leaderboard", description="Se en leaderboard over høyder")
|
@height_group.command(name="leaderboard", description="Se en leaderboard over høyder")
|
||||||
async def height_leaderboard(self, interaction: discord.Interaction):
|
async def height_leaderboard(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -164,17 +172,12 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
if not (result := self.cursor.fetchall()):
|
if not (result := self.cursor.fetchall()):
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Ingen har lagt inn høyden sin enda")
|
embed=embed_templates.error_warning("Ingen har lagt inn høyden sin enda")
|
||||||
)
|
)
|
||||||
|
|
||||||
result_formatted = list(
|
results_formatted = [f"**#{i+1}** <@{row[0]}> - `{row[1]}` cm" for i, row in enumerate(result)]
|
||||||
map(
|
|
||||||
lambda s: f"**#{s[0]+1}** <@{s[1][0]}> - `{s[1][1]}` cm",
|
|
||||||
enumerate(result),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
paginator = misc_utils.Paginator(result_formatted)
|
paginator = misc_utils.Paginator(results_formatted)
|
||||||
view = discord_utils.Scroller(paginator, interaction.user)
|
view = discord_utils.Scroller(paginator, interaction.user)
|
||||||
|
|
||||||
embed = view.construct_embed(discord.Embed(title="Våre høyeste og laveste (👑)"))
|
embed = view.construct_embed(discord.Embed(title="Våre høyeste og laveste (👑)"))
|
||||||
@@ -182,6 +185,8 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
mbti_group = app_commands.Group(name="mbti", description="Se, endre eller fjern MBTI for brukere på serveren")
|
mbti_group = app_commands.Group(name="mbti", description="Se, endre eller fjern MBTI for brukere på serveren")
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@mbti_group.command(name="se", description="Se MBTI-en til en bruker")
|
@mbti_group.command(name="se", description="Se MBTI-en til en bruker")
|
||||||
async def mbti_see(self, interaction: discord.Interaction, bruker: discord.Member = None):
|
async def mbti_see(self, interaction: discord.Interaction, bruker: discord.Member = None):
|
||||||
"""
|
"""
|
||||||
@@ -207,7 +212,7 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
if not user_mbti:
|
if not user_mbti:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Brukeren har ikke lagt inn MBTI-en sin")
|
embed=embed_templates.error_warning("Brukeren har ikke lagt inn MBTI-en sin")
|
||||||
)
|
)
|
||||||
|
|
||||||
user_mbti = user_mbti[0]
|
user_mbti = user_mbti[0]
|
||||||
@@ -235,7 +240,7 @@ class UserFacts(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
others.append((discord_id, mbti))
|
others.append((discord_id, mbti))
|
||||||
|
|
||||||
self._create_mbti_graph((bruker, user_mbti), others)
|
self.create_mbti_graph((bruker, user_mbti), others)
|
||||||
|
|
||||||
with open(f"src/assets/temp/{bruker.id}_mbti.png", "rb") as f:
|
with open(f"src/assets/temp/{bruker.id}_mbti.png", "rb") as f:
|
||||||
image = discord.File(f, filename=f"{bruker.id}_mbti.png")
|
image = discord.File(f, filename=f"{bruker.id}_mbti.png")
|
||||||
@@ -243,6 +248,8 @@ class UserFacts(commands.Cog):
|
|||||||
embed.set_image(url=f"attachment://{bruker.id}_mbti.png")
|
embed.set_image(url=f"attachment://{bruker.id}_mbti.png")
|
||||||
await interaction.response.send_message(embed=embed, file=image)
|
await interaction.response.send_message(embed=embed, file=image)
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@mbti_group.command(name="sett", description="Sett MBTI-en din")
|
@mbti_group.command(name="sett", description="Sett MBTI-en din")
|
||||||
async def mbti_set(self, interaction: discord.Interaction, mbti: str):
|
async def mbti_set(self, interaction: discord.Interaction, mbti: str):
|
||||||
"""
|
"""
|
||||||
@@ -255,9 +262,7 @@ class UserFacts(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if mbti.upper() not in self.mbti_codes:
|
if mbti.upper() not in self.mbti_codes:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(embed=embed_templates.error_warning("Ugyldig MBTI"))
|
||||||
embed=embed_templates.error_warning(interaction, text="Ugyldig MBTI")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
@@ -269,7 +274,7 @@ class UserFacts(commands.Cog):
|
|||||||
(interaction.user.id, mbti.upper(), mbti.upper()),
|
(interaction.user.id, mbti.upper(), mbti.upper()),
|
||||||
)
|
)
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed_templates.success(interaction, text="MBTI satt!"))
|
await interaction.response.send_message(embed=embed_templates.success("MBTI satt!"))
|
||||||
|
|
||||||
@mbti_set.autocomplete("mbti")
|
@mbti_set.autocomplete("mbti")
|
||||||
async def mbti_set_autocomplete_callback(self, interaction: discord.Interaction, current: str):
|
async def mbti_set_autocomplete_callback(self, interaction: discord.Interaction, current: str):
|
||||||
@@ -286,6 +291,8 @@ class UserFacts(commands.Cog):
|
|||||||
app_commands.Choice(name=mbti, value=mbti) for mbti in self.mbti_codes if mbti.startswith(current.upper())
|
app_commands.Choice(name=mbti, value=mbti) for mbti in self.mbti_codes if mbti.startswith(current.upper())
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@mbti_group.command(name="fjern", description="Fjern MBTI-en din")
|
@mbti_group.command(name="fjern", description="Fjern MBTI-en din")
|
||||||
async def mbti_remove(self, interaction: discord.Interaction):
|
async def mbti_remove(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -307,13 +314,13 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
if self.cursor.rowcount == 0:
|
if self.cursor.rowcount == 0:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.success(
|
embed=embed_templates.success("Du hadde ikke lagt inn MBTIen din fra før av men ok :)")
|
||||||
interaction, text="Du hadde ikke lagt inn MBTIen din fra før av men ok :)"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await interaction.response.send_message(embed=embed_templates.success(interaction, text="MBTI fjernet!"))
|
await interaction.response.send_message(embed=embed_templates.success("MBTI fjernet!"))
|
||||||
|
|
||||||
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@mbti_group.command(name="forklaring", description="Skjønner du ikke hva MBTI er? Her er en forklaring")
|
@mbti_group.command(name="forklaring", description="Skjønner du ikke hva MBTI er? Her er en forklaring")
|
||||||
async def mbti_explanation(self, interaction: discord.Interaction):
|
async def mbti_explanation(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -334,7 +341,7 @@ class UserFacts(commands.Cog):
|
|||||||
)
|
)
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
def _create_similarity_matrix(self):
|
def create_similarity_matrix(self):
|
||||||
def similarity(mbti_1, mbti_2):
|
def similarity(mbti_1, mbti_2):
|
||||||
similarity = 0
|
similarity = 0
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
@@ -352,7 +359,8 @@ class UserFacts(commands.Cog):
|
|||||||
|
|
||||||
return similarity_matrix
|
return similarity_matrix
|
||||||
|
|
||||||
def _create_mbti_graph(self, user_mbti: tuple[discord.Member, str], others: list[tuple[discord.Member, str]]):
|
# TODO: fix this. The distances are wrong
|
||||||
|
def create_mbti_graph(self, user_mbti: tuple[discord.Member, str], others: list[tuple[discord.Member, str]]):
|
||||||
user, mbti = user_mbti
|
user, mbti = user_mbti
|
||||||
|
|
||||||
# Create a Graphviz graph
|
# Create a Graphviz graph
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -10,40 +11,30 @@ from . import embed_templates
|
|||||||
from .misc_utils import Paginator
|
from .misc_utils import Paginator
|
||||||
|
|
||||||
|
|
||||||
def get_color(discord_object: discord.User | discord.Member | discord.Role) -> discord.Color:
|
async def send_as_txt_file(interaction: discord.Interaction, content: str, file_path: str):
|
||||||
"""
|
"""
|
||||||
Returns the user's top role color
|
Sends a string as a txt file and deletes the file afterwards
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
-----------
|
----------
|
||||||
discord_object (discord.User|discord.Member|discord.Role): A discord object that has a color attribute
|
interaction (discord.Interaction): Slash command context object
|
||||||
|
content (str): String that's too long to send
|
||||||
Returns
|
file_path (str): Path to file
|
||||||
-----------
|
|
||||||
(discord.Color): The user's displayed color
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if hasattr(discord_object, "color") and str(discord_object.color) != "#000000":
|
# Create file
|
||||||
return discord_object.color
|
with open(file_path, "w", encoding="utf-8") as file:
|
||||||
|
file.write(content)
|
||||||
|
|
||||||
return discord.Colour(0x99AAB5)
|
# Send file
|
||||||
|
txt_file = discord.File(file_path)
|
||||||
|
await interaction.response.send_message(file=txt_file)
|
||||||
|
|
||||||
|
# Delete file
|
||||||
async def sleep_until_midnight(bot):
|
try:
|
||||||
"""Syncs loop with the time of day"""
|
os.remove(file_path)
|
||||||
|
except OSError:
|
||||||
await bot.wait_until_ready()
|
pass
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
if now.hour > 0:
|
|
||||||
sleep_until = now + datetime.timedelta(days=1)
|
|
||||||
sleep_until = sleep_until.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
else:
|
|
||||||
sleep_until = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
|
|
||||||
bot.logger.info(f"Loop sleeping until {sleep_until}")
|
|
||||||
|
|
||||||
await discord.utils.sleep_until(sleep_until)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_file_bytesio(file: discord.Attachment | discord.Asset) -> BytesIO:
|
async def get_file_bytesio(file: discord.Attachment | discord.Asset) -> BytesIO:
|
||||||
@@ -240,37 +231,37 @@ class LobbyView(discord.ui.View):
|
|||||||
@discord.ui.button(label="Bli med", style=discord.ButtonStyle.blurple)
|
@discord.ui.button(label="Bli med", style=discord.ButtonStyle.blurple)
|
||||||
async def join_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def join_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if datetime.now() > self.lobby.ends:
|
if datetime.now() > self.lobby.ends:
|
||||||
embed = embed_templates.error_warning(interaction, text="Lobbyen har allerede startet")
|
embed = embed_templates.error_warning("Lobbyen har allerede startet")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
interaction.user.id in self.lobby.kicked_players
|
interaction.user.id in self.lobby.kicked_players
|
||||||
): # We have to use the ID here to avoid fetching the member object in the selection menu
|
): # We have to use the ID here to avoid fetching the member object in the selection menu
|
||||||
embed = embed_templates.error_warning(interaction, text="Du er blitt kastet ut av lobbyen")
|
embed = embed_templates.error_warning("Du er blitt kastet ut av lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if interaction.user in self.lobby.players:
|
if interaction.user in self.lobby.players:
|
||||||
embed = embed_templates.error_warning(interaction, text="Du er allerede i lobbyen")
|
embed = embed_templates.error_warning("Du er allerede i lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if len(self.lobby.players) >= 10:
|
if len(self.lobby.players) >= 10:
|
||||||
embed = embed_templates.error_warning(interaction, text="Lobbyen er full")
|
embed = embed_templates.error_warning("Lobbyen er full")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
self.lobby.players.append(interaction.user)
|
self.lobby.players.append(interaction.user)
|
||||||
await self.rerender_players(interaction)
|
await self.rerender_players(interaction)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Du har blitt med i lobbyen")
|
embed = embed_templates.success("Du har blitt med i lobbyen")
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
||||||
|
|
||||||
@discord.ui.button(label="Start", style=discord.ButtonStyle.green)
|
@discord.ui.button(label="Start", style=discord.ButtonStyle.green)
|
||||||
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def start_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if interaction.user != self.lobby.host:
|
if interaction.user != self.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan starte")
|
embed = embed_templates.error_warning("Bare hosten kan starte")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if len(self.lobby.players) < 3:
|
if len(self.lobby.players) < 3:
|
||||||
embed = embed_templates.error_warning(interaction, text="Det må være mer enn 2 i lobbyen")
|
embed = embed_templates.error_warning("Det må være mer enn 2 i lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
await self.end_lobby()
|
await self.end_lobby()
|
||||||
@@ -283,30 +274,30 @@ class LobbyView(discord.ui.View):
|
|||||||
async def leave_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def leave_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if interaction.user == self.lobby.host:
|
if interaction.user == self.lobby.host:
|
||||||
embed = embed_templates.error_warning(
|
embed = embed_templates.error_warning(
|
||||||
interaction, text="Du kan ikke forlate lobbyen som host. Slett lobbyen i stedet om ønskelig!"
|
"Du kan ikke forlate lobbyen som host. Slett lobbyen i stedet om ønskelig!"
|
||||||
)
|
)
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if interaction.user not in self.lobby.players:
|
if interaction.user not in self.lobby.players:
|
||||||
embed = embed_templates.error_warning(interaction, text="Du er ikke i lobbyen")
|
embed = embed_templates.error_warning("Du er ikke i lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
self.lobby.players.remove(interaction.user)
|
self.lobby.players.remove(interaction.user)
|
||||||
await self.rerender_players(interaction)
|
await self.rerender_players(interaction)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Du har forlatt lobbyen")
|
embed = embed_templates.success("Du har forlatt lobbyen")
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
||||||
|
|
||||||
@discord.ui.button(label="Slett", style=discord.ButtonStyle.red)
|
@discord.ui.button(label="Slett", style=discord.ButtonStyle.red)
|
||||||
async def delete_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
async def delete_lobby(self, interaction: discord.Interaction, button: discord.ui.Button):
|
||||||
if interaction.user != self.lobby.host:
|
if interaction.user != self.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan slette lobbyen")
|
embed = embed_templates.error_warning("Bare hosten kan slette lobbyen")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
self.lobby.ends = datetime.now()
|
self.lobby.ends = datetime.now()
|
||||||
await interaction.message.delete()
|
await interaction.message.delete()
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Lobbyen har blitt slettet")
|
embed = embed_templates.success("Lobbyen har blitt slettet")
|
||||||
await interaction.response.send_message(embed=embed, delete_after=5)
|
await interaction.response.send_message(embed=embed, delete_after=5)
|
||||||
|
|
||||||
|
|
||||||
@@ -332,11 +323,11 @@ class _KickSelectMenu(discord.ui.Select):
|
|||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
async def callback(self, interaction: discord.Interaction):
|
||||||
if interaction.user != self.parent_view.lobby.host:
|
if interaction.user != self.parent_view.lobby.host:
|
||||||
embed = embed_templates.error_warning(interaction, text="Bare hosten kan kicke spillere")
|
embed = embed_templates.error_warning("Bare hosten kan kicke spillere")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
if str(interaction.user.id) in interaction.data["values"][0]:
|
if str(interaction.user.id) in interaction.data["values"][0]:
|
||||||
embed = embed_templates.error_warning(interaction, text="Du kan ikke kicke deg selv")
|
embed = embed_templates.error_warning("Du kan ikke kicke deg selv")
|
||||||
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
return await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=10)
|
||||||
|
|
||||||
self.parent_view.lobby.kicked_players.append(int(interaction.data["values"][0]))
|
self.parent_view.lobby.kicked_players.append(int(interaction.data["values"][0]))
|
||||||
@@ -352,7 +343,7 @@ class _KickSelectMenu(discord.ui.Select):
|
|||||||
|
|
||||||
await self.rerender_players(interaction)
|
await self.rerender_players(interaction)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, text="Brukeren har blitt sparket fra lobbyen")
|
embed = embed_templates.success("Brukeren har blitt sparket fra lobbyen")
|
||||||
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
await interaction.response.send_message(embed=embed, ephemeral=True, delete_after=5)
|
||||||
|
|
||||||
|
|
||||||
@@ -438,7 +429,7 @@ class TempVoiceHelper:
|
|||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
self.bot.logger.error(f"Failed to create temporary voice channel in {interaction.guild}")
|
self.bot.logger.error(f"Failed to create temporary voice channel in {interaction.guild}")
|
||||||
await interaction.response.send_message(
|
await interaction.response.send_message(
|
||||||
embed=embed_templates.error_fatal(interaction, text="Jeg har ikke tilgang til å opprette en talekanal")
|
embed=embed_templates.error_fatal("Jeg har ikke tilgang til å opprette en talekanal")
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,12 @@
|
|||||||
import discord
|
import discord
|
||||||
|
|
||||||
|
|
||||||
def default_footer(interaction: discord.Interaction, embed: discord.Embed) -> discord.Embed:
|
def error_warning(text: str) -> discord.Embed:
|
||||||
"""
|
|
||||||
Sets a footer containing the command invoker's name, discriminator and avatar
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
-----------
|
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
embed (discord.Embed): An embed object to add the footer to
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-----------
|
|
||||||
(discord.Embed): The passed in embed + footer
|
|
||||||
"""
|
|
||||||
|
|
||||||
return embed.set_footer(icon_url=interaction.user.avatar, text=interaction.user.name)
|
|
||||||
|
|
||||||
|
|
||||||
def error_warning(interaction: discord.Interaction, 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
|
||||||
-----------
|
-----------
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
text (str): The error message
|
text (str): The error message
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -33,18 +15,15 @@ def error_warning(interaction: discord.Interaction, text: str) -> discord.Embed:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
embed = discord.Embed(color=discord.Color.gold(), description=f"⚠️ {text}")
|
embed = discord.Embed(color=discord.Color.gold(), description=f"⚠️ {text}")
|
||||||
default_footer(interaction, embed)
|
|
||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
|
||||||
def error_fatal(interaction: discord.Interaction, text: str) -> discord.Embed:
|
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
|
||||||
-----------
|
-----------
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
text (str): The error message
|
text (str): The error message
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -53,18 +32,15 @@ def error_fatal(interaction: discord.Interaction, text: str) -> discord.Embed:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
embed = discord.Embed(color=discord.Color.red(), description=f"❌ {text}")
|
embed = discord.Embed(color=discord.Color.red(), description=f"❌ {text}")
|
||||||
default_footer(interaction, embed)
|
|
||||||
|
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
|
|
||||||
def success(interaction: discord.Interaction, text: str) -> discord.Embed:
|
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
|
||||||
-----------
|
-----------
|
||||||
interaction (discord.Interaction): Slash command context object
|
|
||||||
text (str): The message
|
text (str): The message
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
@@ -73,6 +49,4 @@ def success(interaction: discord.Interaction, text: str) -> discord.Embed:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}")
|
embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}")
|
||||||
default_footer(interaction, embed)
|
|
||||||
|
|
||||||
return embed
|
return embed
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
|
import datetime
|
||||||
|
import textwrap
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from PIL import ImageDraw
|
||||||
|
from PIL import ImageFont
|
||||||
|
|
||||||
|
MIDNIGHT = datetime.time(hour=19, minute=48, tzinfo=ZoneInfo("Europe/Oslo"))
|
||||||
|
|
||||||
|
|
||||||
class Paginator:
|
class Paginator:
|
||||||
|
"""Interface for managing content. Divides your content into pages of 10 items each"""
|
||||||
|
|
||||||
def __init__(self, content: list):
|
def __init__(self, content: list):
|
||||||
|
"""
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
content (list): The content you want to paginate
|
||||||
|
"""
|
||||||
|
|
||||||
self.content = content
|
self.content = content
|
||||||
self.total_page_count = ceil(len(content) / 10)
|
self.total_page_count = ceil(len(content) / 10)
|
||||||
self.current_page = 1
|
self.current_page = 1
|
||||||
@@ -19,6 +38,7 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(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:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -35,6 +55,7 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(list): The current page of the paginator
|
(list): The current page of the paginator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.get_page(self.current_page)
|
return self.get_page(self.current_page)
|
||||||
|
|
||||||
def next_page(self) -> list | None:
|
def next_page(self) -> list | None:
|
||||||
@@ -45,6 +66,7 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(list): The next page of the paginator
|
(list): The next page of the paginator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.current_page += 1
|
self.current_page += 1
|
||||||
return self.get_page(self.current_page)
|
return self.get_page(self.current_page)
|
||||||
|
|
||||||
@@ -56,6 +78,7 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(list): The previous page of the paginator
|
(list): The previous page of the paginator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.current_page -= 1
|
self.current_page -= 1
|
||||||
return self.get_page(self.current_page)
|
return self.get_page(self.current_page)
|
||||||
|
|
||||||
@@ -67,6 +90,7 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(list): The first page of the paginator
|
(list): The first page of the paginator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.current_page = 1
|
self.current_page = 1
|
||||||
return self.get_page(self.current_page)
|
return self.get_page(self.current_page)
|
||||||
|
|
||||||
@@ -78,5 +102,75 @@ class Paginator:
|
|||||||
----------
|
----------
|
||||||
(list): The last page of the paginator
|
(list): The last page of the paginator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.current_page = self.total_page_count
|
self.current_page = self.total_page_count
|
||||||
return self.get_page(self.current_page)
|
return self.get_page(self.current_page)
|
||||||
|
|
||||||
|
|
||||||
|
async def put_text_in_box(
|
||||||
|
image: np.ndarray,
|
||||||
|
text: str,
|
||||||
|
top_left: tuple,
|
||||||
|
bottom_right: tuple,
|
||||||
|
font_path: str = "./src/assets/fonts/impact.ttf",
|
||||||
|
font_size: int = 10,
|
||||||
|
color: tuple = (0, 0, 0),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Puts text inside a bounding box on the cv2 image with automatic text wrapping, using PIL for text rendering.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
image (np.ndarray): Source cv2 image.
|
||||||
|
text (str): Text to be put on the image.
|
||||||
|
top_left (tuple): (x, y) Top-left corner of the bounding box.
|
||||||
|
bottom_right (tuple): (x, y) Bottom-right corner of the bounding box.
|
||||||
|
font_path (str): Path to the font file. Defaults to Impact font.
|
||||||
|
font_size (int): Initial font size. Defaults to 10.
|
||||||
|
color (tuple): Text color in RGB. Defaults to (0, 0, 0).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Convert the cv2 image to a PIL image
|
||||||
|
image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
|
||||||
|
draw = ImageDraw.Draw(image_pil)
|
||||||
|
|
||||||
|
# Get coordinates of the bounding box
|
||||||
|
x1, y1 = top_left
|
||||||
|
x2, y2 = bottom_right
|
||||||
|
|
||||||
|
# Get the width and height of the bounding box
|
||||||
|
# We multiply by 0.9 to give some padding
|
||||||
|
w, h = (x2 - x1) * 0.9, (y2 - y1) * 0.9
|
||||||
|
|
||||||
|
# Initialize font width/height to 0 in order to run the while loops below at least once
|
||||||
|
font_width = 0
|
||||||
|
font_height = 0
|
||||||
|
|
||||||
|
# Add newlines to the text. Here we have gone with 15 characters per line
|
||||||
|
# This is arbitrary, but seems to work well
|
||||||
|
text = textwrap.fill(text, width=15)
|
||||||
|
|
||||||
|
# Increase the font size until the longest line of text does not fit in the bounding box anymore
|
||||||
|
# We also have to check the height, since the text might be too tall for the box
|
||||||
|
while font_width < w and font_height < h:
|
||||||
|
font = ImageFont.truetype(font_path, font_size)
|
||||||
|
|
||||||
|
longest_line = max(text.split("\n"), key=lambda x: font.getlength(x))
|
||||||
|
font_width = font.getlength(longest_line)
|
||||||
|
|
||||||
|
# bbox doesn't work with multiline text, so we have to calculate the height manually
|
||||||
|
# take the height of the bounding box, multiply by 1.5 (our assumed line dividor height)
|
||||||
|
# after that, we multiply by the number of lines
|
||||||
|
font_box = font.getbbox(text)
|
||||||
|
font_height = ((font_box[3] - font_box[1]) * 1.5) * text.count("\n")
|
||||||
|
|
||||||
|
font_size += 1
|
||||||
|
|
||||||
|
# Actually draw the text
|
||||||
|
draw.multiline_textbbox((w, h), text=text, font=font, align="center", anchor="mm")
|
||||||
|
center_of_box = ((x1 + x2) // 2, (y1 + y2) // 2)
|
||||||
|
draw.multiline_text(center_of_box, text, font=font, fill=color, align="center", anchor="mm")
|
||||||
|
|
||||||
|
# Convert back to cv2 image format and update the original image
|
||||||
|
cv2_image = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
|
||||||
|
np.copyto(image, cv2_image)
|
||||||
@@ -53,9 +53,8 @@ async def fetch_article(title: str):
|
|||||||
# This is wildly inefficent but I don't. fucking. care. Just make it work
|
# This is wildly inefficent but I don't. fucking. care. Just make it work
|
||||||
text = regex.sub(
|
text = regex.sub(
|
||||||
r"(?=\{)(\{([^{}]|(?1))*\})|\[\[(Kategori|Fil):.+?\]\]|\{.+?\}", "", text, flags=regex.MULTILINE | regex.DOTALL
|
r"(?=\{)(\{([^{}]|(?1))*\})|\[\[(Kategori|Fil):.+?\]\]|\{.+?\}", "", text, flags=regex.MULTILINE | regex.DOTALL
|
||||||
) # Remove templates and categories
|
) # Remove templates, categories and images
|
||||||
text = pypandoc.convert_text(text, "markdown", format="mediawiki")
|
text = pypandoc.convert_text(text, "markdown", format="mediawiki")
|
||||||
print(text)
|
|
||||||
text = regex.sub(
|
text = regex.sub(
|
||||||
r"\[(.+?)\]\(.+? \"wikilink\"\)", r"\1", text, flags=regex.MULTILINE | regex.DOTALL
|
r"\[(.+?)\]\(.+? \"wikilink\"\)", r"\1", text, flags=regex.MULTILINE | regex.DOTALL
|
||||||
) # Pandoc adds alt text to links. discord doesn't support that
|
) # Pandoc adds alt text to links. discord doesn't support that
|
||||||
@@ -107,7 +106,7 @@ class Viteboka(commands.Cog):
|
|||||||
|
|
||||||
viteboka_group = app_commands.Group(name="viteboka", description="Søk i Viteboka etter informasjon")
|
viteboka_group = app_commands.Group(name="viteboka", description="Søk i Viteboka etter informasjon")
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 10)
|
@app_commands.checks.cooldown(1, 10)
|
||||||
@viteboka_group.command(name="søk", description="Søk etter en artikkel i Viteboka")
|
@viteboka_group.command(name="søk", description="Søk etter en artikkel i Viteboka")
|
||||||
async def search(self, interaction: discord.Interaction, søkestreng: str):
|
async def search(self, interaction: discord.Interaction, søkestreng: str):
|
||||||
@@ -131,7 +130,7 @@ class Viteboka(commands.Cog):
|
|||||||
response = requests.get(url=API_URL, params=params)
|
response = requests.get(url=API_URL, params=params)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
self.bot.logger.error(f"Failed to search for articles with query {søkestreng}: {response.status_code}")
|
self.bot.logger.error(f"Failed to search for articles with query {søkestreng}: {response.status_code}")
|
||||||
embed = embed_templates.error_fatal(interaction, text="Klarte ikke å søke etter artikler")
|
embed = embed_templates.error_fatal("Klarte ikke å søke etter artikler")
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@@ -139,7 +138,7 @@ class Viteboka(commands.Cog):
|
|||||||
|
|
||||||
if not search_results:
|
if not search_results:
|
||||||
self.bot.logger.info(f"No articles found with query {søkestreng}")
|
self.bot.logger.info(f"No articles found with query {søkestreng}")
|
||||||
embed = embed_templates.error_fatal(interaction, text="Fant ingen artikler som matcher søket")
|
embed = embed_templates.error_warning("Fant ingen artikler som matcher søket")
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
if len(search_results) == 1:
|
if len(search_results) == 1:
|
||||||
@@ -160,16 +159,13 @@ class Viteboka(commands.Cog):
|
|||||||
title, url, text, image = await fetch_article(søkestreng)
|
title, url, text, image = await fetch_article(søkestreng)
|
||||||
except VitebokaException as e:
|
except VitebokaException as e:
|
||||||
self.bot.logger.error(f"Failed to fetch article {søkestreng}: {e}")
|
self.bot.logger.error(f"Failed to fetch article {søkestreng}: {e}")
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_fatal(str(e))
|
||||||
interaction,
|
|
||||||
text=str(e),
|
|
||||||
)
|
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
embed = viteboka_embed(title, url, text, image)
|
embed = viteboka_embed(title, url, text, image)
|
||||||
await interaction.followup.send(embed=embed)
|
await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@app_commands.checks.cooldown(1, 5)
|
@app_commands.checks.cooldown(1, 5)
|
||||||
@viteboka_group.command(name="tilfeldig", description="Få en tilfeldig artikkel fra Viteboka")
|
@viteboka_group.command(name="tilfeldig", description="Få en tilfeldig artikkel fra Viteboka")
|
||||||
async def random(self, interaction: discord.Interaction):
|
async def random(self, interaction: discord.Interaction):
|
||||||
@@ -194,7 +190,7 @@ class Viteboka(commands.Cog):
|
|||||||
response = requests.get(url=API_URL, params=params)
|
response = requests.get(url=API_URL, params=params)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
self.bot.logger.error(f"Failed to fetch random article: {response.status_code}")
|
self.bot.logger.error(f"Failed to fetch random article: {response.status_code}")
|
||||||
embed = embed_templates.error_fatal(interaction, text="Klarte ikke å søke etter en tilfeldig artikkel")
|
embed = embed_templates.error_fatal("Klarte ikke å søke etter en tilfeldig artikkel")
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@@ -204,10 +200,7 @@ class Viteboka(commands.Cog):
|
|||||||
title, url, text, image = await fetch_article(random_article_title)
|
title, url, text, image = await fetch_article(random_article_title)
|
||||||
except VitebokaException as e:
|
except VitebokaException as e:
|
||||||
self.bot.logger.error(f"Failed to fetch article {random_article_title}: {e}")
|
self.bot.logger.error(f"Failed to fetch article {random_article_title}: {e}")
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_fatal(str(e))
|
||||||
interaction,
|
|
||||||
text=str(e),
|
|
||||||
)
|
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
|
|
||||||
embed = viteboka_embed(title, url, text, image)
|
embed = viteboka_embed(title, url, text, image)
|
||||||
@@ -254,10 +247,7 @@ class ArticleButton(discord.ui.Button):
|
|||||||
try:
|
try:
|
||||||
title, url, text, image = await fetch_article(self.article_title)
|
title, url, text, image = await fetch_article(self.article_title)
|
||||||
except VitebokaException as e:
|
except VitebokaException as e:
|
||||||
embed = embed_templates.error_fatal(
|
embed = embed_templates.error_fatal(str(e))
|
||||||
interaction,
|
|
||||||
text=str(e),
|
|
||||||
)
|
|
||||||
return await interaction.followup.send(embed=embed)
|
return await interaction.followup.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
embed = viteboka_embed(title, url, text, image)
|
embed = viteboka_embed(title, url, text, image)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from discord.ext import commands
|
|||||||
|
|
||||||
|
|
||||||
class WebsiteEvents(commands.Cog):
|
class WebsiteEvents(commands.Cog):
|
||||||
"""Miscellaneous commands that don"t fit anywhere else"""
|
"""Uploads Discord events to Sanity CMS"""
|
||||||
|
|
||||||
def __init__(self, bot: commands.Bot):
|
def __init__(self, bot: commands.Bot):
|
||||||
"""
|
"""
|
||||||
@@ -57,6 +57,7 @@ class WebsiteEvents(commands.Cog):
|
|||||||
|
|
||||||
self.bot.logger.info("Finished syncing events! Note errors may have occured")
|
self.bot.logger.info("Finished syncing events! Note errors may have occured")
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_scheduled_event_create")
|
||||||
async def create_event(self, event: discord.ScheduledEvent):
|
async def create_event(self, event: discord.ScheduledEvent):
|
||||||
"""
|
"""
|
||||||
Creates an event in Sanity CMS when an event is added on Discord
|
Creates an event in Sanity CMS when an event is added on Discord
|
||||||
@@ -67,8 +68,9 @@ class WebsiteEvents(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Convert timezone to Europe/Oslo
|
# Convert timezone to Europe/Oslo
|
||||||
time = event.start_time.replace(tzinfo=pytz.utc) # Make sure the datetime object contains the timezone info
|
# We need to set a timezone unit before being able to convert it to another timezone
|
||||||
time = event.start_time.astimezone(pytz.timezone("Europe/Oslo"))
|
# That's why this line is so fucking weird
|
||||||
|
time = event.start_time.replace(tzinfo=pytz.utc).astimezone(pytz.timezone("Europe/Oslo"))
|
||||||
|
|
||||||
document = {
|
document = {
|
||||||
"_type": "event",
|
"_type": "event",
|
||||||
@@ -94,6 +96,7 @@ class WebsiteEvents(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
self.bot.logger.error(f"Failed to create event in Sanity with ID: {event.id}. Response: {response.text}")
|
self.bot.logger.error(f"Failed to create event in Sanity with ID: {event.id}. Response: {response.text}")
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_scheduled_event_delete")
|
||||||
async def delete_event(self, event: discord.ScheduledEvent):
|
async def delete_event(self, event: discord.ScheduledEvent):
|
||||||
"""
|
"""
|
||||||
Deletes an event in Sanity CMS when an event is deleted on Discord
|
Deletes an event in Sanity CMS when an event is deleted on Discord
|
||||||
@@ -111,6 +114,7 @@ class WebsiteEvents(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
self.bot.logger.error(f"Failed to delete event in Sanity with ID: {event.id}. Response: {response.text}")
|
self.bot.logger.error(f"Failed to delete event in Sanity with ID: {event.id}. Response: {response.text}")
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_scheduled_event_update")
|
||||||
async def update_event(self, before: discord.ScheduledEvent, after: discord.ScheduledEvent):
|
async def update_event(self, before: discord.ScheduledEvent, after: discord.ScheduledEvent):
|
||||||
"""
|
"""
|
||||||
Updates an event in Sanity when edited on discord
|
Updates an event in Sanity when edited on discord
|
||||||
@@ -141,7 +145,4 @@ async def setup(bot: commands.Bot):
|
|||||||
bot (commands.Bot): Bot instance
|
bot (commands.Bot): Bot instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot.add_listener(WebsiteEvents(bot).create_event, "on_scheduled_event_create")
|
|
||||||
bot.add_listener(WebsiteEvents(bot).update_event, "on_scheduled_event_update")
|
|
||||||
bot.add_listener(WebsiteEvents(bot).delete_event, "on_scheduled_event_delete")
|
|
||||||
await bot.add_cog(WebsiteEvents(bot))
|
await bot.add_cog(WebsiteEvents(bot))
|
||||||
@@ -20,8 +20,6 @@ from wordcloud import WordCloud as WCloud # Avoid naming conflicts with cog cla
|
|||||||
|
|
||||||
from cogs.utils import embed_templates
|
from cogs.utils import embed_templates
|
||||||
|
|
||||||
# from wordcloud import ImageColorGenerator
|
|
||||||
|
|
||||||
|
|
||||||
class WordCloud(commands.Cog):
|
class WordCloud(commands.Cog):
|
||||||
"""Generate a wordcloud based on the most frequent words posted"""
|
"""Generate a wordcloud based on the most frequent words posted"""
|
||||||
@@ -41,12 +39,16 @@ class WordCloud(commands.Cog):
|
|||||||
# The user's defaultdict has a default value of 0
|
# The user's defaultdict has a default value of 0
|
||||||
self.word_freq_cache = defaultdict(lambda: defaultdict(int))
|
self.word_freq_cache = defaultdict(lambda: defaultdict(int))
|
||||||
|
|
||||||
|
# We cache the consenting users to avoid querying the database every 16:36
|
||||||
|
# It's frail but it works unless you have a skill issue
|
||||||
self.consenting_users = []
|
self.consenting_users = []
|
||||||
self.populate_consenting_users()
|
self.populate_consenting_users()
|
||||||
|
|
||||||
nltk.download("stopwords")
|
nltk.download("stopwords")
|
||||||
|
|
||||||
self.batch_update_word_freqs_loop.start()
|
self.insert_cache_loop.start()
|
||||||
|
|
||||||
|
self.MSG_NO_DATA = "Fant ingen data om deg" # SonarCloud recommended this LMAO. Makes sense but also not
|
||||||
|
|
||||||
def init_db(self):
|
def init_db(self):
|
||||||
"""
|
"""
|
||||||
@@ -78,8 +80,8 @@ class WordCloud(commands.Cog):
|
|||||||
Insert cache to db, stop tasks and close the database connection on cog unload
|
Insert cache to db, stop tasks and close the database connection on cog unload
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await self.batch_update_word_freqs()
|
self.bot.logger.info("Unloading cog")
|
||||||
self.batch_update_word_freqs_loop.cancel()
|
self.insert_cache_loop.cancel()
|
||||||
self.cursor.close()
|
self.cursor.close()
|
||||||
|
|
||||||
def populate_consenting_users(self):
|
def populate_consenting_users(self):
|
||||||
@@ -95,7 +97,7 @@ class WordCloud(commands.Cog):
|
|||||||
)
|
)
|
||||||
self.consenting_users = [row[0] for row in self.cursor.fetchall()]
|
self.consenting_users = [row[0] for row in self.cursor.fetchall()]
|
||||||
|
|
||||||
async def batch_update_word_freqs(self):
|
async def insert_cache(self):
|
||||||
"""
|
"""
|
||||||
Inserts cached word frequencies into the database.
|
Inserts cached word frequencies into the database.
|
||||||
We do this in order to prevent excess database writes
|
We do this in order to prevent excess database writes
|
||||||
@@ -126,20 +128,25 @@ class WordCloud(commands.Cog):
|
|||||||
word_freqs,
|
word_freqs,
|
||||||
)
|
)
|
||||||
except psycopg2.Error as err:
|
except psycopg2.Error as err:
|
||||||
self.bot.db_connection.rollback()
|
|
||||||
self.bot.logger.error(f"Failed to insert wordcloud cache into database - {err}")
|
self.bot.logger.error(f"Failed to insert wordcloud cache into database - {err}")
|
||||||
|
|
||||||
self.word_freq_cache.clear()
|
self.word_freq_cache.clear()
|
||||||
|
|
||||||
@tasks.loop(minutes=20)
|
@tasks.loop(minutes=20)
|
||||||
async def batch_update_word_freqs_loop(self):
|
async def insert_cache_loop(self):
|
||||||
"""
|
"""
|
||||||
Clears and inserts word frequency cache into the database every 20 minutes
|
Clears and inserts word frequency cache into the database every 20 minutes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.bot.logger.info("Dumping word cloud cache to database...")
|
self.bot.logger.info("Dumping word cloud cache to database...")
|
||||||
await self.batch_update_word_freqs()
|
await self.insert_cache()
|
||||||
|
|
||||||
|
@insert_cache_loop.after_loop
|
||||||
|
async def on_insert_cache_loop_cancel(self):
|
||||||
|
if self.insert_cache_loop.is_being_cancelled() and self.word_freq_cache:
|
||||||
|
await self.insert_cache()
|
||||||
|
|
||||||
|
@commands.Cog.listener("on_message")
|
||||||
async def word_freq_listener(self, message: discord.Message):
|
async def word_freq_listener(self, message: discord.Message):
|
||||||
"""
|
"""
|
||||||
Listens for all messages of consenting users
|
Listens for all messages of consenting users
|
||||||
@@ -149,17 +156,52 @@ class WordCloud(commands.Cog):
|
|||||||
message (discord.Message): Message to count
|
message (discord.Message): Message to count
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if message.author.id not in self.consenting_users:
|
if not self.can_count_message(message):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore bot commands
|
tokens = self.tokenize(message.clean_content)
|
||||||
# This is a very naive approach but it works for our use case
|
|
||||||
if not message.clean_content[:2].isalpha():
|
for token in tokens:
|
||||||
return
|
# Enter into cache
|
||||||
|
# This may take a performance hit but who tf cares. We're using python anyway
|
||||||
|
self.word_freq_cache[message.author.id][token] += 1
|
||||||
|
|
||||||
|
def can_count_message(self, message: discord.Message) -> bool:
|
||||||
|
"""
|
||||||
|
Check if a message can be counted
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
message (discord.Message): Message to check
|
||||||
|
|
||||||
|
Returns
|
||||||
|
----------
|
||||||
|
bool: Whether the message can be counted
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
message.author.id in self.consenting_users
|
||||||
|
and not message.clean_content[:2].isalpha() # Naive check for traditional bot commands
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenize(self, text: str) -> list[str]:
|
||||||
|
"""
|
||||||
|
Tokenize a text into words
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
text (str): Text to tokenize
|
||||||
|
|
||||||
|
Returns
|
||||||
|
----------
|
||||||
|
list[str]: List of words
|
||||||
|
"""
|
||||||
|
|
||||||
|
tokens = []
|
||||||
|
|
||||||
# Divide into words
|
# Divide into words
|
||||||
# Also very naive but luckily we only speak norwegian and english :)
|
# Very naive but luckily we only speak norwegian and english :)
|
||||||
words = message.clean_content.split(" ")
|
words = text.split(" ")
|
||||||
|
|
||||||
for word in words:
|
for word in words:
|
||||||
# Filter urls
|
# Filter urls
|
||||||
@@ -172,9 +214,9 @@ class WordCloud(commands.Cog):
|
|||||||
if not word:
|
if not word:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Enter into cache
|
tokens.append(word)
|
||||||
# This may take a performance hit but who tf cares. We're using python anyway
|
|
||||||
self.word_freq_cache[message.author.id][word] += 1
|
return tokens
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_wordcloud(text: str, max_words: int = 4000, allow_bigrams: bool = False) -> BytesIO:
|
def generate_wordcloud(text: str, max_words: int = 4000, allow_bigrams: bool = False) -> BytesIO:
|
||||||
@@ -193,7 +235,7 @@ class WordCloud(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
filter_words = set(stopwords.words("norwegian") + stopwords.words("english"))
|
filter_words = set(stopwords.words("norwegian") + stopwords.words("english"))
|
||||||
mask = np.array(Image.open("./src/assets/word_cloud/mask.png"))
|
mask = np.array(Image.open("./src/assets/word_cloud_mask.png"))
|
||||||
|
|
||||||
wc = WCloud(
|
wc = WCloud(
|
||||||
max_words=max_words,
|
max_words=max_words,
|
||||||
@@ -223,6 +265,7 @@ class WordCloud(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@wordcloud_group.command(name="samtykke", description="Gi samtykke til å samle meldingsdataen din for ordskyer")
|
@wordcloud_group.command(name="samtykke", description="Gi samtykke til å samle meldingsdataen din for ordskyer")
|
||||||
async def consent(self, interaction: discord.Interaction):
|
async def consent(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -235,7 +278,7 @@ class WordCloud(commands.Cog):
|
|||||||
|
|
||||||
if interaction.user.id in self.consenting_users:
|
if interaction.user.id in self.consenting_users:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Du har allerede samtykket"), ephemeral=False
|
embed=embed_templates.error_warning("Du har allerede samtykket"), ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -250,7 +293,7 @@ class WordCloud(commands.Cog):
|
|||||||
self.bot.db_connection.rollback()
|
self.bot.db_connection.rollback()
|
||||||
self.bot.logger.error(f"Failed to insert wordcloud metadata into database - {err}")
|
self.bot.logger.error(f"Failed to insert wordcloud metadata into database - {err}")
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Klarte ikke å skrive til database"),
|
embed=embed_templates.error_fatal("Klarte ikke å skrive til database"),
|
||||||
ephemeral=False,
|
ephemeral=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -260,6 +303,7 @@ class WordCloud(commands.Cog):
|
|||||||
await interaction.response.send_message(embed=embed, ephemeral=False)
|
await interaction.response.send_message(embed=embed, ephemeral=False)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True)
|
||||||
|
@app_commands.checks.cooldown(1, 2)
|
||||||
@wordcloud_group.command(name="slett", description="Fjern samtykke og slett alle dine ordskydata")
|
@wordcloud_group.command(name="slett", description="Fjern samtykke og slett alle dine ordskydata")
|
||||||
async def consent_remove(self, interaction: discord.Interaction):
|
async def consent_remove(self, interaction: discord.Interaction):
|
||||||
"""
|
"""
|
||||||
@@ -274,7 +318,7 @@ class WordCloud(commands.Cog):
|
|||||||
self.consenting_users.remove(interaction.user.id)
|
self.consenting_users.remove(interaction.user.id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Fant ingen data om deg"), ephemeral=False
|
embed=embed_templates.error_warning(self.MSG_NO_DATA), ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
self.word_freq_cache.pop(f"{interaction.user.id}", None)
|
self.word_freq_cache.pop(f"{interaction.user.id}", None)
|
||||||
@@ -291,11 +335,11 @@ class WordCloud(commands.Cog):
|
|||||||
self.bot.db_connection.rollback()
|
self.bot.db_connection.rollback()
|
||||||
self.bot.logger.error(f"Failed to delete wordcloud metadata from database - {err}")
|
self.bot.logger.error(f"Failed to delete wordcloud metadata from database - {err}")
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Klarte ikke å slette fra database"),
|
embed=embed_templates.error_fatal("Klarte ikke å slette fra database"),
|
||||||
ephemeral=False,
|
ephemeral=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
embed = embed_templates.success(interaction, "Meldingsdata er slettet!")
|
embed = embed_templates.success("Meldingsdata er slettet!")
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@@ -323,16 +367,18 @@ class WordCloud(commands.Cog):
|
|||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return await interaction.response.send_message(
|
return await interaction.response.send_message(
|
||||||
embed=embed_templates.error_warning(interaction, text="Fant ingen data om deg"), ephemeral=False
|
embed=embed_templates.error_warning(self.MSG_NO_DATA), ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
freq_list = {f"{word}": freq for word, freq in result}
|
freq_list = {f"{word}": freq for word, freq in result}
|
||||||
|
|
||||||
embed = discord.Embed(description="Her er dataen jeg har lagret om deg")
|
# Create Json file
|
||||||
buffer = StringIO()
|
buffer = StringIO()
|
||||||
buffer.write(json.dumps(freq_list, indent=4))
|
buffer.write(json.dumps(freq_list, indent=4))
|
||||||
buffer.seek(0)
|
buffer.seek(0)
|
||||||
file = discord.File(buffer, filename=f"uiog_word_freqs_{interaction.user.id}.json")
|
file = discord.File(buffer, filename=f"uiog_word_freqs_{interaction.user.id}.json")
|
||||||
|
|
||||||
|
embed = discord.Embed(description="Her er dataen jeg har lagret om deg")
|
||||||
await interaction.response.send_message(embed=embed, file=file, ephemeral=True)
|
await interaction.response.send_message(embed=embed, file=file, ephemeral=True)
|
||||||
|
|
||||||
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
@app_commands.checks.bot_has_permissions(embed_links=True, attach_files=True)
|
||||||
@@ -352,7 +398,7 @@ class WordCloud(commands.Cog):
|
|||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Clear cache first to ensure correct count
|
# Clear cache first to ensure correct count
|
||||||
await self.batch_update_word_freqs()
|
await self.insert_cache()
|
||||||
|
|
||||||
# Fetch word count from database
|
# Fetch word count from database
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
@@ -367,10 +413,12 @@ class WordCloud(commands.Cog):
|
|||||||
|
|
||||||
if not results:
|
if not results:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(
|
||||||
embed=embed_templates.error_warning(interaction, text="Fant ingen data om deg"), ephemeral=False
|
embed=embed_templates.error_warning(self.MSG_NO_DATA), ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch tracking start time metadata
|
# Fetch tracking start time metadata
|
||||||
|
# This doesn't work most of the time so consider using the API call instead of cache call
|
||||||
|
# Or consider removing this feature entirely
|
||||||
self.cursor.execute(
|
self.cursor.execute(
|
||||||
"""
|
"""
|
||||||
SELECT tracked_since_message_channel_id, tracked_since_message_id
|
SELECT tracked_since_message_channel_id, tracked_since_message_id
|
||||||
@@ -400,13 +448,11 @@ class WordCloud(commands.Cog):
|
|||||||
|
|
||||||
word_cloud_file = discord.File(word_cloud, filename=f"wordcloud_{interaction.user.id}.png")
|
word_cloud_file = discord.File(word_cloud, filename=f"wordcloud_{interaction.user.id}.png")
|
||||||
embed = discord.Embed(title="☁️ Her er ordskyen din! ☁️")
|
embed = discord.Embed(title="☁️ Her er ordskyen din! ☁️")
|
||||||
|
embed.description = "Basert på de 4000 mest frekvente ordene dine siden "
|
||||||
if origin_found:
|
if origin_found:
|
||||||
embed.description = (
|
embed.description += f"{origin_msg_timestamp}\n[Se melding]({origin_msg.jump_url})"
|
||||||
f"Basert på de 4000 mest frekvente ordene dine siden {origin_msg_timestamp}\n"
|
|
||||||
+ f"[Se melding]({origin_msg.jump_url})"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
embed.description = "Basert på de 4000 mest frekvente ordene dine siden `ukjent dato`"
|
embed.description += "`ukjent dato`"
|
||||||
embed.set_image(url=f"attachment://wordcloud_{interaction.user.id}.png")
|
embed.set_image(url=f"attachment://wordcloud_{interaction.user.id}.png")
|
||||||
await interaction.followup.send(embed=embed, file=word_cloud_file)
|
await interaction.followup.send(embed=embed, file=word_cloud_file)
|
||||||
|
|
||||||
@@ -428,9 +474,10 @@ class WordCloud(commands.Cog):
|
|||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
# Get member role for future channel permission checking
|
# Get member role for future channel permission checking
|
||||||
member_role = interaction.guild.get_role(779849617651138601)
|
# This shouldn't be hard coded but eh
|
||||||
|
board_member_role = interaction.guild.get_role(779849617651138601)
|
||||||
|
|
||||||
# Fetch last 1000 messages
|
# This is horrible but there's a lot of edge cases to handle so spare me pls
|
||||||
all_messages = ""
|
all_messages = ""
|
||||||
for channel in interaction.guild.text_channels:
|
for channel in interaction.guild.text_channels:
|
||||||
if not channel.permissions_for(interaction.user).send_messages:
|
if not channel.permissions_for(interaction.user).send_messages:
|
||||||
@@ -438,36 +485,27 @@ class WordCloud(commands.Cog):
|
|||||||
|
|
||||||
# Avoid reading secret board member chats
|
# Avoid reading secret board member chats
|
||||||
if (
|
if (
|
||||||
not channel.permissions_for(member_role).send_messages
|
board_member_role # None if invoked outside of UiO Gaming's server. Avoiding crash due to AND check
|
||||||
|
and not channel.permissions_for(board_member_role).send_messages
|
||||||
and not channel.permissions_for(interaction.guild.default_role).send_messages
|
and not channel.permissions_for(interaction.guild.default_role).send_messages
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async for message in channel.history(
|
# Limit search to half of requested messages to avoid rate limits
|
||||||
limit=int(antall / 2)
|
# We assume we can still fetch enough tokens to reach the requested amount
|
||||||
): # Limit search in each channel to half of requested
|
async for message in channel.history(limit=int(antall / 2)):
|
||||||
if message.author != interaction.user:
|
if not self.can_count_message(message):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Filter bot command messages
|
filtered_msg = " ".join(self.tokenize(message.clean_content))
|
||||||
if not message.clean_content[:2].isalpha():
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Filter URLs
|
|
||||||
filtered_msg = re.sub(
|
|
||||||
r"https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)", # noqa: E501
|
|
||||||
"",
|
|
||||||
message.clean_content,
|
|
||||||
)
|
|
||||||
|
|
||||||
all_messages += f"{filtered_msg} "
|
all_messages += f"{filtered_msg} "
|
||||||
except discord.errors.Forbidden:
|
except discord.errors.Forbidden:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not all_messages:
|
if not all_messages:
|
||||||
return await interaction.followup.send(
|
return await interaction.followup.send(
|
||||||
embed=embed_templates.error_warning(interaction, text="Fant ingen data om deg"), ephemeral=False
|
embed=embed_templates.error_warning(self.MSG_NO_DATA), ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Generate word cloud
|
# Generate word cloud
|
||||||
@@ -495,5 +533,4 @@ async def setup(bot: commands.Bot):
|
|||||||
bot (commands.Bot): Bot instance
|
bot (commands.Bot): Bot instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot.add_listener(WordCloud(bot).word_freq_listener, "on_message")
|
|
||||||
await bot.add_cog(WordCloud(bot))
|
await bot.add_cog(WordCloud(bot))
|
||||||
@@ -41,77 +41,67 @@ class Bot(commands.Bot):
|
|||||||
|
|
||||||
self.logger = BotLogger().logger # Initialize logger
|
self.logger = BotLogger().logger # Initialize logger
|
||||||
|
|
||||||
self.guild_id = config.get("dev_guild", UIO_GAMING_GUILD_ID)
|
self.cog_files = set(listdir("./src/cogs"))
|
||||||
|
|
||||||
# Connect to database
|
# Check for missing credentials
|
||||||
# We assume the config file is valid and contains the needed keys, even if they are empty
|
if self.check_credentials(config["database"], DATABASE_RELIANT_COGS):
|
||||||
db = config.get("database")
|
|
||||||
|
|
||||||
for db_credential in db.values():
|
|
||||||
if not db_credential:
|
|
||||||
self.db_connection = None
|
|
||||||
self.logger.warning(
|
|
||||||
f"No database credentials specified. Disabling db reliant cogs:\n{DATABASE_RELIANT_COGS}"
|
|
||||||
)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.db_connection = psycopg2.connect(
|
self.db_connection = psycopg2.connect(
|
||||||
host=db["host"],
|
host=config["database"]["host"],
|
||||||
dbname=db["dbname"],
|
dbname=config["database"]["dbname"],
|
||||||
user=db["username"],
|
user=config["database"]["username"],
|
||||||
password=db["password"],
|
password=config["database"]["password"],
|
||||||
)
|
)
|
||||||
self.db_connection.autocommit = True
|
self.db_connection.autocommit = True # Scary
|
||||||
|
|
||||||
for sanity_credential in config.get("sanity").values():
|
if self.check_credentials(config["sanity"], SANITY_RELIANT_COGS):
|
||||||
if not sanity_credential:
|
self.sanity = config["sanity"]
|
||||||
self.has_sanity_credentials = False
|
|
||||||
self.logger.warning(
|
|
||||||
f"No Sanity credentials specified. Disabling sanity reliant cogs\n{SANITY_RELIANT_COGS}"
|
|
||||||
)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
self.has_sanity_credentials = True
|
|
||||||
|
|
||||||
if not config.get("minecraft").get("rcon_password"):
|
if self.check_credentials(config["minecraft"], MINECRAFT_RELIANT_COGS):
|
||||||
self.has_minecraft_credentials = False
|
self.mc_rcon_password = config["minecraft"]["rcon_password"]
|
||||||
self.logger.warning(
|
|
||||||
f"No Minecraft credentials specified. Disabling minecraft reliatn cogs\n{MINECRAFT_RELIANT_COGS}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self.has_minecraft_credentials = True
|
|
||||||
|
|
||||||
# Fetch misc config values
|
# Fetch misc config values
|
||||||
self.mc_rcon_password = config["minecraft"]["rcon_password"]
|
self.UIO_GAMING_GUILD_ID = UIO_GAMING_GUILD_ID
|
||||||
self.sanity = config["sanity"]
|
self.guild_id = config.get("dev_guild", self.UIO_GAMING_GUILD_ID)
|
||||||
|
self.config_mode = config.get("config_mode")
|
||||||
self.presence = config["bot"].get("presence", {})
|
self.presence = config["bot"].get("presence", {})
|
||||||
self.emoji = config.get("emoji", {})
|
self.emoji = config.get("emoji", {})
|
||||||
self.misc = config.get("misc", {})
|
self.misc = config.get("misc", {})
|
||||||
self.config_mode = config.get("config_mode")
|
|
||||||
|
|
||||||
async def setup_hook(self):
|
async def setup_hook(self):
|
||||||
cog_files = listdir("./src/cogs")
|
|
||||||
|
|
||||||
if self.db_connection is None:
|
|
||||||
cog_files = set(cog_files) - DATABASE_RELIANT_COGS
|
|
||||||
if not self.has_sanity_credentials:
|
|
||||||
cog_files = set(cog_files) - SANITY_RELIANT_COGS
|
|
||||||
if not self.has_minecraft_credentials:
|
|
||||||
cog_files = set(cog_files) - MINECRAFT_RELIANT_COGS
|
|
||||||
|
|
||||||
# Load cogs
|
# Load cogs
|
||||||
for file in cog_files:
|
for file in self.cog_files:
|
||||||
if file.endswith(".py"):
|
if file.endswith(".py"):
|
||||||
name = file[:-3]
|
name = file[:-3]
|
||||||
await bot.load_extension(f"cogs.{name}")
|
await bot.load_extension(f"cogs.{name}")
|
||||||
|
|
||||||
# Sync slash commands to Discord
|
# Sync slash commands
|
||||||
if self.config_mode == "prod":
|
if self.config_mode == "prod":
|
||||||
await self.tree.sync()
|
await self.tree.sync()
|
||||||
else:
|
else:
|
||||||
self.tree.copy_global_to(guild=discord.Object(id=self.guild_id))
|
self.tree.copy_global_to(guild=discord.Object(id=self.guild_id))
|
||||||
await self.tree.sync(guild=discord.Object(id=self.guild_id))
|
await self.tree.sync(guild=discord.Object(id=self.guild_id))
|
||||||
|
|
||||||
|
def check_credentials(self, credentials: dict, dependant_cogs: set[str]) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the credentials are valid. Removes cogs from the class' `cog_files` attribute if not
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
credentials (dict): The credentials to check
|
||||||
|
dependant_cogs (set): The cogs that depend on the credentials
|
||||||
|
|
||||||
|
Returns
|
||||||
|
----------
|
||||||
|
bool: Whether the credentials are valid. If not, the dependant cogs are disabled.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for credential in credentials.values():
|
||||||
|
if not credential:
|
||||||
|
self.logger.warning(f"Missing credentials. Disabling reliant cogs:\n{dependant_cogs}")
|
||||||
|
self.cog_files -= dependant_cogs
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# Create bot instance
|
# Create bot instance
|
||||||
bot = Bot()
|
bot = Bot()
|
||||||
|
|||||||