Merge pull request #9 from UiO-Gaming/discord.py-2.0-migration

This commit is contained in:
Leander West Furumo authored and GitHub committed 2022-11-18 03:54:08 +01:00
commit 3cae2068ad
26 files changed
+3223 -2692

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
# Bidragsguide
Heisann sveisann! Er du interessert i å bidra? Kult!
Det er derimot noen kodestilsregler jeg ønsker at skal følges. Dette dokumentet fungerer som en defacto regelbok for både meg og/eller andre.
## Sjekk koden din
Viktigst av alt er at koden din består flake8 sin PEP8-test. Dette kan du teste på egen maskin ved å installere flake8:
```
pip install flake8
```
for å så kjøre følgende kommando:
```
flake8 --max-line-length=120 --show-source
```
## Regler
### Typing
All kode skal være typet til beste evne. Dette betyr at man for eksempel også gjerne bør spesifisere typene innad i en dict eller lignende.
### Docstrings
Alle funksjoner skal ha docsstrings som følger denne stilen:
```python
def greet(self, navn: str) -> str:
"""
Greets the invoking user using the inputted name
Parameters
----------
navn (str): The user's inputted name
Returns
----------
(str): A greeting
"""
return f'Hello, {name}'
```
Sløyf alt som ikke brukes.
### Sortering av imports
Alle imports skal være gruppert på følgende måte:
1. Standardbibliotek
2. Tredjepartsbibliotek
3. Lokal kode
Innad i hver gruppe skal importeringer sortert alfabetisk med vekt på forelderpakken. Det vil si at hvis du importerer bare en del av en pakke skal den fortsatt være sortert i henhold til forelderpakkens navn.
### Rekkefølge på kommandosjekker (decorators)
Hver kommando er en funksjon som har et eller flere decorators over seg. Dette er sjekker som utføres før kommandoen kjøres. En av disse konverterer også funksjonen til en faktisk kommando som vi kan kjøre gjennom Discord.
Decorators skal sorteres som følgende:
1. is owner
2. Guild only
3. Bot permissions
4. User permissions
5. Cooldown
6. Kommando (denne må alltid være med)
Merk deg at ikke alle disse trenger å være med, og ikke alle sjekker som finnes er nevnt her. Bruk hodet og tenk hva som er logisk for deg om du møter på en slik situasjon.
### Quotation marks
Ta i bruk enkle quotation marks `'` der det er mulig. Multiline comments skal skrives med dobble quotation marks `"`.
### Newline mellom docstring og kode samt mellom siste kodelinje returstatement
Ja, det tittelen sier. Det er et unntak for returstatements hvis det er tatt i bruk early returning.
### Norske parameternavn i kommandoer
Selv om Discord.py 2.0 støtter oversettelser, er ikke dette noe vi implementerer. Med tanke på at botten er norsk og for norske brukere må dette reflekteres i parameternavn siden dette er hva Discord bruker som grensesnitt for kommandoer.
### Engelsk for alt annet
All annen kode holdes på engelsk. Dette inkluderer kommentarer.
Et tips er å ha et engelsk funksjonsnavn, men spesifisere i decoratoren over med navnparameteren.
### Avhengigheter
Hvis en ny avhengighet trengs, sørg for å spesifisere major og minor versjon i requirementsfila.
## Ønsket utforming av ny funksjonalitet
- Bruk embeds til feilmeldinger og som respons om mulig.
- Sett cooldown til 5 sekunder for kommandoer som krever eksterne API kall eller særlig mye prosessering.
- Ellers, settes denne til 2 sekunder.
+3 -3
View File
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
UiO Gaming's multi-purpose Discord bot
Copyright (C) 2022 Leander West Furumo
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
uiogaming-bot Copyright (C) 2022 Leander West Furumo
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
+38 -2
View File
@@ -1,5 +1,41 @@
# UiO Gaming Discord Bot
UiO Gaming Discord Bot er en chattebot for ulike formål. Den skal hjelpe oss med ulike oppgaver og ha mange ulike funksjoner
UiO Gaming Discord Bot er en chattebot for ulike formål. Hvilke formål det er, er jeg ikke helt sikker på.
Mer dokumentasjon kommer snart™
## Installasjon
1. Fjern `.example` fra [config.yaml.example](src/config/config.yaml) i configmappen
2. Fyll inn verdiene i denne configfilen
Du kan så bevege deg videre til ett av to alternativer.
### Valgmulighet 1 - Manuelt
Du bør ha gjort følgende:
- Installert Python 3.10+
- Ha en Postgresql database satt opp
1. Installer avhengigheter
```
pip install -r requirements.txt -U
```
2. Kjør bot
```
python src/run.py
```
### Valgmulighet 2 - Docker
```
docker-compose up
```
## Bidra
Se [CONTRIBUTING.md](CONTRIBUTING.md)
+9 -9
View File
@@ -1,9 +1,9 @@
discord.py
pyyaml
requests
asyncio
psutil
pillow
aiocron
psycopg2
mcrcon
aiocron==1.8.*
asyncio==3.4.*
discord.py==2.1.*
mcrcon==0.7.*
pillow==9.3.*
psutil==5.9.*
psycopg2==2.9.*
pyyaml==6.0.*
requests==2.28.*
+1127 -1161
View File
File diff suppressed because it is too large. Load diff
+222 -93
View File
@@ -1,21 +1,30 @@
from discord.ext import commands
import discord
import psycopg2
import aiocron
import asyncio
from datetime import datetime
from dateutil.relativedelta import relativedelta
from cogs.utils import embed_templates, discord_utils
from dateutil.relativedelta import relativedelta
import discord
from discord import app_commands
from discord.ext import commands
import psycopg2
from cogs.utils import discord_utils, embed_templates
class Birthday(commands.Cog):
def __init__(self, bot):
"""Save the birthday of Discord users and allow them to see the birthdays of other users"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
self.cursor = self.bot.db_connection.cursor()
def init_db(self):
"""Create the necessary tables for the birthday cog to work"""
self.cursor.execute(
"""
CREATE TABLE IF NOT EXISTS birthdays (
@@ -26,22 +35,61 @@ class Birthday(commands.Cog):
)
self.bot.db_connection.commit()
def _fetch_user_birthday(self, user_id):
self.cursor.execute("SELECT birthday FROM birthdays WHERE discord_id = (%s)", (user_id,))
return self.cursor.fetchone()
def __fetch_user_birthday(self, user_id: int) -> datetime | None:
"""
Fetch the birthday of a user in the database
Parameters
----------
user_id (int): The Discord user ID
Returns
----------
(datetime | None): The birthday of the user
"""
self.cursor.execute('SELECT birthday FROM birthdays WHERE discord_id = (%s)', (user_id,))
result = self.cursor.fetchone()
if result:
birthday = result[0]
return datetime(birthday.year, birthday.month, birthday.day)
def __fetch_user_next_birthday(self, user_id: int) -> datetime:
"""
Get date of next birthday for a user
Parameters
----------
user_id (int): The Discord user ID
Returns
----------
(datetime): The date of the next birthday
"""
def _fetch_user_next_birthday(self, user_id):
self.cursor.execute(
"""
SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday
FROM birthdays
WHERE discord_id = %s
""",
(user_id,),
""", (user_id,)
)
return self.cursor.fetchone()
results = self.cursor.fetchone()
if results:
birthday = results[2]
return datetime(birthday.year, birthday.month, birthday.day)
else:
return datetime.now() # This will never happen unless a database error occurs. Keeping the type checker happy
def __fetch_next_birthdays(self) -> list[tuple[int, datetime, datetime]]:
"""
Fetch the 5 first future birthdays of all users in the database
Returns
----------
(list[tuple[int, datetime, datetime]]): A list of tuples containing the user ID, the date of birth and the next birthday
"""
def _fetch_next_birthdays(self):
self.cursor.execute(
"""
SELECT *, CAST(birthday + ((EXTRACT(YEAR FROM AGE(birthday)) + 1) * interval '1' YEAR) AS DATE) AS next_birthday
@@ -50,124 +98,205 @@ class Birthday(commands.Cog):
LIMIT 5
"""
)
return self.cursor.fetchall()
results = self.cursor.fetchall()
# Convert date objects to datetime objects
for result in results:
_, birthday, next_birthday = result
birthday = datetime(birthday.year, birthday.month, birthday.day)
next_birthday = datetime(next_birthday.year, next_birthday.month, next_birthday.day)
return results
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.
Parameters
----------
user_id (int): The Discord user ID
birthday (datetime): The birthday of the user
"""
def _set_user_birthday(self, user_id, birthday):
try:
self.cursor.execute("INSERT INTO birthdays (discord_id, birthday) VALUES (%s, %s)", (user_id, birthday))
self.cursor.execute(
"""
INSERT INTO birthdays (discord_id, birthday)
VALUES (%s, %s)
""", (user_id, birthday))
self.bot.db_connection.commit()
except psycopg2.errors.UniqueViolation:
self.bot.db_connection.rollback()
self.cursor.execute("UPDATE birthdays SET birthday = %s WHERE discord_id = %s", (birthday, user_id))
self.cursor.execute('UPDATE birthdays SET birthday = %s WHERE discord_id = %s', (birthday, user_id))
self.bot.db_connection.commit()
@aiocron.crontab("0 0 * * *")
async def _check_birthdays(self):
await asyncio.sleep(1)
self.cursor.execute(
"""
SELECT *
FROM birthdays
WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM current_date)
AND EXTRACT(DAY FROM birthday) = EXTRACT(DAY FROM current_date);
"""
)
# @aiocron.crontab('0 0 * * *')
# async def __check_birthdays(self):
# """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
#
# self.cursor.execute(
# """
# SELECT *
# FROM birthdays
# WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM current_date)
# AND EXTRACT(DAY FROM birthday) = EXTRACT(DAY FROM current_date);
# """
# )
#
# birthdays = self.cursor.fetchall()
# if birthdays:
# guild = self.bot.get_guild(747542543750660178)
# channel = guild.get_channel(747542544291987597)
# for birthday in birthdays:
# user = self.bot.get_user(birthday[0])
# if user:
# await channel.send(f'Gratulrer med dagen {user.mention}!')
birthdays = self.cursor.fetchall()
if birthdays:
guild = self.bot.get_guild(747542543750660178)
channel = guild.get_channel(747542544291987597)
for birthday in birthdays:
user = self.bot.get_user(birthday[0])
if user:
await channel.send(f"Gratulrer med dagen {user.mention}!")
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='settbursdag', description='Lagrer din bursdag i databasen')
async def birthday_set(self, interaction: discord.Interaction, dag: int, måned: int, år: int):
"""
Allows the user to set their birthday in the database.
@commands.cooldown(1, 5, commands.BucketType.user)
@commands.command()
async def settbursdag(self, ctx, date):
Parameters
----------
interaction (discord.Interaction): Slash command context object
dag (int): Day of the month
måned (int): Month of the year
år (int): Year
"""
try:
date = datetime.strptime(date, "%d.%m.%Y")
date = datetime(år, måned, dag)
except ValueError:
return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Feil format, gjøk!"))
return await interaction.response.send_message(
embed=embed_templates.error_fatal(interaction, text='ikke en gyldig dato, gjøk!'),
ephemeral=True
)
if date.year < 1970:
return await ctx.send(
embed=embed_templates.error_fatal(ctx, text="Ok boomer, men nei. Hvorfor? Unix timestamps. Google it")
return await interaction.response.send_message(
embed=embed_templates.error_fatal(interaction, text='Ok boomer, men nei. Hvorfor? Unix timestamps. Google it'),
ephemeral=True
)
self._set_user_birthday(ctx.author.id, date)
if date > datetime.now():
return await interaction.response.send_message(
embed=embed_templates.error_fatal(interaction, text='Du kan ikke sette en fremtidig dato!'),
ephemeral=True
)
embed = embed_templates.success(ctx, text=f"Bursdag satt til <t:{int(date.timestamp())}:D>")
await ctx.send(embed=embed)
self.__set_user_birthday(interaction.user.id, date)
@commands.cooldown(1, 5, commands.BucketType.user)
@commands.command()
async def fjernbursdag(self, ctx):
embed = embed_templates.success(interaction, text=f'Bursdag satt til {discord.utils.format_dt(date, style="D")}')
await interaction.response.send_message(embed=embed)
self.cursor.execute("DELETE FROM birthdays WHERE discord_id = (%s)", (ctx.author.id,))
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='fjernbursdag', description='Fjerner bursdagen din fra databasen')
async def birthday_remove(self, interaction: discord.Interaction):
"""
Removes the invoking user's birthday from the database
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
self.cursor.execute('DELETE FROM birthdays WHERE discord_id = (%s)', (interaction.user.id,))
self.bot.db_connection.commit()
embed = embed_templates.success(ctx, text="Bursdag fjernet")
await ctx.send(embed=embed)
embed = embed_templates.success(interaction, text='Bursdag fjernet')
await interaction.response.send_message(embed=embed)
@commands.cooldown(1, 5, commands.BucketType.user)
@commands.command()
async def bursdag(self, ctx, *, bruker: discord.Member = None):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='bursdag', description='Viser bursdagen til en bruker. Om ingen er gitt, vises din egen')
async def birthday_show(self, interaction: discord.Interaction, bruker: discord.Member | discord.User | None = None):
"""
Shows the birthday of a user. If none is given, shows the invoking user's one.
Parameters
----------
interaction (discord.Interaction): Slash command context object
bruker (discord.Member | discord.User | None): The user to show the birthday of. Defaults to None.
"""
if not bruker:
bruker = ctx.author
bruker = interaction.user
user = self._fetch_user_birthday(bruker.id)
user = self.__fetch_user_birthday(bruker.id)
if not user:
return await ctx.send(
embed=embed_templates.error_fatal(ctx, text="Brukeren har ikke registrert bursdagen sin")
return await interaction.response.send_message(
embed=embed_templates.error_fatal(interaction, text='Brukeren har ikke registrert bursdagen sin')
)
birthday = datetime(user[0].year, user[0].month, user[0].day)
# Current age information
birthday = datetime(user.year, user.month, user.day)
days_old = (datetime.now() - birthday).days
years_old = relativedelta(datetime.now(), birthday).years
# Next birthday
birthday_current_year = birthday.replace(year=datetime.now().year)
if (datetime.now() - birthday_current_year).days < 0:
next_birthday = birthday_current_year
else:
next_birthday = birthday_current_year.replace(year=datetime.now().year + 1)
# Next birthday information
next_birthday = self.__fetch_user_next_birthday(bruker.id)
next_birthday_days = (next_birthday - datetime.now()).days
embed = discord.Embed(description=bruker.mention, color=discord_utils.get_user_color(bruker))
embed.set_thumbnail(url=bruker.avatar_url)
embed.set_author(name=bruker.name, icon_url=bruker.avatar_url)
embed.add_field(name="Bursdag", value=f"<t:{int(birthday.timestamp())}:D>")
embed.add_field(name="Hvor gammel?", value=f"{years_old} år\n({days_old} dager)", inline=False)
embed.add_field(name="Neste bursdag om", value=f"{next_birthday_days} dager", inline=False)
embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
embed = discord.Embed(description=bruker.mention, color=discord_utils.get_color(bruker))
embed.set_thumbnail(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='Hvor gammel?', value=f'{years_old} år\n({days_old} dager)', inline=False)
embed.add_field(name='Neste bursdag om', value=f'{next_birthday_days} dager', inline=False)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
@commands.command()
async def kommendebursdager(self, ctx):
@app_commands.guild_only()
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='kommendebursdager', description='Viser en liste over de opptil 5 kommende bursdager i serveren')
async def birthdays_upcoming(self, interaction: discord.Interaction):
"""
Lists up to 5 upcoming birthdays of users in the server.
next_birthdays = self._fetch_next_birthdays()
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
next_birthdays = self.__fetch_next_birthdays()
if not next_birthdays:
return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Ingen bursdager"))
return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Ingen bursdager'))
birthday_string = ""
birthday_string = ''
for user in next_birthdays:
discord_user = ctx.guild.get_member(user[0])
if not discord_user:
user_id, _, next_birthday = user
try:
discord_user = await interaction.guild.fetch_member(user_id) # Guild should always be available because of guild_only
except (discord.NotFound, discord.HTTPException):
continue
next_birthday = datetime(user[2].year, user[2].month, user[2].day)
next_birthday_days = (next_birthday - datetime.now()).days
birthday_string += f"{discord_user.name}#{discord_user.discriminator} - <t:{int(next_birthday.timestamp())}:D> ({next_birthday_days} dager)\n"
timestamp = discord.utils.format_dt(next_birthday, style='D')
relative_timestamp = discord.utils.format_dt(next_birthday, style='R')
birthday_string += f'{discord_user.name}#{discord_user.discriminator} - {timestamp} ({relative_timestamp})\n'
embed = discord.Embed(title="Kommende bursdager", description=birthday_string)
embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
if not birthday_string:
return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Ingen bursdager'))
embed = discord.Embed(title='Kommende bursdager', description=birthday_string)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
def setup(bot):
bot.add_cog(Birthday(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(Birthday(bot))
+97 -71
View File
@@ -1,48 +1,55 @@
from discord.ext import commands
import discord
from time import time, perf_counter
from os import environ, getpid
import platform
from os import getpid, environ
from time import time
import discord
from discord import app_commands
from discord.ext import commands
from psutil import Process
from cogs.utils import embed_templates
class BotInfo(commands.Cog):
def __init__(self, bot):
"""View information about the bot"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
@commands.bot_has_permissions(embed_links=True, external_emojis=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command(aliases=['info', 'about', 'om'])
async def botinfo(self, ctx):
@app_commands.checks.bot_has_permissions(embed_links=True, external_emojis=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='botinfo', description='Se informajson om botten')
async def botinfo(self, interaction: discord.Interaction):
"""
Viser informasjon om bot
View information about the bot
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
# Dev user info
dev = await self.bot.fetch_user(170506717140877312)
# Ping
start = perf_counter()
status_msg = await ctx.send('Beregner ping...')
end = perf_counter()
ping = int((end - start) * 1000)
# Memory usage
process = Process(getpid())
memory_usage = round(process.memory_info().rss / 1000000, 1)
# Member stats
"""
This is a pretty stupid way of doing things but it's the best I can come up with.
Using curly brackets for both dicts and sets is retarded becuase
I have to do this shit in order to create a fucking empty set.
Apparently you can't access user presences, only member presences
Because of that I have to use sets instead of ints in order to not count duplicate users.
Fucking hell discord...
"""
#
# This is a pretty stupid way of doing things but it's the best I can come up with.
# Using curly brackets for both dicts and sets is retarded becuase
# I have to do this shit in order to create a fucking empty set.
#
# Apparently you can't access user presences, only member presences
# Because of that I have to use sets instead of ints in order to not count duplicate users.
# Fucking hell discord...
total_members = set([])
online_members = set([])
idle_members = set([])
@@ -61,12 +68,12 @@ class BotInfo(commands.Cog):
offline_members.add(member.id)
# Build embed
embed = discord.Embed(color=ctx.me.color, url=self.bot.misc['website'])
embed.set_author(name=dev.name, icon_url=dev.avatar_url)
embed.set_thumbnail(url=self.bot.user.avatar_url)
embed = discord.Embed(color=interaction.client.user.color, url=self.bot.misc['website'])
embed.set_author(name=dev.name, icon_url=dev.display_avatar)
embed.set_thumbnail(url=self.bot.user.display_avatar)
embed.add_field(name='Dev', value=f'{dev.mention}\n{dev.name}#{dev.discriminator}')
embed.add_field(name='Oppetid', value=self.get_uptime())
embed.add_field(name='Ping', value=f'Ekte ping: {ping} ms\nWebsocket ping: {int(self.bot.latency * 1000)} ms')
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='Servere', value=len(self.bot.guilds))
embed.add_field(name='Discord.py', value=discord.__version__)
embed.add_field(name='Python', value=platform.python_version())
@@ -81,49 +88,60 @@ class BotInfo(commands.Cog):
f'{self.bot.emoji["offline"]}{len(offline_members)}')
embed.add_field(name='Lenker', value=f'[Nettside]({self.bot.misc["website"]}) | ' +
f'[Kildekode]({self.bot.misc["source_code"]})')
embed_templates.default_footer(ctx, embed)
await status_msg.edit(embed=embed, content=None)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command(aliases=['uptime'])
async def oppetid(self, ctx):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='oppetid', description='Se hvor lenge botten har kjørt')
async def uptime(self, interaction: discord.Interaction):
"""
Viser hvor lenge bot har vært kjørende
"""
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='🔌 Oppetid', value=self.get_uptime())
embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def ping(self, ctx):
"""
Viser bot sin ping
"""
start = perf_counter()
status_msg = await ctx.send('Pinging...')
end = perf_counter()
ping = int((end - start) * 1000)
embed = discord.Embed(color=ctx.me.color)
embed.add_field(
name='📶 Ping',
value=f'**Ekte ping:** {ping} ms\n**Websocket ping:** {int(self.bot.latency * 1000)} ms'
)
embed_templates.default_footer(ctx, embed)
await status_msg.edit(embed=embed, content=None)
def get_uptime(self) -> str:
"""
Returns the current uptime of the bot in string format
View the bot's uptime
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
embed = discord.Embed(color=interaction.client.user.color)
embed.add_field(name='🔌 Oppetid', value=self.__get_uptime())
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='ping', description='Se botten sin nåværende ping')
async def ping(self, interaction: discord.Interaction):
"""
View the bot's ping
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
embed = discord.Embed(color=interaction.client.user.color)
embed.add_field(
name='📶 Ping',
value=f'**Websocket ping:** {self.__get_ping()} ms'
)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed, content=None)
def __get_ping(self) -> int:
"""
Get the bot's ping in milliseconds
Returns
----------
(int) The bot's ping in milliseconds
"""
return int(self.bot.latency * 1000)
def __get_uptime(self) -> str:
"""
Returns the current uptime of the bot in string format
Returns
----------
@@ -139,5 +157,13 @@ class BotInfo(commands.Cog):
return f'{days}d, {hours}t, {minutes}m, {seconds}s'
def setup(bot):
bot.add_cog(BotInfo(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(BotInfo(bot))
+3 -3
View File
@@ -4,7 +4,7 @@ import asyncio
class CameraChannel(commands.Cog):
def __init__(self, bot):
def __init__(self, bot: commands.Bot):
self.bot = bot
async def on_voice_state_update(self, member, before, after):
@@ -20,7 +20,7 @@ class CameraChannel(commands.Cog):
await member.move_to(channel=None)
def setup(bot):
async def setup(bot: commands.Bot):
#bot.add_listener(CameraChannel(bot).on_voice_state_update, 'on_voice_state_update')
#bot.add_cog(CameraChannel(bot))
#await bot.add_cog(CameraChannel(bot))
pass
+25 -25
View File
@@ -47,7 +47,7 @@ class CreditUser:
class SocialCredit(commands.Cog):
def __init__(self, bot):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.fuck_uwu.start()
@@ -69,7 +69,7 @@ class SocialCredit(commands.Cog):
def add_new_citizen(func):
async def wrapper(*args, **kwargs):
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():
self._add_citizen(args[1])
await func(*args, **kwargs)
@@ -100,9 +100,9 @@ class SocialCredit(commands.Cog):
async def get_leaderboard(self, ctx, page, worst=False):
if worst:
keyword = "ASC"
keyword = 'ASC'
else:
keyword = "DESC"
keyword = 'DESC'
self.cursor.execute(
f"""
@@ -113,13 +113,13 @@ class SocialCredit(commands.Cog):
result = self.cursor.fetchall()
if not result:
return await ctx.send(embed=embed_templates.error_fatal(ctx, "Ingen brukere er registrert i databasen"))
return await ctx.send(embed=embed_templates.error_fatal(ctx, 'Ingen brukere er registrert i databasen'))
output_list = []
for i, row in enumerate(result):
user = ctx.guild.get_member(row[0])
if user:
output_list.append(f"{i+1}. {user.name}#{user.discriminator} - `{row[1]}` poeng")
output_list.append(f'{i+1}. {user.name}#{user.discriminator} - `{row[1]}` poeng')
return misc_utils.paginator(output_list, int(page))
@@ -134,7 +134,7 @@ class SocialCredit(commands.Cog):
@add_new_citizen
async def social_punishment(self, user_id, points):
print(f"{points} points deducted from {user_id}")
print(f'{points} points deducted from {user_id}')
self.cursor.execute(
"""
UPDATE social_credit
@@ -147,7 +147,7 @@ class SocialCredit(commands.Cog):
@add_new_citizen
async def social_reward(self, user_id, points):
print(f"{points} points given to {user_id}")
print(f'{points} points given to {user_id}')
self.cursor.execute(
"""
UPDATE social_credit
@@ -164,17 +164,17 @@ class SocialCredit(commands.Cog):
if not bruker:
bruker = ctx.author
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()
if not result:
return await ctx.send(
embed=embed_templates.error_fatal(ctx, f"{bruker.mention} er ikke registrert i databasen")
embed=embed_templates.error_fatal(ctx, f'{bruker.mention} er ikke registrert i databasen')
)
db_user = CreditUser(*result)
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 ctx.send(embed=embed)
@commands.command()
@@ -185,13 +185,13 @@ class SocialCredit(commands.Cog):
page = 0
page_data = await self.get_leaderboard(ctx, page)
output_content = "\n".join(page_data["page_content"])
output_content = '\n'.join(page_data['page_content'])
embed = discord.Embed(title="Social credit leaderboard", description=output_content)
embed.set_footer(text=f"Side {page_data['page']}/{page_data['pagecount']}")
embed = discord.Embed(title='Social credit leaderboard', description=output_content)
embed.set_footer(text=f'Side {page_data['page']}/{page_data['pagecount']}')
await ctx.send(embed=embed)
@commands.command(aliases=["skammeliste"])
@commands.command(aliases=['skammeliste'])
async def shamelist(self, ctx, page=0):
try:
page = int(page)
@@ -199,13 +199,13 @@ class SocialCredit(commands.Cog):
page = 0
page_data = await self.get_leaderboard(ctx, page, worst=True)
output_content = "\n".join(page_data["page_content"])
output_content = '\n'.join(page_data['page_content'])
embed = discord.Embed(title="Social credit skammeliste", description=output_content)
embed.set_footer(text=f"Side {page_data['page']}/{page_data['pagecount']}")
embed = discord.Embed(title='Social credit skammeliste', description=output_content)
embed.set_footer(text=f'Side {page_data['page']}/{page_data['pagecount']}')
await ctx.send(embed=embed)
@commands.Cog.listener("on_message")
@commands.Cog.listener('on_message')
async def on_message(self, message):
if message.author.bot:
return
@@ -246,12 +246,12 @@ class SocialCredit(commands.Cog):
for mention in message.mentions:
await self.social_punishment(mention.id, 10)
@commands.Cog.listener("on_reaction_add")
@commands.Cog.listener('on_reaction_add')
async def on_star_add(self, reaction, user):
if user.bot:
return
if reaction.emoji == "⭐":
if reaction.emoji == '⭐':
if reaction.message.author == user:
await self.social_punishment(user.id, 100)
else:
@@ -259,15 +259,15 @@ class SocialCredit(commands.Cog):
await self.social_punishment(user.id, (len(reaction.message.reactions) - 1) * 25)
await self.social_reward(user.id, 25 * len(reaction.message.reactions))
@commands.Cog.listener("on_reaction_remove")
@commands.Cog.listener('on_reaction_remove')
async def on_star_remove(self, reaction, user):
if user.bot:
return
if reaction.emoji == "⭐":
if reaction.emoji == '⭐':
if len(reaction.message.reactions) >= 3:
await self.social_punishment(user.id, 25)
def setup(bot):
bot.add_cog(SocialCredit(bot))
async def setup(bot: commands.Bot):
await bot.add_cog(SocialCredit(bot))
+9 -9
View File
@@ -7,10 +7,10 @@ import json
class Vær(commands.Cog):
def __init__(self, bot):
def __init__(self, bot: commands.Bot):
self.bot = bot
@aiocron.crontab("0 6 * * *")
@aiocron.crontab('0 6 * * *')
async def forecast():
dev = await self.bot.fetch_user(162337748781367296)
@@ -19,9 +19,9 @@ class Vær(commands.Cog):
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.avatar_url)
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
@@ -35,12 +35,12 @@ class Vær(commands.Cog):
rain = data['precipitation']
regn = rain['@value']
temperatur = temp['@value']
embed.add_field(name=f"kl {klokkeslett}", value=f"{temperatur}°C - {regn}mm")
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)
await channel.send('RISE AND SHINE GAMERS!', embed=embed)
def setup(bot):
bot.add_cog(Vær(bot))
async def setup(bot: commands.Bot):
await bot.add_cog(Vær(bot))
+4 -4
View File
@@ -6,7 +6,7 @@ class Wiki(commands.Cog):
@commands.command()
async def wiki_search(self, ctx, *search_term):
search_term = " ".join(search_term)
search_term = ' '.join(search_term)
search = wikipedia.search(search_term)
search = search[0]
@@ -15,11 +15,11 @@ class Wiki(commands.Cog):
summary = wikipedia.summary(search_term, sentences=5)
url = search.url
embed = discord.Embed(title=f"Wikipediasøk for {search_term}", url=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)
def setup(bot):
bot.add_cog(Wiki(bot))
async def setup(bot: commands.Bot):
await bot.add_cog(Wiki(bot))
+195 -270
View File
@@ -1,251 +1,63 @@
from discord.ext import commands
import discord
import asyncio
from sys import exit
from os import listdir, system
import socket
from requests import get
from os import listdir
import requests
from cogs.utils import misc_utils, embed_templates
import discord
from discord.ext import commands
from cogs.utils import embed_templates
class DevTools(commands.Cog):
def __init__(self, bot):
"""Commands for developers to mangage cogs and other functionality of the bot"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['shutdown', 'stoppbått'])
async def stoppbot(self, ctx):
"""
Log out and shut down the bot
"""
embed = discord.Embed(color=ctx.me.color, description='Logger av...')
await ctx.send(embed=embed)
await self.bot.logout()
exit('Bot stopped through command')
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def custommsg(self, ctx, channel: int, *text):
@commands.command(name='custommsg', 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]):
"""
Send a message to a specified channel
Send a message to any channel on any server the bot is in
Parameters
----------
ctx (commands.Context): Context object
channel (int): The channel ID to send the message to
text (tuple[str]): The text to send
"""
# Send message to the requested channel
channel = self.bot.get_channel(channel)
custommessage = ' '.join(text)
await channel.send(custommessage)
# Send confirmation message to the invoker
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='Sent', value=custommessage)
await ctx.send(embed=embed)
@commands.is_owner()
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['listservers'])
async def listguilds(self, ctx, page=0):
"""
Lists all guilds the bot is a member of
"""
guild_list = [f'{guild.name} - {guild.id}' for guild in self.bot.guilds]
page_data = misc_utils.paginator(guild_list, int(page))
page = page_data['page']
pagecount = page_data['pagecount']
guilds = '\n'.join(page_data['page_content'])
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='Guilds', value=guilds)
embed.set_footer(text=f'page: {page}/{pagecount}')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def listusers(self, ctx, page=0):
"""
Lists all users the bot has access to
"""
user_list = [f'{user.name}#{user.discriminator} - {user.id}' for user in self.bot.users]
page_data = misc_utils.paginator(user_list, int(page))
pagecount = page_data['pagecount']
page = page_data['page']
users = '\n'.join(page_data['page_content'])
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='Users', value=users)
embed.set_footer(text=f'page: {page}/{pagecount}')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(alises=['unloadcog', 'disablecog'])
async def unload(self, ctx, cog: str):
"""
Disables a specified cog
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
if name == cog:
self.bot.unload_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been disabled')
return await ctx.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['loadcog', 'enablecog'])
async def load(self, ctx, cog):
"""
Enables a speicifed cog
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
if name == cog:
self.bot.load_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description=f'{cog} loaded')
return await ctx.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['reloadcog'])
async def reload(self, ctx, cog):
"""
Reloads a specified cog
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
if name == cog:
self.bot.reload_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been reloaded')
return await ctx.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def reloadcommand(self, ctx, cog):
"""
Reloads a specified cog
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
if name == cog:
self.bot.reload_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description=f'{cog} has been reloaded')
return await ctx.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['reloadunloadedcogs'])
async def reloadunloaded(self, ctx):
"""
Reloads all cogs, including previously disabled ones
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
self.bot.unload_extension(f'cogs.{name}')
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
self.bot.load_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command(aliases=['reloadallcogs'])
async def reloadall(self, ctx):
"""
Reloads all previously enabled cogs
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
self.bot.reload_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description='Reloaded all previously enabled cogs')
await ctx.send(embed=embed)
@commands.is_owner()
@commands.command()
async def cmd(self, ctx, *, command: str):
"""
Execute terminal commands
"""
system(command)
await ctx.send('Done!')
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def localip(self, ctx):
"""
Sends LAN IP-address
"""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='LAN IP-address', value=s.getsockname()[0])
await ctx.send(embed=embed)
s.close()
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def publicip(self, ctx):
"""
Sends WAN IP-address
inb4 I leak my IP-address
"""
data = get('https://wtfismyip.com/json').json()
ip = data['YourFuckingIPAddress']
location = data['YourFuckingLocation']
isp = data['YourFuckingISP']
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='WAN IP-address', value=f'{ip}\n{location}\n{isp}')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.cooldown(1, 10, commands.BucketType.guild)
@commands.command()
async def changepresence(self, ctx, activity_type, message, status_type):
@commands.command(name='changepresence', description='Endre botten sin nåværende status')
async def changepresence(self, ctx: commands.Context, activity_type: str, message: str, status_type: str):
"""
Changes presence status
Change the bot's presence status
Parameters
----------
ctx (commands.Context): Context object
activity_type (str): The type of activity to set the bot's status to
message (str): The message to set the bot's status to
status_type (str): The type of status to set the bot's status to
"""
activities = {
@@ -253,10 +65,7 @@ class DevTools(commands.Cog):
'listening': 2,
'watching': 3
}
if activity_type in activities:
activity_type = activities[activity_type]
else:
activity_type = 0
activity_type = activities.get(activity_type, 0)
status_types = {
'online': discord.Status.online,
@@ -264,45 +73,49 @@ class DevTools(commands.Cog):
'idle': discord.Status.idle,
'offline': discord.Status.offline
}
if status_type in status_types:
status_type = status_types[status_type]
else:
status_type = discord.Status.online
status_type = status_types.get(status_type, discord.Status.online)
await self.bot.change_presence(
status=status_type,
activity=discord.Activity(type=activity_type, name=message)
)
embed = discord.Embed(color=ctx.me.color, description='Endret Presence!')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True, add_reactions=True)
@commands.is_owner()
@commands.command()
async def leave(self, ctx, *guild_id: int):
@commands.bot_has_permissions(embed_links=True, add_reactions=True)
@commands.command(name='leave', description='Forlater en server. Om ingen er gitt, forlat der det ble sendt fra')
async def leave(self, ctx: commands.Context, *guild_id: int):
"""
Leaves a specified guild
Make bot leave a specified guild. If no guild is specified, leave the guild the command was sent from
Parameters
----------
ctx (commands.Context): Context object
guild_id (int): The ID of the guild to leave
"""
if guild_id == ():
guild_id = ctx.guild.id
else:
guild_id = guild_id
# If no guild id is specified, leave the current guild
guild_id = guild_id if guild_id else ctx.guild.id
# Get guild
try:
guild = await self.bot.fetch_guild(guild_id)
except discord.errors.Forbidden:
embed = embed_templates.error_fatal(ctx, text='Bot is not a member of this guild')
return await ctx.send(embed=embed)
# Send confirmation message for leaving
confirmation_msg = await ctx.send(f'Do you want to leave {guild.name} (`{guild.id}`)?')
await confirmation_msg.add_reaction('✅')
def comfirm(reaction, user):
# Check confirmation
def comfirm(reaction: discord.Reaction, user: discord.Member):
return user == ctx.author and str(reaction.emoji) == '✅'
try:
reaction, user = await self.bot.wait_for('reaction_add', timeout=15.0, check=comfirm)
await self.bot.wait_for('reaction_add', timeout=15.0, check=comfirm)
except asyncio.TimeoutError:
await ctx.message.delete()
await confirmation_msg.delete()
@@ -314,46 +127,158 @@ class DevTools(commands.Cog):
except discord.errors.Forbidden:
pass
@commands.is_owner()
@commands.bot_has_permissions(embed_links=True)
@commands.is_owner()
@commands.command()
async def resetcooldown(self, ctx, command: str):
@commands.command(name='publicip', description='Hent den offentlige IP-adressen til serveren botten kjører på')
async def publicip(self, ctx: commands.Context):
"""
Resets the cooldown period for a specified command
Sends WAN IP-address. Inb4 I leak my IP-address
Parameters
----------
ctx (commands.Context): Context object
"""
try:
self.bot.get_command(command).reset_cooldown(ctx)
except AttributeError:
embed = embed_templates.error_fatal(ctx, text=f'{command} is not a command')
return await ctx.send(embed=embed)
data = requests.get('https://wtfismyip.com/json', timeout=10).json()
ip = data['YourFuckingIPAddress']
location = data['YourFuckingLocation']
isp = data['YourFuckingISP']
embed = discord.Embed(color=ctx.me.color, description='Cooldown reset!')
embed = discord.Embed(color=ctx.me.color)
embed.add_field(name='WAN IP-address', value=f'{ip}\n{location}\n{isp}')
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True, external_emojis=True)
@commands.is_owner()
@commands.command()
async def allemoji(self, ctx):
@commands.bot_has_permissions(embed_links=True)
@commands.group(name='cogs', description='Administrer cogs')
async def cogs(self, ctx: commands.Context):
"""
Sends all emoji the bot has access to
Cog management commands
Parameters
----------
ctx (commands.Context): Context object
"""
embed = discord.Embed(colour=ctx.me.color)
if not ctx.invoked_subcommand:
await ctx.send_help(ctx.command)
emoji_string = ''
for guild in self.bot.guilds:
emoji_string += f'\n**{guild.name}**\n'
for emoji in guild.emojis:
if len(emoji_string) > 2000:
embed.description = emoji_string
await ctx.send(embed=embed)
emoji_string = f'\n**{guild.name}**\n'
emoji_string += f'{emoji} '
@cogs.command(name='unload', description='Avslutt en cog')
async def cogs_unload(self, ctx: commands.Context, cog: str):
"""
Disables a specified cog
embed.description = emoji_string
Parameters
----------
ctx (commands.Context): Context object
cog (str): The name of the cog to disable
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
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.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@cogs.command(name='load', description='Aktiver en cog')
async def cogs_load(self, ctx: commands.Context, cog: str):
"""
Enables a speicifed cog
Parameters
----------
ctx (commands.Context): Context object
cog (str): The name of the cog to enable
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
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.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@cogs.command(name='reload', description='Last inn en cog på nytt')
async def cogs_reload(self, ctx: commands.Context, cog: str):
"""
Reloads a specified cog
Parameters
----------
ctx (commands.Context): Context object
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.send(embed=embed)
embed = embed_templates.error_fatal(ctx, text=f'{cog} does not exist')
await ctx.send(embed=embed)
@cogs.command(name='reloadunloaded', description='Last inn alle cogs på nytt, selv de som ikke er lastet inn')
async def cogs_reloadunloaded(self, ctx: commands.Context):
"""
Reloads all cogs, including previously disabled ones
Parameters
----------
ctx (commands.Context): Context object
"""
# Unload all cogs
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
await self.bot.unload_extension(f'cogs.{name}')
# Load all cogs
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
await self.bot.load_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description='Reloaded all cogs')
await ctx.send(embed=embed)
@cogs.command(name='reloadall', description='Last inn alle cogs på nytt')
async def cogs_reloadall(self, ctx: commands.Context):
"""
Reloads all previously enabled cogs
Parameters
----------
ctx (commands.Context): Context object
"""
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
await self.bot.reload_extension(f'cogs.{name}')
embed = discord.Embed(color=ctx.me.color, description='Reloaded all previously enabled cogs')
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(DevTools(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(DevTools(bot))
+113 -38
View File
@@ -1,38 +1,71 @@
from discord.ext import commands
import discord
import traceback
import sys
from datetime import datetime
import discord
from discord import app_commands
from discord.ext import commands
from cogs.utils import embed_templates
from cogs.utils.misc_utils import ignore_exception
class Errors(commands.Cog):
def __init__(self, bot):
"""Error handlers for commands"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
bot.tree.on_error = self.on_app_command_error # Set error handler for slash commands
@commands.Cog.listener()
async def on_command(self, ctx):
async def on_command(self, ctx: commands.Context):
"""
Prints command execution metadata
Logs prefix command execution metadata
Parameters
----------
ctx (commands.Context): Context object
"""
print(
f'{datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | ' +
f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} - ' +
self.bot.logger.info(
f'{"❌ " if ctx.command_failed else "✔ "} {ctx.command} | ' +
f'{ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}) | ' +
f'{ctx.guild.id}-{ctx.channel.id}-{ctx.message.id}'
)
@commands.Cog.listener()
async def on_command_error(self, ctx, error):
async def on_app_command_completion(self, interaction: discord.Interaction, command: app_commands.Command | app_commands.ContextMenu):
"""
Returns something based on the type of command error
Logs slash command execution metadata
Parameters
----------
interaction (discord.Interaction): Slash command context object
command (app_commands.Command | app_commands.ContextMenu): Command object
"""
# Reset cooldown if command throws an error
self.bot.logger.info(
f'{"❌ " if interaction.command_failed else "✔ "} {command.name} | ' +
f'{interaction.user.name}#{interaction.user.discriminator} ({interaction.user.id}) | ' +
f'{interaction.guild_id}-{interaction.channel_id}-{interaction.id}'
)
@commands.Cog.listener()
async def on_command_error(self, ctx: commands.Context, error: commands.CommandError):
"""
Handle prefix command errors
Parameters
----------
ctx (commands.Context): Context object
error (commands.CommandError): Eror context object
"""
# Reset cooldown if command throws AttributeError
with ignore_exception(AttributeError):
self.bot.get_command(f'{ctx.command}').reset_cooldown(ctx)
@@ -57,31 +90,31 @@ class Errors(commands.Cog):
elif isinstance(error, commands.BotMissingPermissions):
permissions = ', '.join(error.missing_perms)
embed = embed_templates.error_warning(ctx, text='Jeg mangler følgende tillatelser:\n\n' +
f'```\n{permissions}\n```')
return await ctx.send(embed=embed)
text = 'Jeg mangler følgende tillatelser:\n\n' + \
f'```\n{permissions}\n```'
return await ctx.send(text)
elif isinstance(error, commands.MissingPermissions):
permissions = ', '.join(error.missing_perms)
embed = embed_templates.error_warning(ctx, text='Du mangler følgende tillatelser\n\n' +
f'```\n{permissions}\n```')
return await ctx.send(embed=embed)
text = 'Du mangler følgende tillatelser\n\n' + \
f'```\n{permissions}\n```'
return await ctx.send(text)
elif isinstance(error, commands.NotOwner):
embed = embed_templates.error_fatal(ctx, text='Bare boteieren kan gjøre dette')
return await ctx.send(embed=embed)
text = 'Bare boteieren kan gjøre dette'
return await ctx.send(text)
elif isinstance(error, commands.CommandOnCooldown):
embed = embed_templates.error_warning(ctx, text='Kommandoen har nettopp blitt brukt' +
f'Prøv igjen om `{error.retry_after:.1f}` sekunder.')
return await ctx.send(embed=embed)
text = 'Kommandoen har nettopp blitt brukt' + \
f'Prøv igjen om `{error.retry_after:.1f}` sekunder.'
return await ctx.send(text)
elif isinstance(error, commands.NoPrivateMessage):
try:
embed = embed_templates.error_fatal(ctx, text='Denne kommandoen kan bare utføres i servere')
return await ctx.send(embed=embed)
text = 'Denne kommandoen kan bare utføres i servere'
return await ctx.send(text)
except discord.errors.Forbidden: # Thrown if bot is blocked by the user or if the user has closed their DMs
print("DM Blocked!")
print('DM Blocked!')
elif isinstance(error, commands.DisabledCommand):
pass
@@ -92,15 +125,57 @@ class Errors(commands.Cog):
embed = embed_templates.error_fatal(ctx, text='En ukjent feil oppstod!')
await ctx.send(embed=embed)
# Print full exception to console
print(f'Ignoring exception in command {ctx.command}:', file=sys.stderr)
traceback.print_exception(
type(error),
error,
error.__traceback__,
file=sys.stderr
)
# Log full exception to file
self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__)))
async def on_app_command_error(self, interaction: discord.Interaction, error: app_commands.AppCommandError):
"""
Handle slash command errors
Parameters
----------
interaction (discord.Interaction): Slash command context object
error (app_commands.AppCommandError): Eror context object
"""
# Log command usage, just in case
await self.on_app_command_completion(interaction, interaction.command)
if isinstance(error, commands.BotMissingPermissions):
permissions = ', '.join(error.missing_perms)
embed = embed_templates.error_warning(interaction, text='Jeg mangler følgende tillatelser:\n\n' +
f'```\n{permissions}\n```')
return await interaction.response.send_message(embed=embed)
elif isinstance(error, commands.MissingPermissions):
permissions = ', '.join(error.missing_perms)
embed = embed_templates.error_warning(interaction, text='Du mangler følgende tillatelser\n\n' +
f'```\n{permissions}\n```')
return await interaction.response.send_message(embed=embed)
elif isinstance(error, 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, commands.CommandOnCooldown):
embed = embed_templates.error_warning(interaction, text='Kommandoen har nettopp blitt brukt' +
f'Prøv igjen om `{error.retry_after:.1f}` sekunder.')
return await interaction.response.send_message(embed=embed)
embed = embed_templates.error_fatal(interaction, text='En ukjent feil oppstod!')
await interaction.response.send_message(embed=embed)
# Log full exception to file
self.bot.logger.error(''.join(traceback.format_exception(type(error), error, error.__traceback__)))
def setup(bot):
bot.add_cog(Errors(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(Errors(bot))
+77 -14
View File
@@ -2,44 +2,107 @@ from discord.ext import commands
class Fun(commands.Cog):
def __init__(self, bot):
self.bot = bot
"""Simple commands that return short strings for the memes"""
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def norskeuniversiteter(self, ctx):
@commands.command(name='norskeuniversiteter')
async def norskeuniversiteter(self, ctx: commands.Context):
"""
Sends a link to university meme image album
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('<https://imgur.com/a/uGopaSq>')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def informatikkuio(self, ctx):
@commands.command(name='ifi')
async def ifi(self, ctx: commands.Context):
"""
ifi meme 1
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://i.imgur.com/ypyK1mi.jpg')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def informatikkuio2(self, ctx):
@commands.command(name='ifi2')
async def ifi2(self, ctx: commands.Context):
"""
ifi meme 2
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://i.imgur.com/ZqgZEEA.jpg')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def informatikkuio3(self, ctx):
async def ifi3(self, ctx: commands.Context):
"""
ifi meme 3
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://i.imgur.com/Gx9DQE5.jpg')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def uio(self, ctx):
async def uio(self, ctx: commands.Context):
"""
uio meme 1
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://i.imgur.com/188MoIV.jpg')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def ntnu(self, ctx):
async def ntnu(self, ctx: commands.Context):
"""
ntnu meme 1
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://twitter.com/NTNU/status/970667413564993536')
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def ntnu2(self, ctx):
async def ntnu2(self, ctx: commands.Context):
"""
ntnu meme 2
Parameters
----------
ctx (commands.Context): Context object
"""
await ctx.send('https://i.imgur.com/h84fknj.jpg')
def setup(bot):
bot.add_cog(Fun(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(Fun(bot))
+76 -18
View File
@@ -1,31 +1,89 @@
from discord.ext import commands
import random
import re
from datetime import datetime
import discord
from discord.ext import commands
class FunReplies(commands.Cog):
def __init__(self, bot):
"""Reply to messages that trigger certain key words/phrases"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
async def react(self, message):
# 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
self.cooldown_seconds = 120
initial_datetime = datetime(2000, 9, 11) # Set initial datetime far in the past to allow triggering right after boot
self.previous_invokations = {
'borgerlønn': initial_datetime,
'olof palme': initial_datetime,
'lesgo': initial_datetime,
'neuf': initial_datetime,
'yeet': initial_datetime
}
async def reply_to_triggers(self, message: discord.Message):
"""
Replies to messages that trigger certain key words/phrases
Parameters
----------
message (discord.Message): Message object to check for triggers to
"""
if message.author.bot:
return
if re.findall(r'(^|\W)borgerlønn(\W|$)', message.clean_content.lower()) != []:
await message.channel.send('<@267415183931080715> DE SNAKKER OM BORGERLØNN')
message_content = message.content.lower()
elif re.findall(r'(^|\W)olof palme(\W|$)', message.clean_content.lower()) != []:
await message.channel.send('Jeg vet hvem som drepte Olof Palme 👀')
if re.search(r'(^|\W)borgerlønn(\W|$)', message_content, flags=re.IGNORECASE):
if (datetime.now() - self.previous_invokations['borgerlønn']).seconds > self.cooldown_seconds:
await message.channel.send('<@267415183931080715> DE SNAKKER OM BORGERLØNN')
self.previous_invokations['borgerlønn'] = datetime.now()
# The Ultimate spaghetti
elif (
re.match(r'(\W|\s)*lesgo{1}(\s|\W)*', message.clean_content.lower()) or
message.clean_content.lower() == 'lesgo'
):
await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' +
'824764933513281596/3xhpwbakz2361.png')
elif re.search(r'(^|\W)olof palme(\W|$)', message_content, flags=re.IGNORECASE):
if (datetime.now() - self.previous_invokations['olof palme']).seconds > self.cooldown_seconds:
await message.channel.send('Jeg vet hvem som drepte Olof Palme 👀')
self.previous_invokations['olof palme'] = datetime.now()
elif re.search(r'(\W|\s)*le+s+go+(\s|\W)*', message_content):
if (datetime.now() - self.previous_invokations['lesgo']).seconds > self.cooldown_seconds:
await message.channel.send('https://cdn.discordapp.com/attachments/750052141346979850/' +
'824764933513281596/3xhpwbakz2361.png')
self.previous_invokations['lesgo'] = datetime.now()
elif re.search(r'^((er det|hvor)\s+)*(noen|folk|mange)\s+på\s+(rommet|neuf|kontoret)\?*$', message_content, flags=re.IGNORECASE):
if (datetime.now() - self.previous_invokations['neuf']).seconds > self.cooldown_seconds:
# Choose a random user to ping
pingable = ["<@554977854971183125>", "<@276061121776058370>"]
to_be_pinged = random.choice(pingable)
await message.channel.send(f'Ja, {to_be_pinged} bor der')
self.previous_invokations['neuf'] = datetime.now()
elif re.search(r'(^|\W)+ye+et($|\W)+', message_content, flags=re.IGNORECASE):
if (datetime.now() - self.previous_invokations['yeet']).seconds > self.cooldown_seconds:
await message.channel.send('<:Nei:826593267642662912>')
self.previous_invokations['yeet'] = datetime.now()
def setup(bot):
bot.add_listener(FunReplies(bot).react, 'on_message')
bot.add_cog(FunReplies(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
bot.add_listener(FunReplies(bot).reply_to_triggers, 'on_message')
await bot.add_cog(FunReplies(bot))
+552 -567
View File
File diff suppressed because it is too large. Load diff
+58 -22
View File
@@ -1,19 +1,29 @@
import discord
from discord import app_commands
from discord.ext import commands
import requests
import json
from mcrcon import MCRcon
import requests
from cogs.utils import embed_templates
class MCWhitelist(commands.Cog):
def __init__(self, bot):
"""Stand alone cog for handling whitelisting of Discord users on the Minecraft server"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
self.cursor = self.bot.db_connection.cursor()
self.init_db()
def init_db(self):
"""Create the necessary tables for the mc_whitelist cog to work."""
self.cursor.execute(
"""
CREATE TABLE IF NOT EXISTS mc_whitelist (
@@ -24,49 +34,75 @@ class MCWhitelist(commands.Cog):
)
self.bot.db_connection.commit()
@commands.cooldown(1, 10, commands.BucketType.user)
@commands.command()
async def whitelist(self, ctx, minecraftbrukernavn):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='whitelist', description='Whitelist minecraftbrukeren din på serveren vår')
async def whitelist(self, interaction: discord.Interaction, minecraftbrukernavn: str):
"""
Whitelist a minecraft user on the Minecraft server
# fetch minecraft uuid from api
data = requests.get(f"https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}")
Parameters
----------
interaction (discord.Interaction): Slash command context object
minecraftbrukernavn (str): Minecraft username
"""
# Fetch minecraft uuid from api
data = requests.get(f'https://api.mojang.com/users/profiles/minecraft/{minecraftbrukernavn}', timeout=10).json()
if data.status_code != 200:
return await ctx.send(
embed=embed_templates.error_fatal(ctx, text=f"Brukeren `{minecraftbrukernavn}` finnes ikk på minecraft")
return await interaction.response.send_message(
embed=embed_templates.error_fatal(interaction, text=f'Brukeren `{minecraftbrukernavn}` finnes ikke på minecraft'),
ephemeral=True
)
data = data.json()
# check if the discord user or minecraft user is in the db
self.cursor.execute(
"SELECT * FROM mc_whitelist WHERE minecraft_id = %s OR discord_id = %s", (data["id"], ctx.author.id)
"""
SELECT *
FROM mc_whitelist
WHERE minecraft_id = %s OR discord_id = %s
""", (data['id'], interaction.user.id)
)
if self.cursor.fetchone():
return await ctx.send(
return await interaction.response.send_message(
embed=embed_templates.error_fatal(
ctx, text="Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted"
interaction,
text='Du har allerede whitelisted en bruker eller så er brukeren du oppga whitelisted'
)
)
with MCRcon(host="192.168.0.24", password="hallaballayeet", port=25575) as mcr:
mcr.command(f"whitelist add {data['name']}")
mcr.command("whitelist reload")
# Whitelist user on minecraft server
# 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:
mcr.command(f'whitelist add {data["name"]}')
mcr.command('whitelist reload')
# Add user to db
self.cursor.execute(
"""
INSERT INTO mc_whitelist (discord_id, minecraft_id)
VALUES (%s, %s)
""",
(ctx.author.id, data["id"]),
(interaction.user.id, data['id']),
)
self.bot.db_connection.commit()
await ctx.send(
await interaction.response.send_message(
embed=embed_templates.success(
ctx, text=f"`{data['name']}` er nå tilknyttet din discordbruker og whitelisted!"
interaction, text=f'`{data["name"]}` er nå tilknyttet din discordbruker og whitelisted!'
)
)
def setup(bot):
bot.add_cog(MCWhitelist(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(MCWhitelist(bot))
+224 -191
View File
@@ -1,271 +1,304 @@
from discord.ext import commands
import discord
from re import sub
from datetime import datetime
import requests
import json
from asyncio import sleep
from PIL import Image, ImageDraw, ImageFont
import re
from os import remove
import urllib
import discord
from discord import app_commands
from discord.ext import commands
from PIL import Image, ImageDraw, ImageFont
import requests
from cogs.utils import embed_templates
from cogs.utils.misc_utils import ignore_exception
class Misc(commands.Cog):
def __init__(self, bot):
"""Miscellaneous commands that don't fit anywhere else"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command()
async def weeb(self, ctx):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='weeb', description='Kjeft på weebs')
async def weeb(self, interaction: discord.Interaction):
"""
Kjeft på weebs
Call out weebs
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
await ctx.send("<:sven:762725919604473866> Weebs 👉 <#803596668129509417>")
await interaction.response.send_message('<:sven:762725919604473866> Weebs 👉 <#803596668129509417>')
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command(aliases=["owoify", "uwu"])
async def owo(self, ctx, *, tekst: str):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='kweeb', description='김정운, 최고도')
async def kweeb(self, interaction: discord.Interaction):
"""
Oversetter teksten din til owo
Call out kweebs
Parameters
----------
interaction (discord.Interaction): Slash command context object
"""
owo_rules = {"r": "w", "l": "w", "R": "W", "L": "W", "n": "ny", "N": "Ny", "ove": "uv"}
await interaction.response.send_message(
"""
Melding du nettopp sendte ga muligens følgende vibber for brukeren som brukte denne kommandoen:
> KOREA NUMBER ONE!
> OMG I LUUUUV JUNGKOK!!! 🥰😍🥰 STAN JUNGKOOK! 🤞🤞🤞 사랑해!!! ♥♥♥
> JEG EEEEELSKER 김치
> OP OP OP OPPAN GANGNAM STYLE!
Så da kan man spørre seg selv? Hva faen skjedde med god gammeldags weebdom?
Til vår skuffelse finnes den fortsatt, men ble det for mainstream å være weeb.
Så da beveger massen seg til det neste hyperkapitalistiske øst-asiatiske staten med økonomisk boom.
Så hvor ble da av weebsa? Denne videoen svarer ganske godt på det
https://cdn.discordapp.com/attachments/448502155184439297/1027355652059832351/THEY_WENT_TO_KOREA.mp4
Du har nå blitt kalt en kweeb. Det er over for deg.
Våkne!
https://cdn.discordapp.com/attachments/674726496878723082/1042631569396990004/kweeb_er_den_nye_weeb.png
"""
)
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='owo', description='Oversetter teksten din til owo (we_eb)')
async def owo(self, interaction: discord.Interaction, tekst: str):
"""
Translate text into owo (we_eb)
Parameters
----------
interaction (discord.Interaction): Slash command context object
tekst (str): Text to translate
"""
owo_rules = {'r': 'w', 'l': 'w', 'R': 'W', 'L': 'W', 'n': 'ny', 'N': 'Ny', 'ove': 'uv'}
for key, value in owo_rules.items():
tekst = sub(key, value, tekst)
tekst = re.sub(key, value, tekst)
# https://kaomoji.moe/
if not tekst or len(tekst) >= 1000:
embed = embed_templates.error_warning(ctx, text="Teksten er for lang")
return await ctx.send(embed=embed)
if len(tekst) >= 1000:
embed = embed_templates.error_warning(interaction, text='Teksten er for lang')
return await interaction.response.send_message(embed=embed)
embed = discord.Embed(color=ctx.me.color, description=tekst)
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
embed = discord.Embed(color=interaction.client.user.color, description=tekst)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
@commands.command()
async def emne(self, ctx, emnekode: str):
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='emne', description='For NTNU\'ere som ikke kan emnekoder på UiO')
async def course_code(self, interaction: discord.Interaction, emnekode: str):
"""
Translate UiO course codes to their respective course names
data = requests.get(f"https://data.uio.no/studies/v1/course/{emnekode}")
Parameters
----------
interaction (discord.Interaction): Slash command context object
emnekode (str): UiO course code
"""
data = requests.get(f'https://data.uio.no/studies/v1/course/{emnekode}', timeout=10)
if data.status_code != 200:
return await ctx.send(embed=embed_templates.error_fatal(ctx, text="Fant ikke emnekode"))
return await interaction.response.send_message(embed=embed_templates.error_fatal(interaction, text='Fant ikke emnekode'))
emne = data.json()["info"]["name"]
await ctx.send(emne)
await interaction.response.send_message(data.json()['info']['name'])
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 2, commands.BucketType.guild)
@commands.command(aliases=["clapify"])
async def klappifiser(self, ctx, *, tekst):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 2)
@app_commands.command(name='klappifiser', description='Klappifiser tekst')
async def clapify(self, interation: discord.Interaction, tekst: str):
"""
Klapppifiserer teksten din
Add clap emoji between every word of a string
Parameters
----------
interaction (discord.Interaction): Slash command context object
tekst (str): Text to clapify
"""
if not tekst or len(tekst) >= 1000:
embed = embed_templates.error_warning(ctx, text="Teksten er for lang")
return await ctx.send(embed=embed)
if len(tekst) >= 1000:
embed = embed_templates.error_warning(interation, text='Teksten er for lang')
return await interation.response.send_message(embed=embed)
tekst = sub(" ", "👏", tekst)
tekst = re.sub(' ', '👏', tekst)
embed = discord.Embed(color=ctx.me.color, description=f"**{tekst.upper()}**")
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
embed = discord.Embed(color=interation.client.user.color, description=f'**{tekst.upper()}**')
embed_templates.default_footer(interation, embed)
await interation.response.send_message(embed=embed)
@commands.cooldown(1, 5, commands.BucketType.user)
@commands.command(aliases=["filmsøk"])
async def imdb(self, ctx, *, film):
@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):
"""
Se informasjon om en film eller serie
Search for movies on IMDB and displays information about the first result
Parameters
----------
interaction (discord.Interaction): Slash command context object
tittel (str): Movie title
"""
url = "http://www.omdbapi.com/?" + urllib.parse.urlencode({"s": film, "apikey": self.bot.api_keys["omdb"]})
search = requests.get(url).json()
# 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"]
best_result_id = search['Search'][0]['imdbID']
except KeyError:
embed = embed_templates.error_fatal(ctx, text="Fant ikke filmen!")
return await ctx.send(embed=embed)
embed = embed_templates.error_fatal(interaction, text='Fant ikke filmen!')
return await interaction.response.send_message(embed=embed)
data = requests.get(f'http://www.omdbapi.com/?i={best_result_id}&apikey={self.bot.api_keys["omdb"]}').json()
acceptable_media_types = ["movie", "series", "episode"]
if data["Type"] not in acceptable_media_types:
embed = embed_templates.error_fatal(ctx, text="Fant ikke filmen!")
return await ctx.send(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=ctx.author.name, icon_url=ctx.author.avatar_url)
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')
embed.set_footer(text=f'Utgitt: {data["Released"]}')
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'))
embed_templates.default_footer(interaction, embed)
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)
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 ctx.send(embed=embed)
await interaction.response.send_message(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 5, commands.BucketType.guild)
@commands.command(aliases=["identify"])
async def identifiser(self, ctx, bilde=None):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='helligdager', description='Se helligdager i et land for et år')
async def holidays(self, interaction: discord.Interaction, *, land: str = 'NO', år: int = None):
"""
Beskriver hva som er på bildet
Get holidays for a country for a given year
Parameters
----------
interaction (discord.Interaction): Slash command context object
land (str): Country code
år (int): Year
"""
if ctx.message.attachments != []:
bilde = ctx.message.attachments[0].url
land = land.upper()
aar = datetime.now().year if not år else int(år)
if bilde is None:
embed = embed_templates.error_fatal(ctx, text="Du må gi meg et bilde!")
async with ctx.channel.typing():
payload = {"inputs": [{"data": {"image": {"url": bilde}}}]}
header = {"Authorization": f'Key {self.bot.api_keys["clarifai"]}'}
url = (
"https://api.clarifai.com/v2/models/aaa03c23b3724a16a56b629203edc62c"
+ "/versions/aa7f35c01e0642fda5cf400f543e7c40/outputs"
)
data = requests.post(url, data=json.dumps(payload), headers=header)
if data.status_code != 200:
embed = embed_templates.default_footer("API request feilet")
return await ctx.send(embed=embed)
data = data.json()
words = []
for i, concepts in enumerate(data["outputs"][0]["data"]["concepts"]):
words.append(concepts["name"])
if i >= 5:
break
words = ", ".join(words)
await sleep(2)
embed = discord.Embed(color=ctx.me.color)
embed.set_author(name="Clarifai AI", icon_url="https://github.com/Clarifai.png")
embed.description = f"**Ord som beskriver dette bildet:**\n\n{words}"
embed.set_image(url=bilde)
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 5, commands.BucketType.guild)
@commands.command(aliases=["holidays", "fridager", "ferie"])
async def helligdager(self, ctx, land=None, aar=None):
"""
Se hellidagene i et land
"""
if not land:
land = "NO"
else:
land = land.upper()
if not aar:
aar = datetime.now().year
else:
int(aar)
data = requests.get(f"https://date.nager.at/api/v2/publicholidays/{aar}/{land}")
data = requests.get(f'https://date.nager.at/api/v2/publicholidays/{aar}/{land}', timeout=10)
if data.status_code != 200:
embed = embed_templates.error_fatal(
ctx, text="Ugyldig land\nHusk å bruke landskoder\n" + "For eksempel: `NO`"
interaction, text='Ugyldig land\nHusk å bruke landskoder\n' + 'For eksempel: `NO`'
)
return await ctx.send(embed=embed)
return await interaction.response.send_message(embed=embed)
data = data.json()
country = data[0]["countryCode"].lower()
holiday_str = ""
country = data[0]['countryCode'].lower()
# Construct output
holiday_str = ''
for day in data:
date = day["date"]
date = datetime.strptime(date, "%Y-%m-%d").strftime("%d. %B")
date = discord.utils.format_dt(datetime.strptime(day['date'], '%Y-%m-%d'), style='D')
holiday_str += f'**{date}**: {day["localName"]}\n'
embed = discord.Embed(color=ctx.me.color, title=f":flag_{country}: Helligdager {aar} :flag_{country}:")
embed = discord.Embed(color=interaction.client.user.color, title=f':flag_{country}: Helligdager {aar} :flag_{country}:')
embed.description = holiday_str
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed)
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 5, commands.BucketType.guild)
@commands.command()
async def match(self, ctx, *, bruker: discord.Member = None):
@app_commands.checks.bot_has_permissions(embed_links=True)
@app_commands.checks.cooldown(1, 5)
@app_commands.command(name='match', description='Se hvor mye du matcher med en annen')
async def match(self, interaction: discord.Interaction, bruker: discord.Member):
"""
Se hvor mye du matcher med en annen
Get dating compatibility with another user
Parameters
----------
interaction (discord.Interaction): Slash command context object
bruker (discord.Member): User to match with
"""
if not bruker:
embed = embed_templates.error_warning(ctx, text="Du må gi meg en bruker")
return await ctx.send(embed=embed)
if bruker == ctx.author:
if bruker == interaction.user:
embed = embed_templates.error_warning(
ctx, text="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 ctx.send(embed=embed)
return await interaction.response.send_message(embed=embed)
async with ctx.channel.typing():
invoker_id = int(str(interaction.user.id)[11:14])
user_id = int(str(bruker.id)[11:14])
invoker_id = int(str(ctx.author.id)[11:14])
user_id = int(str(bruker.id)[11:14])
match_percent = int((invoker_id + user_id) % 100)
match_percent = int((invoker_id + user_id) % 100)
if bruker.id == self.bot.user.id:
match_percent = 100
if bruker.id == self.bot.user.id:
match_percent = 100
await interaction.user.display_avatar.save(fp=f'./src/assets/temp/{interaction.user.id}_raw.png')
await bruker.display_avatar.save(fp=f'./src/assets/temp/{bruker.id}_raw.png')
await ctx.author.avatar_url_as(format="png").save(fp=f"./src/assets/temp/{ctx.author.id}_raw.png")
await bruker.avatar_url_as(format="png").save(fp=f"./src/assets/temp/{bruker.id}_raw.png")
invoker = Image.open(f'./src/assets/temp/{interaction.user.id}_raw.png').convert('RGBA')
invoker = invoker.resize((389, 389), Image.ANTIALIAS)
user = Image.open(f'./src/assets/temp/{bruker.id}_raw.png').convert('RGBA')
user = user.resize((389, 389), Image.ANTIALIAS)
heart = Image.open('./src/assets/misc/heart.png')
mask = Image.open('./src/assets/misc/heart.png', 'r')
invoker = Image.open(f"./src/assets/temp/{ctx.author.id}_raw.png").convert("RGBA")
invoker = invoker.resize((389, 389), Image.ANTIALIAS)
user = Image.open(f"./src/assets/temp/{bruker.id}_raw.png").convert("RGBA")
user = user.resize((389, 389), Image.ANTIALIAS)
heart = Image.open("./src/assets/misc/heart.png")
mask = Image.open("./src/assets/misc/heart.png", "r")
image = Image.new('RGBA', (1024, 576))
image.paste(invoker, (0, 94))
image.paste(user, (635, 94))
image.paste(heart, (311, 94), mask=mask)
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('./src/assets/fonts/RobotoMono-Medium.ttf', 86)
font_size = font.getsize(f'{match_percent}%')
font_size = ((image.size[0] - font_size[0]) / 2, (image.size[1] - font_size[1]) / 2)
draw.text(font_size, f'{match_percent}%', font=font, fill=(255, 255, 255, 255))
image = Image.new("RGBA", (1024, 576))
image.paste(invoker, (0, 94))
image.paste(user, (635, 94))
image.paste(heart, (311, 94), mask=mask)
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", 86)
font_size = font.getsize(f"{match_percent}%")
font_size = ((image.size[0] - font_size[0]) / 2, (image.size[1] - font_size[1]) / 2)
draw.text(font_size, f"{match_percent}%", font=font, fill=(255, 255, 255, 255))
image.save(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png')
image.save(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png")
f = discord.File(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png')
embed = discord.Embed()
embed.set_image(url=f'attachment://{interaction.user.id}_{bruker.id}_edit.png')
embed_templates.default_footer(interaction, embed)
await interaction.response.send_message(embed=embed, file=f)
f = discord.File(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png")
embed = discord.Embed()
embed.set_image(url=f"attachment://{ctx.author.id}_{bruker.id}_edit.png")
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed, file=f)
with ignore_exception(OSError):
remove(f"./src/assets/temp/{bruker.id}_raw.png")
remove(f"./src/assets/temp/{ctx.author.id}_raw.png")
remove(f"./src/assets/temp/{ctx.author.id}_{bruker.id}_edit.png")
with ignore_exception(OSError):
remove(f'./src/assets/temp/{bruker.id}_raw.png')
remove(f'./src/assets/temp/{interaction.user.id}_raw.png')
remove(f'./src/assets/temp/{interaction.user.id}_{bruker.id}_edit.png')
def setup(bot):
bot.add_cog(Misc(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(Misc(bot))
-67
View File
@@ -1,67 +0,0 @@
from discord.ext import commands
import discord
from datetime import datetime
from cogs.utils import embed_templates
class Mod(commands.Cog):
def __init__(self, bot) -> None:
self.bot = bot
@commands.bot_has_permissions(ban_members=True)
@commands.has_permissions(ban_members=True)
@commands.guild_only()
@commands.command()
async def hackban(self, ctx, brukerid, *, begrunnelse=None):
"""
Bannlys en bruker før de i det hele tatt har blitt med i serveren
"""
try:
brukerid = int(brukerid)
except ValueError:
embed = embed_templates.error_warning(ctx, text='Du må gi meg en bruker-id')
return await ctx.send(embed=embed)
user = discord.Object(id=brukerid)
for banentry in await ctx.guild.bans():
if banentry.user.id == user.id:
embed = embed_templates.error_warning(ctx, text='Denne brukeren er allerede bannlyst fra serveren!')
return await ctx.send(embed=embed)
if begrunnelse is None:
begrunnelse = 'Ingen grunn angitt'
date = datetime.now().strftime('%d. %b. %Y - %H:%M')
try:
await ctx.guild.ban(
user,
reason=f'{date} | {ctx.author.name}#{ctx.author.discriminator} ({ctx.author.id}): {begrunnelse}'
)
except discord.errors.NotFound:
embed = embed_templates.error_warning(ctx, text='Bruker finnes ikke!')
return await ctx.send(embed=embed)
user_object = self.bot.get_user(brukerid)
embed = discord.Embed(color=discord.Color.red(), title='🔨 Bruker utestengt!')
if begrunnelse != 'Ingen grunn angitt':
embed.description = f'`{begrunnelse}`'
if user_object:
user = user_object
embed.set_thumbnail(url=user.avatar_url)
embed.set_author(name=f'{user.name}#{user.discriminator}', icon_url=user.avatar_url)
embed.add_field(name='Bruker', value=f'{user.name}#{user.discriminator}\n{user.mention}', inline=False)
else:
embed.add_field(name='Bruker', value=f'`{user.id}`', inline=False)
embed.add_field(name='Av', value=f'{ctx.author.name}#{ctx.author.discriminator}\n{ctx.author.mention}')
embed = embed_templates.default_footer(ctx, embed)
await ctx.send(embed=embed)
def setup(bot):
bot.add_cog(Mod(bot))
+32 -19
View File
@@ -1,38 +1,51 @@
from discord.ext import commands
import aiocron
from discord.ext import commands
class RepeatedMessages(commands.Cog):
def __init__(self, bot):
"""Send messages at specific points of time"""
def __init__(self, bot: commands.Bot):
self.bot = bot
self.uiog_server = self.bot.get_guild(747542543750660178)
@aiocron.crontab("0 0 * * 5")
@aiocron.crontab('0 0 * * 5')
async def fredag():
guild = self.bot.get_guild(747542543750660178)
channel = guild.get_channel(747542544291987597)
"""Sends a message on Friday at 00:00"""
channel = await self.uiog_server.fetch_channel(747542544291987597)
await channel.send(
"NU ÄR DET FREDAG!\n"
+ "https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4"
'NU ÄR DET FREDAG!\n' +
'https://cdn.discordapp.com/attachments/750052141346979850/851216786259181578/video0.mp4'
)
@aiocron.crontab("0 0 * * 1")
@aiocron.crontab('0 0 * * 1')
async def mandag():
guild = self.bot.get_guild(747542543750660178)
channel = guild.get_channel(747542544291987597)
"""Sends a message on Monday at 00:00"""
channel = await self.uiog_server.fetch_channel(747542544291987597)
await channel.send(
"ENDELIG MANDAG!\n\n"
+ "https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4"
'ENDELIG MANDAG!\n\n' +
'https://cdn.discordapp.com/attachments/678396498089738250/862853827278929940/hvorfor_de_rike_br_spises.mp4'
)
@aiocron.crontab("0 0 1 1 *")
@aiocron.crontab('0 0 1 1 *')
async def new_year():
guild = self.bot.get_guild(747542543750660178)
channel = guild.get_channel(747542544291987597)
"""Sends a message on New Year's Day at 00:00"""
channel = await self.uiog_server.fetch_channel(747542544291987597)
await channel.send(
"GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>\nhttps://youtu.be/5qXkEvHrZ-0"
'GODT NYTTÅR!\n\nNå, meld dere inn i UiO Gaming igjen <:ThisIsAThreat:874998234072903710>'
)
def setup(bot):
bot.add_cog(RepeatedMessages(bot))
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(RepeatedMessages(bot))
+59 -4
View File
@@ -1,18 +1,73 @@
import discord
from .misc_utils import Paginator
def get_user_color(user: discord.User) -> discord.Color:
def get_color(discord_object: discord.User | discord.Member | discord.Role) -> discord.Color:
"""
Returns the user's top role color
Parameters
-----------
user (discord.User): A discord user or member object
discord_object (discord.User|discord.Member|discord.Role): A discord object that has a color attribute
Returns
-----------
(discord.Color): The user's displayed color
"""
if str(user.color) != '#000000':
return user.color
if hasattr(discord_object, 'color') and str(discord_object.color) != '#000000':
return discord_object.color
return discord.Colour(0x99AAB5)
class ScrollerButton(discord.ui.Button):
"""Button that scrolls through pages in a scroller view"""
def __init__(self, paginator: Paginator, button_action: callable, content_constructor: callable, label: str, disabled: bool = False):
"""
Parameters
-----------
paginator (Paginator): The paginator object that contains the data to be paginated
button_action (callable): The function that returns the requested page
content_constructor (callable): A function that takes a paginator object and a page number and returns an embed
"""
super().__init__(label=label, disabled=disabled)
self.paginator = paginator
self.button_action = button_action
self.content_constructor = content_constructor
async def callback(self, interaction: discord.Interaction):
"""
What to do when the button is pressed
Parameters
-----------
interaction (discord.Interaction): Slash command context object
"""
await interaction.response.defer()
content = self.content_constructor(self.paginator, self.button_action(), interaction.message.embeds[0])
await interaction.message.edit(embed=content, view=Scroller(self.paginator, self.content_constructor))
class Scroller(discord.ui.View):
"""View that allows scrolling through pages of data using the pagination module"""
def __init__(self, paginatior: Paginator, content_constructor: callable):
"""
Parameters
-----------
paginator (Paginator): The paginator object that contains the data to be paginated
content_constructor (callable): A function that takes a paginator object and a page number and returns an embed
"""
super().__init__()
self.paginator = paginatior
self.add_item(ScrollerButton(self.paginator, self.paginator.first_page, content_constructor, label='<<', disabled=self.paginator.current_page == 1))
self.add_item(ScrollerButton(self.paginator, self.paginator.previous_page, content_constructor, label='<', disabled=self.paginator.current_page == 1))
self.add_item(ScrollerButton(self.paginator, self.paginator.next_page, content_constructor, label='>', disabled=self.paginator.current_page == self.paginator.total_page_count))
self.add_item(ScrollerButton(self.paginator, self.paginator.last_page, content_constructor, label='>>', disabled=self.paginator.current_page == self.paginator.total_page_count))
+27 -16
View File
@@ -1,70 +1,81 @@
import discord
def default_footer(ctx: discord.ext.commands.Context, embed: discord.Embed) -> discord.Embed:
def default_footer(interaction: discord.Interaction, embed: discord.Embed) -> discord.Embed:
"""
Sets a footer containing the command invoker's name, discriminator and avatar
Parameters
-----------
ctx (discord.ext.commands.Context): The current Discord context
interaction (discord.Interaction): Slash command context object
embed (discord.Embed): An embed object to add the footer to
Returns
-----------
(discord.Embed): The sent in embed + footer
(discord.Embed): The passed in embed + footer
"""
return embed.set_footer(icon_url=ctx.author.avatar_url, text=f"{ctx.author.name}#{ctx.author.discriminator}")
return embed.set_footer(
icon_url=interaction.user.avatar,
text=f'{interaction.user.name}#{interaction.user.discriminator}'
)
def error_warning(ctx: discord.ext.commands.Context, text: str) -> discord.Embed:
def error_warning(interaction: discord.Interaction, text: str) -> discord.Embed:
"""
Creates an embed with a specified error message based on a warning template
Parameters
-----------
ctx (discord.ext.commands.Context): The current Discord context
interaction (discord.Interaction): Slash command context object
text (str): The error message
Returns
-----------
(discord.Embed): An embed object based on the template with the specified text
"""
embed = discord.Embed(color=discord.Color.gold(), description=f"⚠️ {text}")
default_footer(ctx, embed)
embed = discord.Embed(color=discord.Color.gold(), description=f'⚠️ {text}')
default_footer(interaction, embed)
return embed
def error_fatal(ctx: discord.ext.commands.Context, text: str) -> discord.Embed:
def error_fatal(interaction: discord.Interaction, text: str) -> discord.Embed:
"""
Creates an embed with a specified error message based on a warning template
Parameters
-----------
ctx (discord.ext.commands.Context): The current Discord context
interaction (discord.Interaction): Slash command context object
text (str): The error message
Returns
-----------
(discord.Embed): An embed object based on the template with the specified text
"""
embed = discord.Embed(color=discord.Color.red(), description=f"❌ {text}")
default_footer(ctx, embed)
embed = discord.Embed(color=discord.Color.red(), description=f'❌ {text}')
default_footer(interaction, embed)
return embed
def success(ctx: discord.ext.commands.Context, text: str) -> discord.Embed:
def success(interaction: discord.Interaction, text: str) -> discord.Embed:
"""
Creates an embed with a specified message using a template signifying success.
Parameters
-----------
ctx (discord.ext.commands.Context): The current Discord context
interaction (discord.Interaction): Slash command context object
text (str): The message
Returns
-----------
discord.Embed: An embed object based on the template with the specified text
"""
embed = discord.Embed(color=discord.Color.green(), description=f"✅ {text}")
default_footer(ctx, embed)
embed = discord.Embed(color=discord.Color.green(), description=f'✅ {text}')
default_footer(interaction, embed)
return embed
+76 -33
View File
@@ -1,56 +1,99 @@
from typing import Union, Dict
from math import ceil
from contextlib import contextmanager
from math import ceil
@contextmanager
def ignore_exception(*exceptions) -> None:
def ignore_exception(*exceptions: Exception):
"""
Ignores the given exceptions
Parameters
----------
*exceptions: The exceptions you want to ignore
Returns
----------
None
*exceptions tuple[Exception]: The exceptions you want to ignore
"""
try:
yield
except (exceptions):
except exceptions:
pass
def paginator(content: list, page: int) -> Dict[str, Union[int, str]]:
"""
Divides content into 10 element pages
class Paginator:
def __init__(self, content: list):
self.content = content
self.total_page_count = ceil(len(content) / 10)
self.current_page = 1
Parameters
-----------
content: A list of strings
page: The page
def get_page(self, page: int) -> list | None:
"""
Returns the page of the paginator
Returns
-----------
(dict): A dictionay containing content and metadata
keys: pagecount, page, page_content
"""
Parameters
----------
page (int): The page you want to get
pagecount = ceil(len(content) / 10)
Returns
----------
(list|None): The page you requested
"""
if page < 1 or page > self.total_page_count:
return None
if not page or page <= 0 or page > pagecount:
page = 1
start_index = (page - 1) * 10
end_index = page * 10
start_index = (page - 1) * 10
end_index = page * 10
return self.content[start_index:end_index]
page_content = content[start_index:end_index]
def get_current_page(self) -> list:
"""
Returns the current page of the paginator
page_data = {
'pagecount': pagecount,
'page': page,
'page_content': page_content
}
return page_data
Returns
----------
(list): The current page of the paginator
"""
return self.get_page(self.current_page)
def next_page(self) -> list | None:
"""
Returns the next page of the paginator
Returns
----------
(list): The next page of the paginator
"""
self.current_page += 1
return self.get_page(self.current_page)
def previous_page(self) -> list | None:
"""
Returns the previous page of the paginator
Returns
----------
(list): The previous page of the paginator
"""
self.current_page -= 1
return self.get_page(self.current_page)
def first_page(self) -> list | None:
"""
Returns the first page of the paginator
Returns
----------
(list): The first page of the paginator
"""
self.current_page = 1
return self.get_page(self.current_page)
def last_page(self) -> list | None:
"""
Returns the last page of the paginator
Returns
----------
(list): The last page of the paginator
"""
self.current_page = self.total_page_count
return self.get_page(self.current_page)
+13
View File
@@ -1,3 +1,5 @@
config_mode: dev
# Bot settings
bot:
token:
@@ -7,6 +9,17 @@ bot:
activity: listening
type: dnd
# Database
database:
host:
dbname:
username:
password:
# Minecraft server
minecraft:
rcon_password:
# Api keys
api:
clarifai:
+16
View File
@@ -0,0 +1,16 @@
import logging
import logging.handlers
class BotLogger:
"""Logging config for the bot"""
def __init__(self):
logger = logging.getLogger('discord')
logger.setLevel(logging.INFO)
handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
formatter = logging.Formatter('[{asctime}] [{levelname:<8}] {name}: {message}', '%Y-%m-%d %H:%M:%S', style='{')
handler.setFormatter(formatter)
logger.addHandler(handler)
self.logger = logger
+66 -53
View File
@@ -1,84 +1,97 @@
from discord.ext import commands
import discord
import codecs
from os import listdir
from time import time
from codecs import open
import discord
from discord.ext import commands
import psycopg2
import yaml
from os import listdir
import locale
from time import time
import psycopg2
from logger import BotLogger
locale.setlocale(locale.LC_ALL, "")
with open("./src/config/config.yaml", "r", encoding="utf8") as f:
# Load config file
with codecs.open('./src/config/config.yaml', 'r', encoding='utf8') as f:
config = yaml.load(f, Loader=yaml.SafeLoader)
intents = discord.Intents.all()
class Bot(commands.Bot):
"""UiO Gaming Bot"""
def __init__(self):
super().__init__(
command_prefix=commands.when_mentioned_or(config["bot"]["prefix"]), case_insensitive=True, intents=intents
command_prefix=commands.when_mentioned_or(config['bot']['prefix']),
case_insensitive=True,
intents=discord.Intents.all()
)
self.prefix = config["bot"]["prefix"]
self.presence = config["bot"].get("presence", {})
self.logger = BotLogger().logger # Initialize logger
self.api_keys = config.get("api", {})
db = config.get("database", {})
# Connect to database
db = config.get('database', {})
self.db_connection = psycopg2.connect(
host=db["host"],
dbname=db["dbname"],
user=db["username"],
password=db["password"],
host=db['host'],
dbname=db['dbname'],
user=db['username'],
password=db['password'],
)
self.emoji = config.get("emoji", {})
self.misc = config.get("misc", {})
# Fetch misc config values
self.mc_rcon_password = config['minecraft']['rcon_password']
self.presence = config['bot'].get('presence', {})
self.api_keys = config.get('api', {})
self.emoji = config.get('emoji', {})
self.misc = config.get('misc', {})
self.config_mode = config.get('mode')
async def setup_hook(self):
# Load cogs
for file in listdir('./src/cogs'):
if file.endswith('.py'):
name = file[:-3]
await bot.load_extension(f'cogs.{name}')
# Sync slash commands to Discord
if self.config_mode == 'prod':
await self.tree.sync()
else:
self.tree.copy_global_to(guild=discord.Object(id=412646636771344395))
await self.tree.sync(guild=discord.Object(id=412646636771344395))
# Create bot instance
bot = Bot()
activities = {"playing": 0, "listening": 2, "watching": 3}
if bot.presence["activity"].lower() in activities:
activity_type = activities[bot.presence["activity"].lower()]
else:
activity_type = 0
status_types = {
"online": discord.Status.online,
"dnd": discord.Status.dnd,
"idle": discord.Status.idle,
"offline": discord.Status.offline,
}
if bot.presence["type"].lower() in status_types:
status_type = status_types[bot.presence["type"].lower()]
else:
status_type = discord.Status.online
@bot.event
async def on_ready():
if not hasattr(bot, "uptime"):
if not hasattr(bot, 'uptime'):
bot.uptime = time()
for file in listdir("./src/cogs"):
if file.endswith(".py"):
name = file[:-3]
bot.load_extension(f"cogs.{name}")
# Print bot info
print(f'Username: {bot.user.name}')
print(f'ID: {bot.user.id}')
print(f'Version: {discord.__version__}')
print('.' * 50 + '\n')
# Set initial presence
# Presence status
status_types = {
'online': discord.Status.online,
'dnd': discord.Status.dnd,
'idle': discord.Status.idle,
'offline': discord.Status.offline,
}
status_type = status_types.get(bot.presence['type'].lower(), discord.Status.online)
# Presence actitivity
activities = {'playing': 0, 'listening': 2, 'watching': 3}
activity_type = activities.get(bot.presence['activity'].lower(), 0)
print(f"Username: {bot.user.name}")
print(f"ID: {bot.user.id}")
print(f"Version: {discord.__version__}")
print("." * 50 + "\n")
await bot.change_presence(
activity=discord.Activity(type=activity_type, name=bot.presence["message"]), status=status_type
activity=discord.Activity(type=activity_type, name=bot.presence['message']),
status=status_type
)
bot.run(config["bot"]["token"], bot=True, reconnect=True)
bot.run(config['bot']['token'], reconnect=True, log_handler=None)